You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8.6 KiB
8.6 KiB
举报与屏蔽 API(V33)
版本:V33 实现版 状态:已编码实现,待黑盒验收 适用范围:举报、屏蔽、ADMIN 举报处理;仅允许对
MessagingService增加屏蔽校验。 OpenAPI 117 → 125 paths(新增 8 path / 9 operation)。
1. 定位与边界
| 接口 / 模块 | 是否改动 | 关系 |
|---|---|---|
| 投递、Offer、面试、评分、治理、看板、匹配 | 不改 | 屏蔽不影响系统通知、投递、Offer、面试、评分、评价 |
MessagingService |
仅改两方法 | createOrGetDirectConversation、sendMessage 增加屏蔽校验 |
listMessages / getConversation / listConversations |
不改 | 历史消息不删除,按现有权限读取 |
NotificationService |
不改语义 | 复用,处理完成后向举报人发送通知,不新增 NotificationType 枚举 |
| AI 自动审核 / 自动封禁 / 自动警告 / 公平性算法 | 不实现 | 本轮明确排除 |
2. 数据库(V33,三张表)
reports:举报单。(reporter_user_id, idempotency_key)唯一;active_key唯一;target_type仅 USER/MESSAGE;status仅 PENDING/RESOLVED/REJECTED;message_id外键ON DELETE SET NULL;reporter_user_id/reported_user_id外键关联users;@Version乐观锁;evidence_urls使用 TEXT 存 JSON 字符串(不采用 MySQL JSON 列)。block_relations:(blocker_user_id, blocked_user_id)唯一;禁止自己屏蔽自己;外键ON DELETE CASCADE。report_decision_history:report_id外键ON DELETE CASCADE;记录from_status → to_status、操作人、角色、note。
约束强校验:target_type=USER 时 target_user_id 非空且 message_id 为空;target_type=MESSAGE 反之。
3. 举报接口
POST /reports 提交举报
GET /reports/me 我的举报列表(status/page/size)
GET /reports/{id} 举报详情(本人或 ADMIN)
- 身份全取 JWT,不接受
userId/reporterId/reportedUserId注入。 - USER 举报:客户端传
targetUserId,服务端校验存在,targetUserId ≠ 本人,reportedUserId = targetUserId。 - MESSAGE 举报:仅传
messageId,服务端校验存在 + 本人是会话参与者(无权返回 403/2002,不泄露消息是否存在),reportedUserId从消息真实 sender 推导,禁止客户端覆盖。 - 路由:
/reports/me必须在/reports/{id}之前注册,并有路由回归测试。
幂等与防重:
| 场景 | 行为 |
|---|---|
| 同 reporter + idempotencyKey + 相同请求体 | 返回原举报,200(不新增) |
| 同 reporter + idempotencyKey + 不同请求体 | 409 / 9606 |
| 同 reporter + 同目标 + 同 category + 同 messageId/targetUserId + 已有 PENDING | 409 / 9603 |
| 已 RESOLVED/REJECTED | 不占用 active_key,允许重新提交 |
evidenceUrls 校验:最多 5 个、每个 ≤500 字符、仅 http/https;后端不主动请求/抓取 URL(防 SSRF)。
4. 屏蔽接口
PUT /blocks/{blockedUserId} 屏蔽
DELETE /blocks/{blockedUserId} 解除屏蔽(幂等)
GET /blocks/me 我的屏蔽列表(page/size)
- 屏蔽自己 → 400 / 9608;目标不存在复用以有用户不存在错误码。
- PUT 已存在返回 200 不新增;DELETE 不存在返回 200 不报错;GET 仅返回本人主动屏蔽的用户。
- 响应不返回对方手机号/邮箱/密码/简历地址。
- 屏蔽不影响系统通知、投递、Offer、面试、评分、评价。
5. MessagingService 屏蔽控制
createOrGetDirectConversation:若 A 屏蔽 B 或 B 屏蔽 A 任一方向存在,禁止新建 DIRECT 会话。sendMessage:会话内任一方向存在屏蔽,禁止发送新消息。- 对外统一
403 / 2002 / "message cannot be sent",不返回“你已被屏蔽”,不泄露哪一方屏蔽了谁。 - 内部审计记录
BLOCKED_SEND_PROHIBITED。 - 屏蔽期间发送失败的消息直接丢弃,解除屏蔽后不补发。
listMessages、getConversation、listConversations保持现有语义。
6. ADMIN 举报处理接口
GET /admin/reports 举报列表(status/targetType/category/page/size)
GET /admin/reports/{id} 举报详情(含处理历史、脱敏证据 URL、MESSAGE 脱敏内容)
POST /admin/reports/{id}/decision 处理决定
- 仅 ADMIN;COMPANY / STUDENT / 未登录分别 403 / 403 / 401。
decision仅 RESOLVED / REJECTED;REJECTED 必填note。- 仅 PENDING 可处理,重复 → 409 / 9604;非法流转 → 409 / 9605;乐观锁冲突 → 409 / 9605。
- 处理不自动封禁、不删除消息、不修改投递/面试/评分状态。
- 权限矩阵:
| 身份/场景 | HTTP | code |
|---|---|---|
| 未登录 | 401 | 2001 |
| COMPANY / STUDENT | 403 | 2002 |
| ADMIN | 200 | — |
| 非本人非 ADMIN 查详情 | 403 | 2002 |
| 举报不存在 | 404 | 9601 |
7. 错误码
| code | 枚举 | HTTP | 说明 |
|---|---|---|---|
| 9601 | REPORT_NOT_FOUND | 404 | 举报不存在 |
| 9602 | CANNOT_REPORT_SELF | 400 | 不能举报自己 |
| 9603 | REPORT_DUPLICATE | 409 | 同目标同分类已有 PENDING |
| 9604 | ALREADY_DECIDED | 409 | 重复决定 |
| 9605 | INVALID_TRANSITION | 409 | 非法流转 / 乐观锁冲突 |
| 9606 | IDEMPOTENCY_CONFLICT | 409 | 幂等键冲突(不同请求体) |
| 9607 | REASON_REQUIRED | 400 | 理由缺失(注意复用 BAD_REQUEST 由校验层触发) |
| 9608 | CANNOT_BLOCK_SELF | 400 | 不能屏蔽自己 |
| 9609 | REPORT_RATE_LIMITED | 429 | 举报频率超限 |
| 9610 | BLOCKED_SEND_PROHIBITED | 内部 | 仅内部审计/日志,HTTP 不直接暴露 |
复用:BAD_REQUEST(1001)、UNAUTHORIZED(2001)、FORBIDDEN(2002)、用户/消息不存在错误码。
8. 举报限流
- 使用现有 Redis,不新增数据库表;仅限制
POST /reports。 - 每用户默认每小时最多 10 次;key:
report:rate:{userId}:{hour}。 - 超限 → 429 / 9609,记录
REPORT_RATE_LIMITED审计。 - 不影响屏蔽、查询、ADMIN 处理接口;不实现自动封禁。
9. 通知闭环
- 处理完成后向举报人发送一条通知,复用 NotificationService,不新增枚举。
type=REPORT_DECISION、businessType=REPORT、businessId=reportId、idempotencyKey=report-decision-{reportId}。- RESOLVED / REJECTED 均可通知;同一举报最多一条处理结果通知(幂等)。
- 举报提交时不通知被举报人,不通知被举报人的联系方式。
10. 审计
新增 action:REPORT_CREATED、REPORT_DECIDED、BLOCK_CREATED、BLOCK_REMOVED、BLOCKED_SEND_PROHIBITED、REPORT_RATE_LIMITED。
details 仅含:reportId、userId、category、decision、targetType、messageId、requestId、timestamp。
禁止写入:phone、email、password、JWT、resumeUrl、消息全文、文件绝对路径、evidence 文件内容、数据库连接信息。
11. 并发与幂等
- 举报:
(reporter_user_id, idempotency_key)唯一 +active_key唯一 + 创建事务;并发重复仅一条成功,另一条 9603 或同幂等返回原记录。 - 屏蔽:唯一约束保证并发 PUT 不产生重复关系;DELETE 幂等;屏蔽检查与发送消息在同一业务事务内。
- 处理:
@Version乐观锁,冲突 409/9605。
12. 脱敏策略
- 响应永不返回 phone、email、passwordHash、resumeUrl、JWT、消息全文、举报人内部 ID(非 ADMIN)。
- MESSAGE 举报仅返回脱敏
messagePreview,不返回会话内其他敏感内容。 - 对外屏蔽发送统一模糊错误,不泄露屏蔽关系方向。
13. OpenAPI 变化
- 新增 8 path / 9 operation,117 → 125 paths。
- 新 path:
/reports、/reports/me、/reports/{id}、/blocks/{blockedUserId}(PUT+DELETE)、/blocks/me、/admin/reports、/admin/reports/{id}、/admin/reports/{id}/decision。 - 声明:bearerAuth、请求/响应 DTO、USER/MESSAGE 枚举、PENDING/RESOLVED/REJECTED 枚举、400/401/403/404/409/429、屏蔽模糊错误、MESSAGE 需 messageId、USER 需 targetUserId。
14. 测试覆盖要点
- USER 举报;2. MESSAGE 举报;3. MESSAGE 发送者服务端推导;4. 无权访问 messageId;5. 举报自己;6. 不存在 targetUserId;7. evidenceUrls 校验;8. 幂等同请求体;9. 幂等不同请求体;10. PENDING 重复;11. ADMIN 决定;12. 重复决定;13. 决定历史;14. 屏蔽/解除;15. 重复屏蔽/解除;16. 屏蔽后禁止 DIRECT;17. 屏蔽后禁止发送;18. 历史消息可读;19. 失败消息不补发;20. 频率限制;21. 通知幂等;22. 权限矩阵;23. 脱敏;24. 乐观锁并发;25. 业务数据不删除;26. V33 迁移 H2(MODE=MySQL)/MySQL 兼容。