# Phase A 企业注册与入驻基础闭环独立代码复验报告 日期:2026-09-22 ## 1. 复验范围与边界 本次复验针对 Phase A:企业专用注册、`company-register` 验证码用途、验证码原子消费、用户/角色/企业/会话事务、企业入驻聚合查询、测试与 OpenAPI。 未进入 Phase B 已认证企业业务门禁,也未进入 Phase C 管理员用户与角色管理;未修改生产代码、未暂存、未提交、未推送。 ## 2. 前置状态 - 分支:`master` - HEAD:`00153aaf5d92d13e66d6cdc42ecd1fe903d6e666` - 暂存区:空 - 迁移:V1–V46 未修改,未新增 V47 - `pom.xml`:无修改 - 两个既有 Windows stat 缓存假阳性文件的工作区 blob 与 HEAD 相同,未触碰、未暂存 - 历史未跟踪验收文件和 `backend-infra` 文件均保留 ## 3. 独立静态核对 通过项: 1. 注册服务使用事务,按 COMPANY 角色创建用户,并显式写入仅含 COMPANY 的 `user_roles`。 2. 用户、企业保存后 flush,再创建登录会话;企业初始 `verified=false`。 3. 注册审计使用固定脱敏文案,未发现密码、验证码、Token 或摘要输出路径。 4. 验证码消费使用条件更新,条件包含 id、`used=false`、未过期和 attempts 上限,受影响行数必须为 1。 5. 短信用途采用固定白名单,包含 `company-register`,不接受任意非空用途。 6. 匿名规则仅新增 `/auth/company/register`;`/companies/me/onboarding` 仍要求认证。 7. 入驻状态对无记录、PENDING/RESUBMITTED、REJECTED、VERIFIED 和 APPROVED 不一致状态分别处理;不一致状态失败关闭。 8. DTO 未暴露 `userId`、客户端 `role`、`verified`、`passwordHash` 等不应由客户端控制的字段。 9. OpenAPI 中新增注册和入驻查询契约,未改变 V1–V46 或既有业务路径。 ## 4. 定向测试与真实黑盒 实施测试与复验前已执行的 Phase A 定向测试为 **23/23,Failures=0,Errors=0,Skipped=0**,其中包含真实 MySQL/Testcontainers 并发注册测试。 此前全量结果: - `mvn test`:2860/0/0/0 - `mvn clean verify`:2860/0/0/0 - OpenAPI:182 paths / 220 operations,All checks passed 隔离 MySQL 8.4、Redis 7.4、RabbitMQ 3.13 和最新 JAR 的黑盒结果:健康检查通过;发送 `company-register` 验证码后注册成功;密码登录成功;`/auth/me` 返回 COMPANY;入驻查询返回 200;目标数据为 1 个 COMPANY 用户、1 个 COMPANY 角色、1 个企业且 `verified=0`,验证码已消费。临时容器、进程、端口和日志已清理。 ## 5. 阻断项:功能开关绑定不满足严格字面量要求 生产文件:`mydreamjob-server/src/main/java/com/mydreamjob/auth/company/CompanyRegistrationProperties.java` 当前字段为: ```java private boolean enabled = false; ``` Spring Boot 标准布尔绑定会把 `TRUE`、`True`、`yes`、`1`、`on` 均转换为 `true`。独立临时测试使用 `ApplicationContextRunner` 真实绑定配置,逐一设置: ```text app.company-registration.enabled=TRUE app.company-registration.enabled=True app.company-registration.enabled=yes app.company-registration.enabled=1 app.company-registration.enabled=on ``` 复验结果:五个非字面量值均绑定为 enabled=true,测试失败: ```text Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 Expecting empty but was: ["TRUE", "True", "yes", "1", "on"] ``` 这与设计要求“只有字面量 `true` 才开启,其他值不得误开启”不一致,因此 Phase A 独立复验判定 **FAIL**。临时测试已删除,未留在工作区。 整改建议:将配置原始值改为 String,并通过唯一方法严格判断: ```java private String enabled = "false"; public boolean isEnabled() { return "true".equals(enabled); } ``` 同时增加真实 Spring 属性绑定测试,覆盖未设置、`false`、`true`、`TRUE`、`True`、`yes`、`1`、`on`;只有小写字面量 `true` 应开启。 ## 6. 整改前的次要测试覆盖缺口 以下是首次独立复验时发现的证据缺口,不作为已证明的生产缺陷: 1. `CompanyRegistrationTransactionRollbackTest` 目前主要覆盖审计失败,未分别注入并断言 users 保存、user_roles 保存、companies 保存、会话创建和审计写入五类失败;对 user_roles/auth_tokens 的零残留断言也不完整。 2. 注册请求中客户端附带未知的 role/verified/passwordHash 字段的既有测试,可能因验证码未准备而返回 400,不能单独证明未知字段被拒绝;DTO 本身未声明这些字段。 3. tombstone、Redis/数据库/会话依赖异常和每种冲突角色的完整 HTTP 黑盒覆盖仍需单独补齐或明确证据。 ## 7. 复验结论 Phase A 的主要注册、验证码原子消费、事务、并发、入驻聚合和真实中间件路径基本可用,但严格功能开关绑定存在阻断缺陷;另有事务回滚测试覆盖不足。 最终判定:**FAIL,暂不能进入独立通过或提交检查点。** 本次独立复验未修改生产代码、未暂存、未提交、未推送,未进入 Phase B/C。 ## 8. 阻断整改复验(2026-09-22) 针对第 5 节阻断,已按最小范围修改: - `CompanyRegistrationProperties.enabled` 改为字符串,默认值为 `"false"`; - `isEnabled()` 仅返回 `"true".equals(enabled)`; - `CompanyRegistrationFeatureFlagTest` 增加真实 `ApplicationContextRunner` 绑定测试,覆盖 `TRUE`、`True`、`yes`、`1`、`on`、`false` 和 `true`。 整改后验证: - 功能开关及关联测试:**9/9,F=0、E=0、S=0**; - Phase A 定向测试:**24/24,F=0、E=0、S=0**; - `mvn clean verify`:**2861/2861,F=0、E=0、S=0**; - OpenAPI:**182 paths / 220 operations,All checks passed**。 独立绑定测试确认只有字面量 `true` 开启,五种非字面量值均保持关闭。未新增迁移,未进入 Phase B/C,未提交或推送。 功能开关阻断已清除;第 6 节的事务回滚覆盖缺口已在第 9 节补齐。 说明:第 7 节“FAIL”是整改前复验结论;当前状态以第 8、9 节整改复验结果为准。 ## 9. 事务回滚覆盖整改复验(2026-09-22) `CompanyRegistrationTransactionRollbackTest` 已补齐五类独立故障注入: - users 保存失败; - user_roles 真实插入触发器失败(H2 真实 SQL); - companies 保存失败; - 会话创建失败; - 审计写入失败。 每种场景均验证 users、user_roles、companies、auth_tokens 无残留,验证码 `used=false`,并按精确测试手机号清理。测试结果:**5/5,F=0、E=0、S=0**。 ## 10. 阻断修复后的完整回归收口(2026-09-22) 为避免把整改前的历史数字误读为当前结果,最终收口结果单独列示如下: - 功能开关整改后的独立定向:9/9; - 五类事务回滚注入:5/5; - Phase A 完整定向测试:**28/28,F=0、E=0、S=0**; - `mvn clean verify`:**2865/2865,F=0、E=0、S=0,BUILD SUCCESS**; - OpenAPI:**182 paths / 220 operations,All checks passed**; - 真实 MySQL/Testcontainers 并发注册:已执行并通过; - V1–V46 未修改,未新增 V47;未进入 Phase B/C,仍未暂存、未提交、未推送。 期间 H2 user_roles 触发器用于证明真实 SQL 插入失败,触发日志中的预期 SQL 错误属于测试注入证据,不是生产异常;未修改生产错误映射,也未放宽任何断言。 ## 11. 复验重跑收口(2026-09-22) - 独立静态门禁通过:功能开关严格按小写字面量 `true` 开启;匿名放行仅包含 `/auth/company/register`;注册 DTO 未暴露 `userId`/`role`/`verified`/`passwordHash`;验证码白名单、条件消费和 `attempts < maxAttempts` 均已分别在 Service 与 Repository 位置核对;未发现 V47。 - 首次静态脚本曾把 `attempts < c.maxAttempts` 错查在 `SmsVerificationService.java`,造成脚本假阴性;修正为检查 `SmsVerificationCodeRepository.java` 后门禁通过。该问题仅存在于复验脚本,不是产品缺陷。 - Phase A 定向重跑:**28/28,Failures=0、Errors=0、Skipped=0**;真实 `CompanyRegistrationConcurrencyMySqlTest` 启动 MySQL 8.4 Testcontainers 并实际执行。 - OpenAPI:**182 paths / 220 operations,All checks passed**。 - 收尾:无 Java/Maven 残留进程,8080/8081 无监听,暂存区为空;本轮未修改生产代码、未提交、未推送,未进入 Phase B/C。