fix(comments): make duplicate-check atomic with transaction; index content_hash
查重 SELECT 与 INSERT 包进同一事务,串行化并发请求缩小重复窗口(M4); 重复时 rollback 空事务。content_hash 加索引(非唯一,避免误杀不同作者 发相同短内容的合法场景)加速 5 分钟窗口查重,原先全表扫。
This commit is contained in:
parent
1c6974ca68
commit
82ab190e0d
5
migrations/013_comment_content_hash_index.sql
Normal file
5
migrations/013_comment_content_hash_index.sql
Normal file
@ -0,0 +1,5 @@
|
||||
-- 评论内容哈希索引,加速 5 分钟窗口内的重复检测查询。
|
||||
-- 注意不加 UNIQUE 约束:content_hash 基于 parent_id+author+content,
|
||||
-- 不同作者发相同内容(如"顶"、"+1")是合法的,唯一约束会误杀。
|
||||
CREATE INDEX IF NOT EXISTS idx_comments_content_hash
|
||||
ON comments(content_hash);
|
||||
@ -92,7 +92,7 @@ pub async fn create_comment(
|
||||
});
|
||||
}
|
||||
|
||||
let client = get_conn().await.map_err(AppError::db_conn)?;
|
||||
let mut client = get_conn().await.map_err(AppError::db_conn)?;
|
||||
|
||||
// 确认目标文章存在且处于已发布状态。
|
||||
let post_row = client
|
||||
@ -196,7 +196,10 @@ pub async fn create_comment(
|
||||
// 基于文章、父评论、作者与内容计算哈希,防止短时间重复提交。
|
||||
let content_hash = compute_content_hash(post_id, parent_id, &author_name, &content_md);
|
||||
|
||||
let dup: Option<i64> = client
|
||||
// 查重与插入必须在同一事务内,避免并发请求双双通过查重窗口(M4)。
|
||||
let tx = client.transaction().await.map_err(AppError::query)?;
|
||||
|
||||
let dup: Option<i64> = tx
|
||||
.query_opt(
|
||||
"SELECT id FROM comments WHERE post_id = $1 AND content_hash = $2 AND created_at > NOW() - INTERVAL '5 minutes'",
|
||||
&[&post_id, &content_hash],
|
||||
@ -206,6 +209,8 @@ pub async fn create_comment(
|
||||
.map(|r| r.get(0));
|
||||
|
||||
if dup.is_some() {
|
||||
// 重复:回滚空事务后返回。
|
||||
tx.rollback().await.ok();
|
||||
return Ok(CommentResponse {
|
||||
success: false,
|
||||
message: "请勿重复提交".to_string(),
|
||||
@ -246,7 +251,7 @@ pub async fn create_comment(
|
||||
};
|
||||
|
||||
// 插入评论,默认状态为 pending,等待管理员审核。
|
||||
let row = client
|
||||
let row = tx
|
||||
.query_one(
|
||||
"INSERT INTO comments \
|
||||
(post_id, parent_id, depth, author_name, author_email, author_url, \
|
||||
@ -270,6 +275,8 @@ pub async fn create_comment(
|
||||
.await
|
||||
.map_err(AppError::query)?;
|
||||
|
||||
tx.commit().await.map_err(AppError::query)?;
|
||||
|
||||
let comment_id: i64 = row.get(0);
|
||||
|
||||
// 根据邮箱生成 Gravatar 头像链接。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user