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:
@@ -95,7 +95,13 @@ CREATE TABLE activity_registrations (
|
||||
reject_reason VARCHAR(500), checkin_status VARCHAR(32), checkin_at TIMESTAMP WITH TIME ZONE,
|
||||
emergency_contact_name VARCHAR(100), emergency_contact_phone_enc VARCHAR(500), source VARCHAR(64),
|
||||
request_source VARCHAR(64), created_by_operator_id VARCHAR(36), user_confirmed_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
-- Phase 15(V010):生产约束此前测试库完全没有 —— 约束漂移盲区(与 Phase 11/13/14 同族根因)。
|
||||
-- 缺 UNIQUE(activity_id, user_id) 使「取消/拒绝后重新报名」撞唯一键的必崩缺陷在测试期完全不可见;
|
||||
-- 缺两个 CHECK 使非法 status / checkin_status 只在 PostgreSQL 上炸。
|
||||
CONSTRAINT uk_activity_registration UNIQUE (activity_id, user_id),
|
||||
CONSTRAINT chk_reg_status CHECK (status IN ('pending', 'approved', 'rejected', 'cancelled')),
|
||||
CONSTRAINT chk_checkin_status CHECK (checkin_status IN ('not_checked_in', 'checked_in'))
|
||||
);
|
||||
CREATE TABLE service_appointments (
|
||||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), service_location_id VARCHAR(36), service_type VARCHAR(64),
|
||||
@@ -267,6 +273,15 @@ CREATE TABLE activity_capacity_groups (
|
||||
CONSTRAINT uk_capacity_group UNIQUE (activity_id, group_code)
|
||||
);
|
||||
|
||||
-- V040: activity_staff_assignments 活动工作人员指派表(Phase 15 签到数据范围)
|
||||
-- 主办/协办组织关系不授予操作权限(INV-020),签到必须来自显式指派。
|
||||
CREATE TABLE activity_staff_assignments (
|
||||
activity_id VARCHAR(36) NOT NULL, operator_id VARCHAR(36) NOT NULL,
|
||||
assignment_role VARCHAR(32) NOT NULL DEFAULT 'checkin_staff',
|
||||
created_by VARCHAR(36), created_at TIMESTAMP WITH TIME ZONE,
|
||||
PRIMARY KEY (activity_id, operator_id),
|
||||
CONSTRAINT chk_activity_staff_role CHECK (assignment_role IN ('checkin_staff', 'activity_manager'))
|
||||
);
|
||||
-- ============================================================================
|
||||
-- Phase 13(V009 + V021 + V037 + V038):红娘服务三表
|
||||
-- 此前测试库完全缺失,导致 matchmaker 模块零真实行为覆盖、只能写假测试。
|
||||
|
||||
Reference in New Issue
Block a user