topfans/docs/superpowers/plans/2026-06-17-plan-d-notification-test.md
2026-06-22 17:19:48 +08:00

10 KiB
Raw Permalink Blame History

Plan D: 通知模板 + 单元测试 + 集成测试

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task.

父计划: docs/superpowers/plans/2026-06-17-moderation-report-feedback-system.md

Goal: 注册 6 个通知模板 + 单元测试 + 集成测试 + 监控指标

Architecture:

  • 6 个模板注册到 notificationService替换 v1.5 stub 为真实现)
  • Go 单元测试覆盖状态机 + claimed_* 一致性 + Lua + 并发
  • FastAPI 单元测试覆盖 50008/50009/50014 区分
  • 集成测试testcontainers端到端场景
  • Prometheus 13+ 业务指标

Tech Stack: Go test / testify / testcontainers-go / pytest / pytest-asyncio / prometheus/client_golang

仓库: 双仓并行

  • /Users/liulujian/Documents/code/TopFansByGithubGo service + Gateway + 集成测试)
  • /Users/liulujian/Documents/code/TopFans-activity-adminFastAPI 测试)

阶段 D.1:通知模板注册

Task D.1.1: 在 notificationService 注册 6 个模板

Files:

  • Modify: backend/services/notificationService/model/notification.gorepository/notification_repository.go(按现有模板管理方式)

6 个模板spec §8

事件 模板名 接收方 字段
举报达到阈值触发自动隐藏 report_auto_hidden_notice 被举报方 reporter_nickname, target_type, target_id, action, reason, related_url
举报状态变更为 resolved/dismissed report_resolved 举报人 同上
举报成立 + takedown report_takedown_notice 被举报方 同上
举报成立 + ban report_ban_notice 被举报用户 同上 + 短信
举报成立 + warn report_warn_notice 被举报用户 同上
反馈被回复 feedback_replied 反馈人 同上
INSERT INTO notification_templates (code, name, channel, subject, body_template, enabled) VALUES
('report_auto_hidden_notice', '举报自动隐藏通知', 'site_msg',
  '您的内容被多人举报已自动隐藏',
  '您发布的{target_type}ID: {target_id})已被系统自动隐藏。如有异议请联系客服。',
  TRUE),
('report_resolved', '举报处理结果', 'site_msg+push',
  '您的举报已处理',
  '您的举报已处理:{action}。{reason}',
  TRUE),
('report_takedown_notice', '内容下架通知', 'site_msg',
  '您的内容已被下架',
  '您发布的{target_type}ID: {target_id})因{reason}已被下架。',
  TRUE),
('report_ban_notice', '账号封禁通知', 'site_msg+sms',
  '您的账号已被封禁',
  '您的账号因{reason}已被封禁,如有异议请联系客服。',
  TRUE),
('report_warn_notice', '账号警告通知', 'site_msg',
  '您的账号已被警告',
  '您的账号因{reason}已被警告,请遵守社区规范。',
  TRUE),
('feedback_replied', '反馈已回复', 'site_msg+push',
  '您的反馈已回复',
  '您的反馈"{title}"已被回复:{reply_content}',
  TRUE);

Task D.1.2: 替换 moderationService notification_client.go stub

Files:

  • Modify: backend/services/moderationService/client/notification_client.go

将 v1.5 stub 替换为真实 Dubbo 调用 notificationService.SendNotification

func (c *NotificationClient) SendReportAutoHidden(ctx context.Context, userID int64, targetType string, targetID int64) error {
    _, err := c.cli.SendNotification(ctx, &pbNotification.SendNotificationRequest{
        UserID:  userID,
        Template: "report_auto_hidden_notice",
        Vars: map[string]string{
            "target_type": targetType,
            "target_id":   strconv.FormatInt(targetID, 10),
        },
    })
    if err != nil {
        logger.Sugar.Warnw("send notification failed", "template", "report_auto_hidden_notice", "err", err)
        // 通知失败不影响主流程spec §11.2 "仅日志,不影响主流程"
    }
    return err
}
// 同模式SendReportResolved / SendReportTakedownNotice / SendReportBanNotice / SendReportWarnNotice / SendFeedbackReplied

阶段 D.2Go 单元测试

Task D.2.1: report_service_test.go

Files:

  • Create: backend/services/moderationService/service/report_service_test.go

关键测试用例spec §7.2

  • TestSubmitReport_Duplicate —— 同 reporter+target+type 第二次提交返回 50004
  • TestSubmitReport_SelfReport —— reporter_id == owner_uid 返回 50011
  • TestSubmitReport_AutoHideTrigger —— 5 个独立用户举报 → status='auto_hidden'
  • TestClaimReport_Concurrent —— 2 admin 同时 claim → 1 成功 1 返回 50008
  • TestReleaseReport_OnlyOwner —— 非 claimed_by admin 释放返回 0 行
  • TestDismissPathA_ClearsClaimedBy —— pending 状态必 claimed_by=NULLchk_reports_claimed_pair
  • TestDismissPathB_DismissesFromReviewing —— reviewing → dismissed
  • TestDismissPathC_RestoresAsset —— auto_hidden → reviewing → dismissed(restore=true) 恢复业务表
  • TestDismissPathD_PreservesHidden —— auto_hidden → reviewing → dismissed(restore=false) 不恢复
  • TestResolveReport_Takedown —— takedown 业务表软删除 + mts UPSERT
  • TestWithdrawReport_OnlyPending —— 非 pending 撤回返回 0 行 → 50019
  • TestBulkDismiss_MultipleIds —— 批量驳回返回 affected 数
  • TestForceRelease_AnyAdmin —— 任何 admin 可强制释放他人认领
  • TestClearWarn_PreservesHistory —— warn_cleared 保留 warn_count + last_warned_atv1.7 H2
  • TestAutoHideNoop_WhenAssetAlreadyInactive —— 业务表已 inactive 时记 autohide_noop

