fix(interaction): 收藏计数同步维护并新增评论数接口

This commit is contained in:
Zhou Weidong committed 2026-10-21 11:33:59 +08:00
1 parent 88794b816e
commit 65e8fbfc70
11 files changed
+382 -47

No files matched your search

+79 -40
View File
@@ -1,58 +1,97 @@
# INBOX
## [已处理] K84-A 列表卡片与后台补充收藏 / 点赞 / 评论数
## K85 收藏数 / 评论数实时更新
### 背景
### 现象
右侧互动栏要显示收藏、评论、喜欢的数量,但首页 feed 等列表卡片拿不到这三个数。
首页 feed 右侧互动栏已经能显示数字,但**收藏数、评论数点完不变**:
已确认:
- 收藏(点星标)后数字不动,杀掉 App 重进又回到旧值
- 发表评论后评论数不动
- 数据库 `drama` 表有 `favorite_count`、`like_count`(无 comment_count 列,评论数按
`content_comment` 统计,见 `AppDramaDetailVO.commentCount`)
- `AppDramaDetailVO` 已返回 `likeCount / favoriteCount / commentCount`
- **`AppDramaCardVO`(列表卡片)只有 `viewCount`,缺这三个字段** → 客户端列表阶段拿不到
- 后台 `DramaRespVO` 只有 `views`,也缺这三个数
- 客户端 `Drama.fromJson` 已经解析 `likeCount / favoriteCount / commentCount`,**客户端无需改**
### 已定位的根因(勘察结论,不要再重复排查)
### 任务(FruitServer 仓库)
**后端(FruitServer)**
1. `fruitshorts-app-api/.../vo/AppDramaCardVO.java`
- 增加 `Long likeCount`、`Long favoriteCount`、`Long commentCount`
2. `fruitshorts-app-api/.../service/AppCatalogService.java`
- `cards()` 里一次性批量取评论数:`Map<Long, Long> commentCounts = commentService.countByDramas(ids)`
- `toCard()` 填充:
- `likeCount` ← `drama.getLikeCount()`
- `favoriteCount` ← `drama.getFavoriteCount()`
- `commentCount` ← `commentCounts.getOrDefault(drama.getId(), 0L)`
- 不要逐条 `countByDrama`(避免 N+1)
3. `fruitshorts-module-interaction/.../service/CommentService.java`
- 新增 `Map<Long, Long> countByDramas(Collection<Long> dramaIds)`:
`deleted = 0` 且 `drama_id in (...)`,按 `drama_id` 分组计数;空集合返回空 Map
- 现有 `countByDrama(Long)` 可复用该方法的单元素结果或保持不动
4. 后台管理端(注意模块依赖方向:`module-interaction` 依赖 `module-content`,不能反向依赖)
- `fruitshorts-module-content/.../vo/DramaRespVO.java` 增加 `likeCount / favoriteCount / commentCount`
- `DramaService`(module-content)只填充 `likeCount / favoriteCount`(取实体字段),
`commentCount` 先留空
- `fruitshorts-admin-api` 的 Drama 接口(`DramaController` 或 admin 侧小服务)在分页/详情返回前,
用 `CommentService.countByDramas(ids)` 批量回填 `commentCount`
(分页一次查询;详情单条)
1. `drama.favorite_count` 从建剧时置 0 之后**没有任何地方自增/自减**:
- `FavoriteService.toggle()` 只写/删 `user_favorite`,注释写着「计数走异步聚合」,
但仓库里**没有这个聚合任务**,所以收藏数永远是 0
- `remove()` / `removeBatch()` 也不维护计数
- `LikeService` 是同步维护 `drama.like_count` 的(`DramaService.adjustLikeCount`),收藏应对齐这个做法
2. 数据库现状(本机 PG `fruitshorts`):`drama.favorite_count` 全为 0,
而 `user_favorite` 实际有记录(剧 1 有 4 条、剧 2 有 2 条、剧 5 有 5 条……)→ 需要回填
3. 评论数没有单个剧集的计数接口:`GET /app/comments` 是游标翻页,不返回总数
**客户端(ReelShort)**
4. 首页 feed 的收藏回调(`player_view.dart` 约 468 行)是内联实现,**没有调用已有的
`_applyFavoriteDelta`**;只有全屏播放页的 `_toggleSave`(`player_view_actions.dart`)调了
→ 首页收藏后数字不变
5. 两处 rail 的 `commentCount: drama.commentCount` 是静态值,评论面板关闭后不刷新
6. 详情页 `DramaStatsCard` 用 `drama.favoriteCount`,`drama_detail_logic.toggleFavorite()`
切换后不更新显示
### 任务
#### A. FruitServer(后端)
1. `fruitshorts-module-content/.../service/DramaService.java`
- 新增 `adjustFavoriteCount(Long dramaId, int delta)`:SQL 自增,
参考现有 `adjustLikeCount`,减号用 `GREATEST(favorite_count - 1, 0)`
2. `fruitshorts-module-playback/.../service/FavoriteService.java`
- 类注释里「计数走异步聚合」改成与 `LikeService` 一致的同步维护说明
- `toggle()`:插入关系后 `adjustFavoriteCount(+1)`;`physicalDelete` 返回行数 > 0 时 `-1`
- `remove()`:删除成功(返回行数 > 0)时 `-1`
- `removeBatch()`:按 dramaId 逐个 `physicalDelete` 并按其返回值 `-1`
(保持与前端「取消收藏」一致的物理删除语义,避免逻辑删除挡住再次收藏)
3. `fruitshorts-app-api/.../controller/AppCommentController.java`
- 新增 `GET /app/comments/count?dramaId={id}` → `Result<Long>`,
内部 `commentService.countByDrama(dramaId)`(匿名可访问,与列表一致)
4. `sql/` 下加一次性回填脚本 `sql/backfill_favorite_count.sql`
- `UPDATE drama d SET favorite_count = (SELECT count(*) FROM user_favorite f WHERE f.drama_id = d.id AND f.deleted = 0);`
- 只放脚本,不要在应用启动时自动执行
5. 测试
- `AppCatalogControllerContractTest`:列表卡片接口增加 `likeCount / favoriteCount / commentCount` 断言
- 后台分页/详情如有测试,补充三个字段断言
- `CommentService.countByDramas` 补单测(空集合 / 多剧集分组)
- `FavoriteService` 单测:收藏 +1、取消 -1、不会减到负数、removeBatch 多剧集各自扣减
- `AppCommentController` 契约测试:`/app/comments/count` 返回数字
#### B. ReelShort(客户端)
1. `lib/core/data/remote/comment_api.dart`
- 新增 `Future<int> count(String dramaId)`,解析 `data`(Long 可能是字符串,用现有 `jsonToInt`)
2. `lib/core/data/repositories/comment_repository.dart`
- 接口与 `HttpCommentRepository` 同步新增 `count`
3. `lib/pages/player/player_view.dart`
- 首页 feed 的 `onSave` 改为复用 `_toggleSave(context, drama)`(内部已有 `_applyFavoriteDelta`)
- 新增 `Map<String, int> _commentCounts`
- 新增 `_openComments(context, drama)`:`await openCommentsWithAuth(...)`,
结束后 `await commentRepository.count(drama.id)` 并 `setState` 写入 `_commentCounts`;
失败静默(保留旧值,不弹错)
- 首页 rail 与播放页 rail 的 `commentCount` 都改成
`_commentCounts[drama.id] ?? drama.commentCount`;`onComments` 都走 `_openComments`
4. `lib/pages/drama_detail/drama_detail_logic.dart`(收藏数同步)
- 记录本地收藏增量,把调整后的收藏数提供给 `DramaStatsCard`
(不要改 `Drama` 模型;用 getter 或在 view 层叠加 delta)
- 只在切换成功且状态确实变化时 ±1,失败回滚
5. 测试
- `CommentApi.count` 的解析测试(数字 / 字符串两种返回)
- 收藏增量逻辑(可抽纯函数或独立小对象)单测:+1、-1、失败不变
### 验收
- 门禁:`mvn test` 全过。
- 接口自测:`GET /app/home/feed`(或现有首页聚合接口)返回的每个卡片都带三个数字段。
- 后端:`mvn test` 全过;`mvn -pl fruitshorts-app-api -am package -DskipTests` 出包;
本地执行回填脚本后 `GET /app/home/feed` 的 `favoriteCount` 与 `user_favorite` 实际条数一致
- 客户端:`dart analyze lib test` 零告警、`flutter test` 全过
- 功能:收藏后 rail 数字立即 ±1;重进 App 数字不回退;发表评论后关闭面板,评论数 +1(重新拉取)
### Git
- 从 `develop` 拉 `feat/drama-card-counts`。
- 提交:`feat(content): 列表卡片与后台返回收藏点赞评论数`,作者 `Zhou Weidong <502333560@qq.com>`。
- `--no-ff` 合回 `develop` 并 push;`FruitServer/docs/plans/PROGRESS.md` 追加 K84-A 留痕。
- FruitServer:从 `develop` 拉 `fix/favorite-comment-count`,
提交 `fix(interaction): 收藏计数同步维护并新增评论数接口`,`--no-ff` 合回 `develop` 并 push
- ReelShort:从 `develop` 拉 `fix/interaction-count-refresh`,
提交 `fix(player): 收藏与评论数实时刷新`,`--no-ff` 合回 `develop` 并 push
- 作者统一 `Zhou Weidong <502333560@qq.com>`;各自仓库 `docs/plans/PROGRESS.md` 追加 K85 留痕
### 约束
- 全程中文;禁止 `git add -A`;不改客户端代码、不改 specs 仓库;完成后停住等待验收。
- 全程中文;禁止 `git add -A`;不改 specs 仓库;不动 `pubspec.lock` 以外的无关文件
- 完成后停住,等待验收