# N07 消息撤回:实施指令 状态:N07 定向整改完成并通过自动化与干净构建,待独立代码复验与真实 MySQL/Docker 验收。 文档类型:操作指南。用于指导实现者在不扩大范围的前提下完成 N07。 ## 1. 开始前 项目根目录: ```text 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`。 先执行: ```powershell git status --short git diff --cached --name-only git log -5 --oneline git diff --check ``` 如果暂存区非空,停止并报告。保护所有历史未跟踪文件,不得 reset、restore、checkout、clean 或 `git add -A`。 ## 2. 迁移号分配 编码前列出迁移: ```powershell Get-ChildItem mydreamjob-server/src/main/resources/db/migration -File | Sort-Object Name | Select-Object -ExpandProperty Name ``` 同时检查其他待合并分支是否已经占用下一版本。只有负责人确认后才创建: ```text V__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 与接口 新增: ```http 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 禁止出现: ```text 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. 自动化验证 依次执行: ```powershell 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。 完成代码和自动化后,状态只能写为: ```text N07 定向整改完成并通过自动化与干净构建,待独立代码复验与真实 MySQL/Docker 验收。 ```