feat(moderation): make moderation more lenient and add timeout fallback behavior
All checks were successful
Build Backend / build (push) Successful in 2m20s
Build Backend / build-docker (push) Successful in 1m7s

The moderation system now uses a "pass/review" strategy instead of "pass/review/block", treating hard violations the same as review (only routing to manual review rather than auto-rejecting). Add fallback-approve behavior in the hook system so slow moderation APIs never silently block normal content when they time out or get cancelled - timed-out moderation now approves with "ReviewedBy: system" instead of leaving the zero-value result that would reject content. Update post/comment/vote services and handlers to use the new unified behavior. Simplify moderation prompts to emphasize "宁可放过,不可误伤" (prefer false positives over false negatives).
This commit is contained in:
lafay
2026-07-08 01:47:48 +08:00
parent eb931bf1c6
commit a971fb0e29
9 changed files with 324 additions and 267 deletions

View File

@@ -2,6 +2,7 @@ package service
import (
"context"
"errors"
"fmt"
"log"
"strconv"
@@ -216,7 +217,7 @@ func (s *postServiceImpl) reviewPostAsync(postID, userID, title, content string,
fmt.Sscanf(userID, "%d", &authorID)
result := &hook.ModerationResult{}
s.hookManager.TriggerWithMetadata(context.Background(), hook.HookPostPreModerate, authorID, &hook.PostModerateHookData{
triggerErr := s.hookManager.TriggerWithMetadata(context.Background(), hook.HookPostPreModerate, authorID, &hook.PostModerateHookData{
PostID: postID,
Title: title,
Content: content,
@@ -226,6 +227,19 @@ func (s *postServiceImpl) reviewPostAsync(postID, userID, title, content string,
"result": result,
})
// Trigger can return context.DeadlineExceeded/Canceled if a moderation hook
// times out. In that case the result may not have been written at all
// (result.Approved stays false). Treat an unfinished moderation as
// "approved + system" rather than silently rejecting the post, so a slow
// moderation API never blocks normal content.
if triggerErr != nil && (errors.Is(triggerErr, context.DeadlineExceeded) || errors.Is(triggerErr, context.Canceled)) {
log.Printf("[WARN] Post moderation hook timed out or was cancelled, fallback approve post=%s err=%v", postID, triggerErr)
result.Approved = true
result.ReviewedBy = "system"
result.RejectReason = ""
result.NeedsReview = false
}
s.hookManager.Trigger(context.Background(), hook.HookPostModerated, authorID, &hook.PostModeratedHookData{
PostID: postID,
AuthorID: authorID,