diff --git a/docs/MERGE-COMPATIBILITY-2026-11-04.md b/docs/MERGE-COMPATIBILITY-2026-11-04.md new file mode 100644 index 0000000..2390bcc --- /dev/null +++ b/docs/MERGE-COMPATIBILITY-2026-11-04.md @@ -0,0 +1,39 @@ +# 淘宝适配合入主分支的兼容性验证 + +日期:2026-11-04。主分支及 origin/main 基线为 381da60,淘宝适配提交为 774376f。 + +## 兼容边界 + +- H5 的 index.html、js/app.js、js/accounts.js、js/skin-score.js、js/report-ui.js 与 CSS 相对主分支没有改动。淘宝专用裁切、倒计时、商品链接与页眉位于 taobao/,未替换 H5 版本。 +- 共享 accounts.js 仅新增临时淘宝游客 Bearer 会话;微信 Cookie/OAuth 及用户报告接口保留。淘宝登录与游客入口仍校验网关,游客不持久化账号和历史报告。 +- 后台汇总按数据来源区分同 ID 记录,外部淘宝数据只读;实际检测及身份写入仍使用各实例自己的数据目录。 +- 本次发现并修正本地后台 URL 的兼容差异:local 数据的详情、下载、重试、照片链接不追加 source=local,沿用旧路径;外部淘宝来源保留 source=taobao。 + +## 已通过验证 + +- node --test --test-reporter=dot taobao/test/*.test.cjs rednote/test/*.test.cjs +- node taobao/test/h5-compatibility.cjs:H5 游客隐私、HTTPS /state/ 下模拟微信 OAuth、同账号跨设备报告、不同账号隔离、Cookie 丢失及失败处理、注销和重新登录、旧报告不变、异步皱纹加载。 +- node rednote/test/browser.cjs:/state/ 和 /rednote/ 双实例隔离、浏览器身份恢复、历史报告、后台照片和原始 JSON 路径、独立注销;红书操作不改变原 H5 数据。 +- JUZI_ADMIN_BROWSER_QA=1 下 admin-channels、backend、rednote backend 测试:后台渠道筛选、详情、照片来源、导出、同 ID 隔离、外部只读保护、目录边界。 +- 本地链接修正后,重新运行 H5 report-persistence 浏览器测试及上列红书/后台检查,均通过。 +- node taobao/scripts/sync-shared.cjs --check:评分、报告文案和共享拍摄规则与 H5 一致。 +- git diff --check 通过。 + +测试使用隔离数据目录、模拟微信上游和检测结果,不写线上用户数据、不调用收费检测。 + +## 主分支已有的旧测试问题 + +对当前候选与 git archive main 导出的隔离基线执行同一组 31 个 h5/test/*.js 脚本,两者均为 27 通过、4 失败,失败集合和原因一致,无新增失败: + +- factual-copy.js:旧轮廓文案预期与当前主分支文案不同。 +- tone-dark-circle.js:旧预期 9.1,当前主分支计算值为 9.25。 +- scan-wait-timeout.js:抽取代码的 VM 测试上下文缺 savedAccount,导致测试未进入报告页。 +- report-copy-cases.js:需要通过 H5_COPY_CASE_FILE 提供仓库外的私有案例;当前未配置。 + +未为通过这些旧测试而改动 H5 业务文案或评分。原始运行日志保存在 temp/merge-compatibility-20261104/,不入库。 + +## 验证限制与合并方式 + +微信真实平台授权、淘宝真机相机/页眉/分享最终效果仍需发布前手机验收,本地模拟不等同实际平台验收。本次仅合并本地 Git,不部署服务器、不发布小程序、不推送远程。 + +合并前主分支备份:codex/main-before-taobao-20261104(381da60)。主分支可快进到适配分支,不需覆盖已有提交或改写历史。 diff --git a/h5/js/admin.js b/h5/js/admin.js index 8f58275..4862b97 100644 --- a/h5/js/admin.js +++ b/h5/js/admin.js @@ -54,7 +54,7 @@ function selectedChannelQuery(url) { var channel = $('#f-channel').value; return channel ? url + (url.indexOf('?') < 0 ? '?' : '&') + 'channel=' + encodeURIComponent(channel) : url; } -function sourceQuery(source) { return '?source=' + encodeURIComponent(source || 'local'); } +function sourceQuery(source) { return source && source !== 'local' ? '?source=' + encodeURIComponent(source) : ''; } function renderSources(data) { var sources = data.sources || []; $('#channel-summary').textContent = sources.length > 1 diff --git a/h5/server.js b/h5/server.js index 6fae365..714b354 100644 --- a/h5/server.js +++ b/h5/server.js @@ -581,7 +581,7 @@ function sendJson(res, status, obj) { function photoUrls(rec) { const urls = {}; const prefix = entry.rednote ? '/rednote' : ''; - const source = rec.admin_source ? '?source=' + encodeURIComponent(rec.admin_source) : ''; + const source = rec.admin_source && rec.admin_source !== 'local' ? '?source=' + encodeURIComponent(rec.admin_source) : ''; if (rec.img_front) urls.front = `${prefix}/api/admin/photos/${rec.record_id}/front${source}`; if (rec.img_left) urls.left = `${prefix}/api/admin/photos/${rec.record_id}/left${source}`; if (rec.img_right) urls.right = `${prefix}/api/admin/photos/${rec.record_id}/right${source}`; diff --git a/taobao/test/admin-channels.test.cjs b/taobao/test/admin-channels.test.cjs index fbce1de..5de1167 100644 --- a/taobao/test/admin-channels.test.cjs +++ b/taobao/test/admin-channels.test.cjs @@ -62,6 +62,7 @@ test('admin aggregates separate stores with channel filters, collision-safe deta const detail = await json('/api/admin/records/RCOLLIDE?source=' + source); assert.equal(detail.raw_json.marker, expected); assert.equal(detail.record.presentation.marker, expected); assert.equal(detail.readonly, source === 'taobao'); + assert.equal(new URL(detail.photos.front, base).search, source === 'local' ? '' : '?source=taobao', 'local photo URLs retain their legacy shape; external sources remain explicit'); assert.match(detail.raw_json.result.face_maps.sample, /base64 图层已省略/); assert.equal(await (await fetch(base + detail.photos.front)).text(), expected + '-photo'); assert.equal((await json('/api/admin/records/RCOLLIDE/raw?source=' + source)).marker, expected);