轻松聘AI开发
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.
 
 
 

7.6 KiB

N07 消息撤回:实施指令

状态:N07 定向整改完成并通过自动化与干净构建,待独立代码复验与真实 MySQL/Docker 验收。

文档类型:操作指南。用于指导实现者在不扩大范围的前提下完成 N07。

1. 开始前

项目根目录:

C:\Users\15642\Desktop\zhilai

先完整阅读:

  • docs/message-recall-design.md;
  • docs/frontend-integration.md;
  • docs/enterprise-talent-invitation-api.md;
  • docs/report-block-api.md;
  • docs/phase1-scope-and-acceptance.md。

重点代码:

  • messaging/Message.java;
  • messaging/MessageRepository.java;
  • messaging/MessagingService.java;
  • messaging/MessagingController.java;
  • messaging/Conversation.java;
  • messaging/ConversationMemberRepository.java;
  • talentinvitation/;
  • safety/BlockRelationRepository.java;
  • audit/SecurityAuditService.java;
  • config/SecurityConfig.java;
  • common/exception/ErrorCode.java;
  • common/exception/GlobalExceptionHandler.java。

先执行:

git status --short
git diff --cached --name-only
git log -5 --oneline
git diff --check

如果暂存区非空,停止并报告。保护所有历史未跟踪文件,不得 reset、restore、checkout、clean 或 git add -A。

2. 迁移号分配

编码前列出迁移:

Get-ChildItem mydreamjob-server/src/main/resources/db/migration -File |
  Sort-Object Name |
  Select-Object -ExpandProperty Name

同时检查其他待合并分支是否已经占用下一版本。只有负责人确认后才创建:

V<next>__message_recall.sql

禁止修改 V1–V40,禁止擅自假定 V41 可用。

迁移只允许为 messages 增加:

  • status VARCHAR(20) NOT NULL DEFAULT 'SENT';
  • recalled_at DATETIME NULL;
  • version BIGINT NOT NULL DEFAULT 0;
  • status 的受控 CHECK。

不新增历史表,不保存正文副本。

3. 实体与 Repository

修改 Message:

  • 新增字符串状态或受控枚举;
  • 新增 recalledAt;
  • 新增 @Version Long version;
  • 发送创建状态固定为 SENT。

Repository 增加条件更新,条件至少包括:

  • messageId;
  • conversationId;
  • senderUserId;
  • status=SENT;
  • expectedVersion。

更新内容:

  • status=RECALLED;
  • content=消息已撤回;
  • recalledAt=capturedNow;
  • version 原子递增。

不得只用 findById -> set -> save 作为并发保证。

消息分页和未读查询要排除撤回消息的未读贡献,并采用批量查询,禁止逐消息查 Conversation 或 read 状态。

4. DTO 与映射

在 messaging/dto/ 新增白名单 DTO:

  • MessageResponse;
  • 必要时新增 MessagePageResponse。

把发送、列表、撤回响应收敛到同一个映射器。

DIRECT 兼容保留 senderUserId;TALENT_INVITATION 只返回 senderScope。撤回状态下任何路径都只能返回固定占位正文。

不得直接返回 Message Entity,不得继续在 Controller 拼 Map。

5. 服务实现

为 MessagingService 注入 Clock,生产默认系统时区。撤回入口只捕获一次 capturedNow。

实现顺序:

  1. requireConversation;
  2. assertMember;
  3. 查询消息并防止跨会话枚举;
  4. 校验发送者;
  5. 已撤回则幂等返回;
  6. 校验 capturedNow <= createdAt.plusMinutes(2);
  7. 执行条件更新;
  8. 失败时重新读取并区分幂等完成与真实冲突;
  9. 成功后写一次脱敏审计;
  10. 返回白名单响应。

不得把原正文写入日志、异常或审计。

6. Controller 与接口

新增:

POST /conversations/{conversationId}/messages/{messageId}/recall

无 requestBody,不接收 userId。身份从 SecurityContext/JWT subject 获取。

Controller 只做参数接收、身份解析和响应包装,不包含撤回规则。

7. ErrorCode

先搜索现有消息错误码。只在没有可准确复用的代码时新增:

  • MESSAGE_NOT_FOUND;
  • MESSAGE_RECALL_WINDOW_EXPIRED;
  • MESSAGE_RECALL_CONCURRENT_UPDATE。

