- 15~17 社区后端纵切三单(帖子/Feed 作者链路/评论互动关注,226→310) - 18 契约冻结 v1.3.0(31 路径/43 操作,26 项修正照单全收) - 19 快照同步与全仓契约矩阵(173 格零漂移,修 allOf 校验盲区,→325) - 20 收口总表:定型语义汇总(第三波接入依据)与质量事件记录 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
# M3 第二波评论与互动施工报告(T3-06/T3-07/T3-08:单层评论 + 点赞/收藏/关注)
|
||||
|
||||
> 作者:Senior Developer(后端)
|
||||
> 日期:2026-09-09
|
||||
> 工单:T3-06(点赞/收藏幂等写入与计数)+ T3-07(单层评论)+ T3-08 关注最小接口(随本波合并交付,第二波收尾单)
|
||||
> 代码基线:patbond-api `99a3c1f`(282 测试全绿)→ 交付 `7f1dd33`(310 测试全绿)
|
||||
> 结论先行:**评论/点赞/收藏/关注全域十一端点落地 patbond-community;三新码定型采纳(40404/40406/42204,42205 属 T3-03 已启用不涉本单);幂等并发验收硬项实证——并发 N 次 PUT like 恰计 1、PUT+DELETE 竞态终态列值与关系表恒一致(真并发测试);三计数列全部写侧同事务维护、对账专项通过,T3-05 读侧零改动即时生效;互动门禁定型「只认帖子公开面」;与草案偏差 6 项逐条记录;全套 310 测试全绿(+28),`check-secrets --all` 通过。**
|
||||
|
||||
---
|
||||
|
||||
## 1. 提交清单
|
||||
|
||||
按互动 / 评论两个逻辑提交,已推送 `origin/dev`:
|
||||
|
||||
| 提交 | 内容 |
|
||||
| --- | --- |
|
||||
| `19e8cba` | 点赞/收藏/关注幂等互动 + 同事务计数 + 我的收藏列表 + follow-stats + 14 例测试 |
|
||||
| `7f1dd33` | 单层评论幂等创建/游标列表/作者软删 + comment_count 维护 + 14 例测试 |
|
||||
|
||||
工单号对照说明:PM 分解(iteration-3/01)中 T3-06 = 点赞/收藏、T3-07 = 评论、T3-08 = 关注(条件单);本波指派文案中的编号与此相反,本报告与提交信息一律按 PM 分解的正典编号。
|
||||
|
||||
## 2. 端点与错误语义定型表(T3-10 冻结输入)
|
||||
|
||||
### 2.1 端点清单(全部在 patbond-community :8084,强制 Bearer 鉴权)
|
||||
|
||||
| 端点 | 成功 | 语义要点 |
|
||||
| --- | --- | --- |
|
||||
| `GET /api/v1/posts/{postId}/comments` | 200 + `{items,nextCursor,hasMore}` | 仅 visible;`(created_at DESC, id DESC)` 走 `ix_comments_post_created`,keyset 游标;作者与 @ 目标均为 AuthorSummary(批量 + 降级 id-only 同构复用) |
|
||||
| `POST /api/v1/posts/{postId}/comments` | 201 + Comment | `Idempotency-Key` 必带(1~128,trim 后计);content trim 后 1~2000;`replyToUserId` 可选 @ 回复 |
|
||||
| `DELETE /api/v1/comments/{commentId}` | 200 + VoidEnvelope | 顶层短路径;仅评论作者可删(D3-7:帖主删他人评论首版不做);软删 status→deleted + deleted_at 成对(ck_comments_deleted) |
|
||||
| `PUT /api/v1/posts/{postId}/like` | 200 + `{liked:true, likeCount}` | 复合主键幂等;重复 PUT 同终态不重复计数 |
|
||||
| `DELETE /api/v1/posts/{postId}/like` | 200 + `{liked:false, likeCount}` | 取消不存在的点赞不报错不减计数 |
|
||||
| `PUT/DELETE /api/v1/posts/{postId}/bookmark` | 200 + `{bookmarked, bookmarkCount}` | 与点赞同构 |
|
||||
| `GET /api/v1/me/bookmarks` | 200 + `{items,nextCursor,hasMore}` | 项 = FeedCard;`(bookmarks.created_at DESC, post_id DESC)` 走 `ix_post_bookmarks_user_created`;失效帖静默剔除(§4) |
|
||||
| `PUT /api/v1/users/{userId}/follow` | 200 + `{following:true, followerCount}` | 主键幂等;自关注 422/42204;followerCount 为目标粉丝数实时 COUNT |
|
||||
| `DELETE /api/v1/users/{userId}/follow` | 200 + `{following:false, followerCount}` | 幂等;**自取关也是 200 no-op**(行不可能存在,权威 false 即事实;42204 只留给 PUT) |
|
||||
| `GET /api/v1/users/{userId}/follow-stats` | 200 + `{followerCount, followingCount, followedByMe}` | 实时 COUNT 双向索引;查自己 followedByMe 恒 false |
|
||||
|
||||
### 2.2 三新码取舍定型(契约冻结评审输入)
|
||||
|
||||
| 码 | 取舍 | 理由 |
|
||||
| --- | --- | --- |
|
||||
| **40404 COMMENT_NOT_FOUND** | **采纳** | 评论不可见合并位(不存在/已删/所属帖不可见),与 40401/40403/40405 同一防枚举族 |
|
||||
| **40406 USER_NOT_FOUND(community 侧)** | **采纳**(enum 名 `TARGET_USER_NOT_FOUND`,文案同 40400「用户不存在」) | 不复用 40400:该码属 identity 域语义,且四服务的 NoResourceFound 兜底已把 40400 用作「路由不存在」——复用会让「关注目标不存在」与「路径打错」不可区分。触发面:follow PUT/DELETE/stats 的目标、评论 `replyToUserId`(不存在与注销合并,缺席不泄露成因) |
|
||||
| **42204 FOLLOW_RULE_VIOLATION** | **采纳** | 自关注是业务规则违反非参数格式错(ck_user_follows_self 库层兜底),与 42201/42202 规则违反族同构 |
|
||||
| 42205 MEDIA_UPLOAD_STATE_INVALID | 不涉本单 | T3-03 已入 ErrorCode 并启用(media 域 complete 语义),列入草案三新码系口径滞后,无需本单动作 |
|
||||
|
||||
### 2.3 互动门禁定型(本单新增语义,六类路径测试锚定)
|
||||
|
||||
**互动面 = 帖子公开面**:评论(读写删)与点赞/收藏(PUT/DELETE)只对 `status='published' AND deleted_at IS NULL` 的帖子开放。**作者本人的草稿在互动路径上同样 404/40403**——草稿不参与社交域(发布前无人可见、计数无意义),且免除「作者特判」后所有不可见情形保持逐字节一致(防枚举断言实测集合大小 = 1)。这较 T3-04 读路径(draft 对作者可见)是收窄而非矛盾:可见性回答「能不能看」,互动门禁回答「能不能社交」。
|
||||
|
||||
评论删除的 403/404 边界沿 T3-04 定型原则:403/40301 只发给「可见但无权」(他人对 visible 评论,含帖主),一切不可见合并 404/40404。
|
||||
|
||||
### 2.4 幂等语义定型(ADR-019 两形态并用)
|
||||
|
||||
- **二元互动(PUT/DELETE)**:复合主键即幂等键,无键管理。`ON CONFLICT DO NOTHING` / 条件 DELETE 返回实际变更行数,响应恒回权威终态。
|
||||
- **评论创建(表内幂等列)**:`Idempotency-Key` 落 `client_request_id`,规范化 request_hash(`comment.v1\n postId\n replyToUserId\n content(trimmed)`)落库比对;同键同 hash 返回首条(201,库中恰一行,不重复计数);同键异 hash 409/40905;键按作者隔离(`UNIQUE(author_user_id, client_request_id)` 天然全局跨帖——同键换帖 = hash 必异 = 40905,符合直觉);重试撞已删首评 404/40404(T3-04 §2.4 先例)。V5 comments 表幂等列(client_request_id/request_hash + ck_comments_idempotency)原生就位,无表变更。
|
||||
|
||||
## 3. 计数维护与对账说明(工单验收硬项)
|
||||
|
||||
**机制**:三计数列(like_count/comment_count/bookmark_count)只随关系写的**实际变更行数**在**同一事务**内增减——`insertXxx` 冲突返回 0 则不增,`deleteXxx` 删 0 行则不减;评论删除以 `FOR UPDATE` 锁定 visible→deleted 迁移,保证 -1 恰一次。`ck_posts_counts` 非负为库层兜底,从未触发。
|
||||
|
||||
**并发实证**(真多线程集成测试,非串行模拟):
|
||||
|
||||
| 场景 | 结果 |
|
||||
| --- | --- |
|
||||
| 同用户 4 线程并发 PUT like | 全部 200;关系表恰 1 行、like_count 恰 1(M3 验收标准二) |
|
||||
| 同用户并发 PUT + DELETE like | 两边 200;无论竞态先后,终态恒满足 like_count = COUNT(post_likes)(0 行 0 计或 1 行 1 计) |
|
||||
| 3 线程并发 PUT follow | 恰 1 行,follow-stats 计 1 |
|
||||
|
||||
**对账专项**:混合施加/取消后 like/bookmark 列值 = 关系表 COUNT(逐一断言);评论建 3 删 1 后 comment_count = visible 行数 = 列表长度 = 2;删帖后互动路径一律 404、计数列随帖冻结(帖不可见,列值无消费方;T3-05 读侧只对 published 出卡)。运维级漂移修复口径沿 16 号报告 §4(关系表重算列),不做常驻任务。
|
||||
|
||||
**两处记录在案的既有行为**:① 计数 UPDATE 会触发 `trg_posts_updated_at`——互动会推动帖子 updated_at(该列语义是「行最后更新」,内容编辑标记是 version,契约消费方勿以 updated_at 判「编辑过」);② follow 无冗余计数列,followerCount/followingCount 恒实时 COUNT(双向索引支撑,草案即此设计)。
|
||||
|
||||
## 4. 我的收藏列表定型
|
||||
|
||||
- 项形态 = FeedCard(草案预设确认),装配复用 FeedService 同一批量路径(媒体/作者/签名 URL 零新代码)。
|
||||
- 谓词与公共 Feed 恒等(`status='published' AND visibility='public' AND deleted_at IS NULL`):被收藏帖软删/hidden/archived 后**静默剔除**(草案取向定型),剔除在页查询 SQL 内完成——游标键在收藏关系行上(`bookmarks.created_at DESC, post_id DESC`),剔除不破坏翻页不丢不重。
|
||||
- 该谓词同时保证卡片 `publishedAt` 非空不变式对收藏列表继续成立。
|
||||
|
||||
## 5. 与契约草案(openapi-community-draft.yaml)偏差清单
|
||||
|
||||
| # | 草案 | 实现定型 | 理由 / 冻结动作 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 帖子不可见 404/40403(未提作者草稿) | **作者本人草稿在全部互动/评论路径同样 404/40403** | §2.3 互动面=公开面;冻结时在 comments/like/bookmark 各端点描述补「含作者本人草稿」 |
|
||||
| 2 | `CreateCommentRequest.replyToUserId` 未定校验语义 | 目标须为存活用户,否则 **404/40406**(不存在/注销合并) | @ 落库有 FK,放任会 500;与 follow 目标同码同语义 |
|
||||
| 3 | follow DELETE 响应仅列 200/401/404(未提自取关) | **自取关 200 权威 false(no-op)**,42204 只在 PUT | DELETE 幂等语义优先:行不可能存在,权威终态即事实 |
|
||||
| 4 | 草案错误表 42205 列为新码 | 42205 属 T3-03 已启用(media 域),本单零动作 | 冻结时把 42205 从「新增」挪到「既有」口径 |
|
||||
| 5 | 评论删除 404 例名 `commentNotFound`、码位 40404 | 采纳;**评论幂等重试撞已删首评亦归 40404** | 草案未覆盖该边界;冻结时在 `IdempotencyKeyRequiredHeader` 描述补一句(与帖子域 40403 平行) |
|
||||
| 6 | `Comment` schema 无 `updatedAt`(M3 无评论编辑) | 确认不带;`replyToUser` 为完整 AuthorSummary(含降级 id-only 形态,required 收敛沿 16 号报告偏差 #1 的 `[userId]`) | AuthorSummary 收敛口径全域统一,评论侧无新豁免 |
|
||||
|
||||
另三处为草案预设的确认(非偏差):评论列表 DESC 排序 + 正典信封逐字一致(指派文案中的 ASC 备选未采);like/bookmark PUT 重复施加 200 非 409;收藏列表复用 FeedListEnvelope。
|
||||
|
||||
## 6. 测试数变化:282 → 310(+28,0 回归)
|
||||
|
||||
| 模块 | 基线 | 交付 | 新增内容 |
|
||||
| --- | --- | --- | --- |
|
||||
| patbond-common | 3 | 3 | ErrorCode 增 3 码(40404/40406/42204),无行为变化 |
|
||||
| patbond-user | 96 | 96 | — |
|
||||
| patbond-auth | 39 | 39 | — |
|
||||
| patbond-pet | 89 | 89 | — |
|
||||
| patbond-community | 55 | 83 | 见下 |
|
||||
| **合计** | **282** | **310** | `JAVA_HOME=<你的 JDK17 路径> ./mvnw clean test` 一次通过,BUILD SUCCESS;`<repo>/scripts/check-secrets.sh --all` 通过 |
|
||||
|
||||
community 新增 28 例,按工单六类路径 + 三个专项:
|
||||
|
||||
- `CommentIntegrationTest`(14):全形态创建(trim/昵称/@ 回复摘要)、六类路径(content 空白/超长、幂等键缺失/空白/超长、@ 不存在与注销用户 40406、四种不可见帖逐字节一致 40403、删除的 403/404 四格边界)、幂等矩阵专项(同键重放不重计/异 payload 40905/跨作者同键/撞已删首评 40404)、分页不丢不重(7 评 3 页整走 + 删除项剔除)、计数对账专项。
|
||||
- `LikeBookmarkIntegrationTest`(9):like/bookmark 全生命周期幂等四连(施加/重复施加/取消/重复取消权威终态)、多用户累计与 likedByMe/bookmarkedByMe 视角、8 种不可见组合逐字节一致 40403、**真并发双专项**(4 线程 PUT 恰计 1;PUT+DELETE 竞态终态一致)、混合操作对账、收藏列表分页 + 静默剔除(软删与 hidden 各一)+ 卡片形态断言、非法分页入参。
|
||||
- `FollowIntegrationTest`(5):follow 生命周期幂等四连、自关注 42204 / 自取关 no-op、不存在/注销目标三端点 40406 + 畸形 UUID 40000、follow-stats 双向计数与三视角 followedByMe、3 线程并发 follow 恰 1 行。
|
||||
|
||||
## 7. 遗留与交接
|
||||
|
||||
- **T3-10 冻结回填**:§2 定型表(含三新码取舍)+ §5 偏差 6 项即评论/互动域冻结输入。至此第二波后端四单(T3-04/05/06/07)语义全部定型,帖子/Feed/评论/互动四域冻结条件齐备。
|
||||
- **T3-12~14 Flutter 衔接**:乐观更新对账目标即本单权威终态响应(`{liked,likeCount}` 族);回滚基准取响应值而非本地推算。
|
||||
- **P9 共享设施**:CommentCursor/BookmarkCursor 是游标件第 5/6 份复制,幂等键规范化亦复制一份——下沉 common 的拍板权重再升。
|
||||
- 关注列表端点(关注/粉丝明细)按 ADR-018 裁剪不在 M3,需要时按纯增量补入;`visibility='followers'` 语义仍后置。
|
||||
Reference in New Issue
Block a user