feat(post): add idempotency support for post creation with client request ID
All checks were successful
Build Backend / build (push) Successful in 3m42s
Build Backend / build-docker (push) Successful in 1m7s

Add client_request_id field to post creation endpoints (CreateVotePost and CreatePostWithSegments).
When provided, the server uses Redis SetNX-based idempotency to prevent duplicate posts
from double-clicks or network retries within a 24-hour TTL. Duplicate requests return
the originally created post; concurrent in-flight requests return 429 "正在发布中,请稍候".
This commit is contained in:
lafay
2026-06-26 17:00:38 +08:00
parent 28cfcbf9a8
commit d8e56e60dc
8 changed files with 231 additions and 19 deletions

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
@@ -105,13 +106,18 @@ func (h *PostHandler) Create(c *gin.Context) {
return
}
post, err := h.postService.Create(c.Request.Context(), userID, req.Title, content, segments, req.Images, req.ChannelID)
post, err := h.postService.Create(c.Request.Context(), userID, req.Title, content, segments, req.Images, req.ChannelID, req.ClientRequestID)
if err != nil {
var moderationErr *service.PostModerationRejectedError
if errors.As(err, &moderationErr) {
response.BadRequest(c, moderationErr.UserMessage())
return
}
// 幂等命中"进行中"占位:提示客户端稍候,避免重复提交
if errors.Is(err, service.ErrDuplicatePostRequest) {
response.ErrorWithStatusCode(c, http.StatusTooManyRequests, 429, err.Error())
return
}
response.InternalServerError(c, "failed to create post")
return
}

View File

@@ -47,6 +47,11 @@ func (h *VoteHandler) CreateVotePost(c *gin.Context) {
response.BadRequest(c, moderationErr.UserMessage())
return
}
// 幂等命中"进行中"占位:提示客户端稍候,避免重复提交
if errors.Is(err, service.ErrDuplicatePostRequest) {
response.ErrorWithStatusCode(c, http.StatusTooManyRequests, 429, err.Error())
return
}
response.Error(c, http.StatusBadRequest, err.Error())
return
}