8a09001525
Co-Authored-By: Claude Code <noreply@anthropic.com>
7.8 KiB
7.8 KiB
P1a 验收记录
- 阶段:P1a 后端核心链路(计划
docs/plans/2026-09-17-P1a-后端核心链路.md,Task 1–26) - 日期:2026-09-18
- 基线 commits:
d973c71..HEAD(chore: init repository skeleton起,共 28 个提交,不含本记录提交) - 执行模式:子代理驱动 + 双审(每任务先写失败测试→实现→通过→提交;跨任务由子代理接力,主代理与复核代理双重审查)
- 回归门:
pnpm lint && pnpm typecheck && pnpm test && pnpm e2e && pnpm build全绿(数字见文末统计表) - 基础设施:photo-live MySQL/Redis/MinIO compose(
infra/),测试全部跑在真实容器上(MySQL TRUNCATE 隔离 + MinIO 前缀清理 + Redis/BullMQ 真队列)
A01 认证与会话(API 级)
- 证据:
services/api/test/auth.e2e-spec.ts14 用例全绿——正确登录(tokenHash=sha256、库内不存明文、audit auth.login)、错误密码/不存在账号同码 401(防枚举)、未激活(credentialStatus=pending)/停用(sourceStatus=disabled)/本地锁定(localLocked)正确密码也不放行、限速锁定与解锁窗口、会话滑动续期(旧令牌不轮换)、登出即撤销(refresh 401)、seed-admin 幂等(重跑不改写密码,seed.spec.ts)。 - 全链路佐证:
e2e.full-chain.e2e-spec.tsadmin/staffB 均走真实POST /auth/password登录。 - App 端(微信登录等)留 P3:P1a 仅密码会话体系。
A02 权限逐次校验(防中间人/越权)
- 证据:
services/api/test/permissions.spec.ts11 用例——canViewAlbum/canManageAlbum/canUploadToAlbum/canManagePhoto 对 owner/collaborator/admin/非成员/被移除成员的全组合,且每次调用直查 DB 不缓存。 - e2e 全程逐次校验:uploads.e2e(非成员/他册成员 404、被移除成员 renew/complete 403)、photos.e2e(协作者对他人上传 403)、full-chain(owner 移除 staffB 后,同一有效会话的 init → 404 ALBUM_NOT_FOUND、管理历史上传 → 404 PHOTO_NOT_FOUND,权限链即刻失效)。
A03 上传幂等与并发安全
- 证据:
services/api/test/uploads.e2e-spec.ts(init 幂等复用同 uploadId+photoId、同 clientTaskId 异参 409、同名文件不同 clientTaskId 两会话两照片不覆盖、complete 幂等不重复入队/不重复 audit、字节/类型不符 400 后会话可重试、staging 删后 re-complete 走 copyIfAbsent created:false)。 - full-chain 断言:同名
show.jpg两传(900px 真 JPEG 双 presign PUT 实写)→ complete 其一调两次 → photo_jobs 各恰 1 条 → Worker 发布后 outbox 全库恰 2 条 photo.published,无第三条;重复 processPhoto →already不再发自带事件。
A06 上传即公开(发布事务)
- 证据:
services/worker/test/publish.e2e-spec.ts7 用例——发布事务(变体对象 + photo_variants×2 + photos ready/published/entityVersion+1 + outbox payload 契约全集,单事务一致)、重复处理幂等、不可处理态跳过、sweepOnce 认领/僵尸重排、真实 BullMQ Worker happy 路径、损坏文件 3 次退避终败且 visibility 保持 unpublished(失败不公开)、pending_upload 跳过。 - full-chain:API complete 入队 → 真实 Worker 消费 → 双照片 ready+published、MinIO 变体对象(head 复核 jpeg 非零字节)、photo_jobs done(attempt=1)。
- Worker 进程冒烟(Task 21 关注点):
pnpm dev:worker实机启动,photo worker started(concurrency=2、sweep 30s)后首轮 sweepclaimed=0正常完成,无崩溃退出。
A07 公开读性能/稳定性(后端级)
- 证据:
services/api/test/public.e2e-spec.ts——35 张已发布照片跨页稳定(缺省 30 + 次页 5,并集恰 35 无重复;(publishedAt,id)键集游标从不回看已发射行)、同 publishedAt 批次 id DESC 决胜、hidden/deleted/unpublished 永不出现、无变体行的发布照片防御性跳过不炸公开页、签名 URL 可取回真实字节。压测级指标留 G1。
A13 凭证安全(设计级)
- presign 短期凭证:上传 PUT 与读取 GET 均为预签名 URL,TTL 由
SIGNED_URL_TTL_SECONDS=900(env)控制,会话过期滑动续期走 renew。 - 无长期客户端密钥:客户端仅持有会话 token(32B 随机、库存 sha256 哈希)。
- DB 不存签名 URL:库中只有对象键(objectKey),URL 每次请求现签(公开单图即契约规定的过期刷新入口)。
- OSS 生命周期核验(staging 过期清理等)随 G1/P5 落实。
HEIC 实测结论(D12 复核项)
- HEVC 压缩 HEIF:预编译 libvips 无 HEVC 编码器,编码用例自跳过(
pipeline.spec.ts捕获后 skip 并说明);iPhone 真机样本端到端验证留 G2。 - AV1 压缩 HEIF:走同一 libheif 解码路径与
heif格式桶,解码→JPEG 变体用例通过。
偏差与发现登记
| # | 事项 | 处理/结论 |
|---|---|---|
| 1 | quay.io 镜像 | MinIO 镜像改用 quay.io/minio/*(Docker Hub 官方仓库已删除);P2 ov 侧同样 |
| 2 | Prisma 校对 | migrate 产物与 schema 校对存在手工修正流程(生成后人工核对差异再提交) |
| 3 | pnpm -- 语法 |
透传参数给包脚本必须 pnpm <cmd> -- <args>,曾因缺 -- 踩坑,已记入执行注意 |
| 4 | presigner 不签 content-type | S3 presign PUT 不覆盖 Content-Type 头 → complete 时以 head().contentType 与申明值比对兜底(uploads.e2e 有专例) |
| 5 | P2002 matcher 存量 bug | isUniqueViolationOn 对复合索引匹配存在存量缺陷,已修复并回归 |
| 6 | maxWorkers/turbo concurrency 串行化 | api jest maxWorkers: 1 + turbo concurrency: 1:DB 型 spec 共库并行会互相 TRUNCATE 脏数据 |
| 7 | turbo e2e 并发竞态 | test:e2e 同样串行化(依赖同 MySQL/Redis/MinIO 实例),避免跨包 e2e 并发互扰 |
| 8 | collaborator 重新公开 403 | hidden→published 恢复 P1a 从严仅 owner/admin(待需求确认,文件头已登记;photos.e2e 覆盖该 403) |
| 9 | nest-pino → nestjs-pino | 包名勘误:正确包为 nestjs-pino(曾误引 nest-pino) |
| 10 | ESM 方案 (a) | @photo/contracts 为 ESM 包,运行时经 require(esm) 消费 dist(Node ≥ 22.12 前提;jest 侧 moduleNameMapper 映射 TS 源) |
| 11 | request-id UUID | 请求 id 采用 UUID(错误信封/日志/响应头三处一致,x-request-id) |
未尽事项 / 移交
- P1b:viewer 基础页;admin 最小页;宣传图/封面/Logo 上传链路(album_assets presign+确认,复用照片上传机制,R09 依赖);GET /albums/{albumId}/photos 管理照片列表(含未发布/隐藏态);用户账号查询接口(邀请协作者按 account 搜索);Playwright 冒烟。
- P2 关注:outbox 已落库但暂无消费者(副本/通知接入时按 eventId 去重、entityVersion 单调消费);公开端点无限速(点赞限流引入时统一评估公开面配额)。
- G1/P5:OSS 生命周期核验;G2:HEVC-HEIF 真机样本端到端验证。
测试统计汇总(2026-09-18 回归门实测)
| 包 | 单测 suites/tests | e2e suites/tests |
|---|---|---|
| @photo/contracts | 2 / 8 | – |
| @photo/db-cn | –(schema 校验随 migrate/生成流程) | – |
| @photo/storage | 1 / 6(真实 MinIO 集成) | – |
| @photo/api-cn | 6 / 46 | 9 / 120(auth 14、albums 20、members-categories 15、uploads 20、photos 14、public 17、health 4、db.semantics 6、full-chain 10) |
| @photo/worker | 1 / 8 | 1 / 7(publish) |
| 合计 | 10 / 68 | 10 / 127 |
- 附加门:lint 5/5 包、typecheck 8 任务、build 5 任务全绿;
pnpm dev:worker启动冒烟通过。 - 总测试数(单测 + e2e):195。