产品裁定:隐私设置键集**以小程序为准**,用迁移补齐后端缺失列。
V047 新增 allow_activity_invitation(默认 TRUE)与 show_last_online(默认 FALSE)。
默认值**不对称**,与小程序 data 初值严格一致 —— showLastOnline 写成 TRUE 等于默认公开用户在线状态。
既有 6 列保留不动:UserProfileService 的资料详情 DTO 仍在全量返回它们(第 74-79 行),
那是仍在生效的独立读取出口。本迁移只做加法,不做替换。
AccountLifecycleService 的两个 TODO 桩改为委托 PrivacyService 真实读写。
这是**用户真正走的路径**(小程序调 /account/privacy-settings,而 /me/* 与 /profile/* 无人调用),
此前「保存成功」只是回 {updated:true} 而从不落库。
入参校验:已知键类型错误必须显式抛错,未知键宽容忽略 ——
若静默丢弃,用户会看到「保存成功」而设置从未生效,这类缺陷最难排查。
另容错 Boolean 的字符串形式("true"/"false")。
test(privacy): PrivacySettingsUpsertGuardTest 扩至 8 用例,其中 2 个是契约守卫
契约守卫**直接解析小程序源文件**的 PUT payload 与 data 块得出权威键集/默认值,
不抄契约副本(沿用 EntityMigrationParityTest 的纪律)—— 小程序改键即变红。
① 键集:后端返回键必须与小程序 PUT payload 键完全一致(不多不少)
② 默认值:后端默认值必须与小程序 data 初值一致(含 showLastOnline=false 这条不对称项)
③ 桩已接通:经 /account/privacy-settings 写入必须真落库且可读回
④ 已知键传错类型必须报错,不得静默丢弃
其余 4 个为 upsert 行为守卫(连续两次更新不炸且不产生第二行 / 部分更新不重置 / 默认值 / 读取无副作用)。
负向注入:把桩退回「不落库」→ 恰 2 红(③ 与 ④),其余 6 个正确未红。已还原并复跑全绿。
test(deployment): MigrationValidationTest 基线 46 → 47
新增迁移必须同步更新 LATEST_MIGRATION,否则全量回归报「版本号超出当前基线」。
该守卫同时断言「迁移文件总数 == 基线」(不允许缺号),是有效守卫 ——
正确做法是更新常量,不要为了让测试变绿而改断言方式。
636 lines
38 KiB
SQL
636 lines
38 KiB
SQL
DROP ALL OBJECTS;
|
||
|
||
CREATE TABLE users (
|
||
id VARCHAR(36) PRIMARY KEY, wechat_openid VARCHAR(128) NOT NULL UNIQUE,
|
||
wechat_unionid VARCHAR(128), phone VARCHAR(32), account_status VARCHAR(32),
|
||
relationship_status VARCHAR(32), recommendation_enabled BOOLEAN,
|
||
last_login_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE,
|
||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
CREATE TABLE user_relations (
|
||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), target_user_id VARCHAR(36), relation_type VARCHAR(32),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||
UNIQUE (user_id, target_user_id, relation_type)
|
||
);
|
||
-- 与 V002__add_user_and_verification_tables.sql 的 user_profiles 对齐(列名/语义一致),
|
||
-- 供 DiscoverService 搜索(INV-004 搜索不可见)等需要真实资料的用例使用。
|
||
CREATE TABLE user_profiles (
|
||
user_id VARCHAR(36) PRIMARY KEY, display_name VARCHAR(64), gender VARCHAR(16),
|
||
birth_date DATE, height_cm INT, region_code VARCHAR(32), region_name VARCHAR(128),
|
||
marital_status VARCHAR(32), children_status VARCHAR(32), education_code VARCHAR(32),
|
||
occupation_code VARCHAR(32), work_region_code VARCHAR(32), smoking_status VARCHAR(32),
|
||
drinking_status VARCHAR(32), long_term_danyang VARCHAR(32), future_danyang VARCHAR(32),
|
||
relationship_goal VARCHAR(64), marriage_plan VARCHAR(32), self_intro CLOB,
|
||
profile_completion INT DEFAULT 0, profile_status VARCHAR(32) DEFAULT 'in_progress',
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
CREATE TABLE matches (
|
||
id VARCHAR(36) PRIMARY KEY, user_a_id VARCHAR(36), user_b_id VARCHAR(36), status VARCHAR(32), source VARCHAR(32),
|
||
source_id VARCHAR(64), last_activity_at TIMESTAMP WITH TIME ZONE, unmatched_by VARCHAR(36), episode INT,
|
||
matched_at TIMESTAMP WITH TIME ZONE, unmatched_at TIMESTAMP WITH TIME ZONE, successful_at TIMESTAMP WITH TIME ZONE,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||
-- 与 V003__add_review_and_relation_tables.sql:91 的生产约束保持一致。
|
||
-- 测试库若缺此约束,形如 source='liked' 的非法取值会被静默放过,只在 PostgreSQL 上炸。
|
||
CONSTRAINT chk_match_source CHECK (source IN ('user_like', 'activity_mutual', 'matchmaker'))
|
||
);
|
||
|
||
-- Phase 16:活动互选二阶段同意表(与 V024 + V037 + V041 生产 DDL 对齐)。
|
||
-- 此前完全缺失 → 互选相关路径零覆盖(只能写假测试)。
|
||
-- 注意 deleted_at 必须为可空 TIMESTAMP(V037 已为生产表补该列;BaseEntity 逻辑删除依赖它)。
|
||
CREATE TABLE activity_mutual_connection_consents (
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
activity_id VARCHAR(36) NOT NULL,
|
||
user_a_id VARCHAR(36) NOT NULL,
|
||
user_b_id VARCHAR(36) NOT NULL,
|
||
user_a_status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||
user_b_status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||
both_confirmed_at TIMESTAMP WITH TIME ZONE,
|
||
expires_at TIMESTAMP WITH TIME ZONE,
|
||
match_id VARCHAR(36),
|
||
created_at TIMESTAMP WITH TIME ZONE,
|
||
updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
UNIQUE (activity_id, user_a_id, user_b_id),
|
||
CONSTRAINT chk_consent_status CHECK (
|
||
user_a_status IN ('pending', 'accepted', 'declined')
|
||
AND user_b_status IN ('pending', 'accepted', 'declined')
|
||
)
|
||
);
|
||
CREATE TABLE relationship_success_requests (
|
||
id VARCHAR(36) PRIMARY KEY, match_id VARCHAR(36), initiated_by VARCHAR(36), status VARCHAR(32), confirmed_by VARCHAR(36),
|
||
resolved_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE,
|
||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
CREATE TABLE conversations (
|
||
id VARCHAR(36) PRIMARY KEY, match_id VARCHAR(36) UNIQUE, status VARCHAR(32), last_message_id VARCHAR(36),
|
||
last_message_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE,
|
||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
-- 复合主键 (conversation_id, user_id),与 V004/V008 真实表结构一致,无 id 列
|
||
CREATE TABLE conversation_members (
|
||
conversation_id VARCHAR(36), user_id VARCHAR(36), last_read_message_id VARCHAR(36),
|
||
unread_count INT, can_send BOOLEAN, joined_at TIMESTAMP WITH TIME ZONE,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||
PRIMARY KEY (conversation_id, user_id)
|
||
);
|
||
CREATE TABLE messages (
|
||
id VARCHAR(36) PRIMARY KEY, conversation_id VARCHAR(36), sender_id VARCHAR(36), client_message_id VARCHAR(128),
|
||
message_type VARCHAR(32), content CLOB, file_id VARCHAR(36), status VARCHAR(32), risk_level VARCHAR(16),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
-- 与 V004__add_messaging_and_recommendation_tables.sql:71 的生产唯一索引对齐:
|
||
-- CREATE UNIQUE INDEX uk_message_client_id ON messages(sender_id, client_message_id)
|
||
-- WHERE client_message_id IS NOT NULL;
|
||
-- 生产带 WHERE(部分索引),H2 不支持过滤索引;但 H2 的唯一索引对 NULL 采取
|
||
-- SQL 标准语义(多个 NULL 互不冲突),因此省略 WHERE 后语义等价,
|
||
-- 保证「未带幂等键的消息」(如系统 RiskNotice)仍可多条存在。
|
||
-- 这条索引是"正常会话不重复投递"在并发下的真正保障:service 侧的
|
||
-- SELECT-then-INSERT 在并发重放时会有竞态,只有 DB 唯一索引能兜住。
|
||
CREATE UNIQUE INDEX uk_message_client_id ON messages(sender_id, client_message_id);
|
||
CREATE TABLE audit_logs (
|
||
id VARCHAR(36) PRIMARY KEY, operator_id VARCHAR(36), organization_id VARCHAR(36), action VARCHAR(128),
|
||
resource_type VARCHAR(64), resource_id VARCHAR(128), before_data CLOB, after_data CLOB, ip VARCHAR(64),
|
||
user_agent VARCHAR(500), created_at TIMESTAMP WITH TIME ZONE,
|
||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
CREATE TABLE notifications (
|
||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), type VARCHAR(32), title VARCHAR(200), content VARCHAR(2000),
|
||
business_type VARCHAR(32), business_id VARCHAR(128), read_status VARCHAR(16), read_at TIMESTAMP WITH TIME ZONE,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
-- 对齐生产 V036 uk_notification_business:NOT NULL 业务行去重;H2 不支持 partial index,
|
||
-- 但标准 UNIQUE 对 NULL 的语义(多行 NULL 允许共存)与生产 partial index 等价。
|
||
CREATE UNIQUE INDEX uk_notification_business ON notifications(user_id, type, business_type, business_id, content);
|
||
-- 对齐生产 V004/V020 chk_notification_type:缺少此约束会导致写入非法 type 时测试静默通过,
|
||
-- 而真实 PostgreSQL 直接拒绝 —— Phase 11 十种通知曾因此长期带病运行(D3)。
|
||
ALTER TABLE notifications ADD CONSTRAINT chk_notification_type CHECK (
|
||
type IN ('review', 'match', 'matchmaker', 'activity', 'risk', 'account', 'system',
|
||
'appointment', 'counseling', 'premarital', 'local_eligibility', 'service_location')
|
||
);
|
||
CREATE TABLE recommendation_records (
|
||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), target_user_id VARCHAR(36), source VARCHAR(32), score NUMERIC(5,2),
|
||
level VARCHAR(16), reasons CLOB, shown_at TIMESTAMP WITH TIME ZONE, action VARCHAR(32), action_at TIMESTAMP WITH TIME ZONE,
|
||
rule_version INT, created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
CREATE TABLE activity_registrations (
|
||
id VARCHAR(36) PRIMARY KEY, activity_id VARCHAR(36), user_id VARCHAR(36), status VARCHAR(32),
|
||
registered_at TIMESTAMP WITH TIME ZONE, reviewed_by VARCHAR(36), reviewed_at TIMESTAMP WITH TIME ZONE,
|
||
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,
|
||
-- 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),
|
||
preferred_date DATE, preferred_time_slot VARCHAR(32), user_note CLOB, status VARCHAR(32),
|
||
assigned_operator_id VARCHAR(36), confirmed_start_at TIMESTAMP WITH TIME ZONE,
|
||
confirmed_end_at TIMESTAMP WITH TIME ZONE, cancelled_by VARCHAR(36), cancel_reason 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,
|
||
-- Phase 19:生产 V021 的两条 CHECK 此前在测试库完全缺失 → 非法 status/source
|
||
-- 在测试期不可见,上到 PostgreSQL 才撞约束返回 500(Phase 11/13 同族约束漂移根因)。
|
||
CONSTRAINT chk_appointment_status CHECK (status IN (
|
||
'submitted', 'pending_confirmation', 'confirmed', 'completed', 'cancelled'
|
||
)),
|
||
CONSTRAINT chk_source_type CHECK (source IN (
|
||
'user_self', 'matchmaker_referral', 'activity_followup', 'service_station', 'operator_assisted'
|
||
))
|
||
);
|
||
CREATE TABLE counseling_services (
|
||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), service_type VARCHAR(64), request_source VARCHAR(64),
|
||
user_request_summary CLOB, organization_id VARCHAR(36), assigned_operator_id VARCHAR(36), status VARCHAR(32),
|
||
preferred_service_mode VARCHAR(32), preferred_time VARCHAR(128), scheduled_start_at TIMESTAMP WITH TIME ZONE,
|
||
scheduled_end_at TIMESTAMP WITH TIME ZONE, consent_version VARCHAR(32), consented_at TIMESTAMP WITH TIME ZONE,
|
||
completed_at TIMESTAMP WITH TIME ZONE, closed_reason VARCHAR(500), companion_user_id VARCHAR(36),
|
||
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,
|
||
idempotency_key VARCHAR(128),
|
||
-- 生产约束(V022 + V039):此前测试库完全没有 CHECK → 约束漂移盲区(Phase 11/13 同族根因)
|
||
CONSTRAINT chk_counseling_service_type CHECK (service_type IN (
|
||
'relationship_guidance', 'emotional_support', 'premarital_guidance', 'anti_fraud_guidance'
|
||
)),
|
||
CONSTRAINT chk_counseling_status CHECK (status IN (
|
||
'pending', 'assigned', 'scheduled', 'in_progress', 'completed', 'cancelled', 'closed'
|
||
)),
|
||
CONSTRAINT chk_request_source CHECK (request_source IN (
|
||
'user_self', 'matchmaker_referral', 'activity_followup', 'service_station', 'operator_assisted'
|
||
)),
|
||
-- V039 幂等索引:H2 无 partial index,用标准 UNIQUE 表达(PG/H2 均允许多行 NULL 共存,语义等价)
|
||
CONSTRAINT uk_counseling_idem UNIQUE (user_id, idempotency_key)
|
||
);
|
||
-- V022 / V039: counseling_followups 表(跟进正文属 INV-013 敏感数据)
|
||
CREATE TABLE counseling_followups (
|
||
id VARCHAR(36) PRIMARY KEY, counseling_id VARCHAR(36) NOT NULL, operator_id VARCHAR(36) NOT NULL,
|
||
followup_type VARCHAR(32), content CLOB NOT NULL, result_code VARCHAR(64),
|
||
next_followup_at TIMESTAMP WITH TIME ZONE,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
-- V026: service_programs 专项公益服务项目
|
||
-- Phase 19:此前仅在 TEST_SCHEMA_OPTIONAL_TABLES 白名单(= 无人测),
|
||
-- 导致 ServiceProgramService 全链路(创建/编辑/激活/成员审批)零覆盖。已补入并移出白名单。
|
||
CREATE TABLE service_programs (
|
||
id VARCHAR(36) PRIMARY KEY, name VARCHAR(128), description CLOB, cover_file_id VARCHAR(36),
|
||
organizer_org_id VARCHAR(36), start_at TIMESTAMP WITH TIME ZONE, end_at TIMESTAMP WITH TIME ZONE,
|
||
status VARCHAR(32), created_by VARCHAR(36),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||
-- V026 生产约束
|
||
CONSTRAINT chk_program_status CHECK (status IN ('draft', 'active', 'ended', 'cancelled'))
|
||
);
|
||
|
||
-- V028: service_location_operators 服务站运营人员数据范围(复合主键,无 id 列)
|
||
-- Phase 19:此前在可选白名单且测试库缺失 → ServiceAppointmentMapper.canAccessByOperator /
|
||
-- findAccessibleByOperator 的 LEFT JOIN 在测试库直接 BadSqlGrammar,「预约数据范围」路径零覆盖。
|
||
CREATE TABLE service_location_operators (
|
||
service_location_id VARCHAR(36) NOT NULL, operator_id VARCHAR(36) NOT NULL, created_by VARCHAR(36),
|
||
created_at TIMESTAMP WITH TIME ZONE,
|
||
PRIMARY KEY (service_location_id, operator_id)
|
||
);
|
||
|
||
CREATE TABLE service_program_members (
|
||
id VARCHAR(36) PRIMARY KEY, program_id VARCHAR(36), user_id VARCHAR(36), status VARCHAR(32),
|
||
applied_at TIMESTAMP WITH TIME ZONE, approved_at TIMESTAMP WITH TIME ZONE, approved_by VARCHAR(36),
|
||
rejected_reason VARCHAR(500), created_at TIMESTAMP WITH TIME ZONE,
|
||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||
-- 对齐生产 V026:UNIQUE(program_id, user_id)。缺此约束时"被拒/退出后重新申请"
|
||
-- 撞唯一键 500 的必崩缺陷在测试期完全不可见(Phase 15 同族根因)。
|
||
CONSTRAINT uk_program_member UNIQUE (program_id, user_id),
|
||
-- 对齐生产 V026 chk_program_member_status:withdrawn 表示用户退出 / 运营移除
|
||
CONSTRAINT chk_program_member_status CHECK (status IN ('pending', 'approved', 'rejected', 'withdrawn'))
|
||
);
|
||
CREATE TABLE user_local_eligibilities (
|
||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36) UNIQUE, eligibility_type VARCHAR(32),
|
||
declared_region_code VARCHAR(32), verification_status VARCHAR(32), review_task_id VARCHAR(36),
|
||
verified_at TIMESTAMP WITH TIME ZONE, verified_by VARCHAR(36), created_at TIMESTAMP WITH TIME ZONE,
|
||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||
-- 对齐生产 V017:两条 CHECK 此前在测试库完全缺失(与 Phase 19 D9/D23 同族约束漂移)。
|
||
-- 后果实证:AccountLifecycleService.invalidateLocalEligibility 曾把 user_identities 域的
|
||
-- 'failed' 写进本表的 verification_status —— 在测试库"合法"、在真实 PostgreSQL 上
|
||
-- 必然 CHECK 违约并使 closeAccount 整个事务回滚(注销 100% 不可用)。
|
||
CONSTRAINT chk_elig_type CHECK (eligibility_type IN (
|
||
'household_registration', 'residence', 'employment', 'other'
|
||
)),
|
||
CONSTRAINT chk_elig_verify_status CHECK (verification_status IN (
|
||
'declared', 'needs_evidence', 'pending_review', 'verified', 'rejected'
|
||
))
|
||
);
|
||
|
||
-- ============================================================================
|
||
-- 以下表为 Phase 12 复验补齐(测试保真度盲区):
|
||
-- 生产迁移里存在、而测试库此前**完全没有**,导致相关端到端路径无法测试。
|
||
-- 例:GET /api/v1/home 的 getHome() 会调用 UserIdentityMapper.findByUserId,
|
||
-- 测试库缺 user_identities → BadSqlGrammar("Table USER_IDENTITIES not found")
|
||
-- 整个首页接口在测试环境下不可用,任何首页回归都无法验证。
|
||
-- 列定义对齐对应迁移,仅保留实体实际读写的列。
|
||
-- ============================================================================
|
||
|
||
-- V002: user_identities 实名认证表
|
||
CREATE TABLE user_identities (
|
||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), real_name_enc CLOB, id_number_enc CLOB,
|
||
id_number_hash VARCHAR(128), gender VARCHAR(16), birth_date DATE,
|
||
verification_status VARCHAR(32), provider VARCHAR(64), provider_request_id VARCHAR(128),
|
||
verified_at TIMESTAMP WITH TIME ZONE, failed_reason_code VARCHAR(64),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
-- V003: review_tasks 审核任务(首页 buildUserState 需查询审核状态)
|
||
CREATE TABLE review_tasks (
|
||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), review_type VARCHAR(32), status VARCHAR(32),
|
||
assigned_operator_id VARCHAR(36), submitted_at TIMESTAMP WITH TIME ZONE,
|
||
started_at TIMESTAMP WITH TIME ZONE, completed_at TIMESTAMP WITH TIME ZONE,
|
||
final_comment VARCHAR(1000), version INT,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
-- V017: local_eligibility_evidences 本地资格佐证材料
|
||
CREATE TABLE local_eligibility_evidences (
|
||
id VARCHAR(36) PRIMARY KEY, local_eligibility_id VARCHAR(36), file_key VARCHAR(256),
|
||
file_id VARCHAR(36), evidence_type VARCHAR(64), status VARCHAR(32),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
-- V001 + V034 + V035: sensitive_access_logs 敏感数据访问审计
|
||
-- INV-011 的「可核查」落点正是本表(access_result = success / denied)。
|
||
-- 本表此前在测试库**完全不存在**,而 ReviewService.logEvidenceAccess 一定会写它 ——
|
||
-- 后果:任何「审计真的落库了吗」的断言都无从编写,INV-011 只能退化成常量断言;
|
||
-- 生产上新接入的审计路径在测试期零覆盖。与 Phase 12 复验记录的
|
||
-- user_identities / review_tasks 缺失同族(表漂移盲区),此处补齐。
|
||
-- 列定义对齐:V001 建表(id/operator_id/user_id/data_type/reason/ip/created_at)
|
||
-- + V034(access_target/access_result)
|
||
-- + V035(BaseEntity 的 updated_at / deleted_at)。
|
||
-- 不建 FK:生产 REFERENCES operators(id) / users(id),而测试库无 operators 表;
|
||
-- 本库 review_tasks / local_eligibility_evidences 同样只保留列、不建 FK。
|
||
CREATE TABLE sensitive_access_logs (
|
||
id VARCHAR(36) PRIMARY KEY, operator_id VARCHAR(36), user_id VARCHAR(36),
|
||
data_type VARCHAR(64), reason VARCHAR(500), ip VARCHAR(64),
|
||
access_target VARCHAR(128), access_result VARCHAR(32),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
-- V019: home_banners 首页 Banner
|
||
CREATE TABLE home_banners (
|
||
id VARCHAR(36) PRIMARY KEY, title VARCHAR(200), subtitle VARCHAR(300), image_file_id VARCHAR(36),
|
||
button_text VARCHAR(64), target_type VARCHAR(64), target_value VARCHAR(200),
|
||
audience_scope VARCHAR(64), sort_order INT, start_at TIMESTAMP WITH TIME ZONE,
|
||
end_at TIMESTAMP WITH TIME ZONE, status VARCHAR(32), created_by VARCHAR(36), updated_by VARCHAR(36),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||
-- Phase 19:生产 V019 的三条 CHECK 此前在测试库完全缺失(Phase 11/13 同族约束漂移)。
|
||
-- target_type 白名单同时是"禁外链"的服务端落点:外链类型不在白名单内。
|
||
CONSTRAINT chk_banner_target_type CHECK (target_type IN (
|
||
'activity', 'matchmaker', 'counseling', 'premarital', 'service_location', 'article', 'internal_page', 'none'
|
||
)),
|
||
CONSTRAINT chk_banner_audience_scope CHECK (audience_scope IN (
|
||
'all', 'onboarding', 'searching', 'getting_to_know', 'successful'
|
||
)),
|
||
CONSTRAINT chk_banner_status CHECK (status IN (
|
||
'draft', 'scheduled', 'active', 'inactive', 'expired'
|
||
))
|
||
);
|
||
|
||
-- V019: service_locations 善缘服务站
|
||
CREATE TABLE service_locations (
|
||
id VARCHAR(36) PRIMARY KEY, name VARCHAR(128), short_name VARCHAR(64), address CLOB,
|
||
latitude DECIMAL(10, 7), longitude DECIMAL(10, 7), contact_phone_enc CLOB,
|
||
contact_phone_display VARCHAR(32), opening_hours_text CLOB, service_description CLOB,
|
||
cover_file_id VARCHAR(36), primary_organization_id VARCHAR(36), notice_text CLOB,
|
||
status VARCHAR(32), created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
-- Phase 19:生产 V019 的服务站状态 CHECK 此前在测试库缺失(同族约束漂移)
|
||
CONSTRAINT chk_service_location_status CHECK (status IN ('active', 'inactive', 'closed'))
|
||
);
|
||
|
||
-- V042: service_location_announcements 善缘服务站「临时公告」(Phase 19 / U50)
|
||
-- Phase 19:该表此前**完全缺失于测试库**,而公告是本轮为新链路所建 —— 若不补表,
|
||
-- 公告 Service 一旦被任何测试调用即 BadSqlGrammar(表不存在),且
|
||
-- EntityMigrationParityTest 的生产表缺失守卫会直接变红(本轮即由此发现)。
|
||
-- 对齐 V042:去外键(H2 无 operators 表),保留 chk_announcement_status 白名单
|
||
-- (「非法状态只在 PostgreSQL 上炸」的必崩盲区同族根因)。
|
||
CREATE TABLE service_location_announcements (
|
||
id VARCHAR(36) PRIMARY KEY, service_location_id VARCHAR(36) NOT NULL,
|
||
title VARCHAR(200) NOT NULL, content CLOB, status VARCHAR(32) NOT NULL DEFAULT 'draft',
|
||
published_at TIMESTAMP WITH TIME ZONE, expires_at TIMESTAMP WITH TIME ZONE, sort_order INT NOT NULL DEFAULT 0,
|
||
created_by VARCHAR(36), updated_by VARCHAR(36),
|
||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
CONSTRAINT chk_announcement_status CHECK (status IN ('draft', 'published', 'archived'))
|
||
);
|
||
|
||
-- V026: content_articles 婚恋课堂文章
|
||
-- V026: content_categories 内容分类(运营后台「内容分类」tab 的唯一落点)
|
||
-- Phase 19:此前该表完全缺失于测试库,整条分类路径零覆盖(与 Phase 12 缺 user_identities
|
||
-- 导致首页零覆盖同族)。补表同时补生产 chk_category_status,避免非法状态在测试期不可见。
|
||
CREATE TABLE content_categories (
|
||
id VARCHAR(36) PRIMARY KEY, name VARCHAR(128) NOT NULL, code VARCHAR(64) NOT NULL,
|
||
description CLOB, sort_order INT, status VARCHAR(32),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||
-- 对齐生产 V026:UNIQUE(code)。缺此约束时"编码重复"在测试期静默通过,生产 500。
|
||
CONSTRAINT uk_content_category_code UNIQUE (code),
|
||
CONSTRAINT chk_category_status CHECK (status IN ('active', 'inactive'))
|
||
);
|
||
CREATE TABLE content_articles (
|
||
id VARCHAR(36) PRIMARY KEY, category_id VARCHAR(36), title VARCHAR(200), summary VARCHAR(500),
|
||
content CLOB, cover_file_id VARCHAR(36), author_operator_id VARCHAR(36), status VARCHAR(32),
|
||
publish_at TIMESTAMP WITH TIME ZONE, view_count INT, sort_order INT,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||
-- 对齐生产 V026 chk_article_status:此前测试库缺该 CHECK,非法状态只在 PostgreSQL 上炸
|
||
-- (Phase 11/13/14/15 同族约束漂移根因)。
|
||
CONSTRAINT chk_article_status CHECK (status IN ('draft', 'scheduled', 'published', 'offline'))
|
||
);
|
||
|
||
-- V010: activities 公益活动(首页近期活动预览)
|
||
CREATE TABLE activities (
|
||
id VARCHAR(36) PRIMARY KEY, title VARCHAR(200), cover_file_id VARCHAR(36), description CLOB,
|
||
organizer_org_id VARCHAR(36), coorganizer_text VARCHAR(500), start_at TIMESTAMP WITH TIME ZONE,
|
||
end_at TIMESTAMP WITH TIME ZONE, location_name VARCHAR(200), location_address VARCHAR(500),
|
||
male_limit INT, female_limit INT, total_limit INT, min_age SMALLINT, max_age SMALLINT,
|
||
registration_start_at TIMESTAMP WITH TIME ZONE, registration_end_at TIMESTAMP WITH TIME ZONE,
|
||
need_review BOOLEAN, status VARCHAR(32), created_by VARCHAR(36),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||
-- V023(Phase 14):多类型活动扩展列。此前测试库完全缺失,
|
||
-- 导致 Phase 14 全部新增逻辑零覆盖,且非法 activity_type 在测试期不可见
|
||
-- (上到 PostgreSQL 才撞 chk_activity_type 返回 500)。
|
||
activity_type VARCHAR(64) NOT NULL DEFAULT 'matchmaking',
|
||
registration_policy CLOB,
|
||
target_group VARCHAR(200),
|
||
industry_scope VARCHAR(200),
|
||
post_event_interest_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||
CONSTRAINT chk_activity_type CHECK (activity_type IN (
|
||
'matchmaking', 'interest_social', 'relationship_education',
|
||
'premarital_guidance', 'marriage_custom_culture', 'anti_fraud'
|
||
))
|
||
);
|
||
|
||
-- ============================================================================
|
||
-- Phase 14(V001 + V023):活动组织与容量分组
|
||
-- 此前测试库完全缺失 organizations / activity_organizations / activity_capacity_groups,
|
||
-- 且 activities 缺 V023 全部新列 —— Phase 14 新增逻辑零覆盖。
|
||
-- 约束与生产 V023 对齐(chk_org_role 白名单)。
|
||
-- ============================================================================
|
||
|
||
-- V001: organizations 组织表(activity_organizations 的外键目标,且组织名用于活动详情展示)
|
||
CREATE TABLE organizations (
|
||
id VARCHAR(36) PRIMARY KEY, name VARCHAR(200), type VARCHAR(32),
|
||
contact_name VARCHAR(100), contact_phone_enc VARCHAR(500), status VARCHAR(32),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
-- V023: activity_organizations 活动多组织关联表
|
||
CREATE TABLE activity_organizations (
|
||
id VARCHAR(36) PRIMARY KEY, activity_id VARCHAR(36) NOT NULL, organization_id VARCHAR(36) NOT NULL,
|
||
role VARCHAR(32) NOT NULL,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
CONSTRAINT uk_activity_org UNIQUE (activity_id, organization_id),
|
||
CONSTRAINT chk_org_role CHECK (role IN ('host', 'organizer', 'co_organizer', 'partner', 'venue_partner'))
|
||
);
|
||
|
||
-- V023: activity_capacity_groups 活动容量分组表
|
||
CREATE TABLE activity_capacity_groups (
|
||
id VARCHAR(36) PRIMARY KEY, activity_id VARCHAR(36) NOT NULL,
|
||
group_code VARCHAR(32) NOT NULL, rule_definition CLOB,
|
||
capacity INT NOT NULL DEFAULT 0, approved_count INT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
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 模块零真实行为覆盖、只能写假测试。
|
||
-- 约束与生产 V038 对齐(status 白名单已含 liked/passed/confirmed_mutual)。
|
||
-- ============================================================================
|
||
|
||
-- V009: matchmaker_services 红娘服务表
|
||
CREATE TABLE matchmaker_services (
|
||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36) NOT NULL, service_type VARCHAR(32) NOT NULL,
|
||
description VARCHAR(1000), status VARCHAR(32) NOT NULL DEFAULT 'submitted',
|
||
organization_id VARCHAR(36), assigned_operator_id VARCHAR(36),
|
||
assigned_at TIMESTAMP WITH TIME ZONE, completed_at TIMESTAMP WITH TIME ZONE,
|
||
referral_target_type VARCHAR(32), referral_target_id VARCHAR(36),
|
||
referral_source_type VARCHAR(32) NOT NULL DEFAULT 'manual',
|
||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
CONSTRAINT chk_matchmaker_service_type CHECK (service_type IN (
|
||
'recommend_partner', 'review_preference', 'contact_intention',
|
||
'join_activity', 'no_match_help', 'chat_help', 'other',
|
||
'recommendation',
|
||
'referral_counseling', 'referral_activity', 'referral_station'
|
||
)),
|
||
CONSTRAINT chk_matchmaker_status CHECK (status IN (
|
||
'submitted', 'assigned', 'in_service', 'waiting_user', 'completed', 'cancelled'
|
||
))
|
||
);
|
||
|
||
-- V009: matchmaker_recommendations 红娘推荐表
|
||
CREATE TABLE matchmaker_recommendations (
|
||
id VARCHAR(36) PRIMARY KEY, service_id VARCHAR(36) NOT NULL, user_id VARCHAR(36) NOT NULL,
|
||
target_user_id VARCHAR(36) NOT NULL, operator_id VARCHAR(36) NOT NULL, reason VARCHAR(1000),
|
||
status VARCHAR(32) NOT NULL DEFAULT 'pending_view',
|
||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
viewed_at TIMESTAMP WITH TIME ZONE, responded_at TIMESTAMP WITH TIME ZONE,
|
||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
CONSTRAINT chk_rec_status CHECK (status IN (
|
||
'pending_view', 'viewed', 'responded',
|
||
'liked', 'passed', 'confirmed_mutual'
|
||
))
|
||
);
|
||
|
||
-- V009: matchmaker_followups 红娘跟进表
|
||
CREATE TABLE matchmaker_followups (
|
||
id VARCHAR(36) PRIMARY KEY, service_id VARCHAR(36) NOT NULL, operator_id VARCHAR(36) NOT NULL,
|
||
followup_type VARCHAR(32), content VARCHAR(2000) NOT NULL, result_code VARCHAR(64),
|
||
next_followup_at TIMESTAMP WITH TIME ZONE,
|
||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
-- Phase 17:举报与风控三表(与 V012__add_risk_and_report_tables.sql 生产 DDL 对齐)。
|
||
-- 此前完全缺失 → ReportService / RiskReferralService 整条真实 DB 路径零覆盖,
|
||
-- 且 ReportServiceTest 只能写 assertTrue(true) 假绿。已补入并移出 TEST_SCHEMA_OPTIONAL_TABLES 白名单。
|
||
-- 注意:H2 无 operators 表,故去掉生产 DDL 中的 REFERENCES operators(id) 外键;
|
||
-- 保留 status / action_type 的 CHECK 约束,防止非法取值在测试期被静默放过(同 Phase 14/15/16 原则)。
|
||
CREATE TABLE reports (
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
reporter_user_id VARCHAR(36) NOT NULL,
|
||
reported_user_id VARCHAR(36),
|
||
reason_type VARCHAR(64) NOT NULL,
|
||
description CLOB,
|
||
evidence_file_ids CLOB,
|
||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||
process_result VARCHAR(64),
|
||
processed_by VARCHAR(36),
|
||
processed_at TIMESTAMP WITH TIME ZONE,
|
||
process_note CLOB,
|
||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
CONSTRAINT chk_report_status CHECK (status IN ('pending', 'processing', 'resolved', 'rejected', 'dismissed'))
|
||
);
|
||
|
||
CREATE TABLE risk_events (
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
user_id VARCHAR(36) NOT NULL,
|
||
event_type VARCHAR(64) NOT NULL,
|
||
score INT NOT NULL DEFAULT 0,
|
||
source VARCHAR(32) NOT NULL DEFAULT 'auto',
|
||
source_id VARCHAR(128),
|
||
note CLOB,
|
||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
CREATE TABLE risk_actions (
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
user_id VARCHAR(36) NOT NULL,
|
||
action_type VARCHAR(64) NOT NULL,
|
||
reason VARCHAR(256) NOT NULL,
|
||
reason_detail CLOB,
|
||
created_by VARCHAR(36) NOT NULL,
|
||
effective_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
expires_at TIMESTAMP WITH TIME ZONE,
|
||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
CONSTRAINT chk_risk_action_type CHECK (action_type IN ('warning', 'feature_limit', 'temporary_suspend', 'ban')),
|
||
CONSTRAINT chk_risk_action_status CHECK (status IN ('active', 'resolved', 'expired'))
|
||
);
|
||
|
||
-- Phase 18: appeals 表(申诉)—— 此前完全缺失于测试库(只列在 EntityMigrationParityTest
|
||
-- 的可选白名单里 = 无人测),导致申诉整条真实 DB 路径零覆盖,AppealServiceTest 只能写
|
||
-- assertTrue(true) 假绿。对齐 V013__add_appeals_table.sql:去外键(H2 无 operators 表),
|
||
-- 保留 chk_appeal_status / chk_appeal_type 两个白名单(「只在 PG 上炸」的必崩盲区)。
|
||
CREATE TABLE appeals (
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
user_id VARCHAR(36) NOT NULL,
|
||
appeal_type VARCHAR(64) NOT NULL,
|
||
related_action_id VARCHAR(36),
|
||
reason CLOB NOT NULL,
|
||
evidence CLOB,
|
||
contact_method VARCHAR(256),
|
||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||
decision_note CLOB,
|
||
reviewed_by VARCHAR(36),
|
||
reviewed_at TIMESTAMP WITH TIME ZONE,
|
||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
CONSTRAINT chk_appeal_status CHECK (status IN ('pending', 'reviewing', 'approved', 'rejected', 'escalated')),
|
||
CONSTRAINT chk_appeal_type CHECK (appeal_type IN ('ban_appeal', 'suspend_appeal'))
|
||
);
|
||
|
||
|
||
-- Phase 24/幂等检修: idempotency_records(通用 Idempotency-Key 落地表)
|
||
-- 对齐 V045__add_idempotency_records.sql。此前该表不存在于测试库,
|
||
-- 任何走幂等拦截器的写操作在 H2 下都会报表不存在 —— 与 appeals 当初
|
||
-- 整条真实 DB 路径零覆盖是同一类盲区。
|
||
CREATE TABLE idempotency_records (
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
actor_id VARCHAR(64) NOT NULL,
|
||
idempotency_key VARCHAR(255) NOT NULL,
|
||
endpoint VARCHAR(255) NOT NULL,
|
||
state VARCHAR(16) NOT NULL DEFAULT 'PROCESSING',
|
||
response_status INTEGER,
|
||
response_body CLOB,
|
||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||
completed_at TIMESTAMP WITH TIME ZONE,
|
||
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||
CONSTRAINT chk_idempotency_state CHECK (state IN ('PROCESSING', 'COMPLETED'))
|
||
);
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- Phase 23 补齐(2026-09-19):以下 4 张表此前在测试库完全不存在。
|
||
-- 发现经过:MapperBindingGuardTest 报出 5 个「声明了却无 @Select」的方法,
|
||
-- 其中 4 个方法对应的表正是这 4 张 —— 表不存在 + 语句未绑定 = 双重盲区,
|
||
-- 任何测试都走不到这些代码路径,缺陷被"空数据 + 缺表"共同掩盖。
|
||
-- 保真口径:**按 V002 + V037 之后的最终状态**建模,不是按 V002 的初始建表。
|
||
-- V037(align_entity_columns_with_migrations)已给这 4 张表补上 BaseEntity 公共列
|
||
-- (id / created_at / updated_at / deleted_at)与匹配权重列 ——
|
||
-- 若只看 V002 会误判为「表主键是 user_id、无 id 列」,进而写出错误的 SQL。
|
||
-- 尤需注意 deleted_at:4 张表**全部**已有软删列,故相关查询必须带 deleted_at IS NULL。
|
||
-- ---------------------------------------------------------------------------
|
||
|
||
-- V002 + V037: user_preferences 择偶偏好
|
||
-- V002 初始:user_id 为主键,无 id / deleted_at / 权重列
|
||
-- V037 补齐:id + deleted_at + 11 个匹配权重列
|
||
CREATE TABLE user_preferences (
|
||
user_id VARCHAR(36) PRIMARY KEY, id VARCHAR(36),
|
||
min_age INT, max_age INT, age_priority VARCHAR(16),
|
||
min_height_cm INT, max_height_cm INT, height_priority VARCHAR(16),
|
||
region_scope CLOB, region_priority VARCHAR(16),
|
||
marital_statuses CLOB, marital_priority VARCHAR(16),
|
||
min_education_code VARCHAR(32), education_priority VARCHAR(16),
|
||
children_acceptance VARCHAR(32), children_priority VARCHAR(16),
|
||
smoking_preference VARCHAR(32), drinking_preference VARCHAR(32),
|
||
settlement_preference VARCHAR(32), marriage_plan_preference VARCHAR(32),
|
||
age_weight DOUBLE PRECISION DEFAULT 0.15, region_weight DOUBLE PRECISION DEFAULT 0.20,
|
||
marital_weight DOUBLE PRECISION DEFAULT 0.10, children_weight DOUBLE PRECISION DEFAULT 0.05,
|
||
education_weight DOUBLE PRECISION DEFAULT 0.10, height_weight DOUBLE PRECISION DEFAULT 0.05,
|
||
marriage_plan_weight DOUBLE PRECISION DEFAULT 0.15, lifestyle_weight DOUBLE PRECISION DEFAULT 0.10,
|
||
settlement_weight DOUBLE PRECISION DEFAULT 0.05, interest_weight DOUBLE PRECISION DEFAULT 0.10,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
CONSTRAINT chk_pref_priority CHECK (age_priority IN ('required', 'preferred'))
|
||
);
|
||
|
||
-- V002 + V037: user_privacy_settings 隐私设置
|
||
-- V037 补齐:id + deleted_at(V002 时无此两列)
|
||
CREATE TABLE user_privacy_settings (
|
||
user_id VARCHAR(36) PRIMARY KEY, id VARCHAR(36),
|
||
visible_in_recommendations BOOLEAN DEFAULT TRUE, visible_in_search BOOLEAN DEFAULT TRUE,
|
||
show_occupation BOOLEAN DEFAULT TRUE, show_lifestyle_photos BOOLEAN DEFAULT TRUE,
|
||
receive_activity_recommendations BOOLEAN DEFAULT TRUE,
|
||
accept_matchmaker_recommendations BOOLEAN DEFAULT TRUE,
|
||
-- V047:小程序 privacy-settings 契约列。注意 show_last_online 默认 FALSE(不公开在线状态)
|
||
allow_activity_invitation BOOLEAN DEFAULT TRUE,
|
||
show_last_online BOOLEAN DEFAULT FALSE,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
-- V003 + V037: review_reasons 审核原因字典
|
||
-- 注意主键是 code(业务以 code 引用),V037 额外补 id 列以承接 BaseEntity 语义。
|
||
-- V037 补:id + created_at + updated_at + deleted_at
|
||
CREATE TABLE review_reasons (
|
||
code VARCHAR(64) PRIMARY KEY, id VARCHAR(36), name VARCHAR(128),
|
||
category VARCHAR(32), enabled BOOLEAN DEFAULT TRUE, sort_order INT DEFAULT 0,
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE
|
||
);
|
||
|
||
-- V003 + V037: review_items 审核项
|
||
-- V037 补:deleted_at
|
||
CREATE TABLE review_items (
|
||
id VARCHAR(36) PRIMARY KEY, review_task_id VARCHAR(36),
|
||
item_type VARCHAR(32), status VARCHAR(32) DEFAULT 'pending',
|
||
reason_code VARCHAR(64), comment VARCHAR(500),
|
||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE,
|
||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||
CONSTRAINT chk_item_type CHECK (item_type IN ('avatar', 'lifestyle_photos', 'basic_profile', 'self_intro', 'relationship_profile')),
|
||
CONSTRAINT chk_item_status CHECK (status IN ('pending', 'approved', 'need_edit', 'abnormal'))
|
||
);
|
||
|
||
CREATE UNIQUE INDEX uk_idempotency_scope
|
||
ON idempotency_records (actor_id, endpoint, idempotency_key);
|