| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- """Reuse archived details whose interface version is proved; never guess a version."""
- import argparse
- import json
- from pathlib import Path
- import sys
- ROOT = Path(__file__).resolve().parents[1]
- sys.path.insert(0, str(ROOT / 'server/voc-library'))
- from monitoring_details import import_detail
- from monitoring_store import need, sha
- def run(store, generation, out):
- base = ROOT / 'work/voc-shop-chain-audit-20261106-r1/received-r2'
- manifest = {}
- for line in (base / 'FILES.sha256').read_text('utf-8-sig').splitlines():
- digest, name = line.split(' ', 1)
- manifest[name] = digest
- coverage_raw = (base / 'pages/coverage.json').read_bytes()
- need(sha(coverage_raw) == manifest['pages/coverage.json'], 'ARCHIVED_COVERAGE_SHA_MISMATCH')
- td2 = json.loads(coverage_raw)['items'][4]
- need(td2['api'] == 'taobao/get-item-detail/v6', 'ARCHIVED_ENDPOINT_UNPROVEN')
- need(td2['sha256'] == manifest['originals/td2.json'], 'ARCHIVED_ENDPOINT_RAW_MISMATCH')
- sources = [(base / 'originals' / name, manifest['originals/' + name], family, '', endpoint)
- for name, family, endpoint in [('d1.json', 'jd', 'jd.get_item_detail_v1'),
- ('td2.json', 'taobao', 'taobao.get_item_detail_v6')]]
- sources.append((ROOT / 'work/voc-product-restoration-20261106-r1/original-brand-detail.json',
- 'e65742376ef5bd66eb1d14f907adcf6a1a88779cd472f5e8d80dc744e095c470', 'jd', '/result', 'jd.get_item_detail_v1'))
- need(not out.exists(), 'DETAIL_BOOTSTRAP_RECEIPT_ALREADY_EXISTS')
- results = []
- for path, expected, family, envelope, endpoint in sources:
- raw = path.read_bytes()
- need(sha(raw) == expected, 'ARCHIVED_DETAIL_SHA_MISMATCH')
- doc = json.loads(raw)
- response = doc['result'] if envelope else doc
- body = response['data']
- root = envelope + '/data/data/'
- item = root + ('product' if family == 'jd' else 'item')
- product = body['requestParams']['query']['itemId']
- fields = ([{'kind': 'selling_points', 'pointer': item + '/sellPoint', 'format': 'text'},
- {'kind': 'description', 'pointer': item + '/description', 'format': 'html'}] if family == 'jd' else
- [{'kind': 'description', 'pointer': item + '/desc', 'format': 'html'},
- {'kind': 'attributes', 'pointer': item + '/attribute', 'format': 'array'},
- {'kind': 'detail_images', 'pointer': item + '/detailImages', 'format': 'array'}])
- source = {'schema': 'ksf.detail-evidence.v1', 'generation': generation, 'platformFamily': family,
- 'productId': family + ':' + product, 'endpointId': endpoint,
- 'requestId': body['requestId'], 'rawFile': path.name, 'rawSha256': expected,
- 'mapping': {'successChecks': [{'pointer': envelope + '/code', 'equals': 200},
- {'pointer': envelope + '/data/code', 'equals': 0}],
- 'requestProductPointer': envelope + '/data/requestParams/query/itemId',
- 'returnedProductPointer': item + ('/productId' if family == 'jd' else '/itemId'),
- 'requestIdPointer': envelope + '/data/requestId',
- 'titlePointer': item + ('/skuName' if family == 'jd' else '/title'),
- 'observedAtPointer': envelope + '/data/recordTime',
- 'shopIdPointer': item + '/shopId' if family == 'jd' else root + 'seller/shopId',
- 'fields': fields}}
- results.append(import_detail(store, source, path.parent, generation))
- receipt = {'ok': True, 'generation': generation, 'observations': len(results),
- 'newImports': sum(not r['alreadyRegistered'] for r in results), 'results': results,
- 'paidCalls': 0, 'researchWrites': 0, 'productionDeployed': False,
- 'skipped': [{'file': 'td1.json', 'reason': 'ENDPOINT_VERSION_NOT_PROVEN', 'rawRetained': True}],
- 'note': '京东2件卖点字段为空;淘宝td2为图片。td1接口版本未证实,不新导入。旧原件与旧记录保留,均无详情文字覆盖。'}
- out.parent.mkdir(parents=True, exist_ok=True)
- out.write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + '\n', 'utf-8')
- return receipt
- if __name__ == '__main__':
- p = argparse.ArgumentParser(description=__doc__)
- p.add_argument('--store', type=Path, required=True)
- p.add_argument('--generation', required=True)
- p.add_argument('--out', type=Path, required=True)
- args = p.parse_args()
- print(json.dumps(run(args.store, args.generation, args.out), ensure_ascii=False))
|