Task D.2.2: feedback_service_test.go

  • TestClaimFeedback_Concurrent
  • TestReplyFeedback_ClearsClaimedBy —— replied 终态清 claimed_by/claimed_atchk_feedbacks_claimed_pair
  • TestCloseFeedback_OnlyFromReviewing —— 仅从 reviewing 入口
  • TestArchiveFeedback_NotFromReplied —— replied 状态 archive 返回 0 行v2.0 CRITICAL

Task D.2.3: auto_hide_service_test.go

  • TestLuaScript_FirstIncrement —— 首次 INCR 返回 {0, 1}
  • TestLuaScript_DuplicateUser —— 同用户第二次 SETNX 失败返回 {0, 1} 不再 INCR
  • TestLuaScript_ThresholdReached —— 第 N 次 INCR 返回 {1, N}
  • TestLuaScript_ExpireOnFirst —— n=1 时设置 EXPIRE

Task D.2.4: concurrency_test.go

  • TestConcurrentReports_AutoHide —— 100 用户同时举报同对象counter INCR 100 次但只触发 1 次自动隐藏
  • TestConcurrentClaim_OnlyOneWins —— 2 admin 同时 claim1 成功 1 返回 50008
  • TestLockTTL_BypassAfterExpiry —— 5s 锁过期后新请求可重入
  • TestLuaWithLock_Interaction —— errgroup 模拟锁 + Lua 交互

Task D.2.5: 跑测试

cd backend/services/moderationService
go test ./service/... -v -race
# 期望:全部 PASS

阶段 D.3FastAPI 单元测试

Task D.3.1: test_moderation_admin.py

Files:

  • Modify: TopFans-activity-admin/backend/handlers/test_moderation_admin.py

关键测试

  • test_list_reports —— happy path + 筛选
  • test_claim_report_already_claimed —— 返回 50008响应体含 claimed_by/claimed_at
  • test_claim_report_resolved —— 返回 50009
  • test_claim_report_not_found —— 返回 50007
  • test_execute_action_takedown —— 业务表 + mts 双写
  • test_execute_action_dismiss_path_a —— pending → dismissed 快速通道
  • test_execute_action_dismiss_path_c_restore —— 业务表恢复 + Redis 计数清零
  • test_execute_action_warn_no_soft_delete —— warn 不软删除 user
  • test_execute_action_restore_cte —— restore 用 CTE 条件写 mtsv2.5 W6
  • test_bulk_dismiss —— 批量
  • test_force_release —— 强制释放他人认领
  • test_clear_warn_preserves_history —— 保留 warn_count + last_warned_at
  • test_unauthorized —— 无 token → 401
  • Fixturehttpx.AsyncClient + pytestadmin token 用现有 make_test_token()

阶段 D.4:集成测试

Task D.4.1: testcontainers 集成测试

Files:

  • Create: backend/services/moderationService/integration/

隔离策略spec §7.4

  • testcontainers-go 启临时 PG 16 + Redis 7
  • 业务表自动跑阶段 A.1 迁移
  • Redis key 加 test:mod:report:* 前缀

端到端场景

  • 提交举报 → 后台审核 → 状态变更 → 通知
  • 自动隐藏5 个独立用户举报 → 自动隐藏 + 状态表写入
  • dismiss 4 路径覆盖
  • 跨服务事务回滚:业务表软删除成功但 mts UPSERT 失败 → 整体回滚
  • Lua 脚本测试SETNX/INCR/EXPIRE 边界

阶段 D.5:监控指标

Task D.5.1: 在 moderationService 注册 13+ Prometheus 指标

Files:

  • Modify: backend/services/moderationService/main.go

按 spec §11.3 注册指标:

  • moderation.report.submitted.count (counter, tags: target_type, category_code)
  • moderation.report.auto_hidden.count (counter)
  • moderation.report.auto_hidden.noop.count (counter) (v2.3)
  • moderation.report.resolve.duration (histogram)
  • moderation.feedback.reply.duration (histogram)
  • moderation.feedback.terminal_state.claim_attempt.count (counter, tag: status) (v2.3)
  • moderation.actions.failed.count (counter, tags: action_type, error)
  • moderation.actions.auto_hide.count (counter, tag: target_type) (v2.3)
  • moderation.actions.warn_cleared.count (counter) (v2.3)
  • moderation.report.duplicate.count (counter, tag: target_type)
  • moderation.auto_hide.lock.contention.count (counter)
  • moderation.notification.send_failed.count (counter, tag: template)
  • moderation.transaction.rollback.count (counter, tag: flow)
  • moderation.api.latency (histogram, tag: endpoint, status)
  • moderation.dashboard.queue.pending (gauge)
  • moderation.dashboard.queue.auto_hidden (gauge)
  • moderation.redis.counter_reset.count (counter, tag: path=C|D) (v2.3)
  • moderation.rate_limit.hit.count (counter, tag: type, scope) (v2.3)
  • moderation.api.latency.evidence_upload.p95 (histogram) (v2.3)

自审检查清单Plan D

  • 6 个通知模板已注册到 notificationService
  • moderationService notification_client.go stub 替换为真实现
  • Go 单元测试全部 PASS含 claimed_* 一致性、Lua、并发
  • FastAPI 单元测试全部 PASS含 50008/50009/50014 区分)
  • 集成测试testcontainers通过
  • 13+ Prometheus 指标注册
  • 监控指标覆盖业务成功路径 + 失败/异常 + 延迟/SLO