1cf12be904
Co-Authored-By: Claude Code <noreply@anthropic.com>
12 KiB
12 KiB
G4 验收记录:用户接口对接(uniservice /open/v1/users)
对应方案:2026-09-22-G4-用户接口对接-实施计划 执行日期:2026-09-22 外部契约:open-v1-users-api.md(服务方 uniservice,合塑/新友合集团统一后台)
0 基线与保护
- 基线 commit:f193e4f(fix(viewer): remove extra closing brace)。
- 工作树既有改动收编:起始时工作树存在 G4 半成品未提交改动 (docs/open-v1-users-api.md、http-user-source.ts 草稿、user-sync.service.ts 引擎 V1.1 行为变更、.env 两示例新增变量)——本任务全部收编重写/对齐后分批提交。
- 契约/schema 零改动:packages/contracts、db-cn/db-ov schema、main-ov.ts、 同步账本(outbox/sync_inbox/sync_state)均未触碰;引擎红线 1-4 逐条维持 (e2e ①-⑫ 全量断言仍守护)。
1 关键决策及依据
| 决策 | 依据 |
|---|---|
V1.1 初始凭据:同步新建用户直接 credentialStatus=active,账号=手机号,初始密码=手机号后 6 位(不足回退 '000000'),全局生效(含 mock 源) |
用户拍板(2026-09-22,真实接口上线即用);P4a「待激活+哨兵哈希」闭环退役,auth.service 等代价 dummy verify 防御保留兼容历史行;activate 端点对 active 用户幂等,reset-password 仍为管理员轮换出口 |
| HTTP 源每批全量拉取,不做增量 | 上游 API 无增量过滤参数(仅 page/page_size/company_id/status/keyword/ids);幂等由引擎既有行级 sourceVersion 比较保证(213 用户/200 每页≈2 页,成本可忽略) |
游标协议:续翻块编码 p:N;纯数值水位绝不解析为页码 |
半成品草稿的严重 bug:引擎增量批传入水位(epoch 秒,如 "1758520000")被 parseInt 当页号→请求第 17 亿页→空页"成功"空转。单测有专项回归用例 |
| 一人多档按 phone 全局(跨上游页)去重,保留 updated_at 最新行 | 草稿仅页内去重:同 phone 两行分落两页时后应用的旧行(如离职档)会覆盖新行状态。全量先拉齐再切块,消除跨页分裂 |
external_user_mappings.sourceSystem 随源:mock='mock'、http='uniservice' |
UserSourcePort 新增只读 sourceSystem;映射锚点 (sourceSystem, externalUserId) 命名空间按源隔离,既有 mock: 映射自然成为孤儿(预期,见 §5) |
| 模块装配 env 开关 `USER_SYNC_SOURCE=mock | http`,缺省 mock |
2 改动清单(提交序列)
| commit | 内容 |
|---|---|
42878f1 |
docs(g4):接口文档 + 实施计划入库 |
b7754d5 |
test(api):e2e ①⑤⑪ + 文件头按 V1.1 重写(新增"同步即可登录"断言,强度不低于旧哨兵断言) |
52cd316 |
refactor(api):端口 sourceSystem + 引擎收编(initialPassword helper、删哨兵死代码、注释 V1.1 化、auth.service 注释历史化) |
d732caa |
feat(api):HttpUserSource 适配器(全量拉取+全局去重+分块续翻缓存+页数护栏)+ 5 单测(fetch mock) |
8c255eb |
feat(api):USER_SYNC_SOURCE 开关装配 + .env.example / .env.production.example-cn |
| (本提交) | docs(g4):本验收记录 |
文件:user-source.port.ts、mock-user-source.ts、user-sync.service.ts、http-user-source.ts(重写)、user-sync.module.ts、auth.service.ts(仅注释)、test/user-sync.e2e-spec.ts、test/http-user-source.spec.ts(新增)、.env 两示例。
3 测试证据
pnpm --filter @photo/api-cn typecheck:0 error。- 单测全量:
pnpm --filter @photo/api-cn test→ 8 套件 / 56 用例全绿(含 http-user-source.spec 5 用例:跨页去重/水位不当页码回归/分块续翻缓存/401·500·success=false·env 缺失/无手机号跳过)。 - user-sync e2e:
test:e2e -- user-sync.e2e-spec→ 12/12 全绿(红线逐条断言维持;⑪ 新语义:初始密码登录 200 → activate 幂等 → reset-password 轮换后初始密码 401)。 - 全量 e2e:
test:e2e→ 22/24 套件通过(249/257 用例),2 个失败均与本分支无关(见 §5 残留,均已取证)。
环境事件登记(复现性说明)
首次全量 e2e 出现 8 套件失败,排查定位为本地游离 pnpm dev:worker 进程(PID 56112)抢消费 e2e 照片处理队列(测试自行直调 PublishService,worker 抢先将照片置 ready → processPhoto 返回 'already')。经用户确认终止该进程后复跑:uploads/watermark/batch-ops/full-chain/likes-realtime/public-ov 全部恢复绿。本地跑全量 e2e 前须确认无游离 worker。
4 部署注意(P5 线)
- 生产/测试服务器 cn 侧 env 三变量:
USER_SYNC_SOURCE=http+OPEN_API_USERS_URL=https://uniservice.server.hnxinyouhe.com/open/v1/users+OPEN_API_USERS_KEY=<服务方下发>(key 仅服务端持有,勿入前端/仓库)。 - 本地开发 .env 切勿设
USER_SYNC_SOURCE=http——模块 import 时读 env,会导致 user-sync e2e 的app.get(MockUserSource)解析失败。 - key 未配置/错误的表现:每批 sync failed(批次行 error 落"鉴权失败/HTTP 401"), 不影响任何既有用户/会话——引擎批次级故障隔离语义。
- 测试服务器(219.157.255.213)换真实 key 后建议手动触发一批 (POST /v1/admin/user-sync/runs)并核对 status/runs/errors 与 users 新增 (账号=手机号、可手机号后 6 位登录)——属 P5 待办。
4.5 真实联调证据(2026-09-22,用户提供正式 key 后执行)
- 连通性:
GET /open/v1/users(X-Api-Key)200,返回真实档案(合塑/新友合)。 - 首批(临时脚本走 AppCnModule 真实装配 +
engine.tick(),env 仅命令行注入不落盘): state=success,13s,added=230 / updated=0 / disabled=0 / failed=0;external_user_mappings(sourceSystem='uniservice')230 条;水位 1789973885(epoch 秒)。 - 幂等:紧接第二批 added=0 / updated=0 / failed=0,水位不动——行级版本比较按设计工作。
- V1.1 凭据抽查:在职用户(手机号账号)
verifyPassword(手机号后 6 位)=true、 错误口令=false;用户分布 110 active / 104 disabled(status=all 含离职停用,预期)。 - 格式登记:上游
updated_at为YYYY-MM-DD HH:mm:ss(非 ISO-T),V8 按本地时区 解析——仅作单调版本号用途,语义无影响。 - 临时脚本已删除(不入库);key 未写入任何文件/仓库。
4.6 测试服务器部署(2026-09-22 当日完成)
- 镜像:
photo-live-server:3a8a1cf-20260922(git archive HEAD 干净构建,排除工作区 未提交 viewer 改动;216MB gz);cn/ov 两栈 server 容器均滚动至该 tag,web 保持 各自当前 tag(cn=ui02-20260922 / ov=viewer-fix)。无新增 DB 迁移,跳过 migrate。 - env:
.env.production-cn追加USER_SYNC_SOURCE=http+ URL + KEY(chmod 600, key 未入库)。 - 结果:manual 批 14s added=230 / failed=0,水位 1790002685;随后新定时器 cron 批
added=0(服务器侧幂等验证 ✓);分布 110 active / 104 disabled,与本地一致;
uniservice映射 230。内部 health 双栈{"status":"ok"}。 - 运维经验登记:①api 容器重建后 web 容器需一并 restart(nginx 缓存旧上游容器
IP → 502);②
.env.production-cn的 ADMIN_PASSWORD 与库中不符(后台改过密码), 定时同步不受影响,管理端点手动触发请用后台实际密码;③服务器库名为 framevia_cn/ov(非部署文档示例的 photo_cn/ov),MySQL 8.4trigger/cursor为保留字,手工 SQL 需反引号。
4.7 用户列表分页与同步档案展示(2026-09-22 同日迭代上线)
- 范围:管理后台用户列表改标准页码分页(
page/pageSize偏移模式 +total,游标 模式兼容保留);同步档案(工号/邮箱/公司/头像)落external_user_mappings.profile(新 JSON 列迁移 20260922174000)经AdminUser.synced展示;幂等跳过路径的展示 补偿写解决存量 230 用户回填(版本门跳过但快照缺失 → 仅刷映射行)。提交f6ff1d4/2f6b6af/ eebf3b3;服务器镜像eebf3b3-20260922+ cn webadmin-eebf3b3-20260922已部署,迁移已应用。 - 同步间隔调整(用户指令):生产
SYNC_USERS_INTERVAL_MS=43200000(每 12 小时 一批,原 5 分钟);服务器 env 已设并重建生效(日志核实)。缺省 5 分钟保留为 开发/演示节奏。注意:12h 间隔下容器重启后首批要等 12h——存量回填/即时同步 用后台「手动同步」按钮触发。 - uniservice 服务端不可达事件(进行中):部署后定时批次 fetch failed;测试服务 器/开发机两处独立网络均超时,DNS 正常、其余站点可达 → 服务方侧故障(09:10 尚 正常)。批次隔离语义正确:失败批不动任何用户。恢复后手动触发一批即完成 230 用户档案回填(工号/邮箱/公司/头像列即有数据)。
- 既有登记沿用:UI-04 线 embed e2e/
embedSlots.spec.tsdisplayMode 类型漂移为 线外残留(本迭代观察到 admin typecheck 同源 3 错,非本域文件)。
4.8 构建环境事件与分页档位调优(2026-09-22 晚)
- 分页档位调优(用户手改 + 单测同步):默认每页 15、档位 [15,20,50,100](80a7658)。
- 事件 A:本机 Docker Hub 不可达(TLS 握手超时持续 20+ 分钟,同日早些时候正常)。 应对:构建挪到测试服务器(其 Docker 配有国内加速器 docker.1ms.run/1panel.live, 基础镜像可拉):git archive HEAD 解包 scp → 服务器 docker build → 直接 up(免镜像 传输)。此路径已验证可用,Hub 再抽风时的标准预案。
- 事件 B:服务器新拉的
nginx:alpine(当日最新 digest)entrypoint 卡死—— 10-listen-on-ipv6-by-default.sh 在该服务器挂起(本地构建缓存旧 digest 无此问题; nginx 配置本身 nginx -t 通过)。临时补丁:构建期RUN rm -f /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh(服务器侧 web-patched.Dockerfile,产物即线上 admin-80a7658-20260922,已 healthy)。 待办:仓库 Dockerfile 的FROM nginx:alpine钉具体版本(待本机 Hub 恢复统一 处理),并把补丁行正式化(或钉版本后确认无需补丁)。 - 中途回滚一次(admin-eebf3b3 web)恢复服务后再上补丁版——停机窗口约 4 分钟。
5 残留与登记偏差
- embed.e2e-spec 失败(既有,非本分支):API settings 响应含 UI-04 线新增
displayMode字段而该 spec 期望对象未更新——基线f193e4f上复现同样失败 (取证:checkout 基线单跑 exit 1)。归属 UI-04 线待修。 - health.e2e-spec 全量长跑偶发(非本分支):两次全量中出现一次,单独跑恒 6/6 绿;长跑资源竞争瞬时问题,登记观察。
- admin UI
sourceVersion展示从 mock 自增版本(1,2,3…)变为 epoch 秒 (如 1758520000)——纯展示差异,契约仍 int64。 - 无 phone 上游行被跳过(warn 日志)——V1.1 账号=手机号下无法登录故不同步; 数量可观时人工复核。
RECONCILE_EVERY_N_RUNS周期对账对 http 源语义弱化(每批本就全量)—— 引擎机制保留不动,无害。- 飞书登录对接(union_id/open_id 议题,接口文档 §5)未做——后续可选线, YAGNI登记。