- 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>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# 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.0(32 路径/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 前置短路。
|
||||
- **宠物头像权限按「本次碰了哪些字段」定档**:仅头像=WRITE(owner+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 建 PR(dev→main)→ CI 状态检查转绿 → 合并 → 打 tag → 登记发布记录。
|
||||
@@ -28,12 +28,13 @@
|
||||
| 服务 | 监听 | 用途 | 归属决策 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| gitea | **127.0.0.1:3000** | 代码托管 + Actions 调度 | [CI Runner 手册](ci-runner-setup.md) | ✅ 运行(2026-09-11 起改为仅本机监听) |
|
||||
| nginx | 0.0.0.0:80/443 | 反向代理,按域名分流 | —— | ✅ 运行 |
|
||||
| nginx | 0.0.0.0:80/443 | 反向代理,按域名分流(`sites-enabled/`:`git.patbond.cn`、`patbond-doc`) | —— | ✅ 运行 |
|
||||
| **文档站(patbond-doc)** | 经 nginx 443 | mkdocs 构建产物,含架构/部署/迭代全部文档 | —— | ✅ 运行;⚠️ **公开可访问,待评估是否加 basic auth 或 IP 白名单**(无凭证内容,但暴露内部架构细节) |
|
||||
| act_runner(容器) | 无监听(主动回连) | Gitea Actions 执行器 | [CI Runner 手册](ci-runner-setup.md) | ✅ 运行 |
|
||||
| dockerd / containerd | 本机 socket | 容器运行时(runner 与 job 容器) | ADR-006 | ✅ 运行 |
|
||||
| sshd | 0.0.0.0:22 | SSH | —— | ✅ 运行 |
|
||||
| 腾讯云 agent(barad_agent / YDEyes / YDLive / stargate) | —— | 云监控与主机安全,云厂商预装 | —— | ✅ 运行(预期存在) |
|
||||
| **nacos** | ~~*:8848 / *:9848~~ | **项目已不使用** | **ADR-002 已移除** | ⛔ 已停止;`nacos.service` 仍为 enabled,**待 `systemctl disable`** |
|
||||
| **nacos** | ~~*:8848 / *:9848~~ | **项目已不使用** | **ADR-002 已移除** | ⛔ 已停止 **且已 `systemctl disable`**(2026-09-11,重启不再自启) |
|
||||
|
||||
## 3. 核对方法
|
||||
|
||||
@@ -60,3 +61,11 @@ sudo last -20
|
||||
3. **决策与环境同步**:ADR 决定移除某组件时,**同一次收口内**必须停服务、禁自启、删防火墙规则,并更新本页。
|
||||
4. **凭证轮换触发条件**:任何「外部可调用内部 API」的迹象,一律视为对应 token 已泄露并轮换(`INTERNAL_TOKEN`、`SECRET_KEY`)。
|
||||
5. **本页与实际不符即为缺陷**:核对时发现未登记的开放端口或常驻服务,按缺陷处理——先查清用途,无正当理由即关闭。
|
||||
6. **凭证不进任何可留存介质**:生成/轮换凭证时一律「直接落盘不回显」(如 `NEW=$(gitea generate secret X); sed -i ...; unset NEW`),**不打印到终端、不粘贴进聊天记录、不写入报告或日志**。协作规则原本只约束「不入库」,2026-09-11 的处置中新 token 被回显并粘贴,故扩展此条。
|
||||
7. **配置备份不留在配置目录**:`sites-available/*.bak.*` 之类应移出(如 `/root/nginx-backups/`)——留在配置目录里,一旦 include 通配符被改宽或软链手误就会激活旧配置。
|
||||
|
||||
## 5. 处置与核对记录
|
||||
|
||||
| 日期 | 动作 | 触发 |
|
||||
| --- | --- | --- |
|
||||
| 2026-09-11 | Gitea 改仅监听 127.0.0.1;防火墙删 3000/8848/9848/2222;停并 disable Nacos;清理被注入的 gitconfig;nginx 增 `/api/internal/` 全拒;轮换 `INTERNAL_TOKEN`(两次——首次回显后按纪律 6 重做);外部验证四项通过 | [安全事件](iterations/iteration-3.5/07-security-incident-20260911.md) |
|
||||
|
||||
@@ -103,6 +103,7 @@ nav:
|
||||
- 03 后端资料与头像: development/iterations/iteration-3.5/03-backend-profile-avatar.md
|
||||
- 04 契约冻结 v1.4.0: development/iterations/iteration-3.5/04-contract-freeze-v140.md
|
||||
- 05 资料页与头像 UI: development/iterations/iteration-3.5/05-profile-avatar-ui.md
|
||||
- 06 M3.5 收口: development/iterations/iteration-3.5/06-wave2-closure.md
|
||||
- 07 安全事件复盘: development/iterations/iteration-3.5/07-security-incident-20260911.md
|
||||
- API:
|
||||
- 契约说明: api/index.md
|
||||
|
||||
Reference in New Issue
Block a user