feat: 评论/帖子编辑历史与编辑标记,内容锁定
- 新增 smartDiff 差异计算与 EditHistoryModal 编辑历史弹窗,替换原 CommentEditHistoryModal - 新增 PostEditedMark 编辑标记展示 - 新增内容锁定机制(content_lock)防止并发编辑冲突 - 审核与通知服务适配
This commit is contained in:
@@ -2,6 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@@ -83,7 +84,7 @@ func (h *Handlers) PostComments(c *gin.Context) {
|
||||
|
||||
// CreateCommentRequest 评论请求
|
||||
type CreateCommentRequest struct {
|
||||
Content string `json:"content" binding:"required,min=1"`
|
||||
Content string `json:"content" binding:"required,min=1,max=5000"`
|
||||
ParentID *uint `json:"parent_id"` // 非空 = 回复该评论(发为其子评论)
|
||||
}
|
||||
|
||||
@@ -109,11 +110,24 @@ func (h *Handlers) CreateComment(c *gin.Context) {
|
||||
}
|
||||
// staff 直发;已过审用户在站点开启免审时可直发;其余进待审队列(通过时才发业务通知)
|
||||
status := h.resolvePublishStatus(claims.ID, claims.Role)
|
||||
comment, parent, err := h.Comment.Create(claims.ID, uint(id), req.Content, req.ParentID, status)
|
||||
comment, parent, necroPoints, err := h.Comment.Create(claims.ID, uint(id), req.Content, req.ParentID, status, model.Role(claims.Role))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
switch {
|
||||
case errors.Is(err, service.ErrPostReplyLocked):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrInsufficientPoints):
|
||||
// 旧帖回复扣分余额不足(文案含具体规则)
|
||||
c.JSON(http.StatusPaymentRequired, gin.H{"error": err.Error()})
|
||||
default:
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
// 旧帖回复扣分:系统提醒本人(ActorID=0,不经过自我通知过滤)
|
||||
if necroPoints > 0 {
|
||||
h.Notification.CreateSystem(claims.ID, model.NotificationTypeNecroReply, uint(id), comment.ID,
|
||||
fmt.Sprintf("您回复了长期没有新回复的旧帖,已扣除 %d 积分", necroPoints))
|
||||
}
|
||||
if comment.Status == model.ContentStatusPublished {
|
||||
if parent == nil {
|
||||
// 主评论:通知帖子作者(排除自己评论自己的帖子)
|
||||
@@ -146,7 +160,7 @@ func (h *Handlers) UpdateComment(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Content string `json:"content"`
|
||||
Content string `json:"content" binding:"omitempty,min=1,max=5000"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
@@ -157,7 +171,9 @@ func (h *Handlers) UpdateComment(c *gin.Context) {
|
||||
switch {
|
||||
case errors.Is(err, service.ErrCommentNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrCommentForbidden):
|
||||
case errors.Is(err, service.ErrCommentForbidden),
|
||||
errors.Is(err, service.ErrCommentPostLocked),
|
||||
errors.Is(err, service.ErrCommentEditLocked):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrCommentDeleted),
|
||||
errors.Is(err, service.ErrCommentNotChanged):
|
||||
@@ -256,3 +272,39 @@ func (h *Handlers) PurgeComment(c *gin.Context) {
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "已彻底删除"})
|
||||
}
|
||||
|
||||
// CommentEditHistory 评论编辑历史(登录即可见;分页)
|
||||
func (h *Handlers) CommentEditHistory(c *gin.Context) {
|
||||
claims := middleware.CurrentUser(c)
|
||||
cid, err := strconv.ParseUint(c.Param("cid"), 10, 64)
|
||||
if err != nil || cid == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的评论 ID"})
|
||||
return
|
||||
}
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
|
||||
items, total, err := h.Comment.ListEditHistory(h.loadActor(claims.ID), uint(cid), page, size)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, service.ErrCommentNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrCommentForbidden):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取编辑历史失败"})
|
||||
}
|
||||
return
|
||||
}
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if size < 1 || size > 50 {
|
||||
size = 10
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"items": items,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"size": size,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user