保持:

  • 未登录 401;
  • 非成员、非发送者和跨会话探测 403/2002;
  • 超时和并发冲突 409。

不得改变已有错误码含义。ErrorCode 和 GlobalExceptionHandler 修改前应与集成负责人确认。

8. 审计

成功状态转换写一条 MESSAGE_RECALLED。

details 只允许:

  • messageId;
  • conversationId;
  • conversationType;
  • status;
  • requestId。

幂等重放、超时、越权和并发失败不写成功审计。禁止记录正文、对方 ID、联系方式、IP/UA 明文或凭据。

9. 必须新增的测试

建议测试类:

  • MessageRecallServiceTest;
  • MessageRecallControllerTest;
  • MessageRecallPersistenceIntegrationTest;
  • MessageRecallPrivacyIntegrationTest。

必须覆盖:

  1. 窗口内撤回;
  2. 1:59 与恰好 2:00 成功;
  3. 超过 2:00 的最小时间单位失败;
  4. 重复撤回 HTTP 200 且不重复审计;
  5. 非发送者、非成员、ADMIN、匿名;
  6. messageId 属于另一 conversationId 时 403/2002;
  7. 两个并发请求只有一次条件更新和审计;
  8. 撤回后数据库正文为固定占位;
  9. 列表、撤回响应均不返回原正文;
  10. DIRECT 保留 senderUserId;
  11. TALENT_INVITATION 不返回 senderUserId、createdBy 或学生 ID;
  12. 撤回消息不计 unreadCount;
  13. 已有 message_reads 不删除;
  14. markRead 不为撤回消息产生新记录;
  15. 消息分页无逐消息查询;
  16. 审计 details 白名单;
  17. V33 屏蔽发送回归;
  18. V42 邀约会话隐私回归;
  19. DIRECT MATCHED 守卫回归;
  20. candidate_dimension_scores 不变。

完整 JSON 禁止出现:

originalContent
previousContent
studentUserId
candidateId
phone
email
resumeUrl
candidateKey
candidateKeyDigest
ipAddress
userAgent
password
token
secret

10. OpenAPI 与文档

更新:

  • docs/messaging-api.md,不存在则新增;
  • docs/frontend-integration.md;
  • docs/backend-development-roadmap.md;
  • docs/phase1-scope-and-acceptance.md;
  • docs/openapi-rc.json;
  • docs/verify_openapi.py;
  • 本设计和实施指令状态。

目标预计为 173 paths / 211 operations,以实际静态和运行时结果为准。

校验脚本必须递归检查消息响应 schema,不得只检查计数。

11. 自动化验证

依次执行:

cd C:\Users\15642\Desktop\zhilai\mydreamjob-server
mvn -Dtest="*MessageRecall*Test,*Messaging*Test,*TalentInvitation*Test" test
mvn test
mvn clean verify
python ..\docs\verify_openapi.py
git diff --check

记录真实 Tests run、Failures、Errors、Skipped 和 OpenAPI 计数。禁止删除测试或修改期望语义掩盖回归。

12. 真实 MySQL/Docker 验收

自动化完成后由负责人授权执行:

  • Flyway 新迁移 success=1;
  • ddl-auto=validate 通过;
  • 窗口边界与并发撤回;
  • 重复撤回只一条审计;
  • 数据库正文不可恢复为业务明文;
  • 权限矩阵;
  • DIRECT 与邀约完整 JSON 脱敏;
  • unreadCount 与 read 记录;
  • 消息分页和未读 SQL EXPLAIN;
  • RabbitMQ 无新增消息;
  • 运行时 OpenAPI 与静态文件一致。

未执行真库验收不得标记冻结。

13. Git 边界

本轮实现期间:

  • 不操作 Docker,除非负责人明确要求黑盒;
  • 不暂存验收脚本、日志、请求体或 .env;
  • 不执行 git add -A;
  • 不提交、不推送,直到独立验收完成;
  • 不删除历史未跟踪文件;
  • 不进入 N08。

完成代码和自动化后,状态只能写为:

N07 定向整改完成并通过自动化与干净构建,待独立代码复验与真实 MySQL/Docker 验收。