feat(community): 评论改为独立二级页面 + 评论内容安全审核 + 评论数按实发实时纠正
前端: - 新增 pages/comments 独立评论页(列表 + 底部输入条 + 回复),社区卡片点评论改为 navigateTo - 输入条用普通流式布局 + 原生 adjust-position 做键盘避让,聚焦期间不 setData 碰输入框 - 评论只能配 1 张图;底部常驻「审核后公开」提示;发送按钮在途「审核中…」防连点 - 发布结果按状态区分:过审即时展示,其余提示「已提交,审核通过后展示」 - 评论页把已发布评论数回传社区列表,卡片数字实时更新 后端: - CreateComment 文本判为违规时直接拒收(ErrContentRejected),不入库 - 社区列表/用户帖子列表用「已发布评论实时条数」覆盖 comment_count, 修正异步过审/删待审/后台改状态导致的计数漂移 - IDCard 手机号返回完整号码(上一批遗留) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,35 @@ func (s *Service) attachPostImages(posts []model.Post) {
|
||||
}
|
||||
}
|
||||
|
||||
// attachCommentCounts 用「已发布评论」的实时条数覆盖 comment_count。
|
||||
// 维护型计数器在这些路径上会漂移:图片评论异步过审后没补计数、删除待审评论时误减、
|
||||
// 后台审核改状态没同步。直接按实际已发布条数算,永远和评论弹层「共 X 条」一致。
|
||||
func (s *Service) attachCommentCounts(posts []model.Post) {
|
||||
if len(posts) == 0 {
|
||||
return
|
||||
}
|
||||
ids := make([]string, len(posts))
|
||||
for i := range posts {
|
||||
ids[i] = posts[i].ID
|
||||
}
|
||||
type row struct {
|
||||
PostID string
|
||||
N int
|
||||
}
|
||||
var rows []row
|
||||
s.db.Model(&model.Comment{}).
|
||||
Select("post_id, count(*) as n").
|
||||
Where("post_id IN ? AND status = ?", ids, model.PostPublished).
|
||||
Group("post_id").Scan(&rows)
|
||||
cnt := make(map[string]int, len(rows))
|
||||
for _, r := range rows {
|
||||
cnt[r.PostID] = r.N
|
||||
}
|
||||
for i := range posts {
|
||||
posts[i].CommentCount = cnt[posts[i].ID]
|
||||
}
|
||||
}
|
||||
|
||||
// tabTag 将 feed tab 映射为标签过滤(空表示不过滤)
|
||||
func tabTag(tab string) string {
|
||||
switch tab {
|
||||
@@ -81,6 +110,7 @@ func (s *Service) ListPosts(userID, tab string, offset, limit int) ([]model.Post
|
||||
return nil, 0, err
|
||||
}
|
||||
s.attachPostImages(posts)
|
||||
s.attachCommentCounts(posts)
|
||||
s.markFollowed(userID, posts)
|
||||
return posts, total, nil
|
||||
}
|
||||
@@ -299,6 +329,10 @@ func (s *Service) CreateComment(userID, postID string, content string, images []
|
||||
|
||||
// 评论也过内容安全(scene=2 评论)。图片这里已经是可访问 URL
|
||||
status, _ := s.moderateInitial(content, user.OpenID, 2, len(images) > 0)
|
||||
// 文本被判违规:直接拒绝、不入库,让用户知道发不出去
|
||||
if status == model.PostRejected {
|
||||
return nil, ErrContentRejected
|
||||
}
|
||||
|
||||
comment := model.Comment{
|
||||
PostID: postID, UserID: userID, AuthorName: name, Content: content,
|
||||
|
||||
@@ -24,6 +24,9 @@ var ErrForbidden = errors.New("无权操作")
|
||||
// ErrInvalidParam 参数不合法(handler 据此返回 40000)
|
||||
var ErrInvalidParam = errors.New("参数不合法")
|
||||
|
||||
// ErrContentRejected 内容安全判定为违规,直接拒绝发送(不入库)
|
||||
var ErrContentRejected = errors.New("内容包含违规信息,无法发布")
|
||||
|
||||
// Service 业务逻辑聚合,方法按领域分散在各文件
|
||||
type Service struct {
|
||||
db *gorm.DB
|
||||
|
||||
@@ -215,6 +215,7 @@ func (s *Service) ListUserPosts(viewerID, userID string, offset, limit int) ([]m
|
||||
return nil, 0, err
|
||||
}
|
||||
s.attachPostImages(posts)
|
||||
s.attachCommentCounts(posts)
|
||||
s.markFollowed(viewerID, posts)
|
||||
return posts, total, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user