Files
lixi 95976ae2c3
CI / docs-build (push) Failing after 1s
docs: M3.5 体验补齐——任务拆解与第一批客户端修复报告入档
- 01 任务拆解:用户实测 6 项反馈的分类与处置;**数据模型审计推翻迁移预估**
  (nickname/avatar_asset_id 列 V1/V3 早已存在、purpose 白名单是配置项)→ 本批零迁移
- 02 第一批已交付:中文本地化 + 日期录入收口 + 花费卡月份(flutter 502→526)
- 6 项待拍板(首页 demo 裁剪范围、获赞端点形态、头像写权限档等)

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

143 lines
12 KiB
Markdown
Raw Permalink 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.
# 01 M3.5 任务拆解:体验补齐
**迭代定位**:M3 收官(v0.3.0 发布)后,用户桌面实测反馈 6 项问题的补齐迭代。规模远小于 M1~M3,不做 8 角色开工分析。
**执行人**:主会话(PM agent 两次卡死未落盘,实情由主会话独立审计取得)
**日期**2026-09-10
---
## 0. 缘起与范围
用户 2026-09-10 在 Linux 桌面实测 M3 成果,反馈 6 项问题。分类后:
| 用户反馈 | 定性 | 归属 |
| --- | --- | --- |
| ① 日历英文 | 缺陷(从未配本地化) | **第一批已修**M3.5-01 |
| ② 月份只能 `< >` 切 | 可用性缺陷,已实际致误录 | **第一批已修**M3.5-02 |
| ⑤ 本月花费 ¥0 | **不是 bug**:记录在 2026-04-09、当天 2026-09-109 月确实为 0;根因是 ② | **第一批已修** UI 可自查性(M3.5-03 |
| ③ 宠物无头像上传 | ADR-010 剪出项,M3 媒体链路已就位 | 本批 |
| ④ 资料页无法改昵称/头像、显示 demo | 代码层缺口 | 本批 |
| ⑥ 资料页关注/获赞/作品是假数据 | 同 ④(同一 demo 页) | 本批 |
| (未提)首页顶部 demo | 混合性质,须裁剪 | 本批部分 |
**本批范围一句话**:把「用户资料(昵称+头像)与宠物头像」从 demo 打通为真实读写,资料页数据真实化,并裁剪首页 demo 中属本迭代的部分。
---
## 1. 数据模型实情审计(**推翻了原先的迁移预估**)
开工前一度以为需要 Flyway V6 加列。**逐一核实原始 SQL 与代码后确认:所需列全部早已存在,本批零迁移。**
| 需求 | 原预估 | **实情(已核实)** |
| --- | --- | --- |
| 用户昵称 | `identity.users` 无 nickname 列,需加 | **V1 第 63 行就有** `nickname varchar(32)`,含 `ck_users_nickname`btrim + 1~32 长度);`UserRepository.insertUser(id, username, nickname, phone)` 注册时已在写 |
| 用户头像 | 需加列 | **V1 第 67 行就有** `avatar_asset_id uuid`,含 `fk_users_avatar_asset`(→`media.assets` ON DELETE SET NULL)与索引 `ix_users_avatar_asset` |
| 宠物头像 | 需加列 | **V3 第 64 行就有** `avatar_asset_id uuid REFERENCES media.assets(id) ON DELETE SET NULL`,含索引 `ix_pets_avatar`。但 **pet 模块代码零处读写它**grep 无命中) |
| 获赞总数 | 需加冗余列 | `community.posts` 已有 `like_count/comment_count/bookmark_count` 冗余列(M3 写侧同事务维护),`SUM(like_count)` 即可 |
| 头像 media 用途 | 需加枚举/约束迁移 | `media.assets.purpose``varchar(32) NOT NULL` **无 CHECK 约束**;白名单在 **配置项** `MediaProperties.allowedPurposes = List.of("post_image")` —— 加新用途只改配置 + 契约枚举 |
**已就位可直接复用的能力**
- `/internal/users/profiles` 已返回 `PublicProfileResponse(userId, nickname, avatarAssetId)`**nickname→username 回退在 SQL 层完成**M3 T3-05 交付)。即 Feed 作者名一旦用户设了昵称即自动生效,无需改社区侧。
- media 两步上传(user :8082+ 客户端 `MediaUploader` 六态编排(M3 T3-03/T3-13)。
- `MediaAssetGateway` 的 asset 校验先例(ready + 属本人,M3 T3-04)。
- `GET /api/v1/me/posts`(含草稿)、`GET /api/v1/me/bookmarks``GET /api/v1/users/{id}/follow-stats` 均已实现。
**真实缺口只在代码层**`MeResponse` 只有 `(userId, username, phone, createdAt)` 缺昵称/头像;**无任何写接口**(无 `PATCH /me`);pets 不读写头像列;获赞总数无端点。
---
## 2. 工单拆解
编号自 **T3.5-04** 起(01~03 已由第一批客户端修复占用)。
### 第一波:后端与契约
#### T3.5-04 用户资料读写(user 模块)
- **仓库**patbond-apipatbond-user
- **描述**`GET /api/v1/me` 响应补 `nickname``avatarUrl`(预签名 GET,沿用 M3 私有桶签名读口径,URL 会过期不得持久化);新增 `PATCH /api/v1/me` 支持改 `nickname``avatarAssetId`(置 null 即清除头像)。校验:nickname 与 `ck_users_nickname` 对齐(btrim、1~32);avatarAssetId 必须 `status='ready'`、属当前用户、`purpose='user_avatar'`(复用 MediaAssetGateway 同类校验语义,错误码沿用 40405/42203)。`MediaProperties.allowedPurposes``user_avatar`
- **验收**:设昵称后 `/me``/internal/users/profiles` 双端一致;清空昵称回退 username(回退逻辑已在 SQL 层,勿重复实现);非 ready/非本人/错 purpose 的 asset 被拒;六类路径覆盖。
- **依赖**:无。**规模**M
#### T3.5-05 宠物头像读写(pet 模块)
- **仓库**patbond-apipatbond-pet
- **描述**`PATCH /api/v1/pets/{petId}` 支持 `avatarAssetId`(含置 null);宠物详情/列表/FeedCard 无关响应补 `avatarUrl`(预签名 GET,community 侧本地现签先例见 M3 T3-04)。asset 校验复用 `MediaAssetGateway`ready + 属本人)+ `purpose='pet_avatar'`。权限沿用 `PetAccessService`:改头像属 **WRITE 档**owner+caregiver)还是 **MANAGE 档**(仅 owner)——见待拍板 D3.5-3。`allowedPurposes``pet_avatar`
- **验收**:owner 设头像后详情返回可访问 URLviewer 改被拒;version 乐观锁沿用 40902;非法 asset 被拒。
- **依赖**:无(与 T3.5-04 可并行,但同仓需串行提交)。**规模**:S~M
#### T3.5-06 获赞总数聚合(community 模块)
- **仓库**patbond-apipatbond-community
- **描述**:提供当前用户的社区统计:获赞总数(`SUM(like_count)` over 本人未删帖)、作品数(已发布帖数)。端点形态见待拍板 D3.5-2(新增 `GET /api/v1/me/community-stats` vs 扩展既有 follow-stats)。**不引入新冗余列**——写侧维护成本高于读侧聚合收益,且量级远未到瓶颈。
- **验收**:草稿/软删帖不计入;空数据返回 0 而非 null;口径写入契约描述。
- **依赖**:无。**规模**S
#### T3.5-07 契约冻结 v1.4.0
- **仓库**patbond-docopenapi.yaml+ patbond-api(快照同步)
- **描述**:沿用 M2/M3 迭代式冻结:草案(TODO-FREEZE 标注待定型点)→ 随 04/05/06 实现定型回填 → 拍板 → 合入 **v1.4.0****四模块字节级快照同步**pet/auth/community/user 的 `src/test/resources/contract/`)→ 契约矩阵扩展新操作全响应格。
- **验收**`mkdocs build --strict` 通过;契约矩阵零漂移;升版后 CI 绿(**漏快照同步必红**,见 iteration-3/19)。
- **依赖**T3.5-04/05/06 定型。**本波闸门,不冻结不放行第二波前端。规模**:M
### 第二波:客户端
#### T3.5-08 资料页真实化 + 编辑页
- **仓库**patbond-flutter
- **描述**`lib/features/profile/profile_page.dart`(现 176 行全硬编码 demo:「萌宠新手(豆豆家长)」/24/1.8k/2)替换为真实数据——昵称(空则 username)、头像、获赞、作品数、关注数;新增编辑页(昵称输入 + 头像上传复用 `MediaUploader`,复用 M3 的 gating/失败语义);四态齐备。
- **验收**:登录 `llx` 显示 `llx` 而非 demo 文案;改昵称后 Feed 作者名同步(同一后端回退链);头像上传全链路;四态有 widget 测试。
- **依赖**T3.5-07 冻结。**规模**L
#### T3.5-09 宠物头像上传接线
- **仓库**patbond-flutter
- **描述**:宠物详情页头像的铅笔角标(**当前已渲染但无功能**)接 `MediaUploader`;列表/详情展示真实头像(`SignedNetworkImage` 复用,缓存 key 剥签名参数已就位);无头像回退现有爪印占位。
- **验收**:上传后详情与列表同步显示;viewer 不显示编辑入口;失败态可重试。
- **依赖**T3.5-07 冻结。**规模**M
#### T3.5-10 首页 demo 裁剪
- **仓库**patbond-flutter
- **描述**:首页顶部 demo 分项处置——问候语「下午好,豆豆」改真实昵称(**本批做**);天气/位置(接外部服务,**建议出本批**);圈子「柴犬圈/猫咪圈/救助站」(实为话题,ADR-018 已剪出,**建议出本批**);促销卡「新用户首单立减 ¥20」(属 M5 服务域,**建议出本批**)。见待拍板 D3.5-1。
- **验收**:按拍板结果,保留项标注为「刻意保留的占位」并在报告登记,避免下次实测重复反馈。
- **依赖**T3.5-04 定型(昵称字段)。**规模**:S~M
---
## 3. 波次与关键路径
```text
第一波: T3.5-04 / T3.5-05 / T3.5-06(同仓串行提交)→ [T3.5-07 契约冻结 v1.4.0 闸门]
第二波: T3.5-08(L) / T3.5-09 / T3.5-10
```
预计一到两波收,无 L 工单堆叠在关键路径(仅 T3.5-08 一个 L)。发布按 releases.md 的**新流程走 PR**(main 已受保护,首发的直推写法已作废)。
---
## 4. 待拍板清单
| # | 决策 | 选项与影响 | 建议 |
| --- | --- | --- | --- |
| D3.5-1 | **首页 demo 裁剪范围** | 天气/位置需接外部服务(和风天气类,含 key 管理与配额);圈子=话题(ADR-018 剪出);促销卡属 M5 服务域 | **仅做问候语真实化**,其余三项留待对应里程碑,并在代码注释与报告显式标注「刻意保留的 demo 占位」 |
| D3.5-2 | **获赞总数端点形态** | A. 新增 `GET /api/v1/me/community-stats`(语义清晰、可扩展);B. 扩展既有 `follow-stats`(少一个端点,但语义混杂——它现在是「某用户的关注数」,加"我的获赞"会变成两种主体) | **A** |
| D3.5-3 | **宠物头像的写权限档** | WRITEowner+caregiver 都能改)vs MANAGE(仅 owner | **WRITE**——头像属日常照护信息,与体重/疫苗同档;照护人本就能改这些 |
| D3.5-4 | **nickname 唯一性** | 现 DB 无唯一约束(仅长度/btrim CHECK)。允许重名(社区常见,靠 userId 区分)vs 加唯一约束(需迁移,破本批零迁移前提) | **允许重名**,不加约束 |
| D3.5-5 | **注册时是否让用户填昵称** | 现注册只收 username/phone/passwordnickname 走 `insertUser` 但值来源需确认)。加一个可选昵称输入 vs 保持不填、注册后到资料页设 | **保持不填**(少一步注册摩擦),资料页可设 |
| D3.5-6 | **版本号** | v0.3.1(补丁语义)vs v0.4.0(含新端点,属功能增量) | **v0.4.0**——加了端点与字段,不是纯修补 |
---
## 5. 风险清单
| # | 风险 | 缓解 |
| --- | --- | --- |
| R1 | **契约升版漏同步快照** → 四模块守卫测试全红 | 冻结与快照同步放**同一工单**(T3.5-07)连贯执行,iteration-3/19 已有可照抄的操作序列 |
| R2 | 头像 URL 是**会过期的预签名 GET**,客户端若持久化会出现"图突然裂" | 沿用 M3 纪律:URL 不入本地存储;`SignedNetworkImage` 缓存 key 已剥签名参数 |
| R3 | 同仓(patbond-api)三个后端工单并行会抢工作树 | 同仓串行派工(M2/M3 已验证的模式) |
| R4 | 首页 demo 裁剪范围失控,滑向"顺手把首页重做一遍" | D3.5-1 钉死范围;保留项显式登记,避免反复 |
| R5 | 头像功能上线后,真机验证清单需新增项(弱网上传头像、头像缓存) | 收口时按维护约定登记进 `development/device-verification.md` |
---
## 6. 与既有纪律的衔接
- **零迁移**:本批不新增 Flyway 版本(下一个版本号仍为 V6,留给后续真正需要建表的迭代)
- **契约先行**:T3.5-07 冻结前,前端不得依赖未冻结字段
- **分支保护**:日常推 dev;发布走 PR(releases.md「发布后生效的纪律」)
- **路径参数化**:文档内命令一律 `cd <你的工作区>/<仓名>`