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

12 KiB
Raw Permalink Blame History

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_nicknamebtrim + 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.purposevarchar(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/bookmarksGET /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 响应补 nicknameavatarUrl(预签名 GET,沿用 M3 私有桶签名读口径,URL 会过期不得持久化);新增 PATCH /api/v1/me 支持改 nicknameavatarAssetId(置 null 即清除头像)。校验:nickname 与 ck_users_nickname 对齐(btrim、1~32);avatarAssetId 必须 status='ready'、属当前用户、purpose='user_avatar'(复用 MediaAssetGateway 同类校验语义,错误码沿用 40405/42203)。MediaProperties.allowedPurposesuser_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 校验复用 MediaAssetGatewayready + 属本人)+ purpose='pet_avatar'。权限沿用 PetAccessService:改头像属 WRITE 档owner+caregiver)还是 MANAGE 档(仅 owner)——见待拍板 D3.5-3。allowedPurposespet_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. 波次与关键路径

第一波: 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 <你的工作区>/<仓名>