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。
实现顺序:
- requireConversation;
- assertMember;
- 查询消息并防止跨会话枚举;
- 校验发送者;
- 已撤回则幂等返回;
- 校验
capturedNow <= createdAt.plusMinutes(2); - 执行条件更新;
- 失败时重新读取并区分幂等完成与真实冲突;
- 成功后写一次脱敏审计;
- 返回白名单响应。
不得把原正文写入日志、异常或审计。
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:59 与恰好 2:00 成功;
- 超过 2:00 的最小时间单位失败;
- 重复撤回 HTTP 200 且不重复审计;
- 非发送者、非成员、ADMIN、匿名;
- messageId 属于另一 conversationId 时 403/2002;
- 两个并发请求只有一次条件更新和审计;
- 撤回后数据库正文为固定占位;
- 列表、撤回响应均不返回原正文;
- DIRECT 保留 senderUserId;
- TALENT_INVITATION 不返回 senderUserId、createdBy 或学生 ID;
- 撤回消息不计 unreadCount;
- 已有 message_reads 不删除;
- markRead 不为撤回消息产生新记录;
- 消息分页无逐消息查询;
- 审计 details 白名单;
- V33 屏蔽发送回归;
- V42 邀约会话隐私回归;
- DIRECT MATCHED 守卫回归;
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 验收。