fix(activity): close Phase 15 KEEP-VERIFY/EXTEND — 2 critical + 14 other defects, 3 test-fidelity fixes
Phase 15 (activity registration / review / checkin) verification closure. Two CRITICAL defects, both "production always had it, test schema never did": - D1: generateCheckinToken wrote the token to Redis with value "used", while scanCheckin rejects any token marked "used" -> every freshly issued check-in code was dead on arrival, staff scanning failed 100% of the time. Issuing now writes "issued"; "used" is set only after a successful scan. - D9: UNIQUE(activity_id, user_id) exists in production, but re-registering after cancel/reject went through INSERT -> users who cancelled once could never register for that activity again (duplicate key 500). The H2 test schema lacked the UNIQUE, so the path had zero coverage. Now the original row is reused via an atomic conditional UPDATE. Checkin (D2-D8): - D2: duplicate check-in was read-then-write; now atomic checkinIfNotCheckedIn and repeat scans are idempotent (alreadyCheckedIn) without duplicate audit. - D3: no data scope at all -> added activity staff assignment (V040) and assertCheckinScope; INV-020 (org relation grants nothing). - D4: manualCheckin did not validate activity existence/cancellation. - D5: getCheckinStats NPE on missing activity; remainingCount was semantically "free seats" -> replaced with notCheckedInCount. - D6: malformed timestamp caused 500 -> 400. D7: constant-time signature compare. Registration / review / cancel (D10-D13): - D10: backend registration list only queried status='approved' -> the review screen never saw pending rows (review unusable). - D11: registration_start_at / registration_end_at were never enforced. - D12: cancel had no audit log (the only write without one). - D13: reject/cancel were read-then-full-update -> rejectIfPending/cancelIfActive. Capacity groups (D14, plan item 2): groups were stored and read back but never participated in capacity -> incrementIfAvailable / decrementIfPositive now make approved_count real; activities without groups are unaffected. Frontend contract (F1-F2): - F1: admin-web had no check-in surface at all -> ActivityCheckinView.vue (paste/scan token, manual check-in, stats) + /activities/checkin route & nav. - F2: miniapp had no cancel-registration wrapper although the endpoint existed. Test fidelity (T1-T3): - T1: test schema gained UNIQUE(activity_id,user_id) + chk_reg_status + chk_checkin_status; two new guards parse the real V010 DDL and compare. - T2: 3 assertTrue(true) placeholders in CheckinServiceTest (covering duplicate check-in, expiry, no-sensitive-plaintext) replaced with 5 real guards that parse the real source and real production DDL. - T3: activity_registrations + activity_staff_assignments moved into REQUIRED_IN_TEST_SCHEMA; MigrationValidationTest baseline 39 -> 40. Note: ActivityStaffAssignment must NOT extend BaseEntity - the table is a composite PK (activity_id, operator_id); BaseEntity would add id/updated_at/ deleted_at to every INSERT and break on PostgreSQL (caught by EntityMigrationParityTest immediately). Tests: ActivityPhase15Test (21), CheckinServiceTest (5), 2 new constraint guards. Negative verification: 3 injected defects -> 8 assertions turned red, all restored. Regression: 474 tests / 0 failures / 0 errors / 3 skipped / BUILD SUCCESS (baseline 450). admin-web vue-tsc 0 error, vite build OK.
This commit is contained in:
@@ -400,12 +400,20 @@ D2——`RelationshipService.createMatch` 不写 episode/lastActivityAt,与 `M
|
||||
|
||||
**Files:** activity registration、checkin 服务和 U29~U32 页面。
|
||||
|
||||
- [ ] 回归报名、审核、取消、签到码、重复签到和名额并发。
|
||||
- [ ] 增加非 matchmaking 活动和 capacity group 场景测试。
|
||||
- [ ] 服务站操作员只能操作指定活动或站点任务。
|
||||
- [x] 回归报名、审核、取消、签到码、重复签到和名额并发。
|
||||
- [x] 增加非 matchmaking 活动和 capacity group 场景测试。
|
||||
- [x] 服务站操作员只能操作指定活动或站点任务。
|
||||
|
||||
**Gate:** 重复请求和并发报名不超额;签到不可跨活动复用。
|
||||
|
||||
> **2026-09-17 复验关闭**:修复 16 处缺陷(8 处签到 + 5 处报名/审核/取消 + 1 处容量分组 + 2 处前端契约)
|
||||
> + 3 处测试保真度缺陷。两处必崩:**D1 签到码一生成即作废**(`generateCheckinToken` 写 `"used"`,
|
||||
> 扫码 100% 失败)、**D9 取消后永远无法再报名**(撞生产 `UNIQUE(activity_id,user_id)`,
|
||||
> 测试库缺该约束故零覆盖)。新增 V040 `activity_staff_assignments` 落实签到数据范围(INV-020)。
|
||||
> 一轮负向验证:3 处注入 → 8 条用例精准变红并还原。全量回归 **474 tests 全绿**(基线 450)。
|
||||
> 报告:`docs/Phase-15-V11-Recovery-Completion-Report-2026-09-17.md`
|
||||
> **Phase 16 未自动进入,等待用户确认。**
|
||||
|
||||
### Phase 16:活动互选与双方开启沟通确认 — REFACTOR
|
||||
|
||||
**Files:** activity_mutual、relationship、messaging 模块及 activity-mutual 页面。
|
||||
|
||||
Reference in New Issue
Block a user