Files
danyang-match/docs/PROGRESS-ASSESSMENT-2026-09-21.md
WorkBuddy 8bd1ab804a fix(organization): 修复创建机构审计缺操作人;补全机构修复的验证并部署 Docker 测试环境
一、机构审计归属缺陷(本轮活栈验收中新发现)
- 现象:audit_logs 中 CREATE_ORGANIZATION 的 operator_id 7/7 为 NULL,
  而 DISABLE/ENABLE_ORGANIZATION 均正确记录操作人。审计不可归属 = 无效审计。
- 根因:OrganizationController.create 持有 @AuthenticationPrincipal OperatorPrincipal,
  却只把 params 传给 Service,未透传 principal.id() → saveAuditLog(null, ...)。
- 修复:createOrganization(params, operatorId) 签名透传;新增测试断言
  createOrganizationRecordsOperatorInAuditLog 长期守卫。
- 复验:新写入的 7 条 CREATE 审计全部带 operator_id,旧 14 条仍为 NULL(前后对照清晰)。

二、补全 rc.22 的验证缺口(此前记为「已修复待验证」)
- 全量回归 757 / 0 / 0 / 3,BUILD SUCCESS,108 个测试类,6:44(基线 741 + 新增 16)。
- 负向注入:ORG_STATUS_ACTIVE 改回 "normal" → 16 例变 3 失败 + 7 错误
  (守卫三条断言各自命中 + 服务侧 7 例被 H2 CHK_ORG_STATUS 拦下)。
  注入采用「仓库只读挂载 + 容器内副本改写」,工作区哈希前后一致,源码零污染。
- 活栈实测(Docker 全栈):创建/停用/启用均 2xx、非法输入 400、不存在 404、全程零 5xx(15/15)。
- 对已部署栈 pytest tests/e2e → 65 passed。

三、Docker 测试环境部署与工具链固化
- 测试镜像 danyang-match-backend-test 与命名卷 danyang-m2-cache 因 Docker Desktop 重装丢失,
  构建定义此前是临时产物 → 落盘为仓库资产 infrastructure/test-toolchain/Dockerfile。
- 本地栈统一使用 docker compose -p danyang(原项目名 infra 与另一项目撞名,
  其 --remove-orphans 会误删本项目容器;已确认邻项目数据无丢失,见 known-risks KR-09)。

四、UI 门禁脆弱性修复
- ui-document-baseline-contracts.mjs 解析 docker-compose.staging.yml 时报假红
  (「守卫自身失效」);真因是该文件为 CRLF 而守卫正则要求 \n 紧随 container_name 之后
  (prod.yml 是 LF 所以此前能过)。解析改为行尾无关,真实断言未削弱。门禁 18/18。

五、文档
- STATUS.md 新增 rc.23 并将 rc.22 由「待验证」订正为「已验证关闭」。
- 实施计划附 B.1、PROGRESS-ASSESSMENT-2026-09-21.md 同步订正。
- known-risks.md:KR-07 解除(Docker 已恢复)、KR-01/KR-06 按复测订正,
  新增 KR-08(Docker Hub 域名 DNS 被投毒,改用国内镜像源绕行)与 KR-09(compose 项目名撞名)。

验证状态:全量回归 757/0/0/3 全绿;UI 契约门禁 18/18;对已部署栈 E2E 65/65;
负向注入可变红;工作区哈希未被注入过程污染。
未验证:GitHub Actions 首次真绿(仍未推送,见 KR-06)。
2026-09-22 00:46:10 +08:00

