Files
patbond-doc/docs/development/iterations/iteration-3.5/06-wave2-closure.md
T
lixi 38d9e97174
CI / docs-build (push) Successful in 2m58s
docs: M3.5 收口报告 + 暴露面清单补全(文档站/处置记录/凭证纪律)
- 06 M3.5 收口:6 项用户反馈处置结果、迁移预估被审计推翻的教训、
  关键语义定型(/me 不回退、PATCH 三态、头像权限按字段定档、昵称按码点计)、
  实现期 4 项发现、契约 v1.4.0 纯增量承诺、遗留 5 项
- server-exposure.md 补:patbond-doc 文档站(漏记)、2026-09-11 处置记录、
  纪律 6「凭证不进任何可留存介质」与纪律 7「配置备份不留配置目录」

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-11 18:05:12 +08:00

80 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 06 M3.5 收口:体验补齐
**执行日期**2026-09-10 ~ 2026-09-11
**交付形态**:客户端体验修复 + 用户资料与头像全链路 + 契约冻结 v1.4.0;另含一次安全事件的处置与固化
---
## 0. 概要
M3.5 由用户在 v0.3.0 发布后的桌面实测反馈驱动(6 项问题),分两批交付。
| 批次 | 工单 | 提交 | 测试 |
| --- | --- | --- | --- |
| 第一批(纯客户端) | M3.5-01 中文本地化 / -02 日期录入收口 / -03 花费卡月份 | flutter `6038901``7d5c84d` | 502→**526** |
| 第一波(后端) | T3.5-04 用户资料读写 / -05 宠物头像 / -06 获赞聚合 | api `a5634c5``d98a400` | 334→**379** |
| 闸门 | T3.5-07 契约冻结 **v1.4.0** + 四模块快照同步 + 矩阵扩展 | doc `5f02909` / api `3cd8005` | 矩阵 173→**181** 格 |
| 第二波(前端) | T3.5-08 资料页+编辑页 / -09 宠物头像接线 / -10 首页问候语 | flutter `a4a97c0``6945436` | 526→**597** |
**波末状态**patbond-api **379** 测试、patbond-flutter **597** 测试全绿;契约 v1.4.032 路径/45 操作/75 schema);**零 Flyway 迁移**ADR-022)。
## 1. 用户 6 项反馈的处置结果
| 反馈 | 处置 |
| --- | --- |
| ① 日历英文 | ✅ 补 `flutter_localizations` + zh-CN 三件套(此前从未配置,Flutter 静默回退英文);`datePickerTheme` 上品牌色,只复用已审计色对 |
| ② 月份只能 `< >` 切 | ✅ 抽 `pickAppDate` 收口 7 处裸调用;保留手输铅笔 + 表单行「今天」快捷(原生 `showDatePicker` 无法注入弹窗内动作,放表单行反而一键落值、绕开月份导航) |
| ③ 宠物无头像 | ✅ 铅笔角标接 `MediaUploader``purpose=pet_avatar`),列表/详情展示预签名头像 |
| ④ 资料页无法改昵称/头像、显示 demo | ✅ 176 行硬编码 demo 退役;新增编辑页(昵称 + 头像 + 各自显式清除入口) |
| ⑤ 本月花费 ¥0 | ✅ **非 bug**——记录在 2026-04-09、当天 09-10,9 月确为 0;根因是 ②。改为显示实际月份(「9 月花费」)+ 四张数据卡补可点提示(原本都可点却无提示) |
| ⑥ 资料页统计是假数据 | ✅ 关注/我关注/获赞/作品四个数字全部真实(`/me` + `/me/community-stats` + `follow-stats` |
| (未提)首页 demo | ✅ 仅问候语真实化(ADR-022);天气/位置/圈子/促销卡刻意保留并在代码标注去向,另有 widget 用例反向钉住「保留项仍在」 |
## 2. 开工审计推翻了迁移预估(本迭代最大的省事项)
原估「需 Flyway V6 加列、规模 L」。逐一核实原始 SQL 后确认**所需列全部早已存在**:
- `identity.users.nickname`V1 第 63 行,含 btrim + 1~32 CHECK)、`avatar_asset_id`(V1 第 67 行,含 FK + 索引)
- `pet_health.pets.avatar_asset_id`(V3 第 64 行,含索引)——但 pet 模块代码此前**零处读写**
- `community.posts.like_count` 等冗余列(V5)——获赞总数 `SUM` 即可
- `media.assets.purpose` 无 CHECK 约束,白名单在**配置项** `MediaProperties.allowedPurposes` → 加 `user_avatar`/`pet_avatar` 只改配置
教训:**转述不可采信**。「`identity.users` 无 nickname」来自 M3 T3-05 报告的一句表述,实际那句说的是「契约未暴露 nickname」。核实原始 SQL 只花几分钟,却把工作量预估降了一档。
## 3. 关键语义定型
- **`/me` 不做 username 回退**(返回 DB 原值):回退是展示约定,若放进本人编辑态,编辑页会预填 `llx`,一保存就把它固化成真昵称,`/internal` 的回退链从此永不触发。**回退只在客户端展示层做一层**(`nickname ?? username`)。
- **PATCH 三态**(键缺省=不改 / 显式 null=清空 / 给值=设置):客户端以 `PatchField<T>` 类型承载,序列化单一路径。若把未改字段也发成 null,用户只改昵称就会连头像一起被清掉。纯空白昵称为 400 而非隐式清空;空 patch 前置短路。
- **宠物头像权限按「本次碰了哪些字段」定档**:仅头像=WRITEowner+caregiver),碰任一资料字段=MANAGE(仅 owner),混合取更严——堵住 caregiver 把改名夹带进头像请求。客户端纯头像 PATCH 只带 `version` + `avatarAssetId`(测试断言 `keys.length == 2`)。
- **`avatarAssetId` 只写不读**:响应不外露,「有头像」等价 `avatarUrl != null`
- **昵称长度按码点计**`runes.length`):32 个 emoji 的合法昵称 UTF-16 长度为 64,按 `String.length` 会误拒数据库存得下的昵称。
## 4. 实现期发现与修正
1. **`/api/v1/me` 错线(先于本迭代存在)**:该端点由 user 服务(:8082) 提供,但 `ApiAuthRepository` 只挂了 auth(:8081),实测 404。此前无人消费 `me()` 故一直未暴露;已加 `userApi` 线路。
2. **Feed 作者名 ≤60s 滞后是服务端设计**`AuthorProfileGateway` 有 60s TTL 进程内缓存;`/me` 无缓存立即生效,故存在一分钟「资料页已变、Feed 还是旧名」的窗口。未改服务端,已写入注释与真机清单。
3. **1×1 极小 PNG 能上传能下载但 Flutter 解码器拒绝**`Codec failed to produce an image`),会被误判成「头像没传上」;夹具改 16×16。
4. 中文「9月10日周四」在日期弹窗头部 26px 起折行 → `headerHeadlineStyle` 32→22(只有真跑起来才看得见)。
## 5. 契约冻结 v1.4.0
- 规模:路径 31→**32**、操作 43→**45**、schema 72→**75**(多出的 `CommunityStatsEnvelope` 为保持「每个 200 响应都 `$ref` 一个 Envelope」的一致性)
- **对 v1.3.0 纯增量**:无字段删改、无类型变更、无必填收紧,已写入 `info.description` 作为对既有集成方的承诺
- 零新增错误码;四模块快照 md5 与正典一致;11 格「未声明字段即漂移」的红全部转绿;mutation 三处定向注毒自证有效
## 6. 安全事件(并行处置,已闭环)
本迭代期间 CI 全面失效,根因为 Gitea 被注入 gitconfig 的 `packObjectsHook`。完整复盘见 [07 号](07-security-incident-20260911.md),处置与纪律固化见新建的 [服务器暴露面清单](../../server-exposure.md)。要点:攻击未达成代码执行、三仓代码经核对未被篡改、无系统层入侵;服务器侧新增「决策与环境同步」等 7 条纪律。
## 7. 遗留
1. **「我的收藏与草稿」列表页未做**(工单许可的裁剪):后端与仓库层均已就位,缺两个页面 + 导航,建议独立小工单(S~M,可照抄既有 `CursorPage` 四态列表骨架)
2. 月份网格选择器未做(年份网格 + 手输 + 「今天」已覆盖实测痛点)
3. `SegmentedButton` 选中态仍是 `fromSeed` 派生粉底(M2 遗留主题债),建议并入后续主题收敛单
4. `widthPx/heightPx` 恒 null(M3 观察项,单图帖回落 4:3)、`eventVersion` 口径未定型——两项均未在本迭代处理
5. 真机验证:`device-verification.md` 新增 M3.5 节(头像上传弱网、头像缓存等 4 项 + 1 备注)
## 8. 待发布
v0.4.0 尚未发布。**main 已受分支保护,须走 PR 流程**(见 [发布记录](../../releases.md)「发布后生效的纪律」):三仓 CI 绿 + E2E 双份回归 → Gitea 建 PRdev→main)→ CI 状态检查转绿 → 合并 → 打 tag → 登记发布记录。