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

@@ -38,12 +38,15 @@ const (
PrefixChannelsAllList = "channels:all_list"
// 消息缓存相关
keyPrefixMsgHash = "msg_hash" // 消息详情 Hash
keyPrefixMsgIndex = "msg_index" // 消息索引 Sorted Set
keyPrefixMsgCount = "msg_count" // 消息计数
keyPrefixMsgSeq = "msg_seq" // Seq 计数器
keyPrefixMsgPage = "msg_page" // 分页缓存
keyPrefixMsgIdempotent = "msg_idem" // 消息幂等键
keyPrefixMsgHash = "msg_hash" // 消息详情 Hash
keyPrefixMsgIndex = "msg_index" // 消息索引 Sorted Set
keyPrefixMsgCount = "msg_count" // 消息计数
keyPrefixMsgSeq = "msg_seq" // Seq 计数器
keyPrefixMsgPage = "msg_page" // 分页缓存
keyPrefixMsgIdempotent = "msg_idem" // 消息幂等键
// 帖子创建幂等键前缀clientRequestID -> 已创建的 postID
keyPrefixPostIdempotent = "post_idem"
)
// PostListKey 生成帖子列表缓存键
@@ -197,6 +200,12 @@ func MessageIdempotentKey(senderID, clientMsgID string) string {
return fmt.Sprintf("%s:%s:%s", keyPrefixMsgIdempotent, senderID, clientMsgID)
}
// PostCreateIdempotentKey 帖子创建幂等键 (userID + clientRequestID -> postID)
// 用于在 SetNX 原子抢占期间识别同一客户端请求的重试,避免重复发帖。
func PostCreateIdempotentKey(userID, clientRequestID string) string {
return fmt.Sprintf("%s:%s:%s", keyPrefixPostIdempotent, userID, clientRequestID)
}
// UserReadSeqKey 用户已读位置缓存键OpenIM 风格 SEQ_USER_READ:{convID}:{userID}
func UserReadSeqKey(convID, userID string) string {
return fmt.Sprintf("%s:%s:%s", PrefixUserReadSeq, convID, userID)