220 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目进度评估报告 — 2026-09-21
> 评估方式:以 `docs/STATUS.md`(权威状态表)为索引,**逐项回溯代码、迁移、测试与 CI 配置实测复核**,
> 不采信文档中的历史「✅」声明。凡本文标注「实测」的结论均可按「核查证据」栏复现。
>
> 评估基线:`main` @ `60c591f`(2026-09-21 11:13),工作区干净;最新 tag `v101-dod-complete-2026-09-21`。
---
## 一、总体结论
**代码与测试面已具备进入 UAT 的条件;阻塞点是人工确认与环境补绿,外加一处此前登记但未修复的运营端写操作缺陷。**
| 维度 | 结论 |
|---|---|
| 业务功能 | Phase 01~22 全部 KEEP-VERIFY 复验关闭;Phase 23 批 1~3 完成 |
| 质量门禁 | 后端 741 / 0 失败 / 0 错误 / 3 跳过;UI 契约门禁 18/18;E2E 本机活栈 65 通过 |
| 数据层 | 迁移 V001~V051(51 个文件,无缺号);H2 测试库表漂移盲区已在本轮前多轮补齐 |
| 停点 | **Phase 24(UAT)启动门前** —— 按项目规则须由用户确认,不得自动进入 |
| 风险等级 | **中** —— 无架构级问题,但存在 1 项 High 未修缺陷 + 2 项环境/流程阻塞 |
---
## 二、进度快照
### 2.1 Phase 矩阵
| Phase 区间 | 状态 | 证据 |
|---|---|---|
| 01~06 基线 / Auth / RBAC / Profile / 审核 / Match Engine | ✅ 关闭 | 各 Phase Completion Report |
| 07 V1.1 首页 + 找对象 | ✅ 关闭 | 2026-09-16 用户验收(基本达标);11 页 UI 验收通过 |
| 08~13A Like/Match/Block · Chat · 图片安全 · 通知 · 关系扩展 · 红娘转介 · 咨询辅导 | ✅ 关闭 | 2026-09-16/17 KEEP-VERIFY,共修复 40+ 真实缺陷 |
| 14~20 多类型活动 · 报名签到 · 互选双确认 · 举报风控 · 申诉 · 运营后台 · Analytics | ✅ 关闭 | 2026-09-17 复验,修复 60+ 缺陷(含 4 处必崩级) |
| 21 账户生命周期 | ✅ 关闭 | 2026-09-18;注销三处 TODO 桩已实现;数据保留流程 V049 落地 |
| 22 Feature Flag | ✅ 关闭 | 死配置已移除;`PUT /feature-flags` 真持久化(V048) |
| 23 可信系统测试重建 | ⚠️ **部分关闭** | 假覆盖归零、E2E 场景真实实现;**仅余 CI 上首次跑绿** |
| 23B E2E 基建 + 横向加固(HTTP 语义 / 身份收敛 / 幂等键) | ✅ 关闭 | 幂等键 30 端点、105 处真实状态码 |
| V1.0.1 配置与规则纠偏 | ✅ 关闭 | rc.21(2026-09-21);governance 六表 + 并发强校验 + canonical key |
| 24 UAT | ⏳ 待启动 | 用例 46 条(9 角色 × 7 路径)已编写;**需用户确认** |
| 25 RC 内容 / 26 生产 Smoke | ⏳ 待运营 | 真实素材未提供;13 项 smoke 待执行 |
### 2.2 量化指标
| 指标 | 实测值 | 核查方式 |
|---|---|---|
| 后端测试 | 741 / 0 failures / 0 errors / 3 skipped | `docs/STATUS.md` rc.21 行(离线容器 `mvn -o test`,命令见计划附 B-9) |
| 测试类文件 | 108 个 | `backend/src/test/**/*.java` 计数 |
| UI 契约门禁 | 18/18(`UI_GUARDS_MIN` 下限守卫防删除报绿) | `ui-tests/` 18 个 `.mjs`;执行器 `scripts/run-ui-contracts.mjs` |
| E2E 用例 | 65 通过(本机真栈真库) | `tests/e2e/` 14 个测试文件 |
| 数据库迁移 | V001~V051,51 个,无缺号重复 | `backend/src/main/resources/db/migration` |
| CI job | 7 个:backend / admin-web / miniapp / migration / test-shell-guard / ui-contracts / e2e | `.github/workflows/ci.yml` |
| 待产品决策 | 13 项 + 第三方开放遗留 7 项 | `docs/PENDING_DECISIONS.md`、`docs/governance/open-questions.md` |
---
## 三、关键发现:一处 High 缺陷登记后未修复
### 3.1 机构创建 / 停用 / 启用三个写操作必然失败(High,**2026-09-22 已验证关闭**)
> **处置完成(2026-09-22 复核)**:本项已按第五节 P0-1 执行完毕 ——
> `Constants` 新增 `ORG_STATUS_ACTIVE/INACTIVE`、三处状态值修正、`type` 白名单前置校验、
> 测试库 `schema-h2.sql` 补两条 CHECK 及 NOT NULL/DEFAULT、
> 新增 `OrganizationConstraintParityTest` 与 `OrganizationServiceTest`。
>
> **2026-09-22 完成全部补跑验证(此前记为「已修复待验证」的缺口已补齐)**:
> 1. **全量回归 757 / 0 / 0 / 3,BUILD SUCCESS**(108 个测试类,6:44;基线 741 + 新增 16 = 757);
> 2. **负向注入可红**:把 `Constants.ORG_STATUS_ACTIVE` 改回 `"normal"` 后,16 个用例变
> **3 失败 + 7 错误** —— 守卫 `backendWhitelistsMatchProductionDdl` /
> `orgStatusConstantsCoverProductionWhitelist` / `orgStatusMustNotReuseAccountStatusValues`
> 三条断言各自命中该误用,服务侧 7 例被 H2 `CHK_ORG_STATUS` 拦下。注入采用
> **仓库只读挂载 + 容器内副本改写**,工作区哈希前后一致(`28aaaedb…`),源码零污染;
> 3. **活栈实测(Docker 全栈)**:创建 / 停用 / 启用**全部 2xx** 且落库为 `active`/`inactive`,
> 非法 `type` 与缺字段 400 `VALIDATION_ERROR`,不存在机构 404 `NOT_FOUND`,
> **全流程零 5xx**(15/15 通过);
> 4. **对已部署栈跑 `pytest tests/e2e`:65 passed**。
>
> **本项状态:已验证关闭。**
> 另:活栈验收中新发现同族缺陷「创建机构审计缺操作人」——`CREATE_ORGANIZATION` 审计行
> `operator_id` 7/7 为 NULL(停用/启用却正确记录),已一并修复并复验,见 `docs/STATUS.md` rc.23。
该问题于 2026-09-19 已在实施计划「附 B.1」中登记并实测复现,但截至本次评估(2026-09-21)**代码未改动**。
**缺陷本体**(实测复核,2026-09-21):
| 位置 | 写入值 | 生产约束 | 结果 |
|---|---|---|---|
| `OrganizationService.java:43` `createOrganization` | `ACCOUNT_STATUS_NORMAL` = `"normal"` | `V001:45 chk_org_status CHECK (status IN ('active','inactive'))` | 违约 → 500 |
| `OrganizationService.java:63` `disableOrganization` | `ACCOUNT_STATUS_SUSPENDED` = `"suspended"` | 同上 | 违约 → 500 |
| `OrganizationService.java:73` `enableOrganization` | `ACCOUNT_STATUS_NORMAL` = `"normal"` | 同上 | 违约 → 500 |
**根因**:`organizations.status` 复用了语义上属于 `users` 表的常量 `Constants.ACCOUNT_STATUS_*`。
`Constants` 中**不存在**机构状态常量(`ORG_STATUS_ACTIVE/INACTIVE` 均无)—— 典型的「跨表常量误用」,
与项目此前多轮修复的「实体↔迁移漂移」「CHECK 白名单未对齐」同族。
**二阶影响(决定优先级)**:
`activities.organizer_org_id` 为 NOT NULL → **无机构则无法创建活动**。
即运营端「新建机构 → 新建活动 → 用户报名 → 互选」这条链路的第一环是断的。
而 UAT 用例覆盖活动互选、报名、签到等场景,因此该缺陷**可能阻塞 UAT 执行**。
**为什么门禁全绿却没发现**:
1. 108 个测试类中**没有任何一个**引用 `OrganizationService` / `createOrganization`(实测:0 命中);
2. `tests/e2e/sql/seed_e2e_activity.sql` 是**直接写库**插入机构与活动,绕过了 Service 层
—— E2E 全绿掩盖了 API 路径的失败;
3. H2 测试库此前长期缺约束复刻,即便有测试也可能静默通过。
> 这正好复现项目已总结过两次的模式:**「测试环境用数据为空 / 直连绕过,掩盖代码必崩」**。
**附带问题(同文件,Low)**:
- `createOrganization` 与 `updateOrganization` 均**未校验 `type`** 白名单
(`chk_org_type` 仅允许 women_federation / trade_union / charity_center / volunteer_team /
civil_affairs_partner / other);
- 私有 `saveAuditLog` 未填 `ip` / `userAgent`(Phase 17 只增强了 `AuditLogService.recordSensitiveAccess`)。
**建议修法**:新增 `Constants.ORG_STATUS_ACTIVE/INACTIVE`,三处改为 `active` / `inactive`;
补 `type` 白名单校验;新增 `OrganizationServiceTest`(走真实 DB,覆盖创建/停用/启用/type 非法),
另加一条**解析 V001 DDL 取最终白名单**的常量守卫(参照 `MatchmakerReferralPhase13Test` 的写法,
不要硬编码白名单副本 —— 那条教训已记录在 Phase 13 报告中)。修复后需 `git tag` 打点并回读验证。
### 3.2 其余已登记未处置项
| 项 | 级别 | 说明 |
|---|---|---|
| 用户侧取消预约不校验状态 | Medium | `ServiceAppointmentService.cancelAppointment` 只校验归属,已确认/已完成也能被本人取消;运营侧明确拒绝 `completed`。两侧口径不一致,**需产品裁定**(计划附 B.3) |
| Phase 22 Deployment/Ops 5 项未复验 | Medium | 删除镜像跳过测试的发布路径、生产关 Mock/Seed/Swagger、端口依赖检测、**备份加密 + 真实恢复演练**、Rollback 目标(`REMAINING_WORK.md` B 组) |
| 多实例下 flag 内存值不自动刷新 | Low | 已知边界,需各自重启(Phase 22 报告) |
---
## 四、环境与流程风险
### 4.1 Git 在当前执行环境不可用(阻塞提交纪律)
实测:`git` 报 `fatal: detected dubious ownership in repository`。
仓库目录属主为 `S-1-5-21-881075190-…`,当前用户为 `CLIMBER/13070`,二者不匹配。
**影响**:`AGENTS.md` 的版本控制约定(每批独立 commit + 单段名 tag + `git bundle` 自保 + **回读验证**)
是项目防御「常驻进程删 refs 导致 HEAD 悬空」的核心手段,该链路当前**无法执行**。
本次评估为只读目的,以 `GIT_CONFIG_COUNT/KEY/VALUE` 环境变量临时注入 `safe.directory` 绕过,
**未修改任何全局配置**。
**建议**:由用户决定是否执行
`git config --global --add safe.directory 'E:/develop/公益婚恋服务平台'`。
在修复前,任何需要提交的工作都应先确认该前提,否则改动无法被打点保护。
(另:`feat-ui-v11` 已被 `main` **完全包含**(落后 71 个提交、领先 0),仅为冗余锚点,
可考虑清理,但 `AGENTS.md` 目前要求保留 —— 属文档与事实的轻微不一致。)
### 4.2 CI 回归防线从未在真实 CI 上验证过
7 个 job 均已配置,其中 `ui-contracts`(18 门禁)与 `e2e`(Compose 起全栈 + seed + pytest)配置完整,
但 **E2E 作业从未在 GitHub Actions 上跑绿过一次**(计划 Task 2.3 Step 3)。
本机已用同一序列模拟实测 65/65,但「本机绿」与「CI 绿」不是同一件事
—— 此前已发生过「本机有遗留 admin 账号所以本地一直绿、CI 全新库必然 401」的先例。
### 4.3 文档体量与自相矛盾
`docs/` 91 个文件、`STATUS.md` 单文件 90KB,内含多处「历史声明块(已作废)」并列保留;
`PENDING_DECISIONS.md` 与 `docs/governance/*` 三表职责交叉并依赖「矛盾以 governance 三表为准」的约定;
`REMAINING_WORK.md` 被迫以「原编号索引」兼容历史引用。
这是治理的必要代价,但已产生实际成本:文档中出现过与实际严重不符的陈述
(如 `CHANGELOG.md` 3.3MB vs 实际 4.2KB、指向从未存在的报告文件)。
**建议**:不再新增并列的「状态类」文档;本评估报告即为一次性快照,结论应回写进 `STATUS.md` 而非另立体系。
---
## 五、下一步建议
### 优先级 P0 — 建议立即做(无需用户决策,纯技术清偿)
1. **修复机构三个写操作 + 补测试与守卫**(3.1)。✅ **已修复且已通过全部验收项(2026-09-22)**:
全量回归 **757 / 0 / 0 / 3 全绿**;负向注入 16 例变 **3 失败 + 7 错误**(守卫三条断言全中 + H2 `CHK_ORG_STATUS` 拦下 7 例);
活栈实测 `POST /operator/organizations` 与停用/启用**均 2xx**、非法输入 400、不存在 404、全程零 5xx(15/15);
对已部署栈 `pytest tests/e2e` **65 passed**。**本项关闭。**
附带清偿同族缺陷「创建机构审计缺操作人」(见 rc.23)。
2. **确认 Git 可用性**(4.1)。决定是否加 `safe.directory`,否则先不要写代码 —— 写了也打不了点。
### 优先级 P1 — 需用户/环境配合
3. **推送一次 CI,拿到 E2E 与 ui-contracts 的首次真绿**(需有 Docker Hub 出口的 runner)。
完成后 Phase 23 可由「部分关闭」转为关闭。
4. **管理端桌面端目视复验(1440px)**(批 4 Task 4.1)。小程序 375px 增补页面同理。
这是当前唯一「代码已就绪但从未被真人看过」的验收项。
5. **13 项产品决策 + 7 项第三方开放遗留**(批 4 Task 4.2)。
建议分批:先决 `OQ-MATCH-001`(推荐阈值)、`OQ-PROFILE-001`(照片上限)、
`OQ-IDENTITY-001`(活体核验)三项 —— 它们已被配置化,决策后只需改配置值;
Banner 最大活跃数(`OQ-BANNER-001`)是真缺口,需产品定值后**需实现** active-count 校验。
### 优先级 P2 — UAT 启动后的并行工作
6. **Phase 24(UAT)启动确认** —— 按项目规则须用户明确同意,AI 不得自动进入。
启动前建议先完成 P0-1 与 P1-4。
7. **Phase 22 Deployment/Ops 5 项复验**,其中「备份加密 + 真实恢复演练」是上线硬门槛。
8. **用户侧取消预约口径统一**(3.2)—— 一次性产品裁定即可关闭。
### 优先级 P3 — 运营侧长周期
9. Phase 25 真实素材(Banner / 课堂 / 反诈 / 关于我们 / 组织信息),**不得 Seed 假数据**。
10. Phase 26 上线前 13 项 smoke + RC 冻结(镜像 / DB 版本 / 配置清单 / Rollback 目标)。
---
## 六、本次评估的核查证据
| 结论 | 证据 |
|---|---|
| 代码与测试就绪 | `docs/STATUS.md` 文首「当前有效结论」+ rc.19~rc.21 版本演进行 |
| 机构缺陷未修 | `backend/src/main/java/com/danyang/match/platform/organization/service/OrganizationService.java:43,63,73`;`Constants.java:11,14`;`V001__init_base_tables.sql:45`(三处逐一读源确认) |
| 机构零测试覆盖 | `backend/src/test/**` 全量检索 `OrganizationService` / `createOrganization` → 0 命中(108 个测试类) |
| E2E 绕过 Service 层 | `tests/e2e/sql/seed_e2e_activity.sql` 直插库 |
| CI 配置完整但未验证 | `.github/workflows/ci.yml` 全文;计划 Task 2.3 Step 3 仍为未勾选项 |
| Git 不可用 | `git rev-parse HEAD` 报 `dubious ownership`;属主 SID 与当前用户不一致 |
| 迁移基线 | `db/migration` 计数 51,最新 `V051__canonical_liveness_flag_key.sql` |
| 剩余项清单 | `REMAINING_WORK.md`、`docs/PENDING_DECISIONS.md`、计划「批 4 / 批 5」 |
---
*评估人:WorkBuddy · 创建:2026-09-21*
*性质:一次性快照。结论请回写 `docs/STATUS.md`,本文件不作为长期状态源。*