Files
danyang-match/数据库详细设计与核心数据模型.md
T

2780 lines
45 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.
# 丹阳公益婚恋服务平台
# V1.0 数据库详细设计与核心数据模型
版本:V1.0
数据库建议:PostgreSQL
适用范围:微信小程序用户端、公益红娘工作台、运营管理后台
设计目标:结构清晰、敏感数据分层、状态可追溯、支持事务、便于后续扩展
---
# 第一章 数据库设计原则
V1.0数据库设计必须坚持:
- 账号数据与婚恋资料分离
- 公共资料与敏感资料分离
- 用户状态与关系状态分离
- 审核数据可追溯
- 风控数据独立
- 聊天数据独立
- 活动数据独立
- 红娘服务独立
- 权限与组织独立
- 重要业务状态不通过删除实现
- 核心业务使用数据库约束兜底
- PostgreSQL作为业务真实数据源
不建议:
- 一个users表塞所有字段
- 使用大量JSON替代清晰结构
- 核心关系依赖Redis
- 不做唯一约束
- 只依赖前端校验
---
# 第二章 核心数据域
V1.0数据库建议划分为:
```text
01 用户账号域
02 实名认证域
03 婚恋资料域
04 人工审核域
05 推荐与关系域
06 聊天消息域
07 公益活动域
08 公益红娘域
09 举报风控域
10 系统通知域
11 组织权限域
12 审计日志域
13 配置与统计域
```
---
# 第三章 核心表总览
建议V1.0核心表:
```text
users
user_identities
user_profiles
user_preferences
user_interests
interest_dictionary
user_photos
user_privacy_settings
review_tasks
review_items
review_reasons
recommendation_records
user_relations
matches
relationship_success_requests
conversations
conversation_members
messages
activities
activity_registrations
activity_feedback
matchmaker_services
matchmaker_recommendations
matchmaker_followups
reports
report_evidences
user_risk_profiles
risk_events
risk_actions
appeals
notifications
organizations
operators
roles
permissions
role_permissions
operator_roles
audit_logs
sensitive_access_logs
system_configs
match_rule_configs
```
---
# 第四章 通用字段规范
大部分业务表建议包含:
```text
id
created_at
updated_at
```
需要软删除的表:
```text
deleted_at
```
业务状态不要用:
```text
is_deleted = 0/1
```
优先采用:
```text
deleted_at IS NULL
```
---
# 第五章 ID设计
推荐:
> UUID / UUIDv7
或:
> 应用生成的分布式唯一ID。
不要让外部接口直接暴露连续自增ID。
例如:
```text
user_id = 018f...
```
或者:
```text
usr_xxxxxxxxx
```
如果数据库内部使用BIGINT,也建议API层映射公共ID。
---
# 第六章 users 用户账号表
职责:
> 只保存账号和平台级状态。
建议结构:
```sql
CREATE TABLE users (
id UUID PRIMARY KEY,
wechat_openid VARCHAR(128) NOT NULL,
wechat_unionid VARCHAR(128),
phone VARCHAR(32),
account_status VARCHAR(32) NOT NULL DEFAULT 'normal',
relationship_status VARCHAR(32) NOT NULL DEFAULT 'searching',
recommendation_enabled BOOLEAN NOT NULL DEFAULT TRUE,
last_login_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
```
---
# 第七章 users约束
唯一约束:
```sql
CREATE UNIQUE INDEX uk_users_openid
ON users(wechat_openid)
WHERE deleted_at IS NULL;
```
手机号:
```sql
CREATE UNIQUE INDEX uk_users_phone
ON users(phone)
WHERE phone IS NOT NULL
AND deleted_at IS NULL;
```
unionid:
如业务使用:
```sql
CREATE UNIQUE INDEX uk_users_unionid
ON users(wechat_unionid)
WHERE wechat_unionid IS NOT NULL
AND deleted_at IS NULL;
```
---
# 第八章 users状态约束
account_status:
```text
normal
reviewing_risk
limited
suspended
banned
closed
```
relationship_status:
```text
searching
getting_to_know
paused
matched_successfully
service_exited
```
建议使用:
> VARCHAR + CHECK
而不是PostgreSQL ENUM。
原因:
后续状态扩展和数据库迁移更方便。
---
# 第九章 user_identities 实名认证表
高度敏感。
与users严格分离。
字段建议:
```sql
CREATE TABLE user_identities (
id UUID PRIMARY KEY,
user_id UUID NOT NULL UNIQUE,
real_name_enc TEXT NOT NULL,
id_number_enc TEXT NOT NULL,
id_number_hash VARCHAR(128) NOT NULL,
gender VARCHAR(16),
birth_date DATE,
verification_status VARCHAR(32) NOT NULL,
provider VARCHAR(64),
provider_request_id VARCHAR(128),
verified_at TIMESTAMPTZ,
failed_reason_code VARCHAR(64),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT fk_identity_user
FOREIGN KEY (user_id) REFERENCES users(id)
);
```
---
# 第十章 实名敏感字段
```text
real_name_enc
id_number_enc
```
必须:
> 加密后存储。
同时建议存:
```text
id_number_hash
```
用于:
- 防重复身份证注册
- 查询重复身份
但不能通过Hash还原身份证。
---
# 第十一章 身份证重复约束
如果产品规则限制:
> 一个身份证只能一个有效账号
可建立:
```sql
CREATE UNIQUE INDEX uk_identity_hash
ON user_identities(id_number_hash)
WHERE verification_status = 'verified';
```
具体注销后是否允许重新注册,应由后续业务规则决定。
---
# 第十二章 user_profiles 婚恋资料表
职责:
> 用户可以参与匹配的主要资料。
建议:
```sql
CREATE TABLE user_profiles (
user_id UUID PRIMARY KEY,
display_name VARCHAR(64),
gender VARCHAR(16) NOT NULL,
birth_date DATE NOT NULL,
height_cm SMALLINT,
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 TEXT,
profile_completion SMALLINT NOT NULL DEFAULT 0,
profile_status VARCHAR(32) NOT NULL DEFAULT 'in_progress',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT fk_profile_user
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第十三章 用户年龄不要单独存
年龄随时间变化。
因此:
> 存birth_date,不存age作为业务真相。
展示时:
```text
age = 当前日期 - birth_date
```
如为了搜索性能需要,可建立表达式索引或预计算字段,但不要让前端修改age。
---
# 第十四章 profile_status
建议:
```text
in_progress
completed
pending_review
approved
need_edit
rejected
```
但要注意:
> Profile状态主要用于快速业务查询。
完整审核历史仍以review_tasks为准。
---
# 第十五章 user_preferences 择偶条件表
一用户一条主配置。
```sql
CREATE TABLE user_preferences (
user_id UUID PRIMARY KEY,
min_age SMALLINT,
max_age SMALLINT,
age_priority VARCHAR(16),
min_height_cm SMALLINT,
max_height_cm SMALLINT,
height_priority VARCHAR(16),
region_scope JSONB,
region_priority VARCHAR(16),
marital_statuses JSONB,
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),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT fk_preference_user
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第十六章 为什么部分条件可以JSONB
适合JSONB:
```text
region_scope
marital_statuses
```
因为本质是小型多选集合。
不建议把整个择偶条件全部放一个JSONB中。
核心匹配字段仍应独立列。
---
# 第十七章 Priority约束
```text
required
preferred
```
所有支持硬条件/软条件的字段统一使用。
---
# 第十八章 user_interests 兴趣关系表
```sql
CREATE TABLE user_interests (
user_id UUID NOT NULL,
interest_code VARCHAR(32) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY(user_id, interest_code),
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第十九章 interest_dictionary 兴趣字典
```sql
CREATE TABLE interest_dictionary (
code VARCHAR(32) PRIMARY KEY,
name VARCHAR(64) NOT NULL,
sort_order INT NOT NULL DEFAULT 0,
enabled BOOLEAN NOT NULL DEFAULT TRUE
);
```
例如:
```text
travel
reading
sports
fitness
photography
movie
music
food
cooking
outdoor
pets
games
culture
charity
```
---
# 第二十章 user_photos 用户照片表
```sql
CREATE TABLE user_photos (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
file_key VARCHAR(512) NOT NULL,
photo_type VARCHAR(32) NOT NULL,
sort_order SMALLINT NOT NULL DEFAULT 0,
review_status VARCHAR(32) NOT NULL DEFAULT 'pending',
review_reason VARCHAR(256),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
photo_type:
```text
avatar
lifestyle
```
---
# 第二十一章 主头像约束
建议应用层保证:
> 一个用户只有一个有效avatar。
数据库可建立部分唯一索引:
```sql
CREATE UNIQUE INDEX uk_user_avatar
ON user_photos(user_id)
WHERE photo_type = 'avatar'
AND deleted_at IS NULL;
```
---
# 第二十二章 user_privacy_settings 隐私设置
```sql
CREATE TABLE user_privacy_settings (
user_id UUID PRIMARY KEY,
visible_in_recommendations BOOLEAN NOT NULL DEFAULT TRUE,
visible_in_search BOOLEAN NOT NULL DEFAULT TRUE,
show_occupation BOOLEAN NOT NULL DEFAULT TRUE,
show_lifestyle_photos BOOLEAN NOT NULL DEFAULT TRUE,
receive_activity_recommendations BOOLEAN NOT NULL DEFAULT TRUE,
accept_matchmaker_recommendations BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第二十三章 review_tasks 审核任务
```sql
CREATE TABLE review_tasks (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
review_type VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'pending',
assigned_operator_id UUID,
submitted_at TIMESTAMPTZ NOT NULL,
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
final_comment VARCHAR(1000),
version INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第二十四章 审核任务索引
```sql
CREATE INDEX idx_review_tasks_status_time
ON review_tasks(status, submitted_at);
```
审核员:
```sql
CREATE INDEX idx_review_tasks_operator
ON review_tasks(assigned_operator_id, status);
```
---
# 第二十五章 review_items 审核明细
```sql
CREATE TABLE review_items (
id UUID PRIMARY KEY,
review_task_id UUID NOT NULL,
item_type VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL,
reason_code VARCHAR(64),
comment VARCHAR(500),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(review_task_id)
REFERENCES review_tasks(id)
);
```
item_type:
```text
avatar
lifestyle_photos
basic_profile
self_intro
relationship_profile
```
---
# 第二十六章 review_reasons 审核原因字典
```sql
CREATE TABLE review_reasons (
code VARCHAR(64) PRIMARY KEY,
name VARCHAR(128) NOT NULL,
category VARCHAR(32),
enabled BOOLEAN NOT NULL DEFAULT TRUE,
sort_order INT NOT NULL DEFAULT 0
);
```
例如:
```text
AVATAR_NOT_CLEAR
PHOTO_NOT_SELF
CONTACT_INFO_INCLUDED
AD_CONTENT
PROFILE_CONFLICT
INAPPROPRIATE_CONTENT
```
---
# 第二十七章 recommendation_records 推荐记录
用于:
- 避免重复推荐
- 计算冷却期
- 统计曝光
- 分析推荐效果
```sql
CREATE TABLE recommendation_records (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
target_user_id UUID NOT NULL,
source VARCHAR(32) NOT NULL,
score NUMERIC(5,2),
level VARCHAR(16),
reasons JSONB,
shown_at TIMESTAMPTZ NOT NULL,
action VARCHAR(32),
action_at TIMESTAMPTZ,
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(target_user_id) REFERENCES users(id)
);
```
---
# 第二十八章 推荐索引
```sql
CREATE INDEX idx_recommend_user_time
ON recommendation_records(user_id, shown_at DESC);
```
双方关系:
```sql
CREATE INDEX idx_recommend_pair
ON recommendation_records(user_id, target_user_id);
```
---
# 第二十九章 user_relations 用户单向关系
```sql
CREATE TABLE user_relations (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
target_user_id UUID NOT NULL,
relation_type VARCHAR(32) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(target_user_id) REFERENCES users(id),
CONSTRAINT chk_not_self_relation
CHECK (user_id <> target_user_id)
);
```
---
# 第三十章 user_relations唯一约束
同方向同一关系类型:
```sql
CREATE UNIQUE INDEX uk_user_relation
ON user_relations(user_id, target_user_id, relation_type);
```
relation_type:
```text
liked
passed
blocked
```
---
# 第三十一章 blocked优先级
业务逻辑中:
```text
blocked > liked / passed
```
一旦拉黑:
- 推荐层排除
- 聊天层禁止
- 红娘层排除
是否物理删除旧liked,可不删除。
保持行为历史更利于审计。
---
# 第三十二章 matches 双向匹配表
```sql
CREATE TABLE matches (
id UUID PRIMARY KEY,
user_a_id UUID NOT NULL,
user_b_id UUID NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'active',
source VARCHAR(32) NOT NULL DEFAULT 'user_like',
matched_at TIMESTAMPTZ NOT NULL,
unmatched_at TIMESTAMPTZ,
successful_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(user_a_id) REFERENCES users(id),
FOREIGN KEY(user_b_id) REFERENCES users(id),
CONSTRAINT chk_match_users
CHECK (user_a_id <> user_b_id)
);
```
---
# 第三十三章 matches标准化顺序
应用层必须保证:
```text
user_a_id < user_b_id
```
或者基于UUID字符串排序。
并建立:
```sql
CREATE UNIQUE INDEX uk_match_pair
ON matches(user_a_id, user_b_id);
```
如果未来允许同一双方历史多次重新匹配,可以改为:
> 活跃关系部分唯一索引。
V1建议一个pair一条match记录,通过状态保存历史。
---
# 第三十四章 match status
```text
active
getting_to_know
unmatched
successful
```
---
# 第三十五章 relationship_success_requests
用于:
> 成功牵手必须双方确认。
```sql
CREATE TABLE relationship_success_requests (
id UUID PRIMARY KEY,
match_id UUID NOT NULL,
initiated_by UUID NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'pending',
confirmed_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
resolved_at TIMESTAMPTZ,
FOREIGN KEY(match_id) REFERENCES matches(id),
FOREIGN KEY(initiated_by) REFERENCES users(id),
FOREIGN KEY(confirmed_by) REFERENCES users(id)
);
```
---
# 第三十六章 conversations 会话表
```sql
CREATE TABLE conversations (
id UUID PRIMARY KEY,
match_id UUID NOT NULL UNIQUE,
status VARCHAR(32) NOT NULL DEFAULT 'active',
last_message_id UUID,
last_message_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(match_id) REFERENCES matches(id)
);
```
---
# 第三十七章 conversation_members
```sql
CREATE TABLE conversation_members (
conversation_id UUID NOT NULL,
user_id UUID NOT NULL,
last_read_message_id UUID,
unread_count INT NOT NULL DEFAULT 0,
can_send BOOLEAN NOT NULL DEFAULT TRUE,
joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY(conversation_id, user_id),
FOREIGN KEY(conversation_id) REFERENCES conversations(id),
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第三十八章 messages 消息表
```sql
CREATE TABLE messages (
id UUID PRIMARY KEY,
conversation_id UUID NOT NULL,
sender_id UUID,
client_message_id VARCHAR(128),
message_type VARCHAR(32) NOT NULL,
content TEXT,
file_id UUID,
status VARCHAR(32) NOT NULL DEFAULT 'sent',
risk_level VARCHAR(16),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(conversation_id) REFERENCES conversations(id),
FOREIGN KEY(sender_id) REFERENCES users(id)
);
```
---
# 第三十九章 消息幂等唯一索引
```sql
CREATE UNIQUE INDEX uk_message_client_id
ON messages(sender_id, client_message_id)
WHERE client_message_id IS NOT NULL;
```
---
# 第四十章 消息查询索引
```sql
CREATE INDEX idx_messages_conversation_time
ON messages(conversation_id, created_at DESC);
```
---
# 第四十一章 message_type
```text
text
image
system
risk_notice
```
---
# 第四十二章 聊天内容是否加密
数据库层面建议至少:
> 磁盘/云数据库加密 + 严格访问权限。
如果后续提高安全等级,可对聊天正文做应用层加密。
但要注意:
- 风控检测
- 举报取证
- 消息搜索
会增加实现复杂度。
V1先以严格访问控制和审计为主。
---
# 第四十三章 activities 活动表
```sql
CREATE TABLE activities (
id UUID PRIMARY KEY,
title VARCHAR(200) NOT NULL,
cover_file_id UUID,
description TEXT,
organizer_org_id UUID NOT NULL,
coorganizer_text VARCHAR(500),
start_at TIMESTAMPTZ NOT NULL,
end_at TIMESTAMPTZ NOT NULL,
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 TIMESTAMPTZ,
registration_end_at TIMESTAMPTZ,
need_review BOOLEAN NOT NULL DEFAULT TRUE,
status VARCHAR(32) NOT NULL DEFAULT 'draft',
created_by UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
FOREIGN KEY(organizer_org_id) REFERENCES organizations(id)
);
```
---
# 第四十四章 activity_registrations
```sql
CREATE TABLE activity_registrations (
id UUID PRIMARY KEY,
activity_id UUID NOT NULL,
user_id UUID NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'pending',
registered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
reviewed_by UUID,
reviewed_at TIMESTAMPTZ,
reject_reason VARCHAR(500),
checkin_status VARCHAR(32) NOT NULL DEFAULT 'not_checked_in',
checkin_at TIMESTAMPTZ,
emergency_contact_name VARCHAR(100),
emergency_contact_phone_enc TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(activity_id) REFERENCES activities(id),
FOREIGN KEY(user_id) REFERENCES users(id),
UNIQUE(activity_id, user_id)
);
```
---
# 第四十五章 registration status
```text
pending
approved
rejected
cancelled
```
checkin_status:
```text
not_checked_in
checked_in
```
---
# 第四十六章 activity_feedback
```sql
CREATE TABLE activity_feedback (
id UUID PRIMARY KEY,
activity_id UUID NOT NULL,
user_id UUID NOT NULL,
satisfaction SMALLINT,
join_again VARCHAR(32),
interested_user_id UUID,
comment VARCHAR(1000),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(activity_id) REFERENCES activities(id),
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(interested_user_id) REFERENCES users(id),
UNIQUE(activity_id, user_id)
);
```
---
# 第四十七章 活动意向隐私
interested_user_id:
> 不能直接向对方公开。
只有双方互选后:
> 触发匹配逻辑。
---
# 第四十八章 matchmaker_services 公益红娘服务
```sql
CREATE TABLE matchmaker_services (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
service_type VARCHAR(32) NOT NULL,
description VARCHAR(1000),
status VARCHAR(32) NOT NULL DEFAULT 'submitted',
organization_id UUID,
assigned_operator_id UUID,
assigned_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(organization_id) REFERENCES organizations(id),
FOREIGN KEY(assigned_operator_id) REFERENCES operators(id)
);
```
---
# 第四十九章 红娘服务索引
```sql
CREATE INDEX idx_matchmaker_status
ON matchmaker_services(status, created_at);
```
按红娘:
```sql
CREATE INDEX idx_matchmaker_operator
ON matchmaker_services(assigned_operator_id, status);
```
---
# 第五十章 matchmaker_recommendations
```sql
CREATE TABLE matchmaker_recommendations (
id UUID PRIMARY KEY,
service_id UUID NOT NULL,
user_id UUID NOT NULL,
target_user_id UUID NOT NULL,
operator_id UUID NOT NULL,
reason VARCHAR(1000),
status VARCHAR(32) NOT NULL DEFAULT 'pending_view',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
viewed_at TIMESTAMPTZ,
responded_at TIMESTAMPTZ,
FOREIGN KEY(service_id) REFERENCES matchmaker_services(id),
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(target_user_id) REFERENCES users(id),
FOREIGN KEY(operator_id) REFERENCES operators(id)
);
```
---
# 第五十一章 matchmaker_followups
```sql
CREATE TABLE matchmaker_followups (
id UUID PRIMARY KEY,
service_id UUID NOT NULL,
operator_id UUID NOT NULL,
followup_type VARCHAR(32),
content VARCHAR(2000) NOT NULL,
result_code VARCHAR(64),
next_followup_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(service_id) REFERENCES matchmaker_services(id),
FOREIGN KEY(operator_id) REFERENCES operators(id)
);
```
---
# 第五十二章 reports 举报表
```sql
CREATE TABLE reports (
id UUID PRIMARY KEY,
reporter_user_id UUID NOT NULL,
target_user_id UUID NOT NULL,
report_type VARCHAR(64) NOT NULL,
description VARCHAR(2000),
status VARCHAR(32) NOT NULL DEFAULT 'pending',
risk_level VARCHAR(16) NOT NULL DEFAULT 'normal',
processed_by UUID,
processed_at TIMESTAMPTZ,
result_code VARCHAR(64),
result_comment VARCHAR(2000),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(reporter_user_id) REFERENCES users(id),
FOREIGN KEY(target_user_id) REFERENCES users(id)
);
```
---
# 第五十三章 举报索引
```sql
CREATE INDEX idx_reports_status_risk
ON reports(status, risk_level, created_at);
```
按被举报用户:
```sql
CREATE INDEX idx_reports_target
ON reports(target_user_id, created_at DESC);
```
---
# 第五十四章 report_evidences
```sql
CREATE TABLE report_evidences (
id UUID PRIMARY KEY,
report_id UUID NOT NULL,
file_key VARCHAR(512) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(report_id) REFERENCES reports(id)
);
```
---
# 第五十五章 user_risk_profiles
一用户一条风险摘要。
```sql
CREATE TABLE user_risk_profiles (
user_id UUID PRIMARY KEY,
score INT NOT NULL DEFAULT 0,
level VARCHAR(16) NOT NULL DEFAULT 'normal',
report_count INT NOT NULL DEFAULT 0,
block_count INT NOT NULL DEFAULT 0,
last_event_at TIMESTAMPTZ,
last_calculated_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第五十六章 risk_events
```sql
CREATE TABLE risk_events (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
event_type VARCHAR(64) NOT NULL,
score_delta INT NOT NULL DEFAULT 0,
source_type VARCHAR(32),
source_id UUID,
metadata JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第五十七章 risk_actions 风控处置
```sql
CREATE TABLE risk_actions (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
action_type VARCHAR(32) NOT NULL,
reason VARCHAR(1000),
operator_id UUID,
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(operator_id) REFERENCES operators(id)
);
```
action_type:
```text
warning
feature_limit
temporary_suspend
ban
manual_investigation
```
---
# 第五十八章 appeals 申诉表
```sql
CREATE TABLE appeals (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
related_action_id UUID,
content VARCHAR(2000) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'pending',
processed_by UUID,
processed_at TIMESTAMPTZ,
result VARCHAR(1000),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第五十九章 notifications 系统通知
```sql
CREATE TABLE notifications (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
type VARCHAR(32) NOT NULL,
title VARCHAR(200) NOT NULL,
content VARCHAR(2000),
business_type VARCHAR(32),
business_id UUID,
read_status VARCHAR(16) NOT NULL DEFAULT 'unread',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
read_at TIMESTAMPTZ,
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第六十章 通知索引
```sql
CREATE INDEX idx_notifications_user
ON notifications(user_id, read_status, created_at DESC);
```
---
# 第六十一章 organizations 组织表
```sql
CREATE TABLE organizations (
id UUID PRIMARY KEY,
name VARCHAR(200) NOT NULL,
type VARCHAR(32) NOT NULL,
contact_name VARCHAR(100),
contact_phone_enc TEXT,
status VARCHAR(32) NOT NULL DEFAULT 'active',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
type:
```text
women_federation
trade_union
charity_center
volunteer_team
civil_affairs_partner
other
```
---
# 第六十二章 operators 工作人员表
```sql
CREATE TABLE operators (
id UUID PRIMARY KEY,
organization_id UUID,
name VARCHAR(100) NOT NULL,
phone_enc TEXT,
phone_hash VARCHAR(128),
username VARCHAR(100) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'active',
last_login_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(organization_id) REFERENCES organizations(id)
);
```
---
# 第六十三章 工作人员唯一索引
```sql
CREATE UNIQUE INDEX uk_operator_username
ON operators(username);
```
手机号如果作为登录凭证,可对phone_hash建立唯一索引。
---
# 第六十四章 roles 角色表
```sql
CREATE TABLE roles (
id UUID PRIMARY KEY,
code VARCHAR(64) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
description VARCHAR(500),
system_role BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
---
# 第六十五章 默认角色
```text
super_admin
organization_admin
reviewer
matchmaker
activity_manager
risk_operator
volunteer
```
---
# 第六十六章 permissions 权限表
```sql
CREATE TABLE permissions (
id UUID PRIMARY KEY,
code VARCHAR(128) NOT NULL UNIQUE,
name VARCHAR(200) NOT NULL,
module VARCHAR(64) NOT NULL,
description VARCHAR(500)
);
```
---
# 第六十七章 role_permissions
```sql
CREATE TABLE role_permissions (
role_id UUID NOT NULL,
permission_id UUID NOT NULL,
PRIMARY KEY(role_id, permission_id),
FOREIGN KEY(role_id) REFERENCES roles(id),
FOREIGN KEY(permission_id) REFERENCES permissions(id)
);
```
---
# 第六十八章 operator_roles
```sql
CREATE TABLE operator_roles (
operator_id UUID NOT NULL,
role_id UUID NOT NULL,
PRIMARY KEY(operator_id, role_id),
FOREIGN KEY(operator_id) REFERENCES operators(id),
FOREIGN KEY(role_id) REFERENCES roles(id)
);
```
---
# 第六十九章 audit_logs 操作审计
```sql
CREATE TABLE audit_logs (
id UUID PRIMARY KEY,
operator_id UUID,
organization_id UUID,
action VARCHAR(128) NOT NULL,
resource_type VARCHAR(64) NOT NULL,
resource_id VARCHAR(128),
before_data JSONB,
after_data JSONB,
ip VARCHAR(64),
user_agent VARCHAR(500),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
---
# 第七十章 审计数据要求
before_data / after_data:
必须脱敏。
禁止写入:
- 完整身份证
- 完整手机号
- 密码
- Token
- 聊天正文
---
# 第七十一章 sensitive_access_logs
专门记录敏感资料查看。
```sql
CREATE TABLE sensitive_access_logs (
id UUID PRIMARY KEY,
operator_id UUID NOT NULL,
user_id UUID NOT NULL,
data_type VARCHAR(64) NOT NULL,
reason VARCHAR(500),
ip VARCHAR(64),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY(operator_id) REFERENCES operators(id),
FOREIGN KEY(user_id) REFERENCES users(id)
);
```
---
# 第七十二章 system_configs
轻量系统配置。
```sql
CREATE TABLE system_configs (
config_key VARCHAR(128) PRIMARY KEY,
config_value JSONB NOT NULL,
description VARCHAR(500),
updated_by UUID,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
---
# 第七十三章 推荐配置示例
```text
daily_recommendation_limit
max_profile_photos
max_interest_tags
recommendation_cooldown_days
activity_enabled
matchmaker_enabled
chat_image_enabled
```
---
# 第七十四章 match_rule_configs
推荐独立结构:
```sql
CREATE TABLE match_rule_configs (
id UUID PRIMARY KEY,
version INT NOT NULL,
status VARCHAR(16) NOT NULL,
age_weight INT NOT NULL,
region_weight INT NOT NULL,
marital_weight INT NOT NULL,
children_weight INT NOT NULL,
education_weight INT NOT NULL,
height_weight INT NOT NULL,
marriage_plan_weight INT NOT NULL,
settlement_weight INT NOT NULL,
lifestyle_weight INT NOT NULL,
interest_weight INT NOT NULL,
minimum_score INT NOT NULL DEFAULT 60,
created_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
activated_at TIMESTAMPTZ
);
```
---
# 第七十五章 匹配规则版本化
不要直接覆盖原权重。
建议:
```text
version 1
version 2
version 3
```
只有一条:
```text
status = active
```
这样以后可以追溯:
> 某次推荐使用哪套规则。
recommendation_records可以增加:
```text
rule_version
```
---
# 第七十六章 ER核心关系概览
```text
users
│
├──1:1── user_identities
├──1:1── user_profiles
├──1:1── user_preferences
├──1:1── user_privacy_settings
│
├──1:N── user_photos
├──1:N── user_interests
├──1:N── review_tasks
├──1:N── recommendation_records
├──1:N── user_relations
├──N:N── matches
├──N:N── conversations
├──1:N── activity_registrations
├──1:N── matchmaker_services
├──1:N── reports
├──1:1── user_risk_profiles
└──1:N── notifications
```
---
# 第七十七章 匹配关系ER
```text
User A
│
├── liked ──────> User B
│
User B
│
└── liked ──────> User A
│
↓
Match
│
↓
Conversation
│
↓
Messages
```
---
# 第七十八章 红娘关系ER
```text
User
↓
MatchmakerService
↓
Operator / Matchmaker
↓
MatchmakerRecommendation
↓
TargetUser
```
红娘推荐不会直接写入:
```text
user_relations.liked
```
必须由用户本人操作。
---
# 第七十九章 活动关系ER
```text
Activity
│
├── ActivityRegistration ── User
│
└── ActivityFeedback ───── User
│
└── interested_user_id
```
---
# 第八十章 审核关系ER
```text
User
↓
ReviewTask
↓
ReviewItem
```
一次资料修改:
> 新建新的ReviewTask。
不要覆盖历史审核任务。
---
# 第八十一章 数据保留原则
以下数据不能因为状态结束就删除:
- 匹配记录
- 红娘服务记录
- 审核记录
- 举报记录
- 风控处置
- 活动报名
- 操作日志
原因:
> 服务质量、纠纷处理、统计、审计都需要历史。
---
# 第八十二章 软删除建议
适合soft delete:
- 用户照片
- 活动
- 某些运营配置
不建议简单soft delete:
- messages
- audit_logs
- reports
- review_tasks
这些属于业务记录。
---
# 第八十三章 用户注销后的数据策略
用户注销后:
```text
users.account_status = closed
```
同时:
- recommendation_enabled = false
- search_visibility = false
- 停止匹配
- 停止新聊天
根据数据生命周期策略:
> 对公开资料匿名化或进入待清理流程。
具体删除周期需在隐私合规设计章节明确。
---
# 第八十四章 数据脱敏查询层
数据库Entity不直接返回前端。
必须经过DTO转换。
例如:
```text
PublicProfileDTO
```
不会包含:
```text
real_name_enc
id_number_enc
phone
```
---
# 第八十五章 公共用户资料DTO
建议包含:
```text
userId
displayName
age
height
region
education
occupation
maritalStatus
childrenStatus
interests
photos
relationshipGoal
marriagePlan
verified
reviewed
recommendReasons
```
---
# 第八十六章 自己查看自己DTO
SelfProfileDTO可增加:
```text
phoneMasked
reviewStatus
profileCompletion
privacySettings
```
仍然不要默认返回完整身份证。
---
# 第八十七章 红娘DTO
MatchmakerUserDTO:
可查看:
- 公开资料
- 择偶条件
- 服务状态
- 推荐记录
- 跟进记录
不可包含:
- 身份证
- 完整聊天正文
- 风控证据
---
# 第八十八章 管理员DTO
AdminUserDTO根据权限动态裁剪。
敏感数据接口应独立:
```text
GET /admin/users/{id}/sensitive-identity
```
而不是:
> 用户详情接口默认带完整实名信息。
---
# 第八十九章 数据库索引重点
重点索引建议:
```text
users(account_status)
users(relationship_status)
user_profiles(gender, region_code)
user_profiles(birth_date)
user_profiles(profile_status)
review_tasks(status, submitted_at)
user_relations(user_id, target_user_id)
user_relations(target_user_id, relation_type)
matches(user_a_id, user_b_id)
messages(conversation_id, created_at)
activities(status, start_at)
activity_registrations(activity_id, status)
matchmaker_services(status, assigned_operator_id)
reports(status, risk_level)
reports(target_user_id)
notifications(user_id, read_status, created_at)
```
---
# 第九十章 匹配初筛查询建议
V1不要做全量暴力评分。
数据库先筛:
```text
性别
审核通过
账号正常
婚恋状态正常
隐私允许
年龄粗范围
地区
```
然后应用层继续:
```text
双方required条件
preferred条件
权重评分
```
---
# 第九十一章 年龄查询建议
使用birth_date转换条件。
例如用户要求:
> 28~35岁。
换算成:
```text
birth_date BETWEEN 某两个日期
```
这样比数据库逐条计算年龄更利于索引。
---
# 第九十二章 大字段控制
以下字段不要放主列表查询:
- self_intro
- 活动description
- 举报description
- 红娘followup全文
- 消息content
列表页只查询必要字段。
---
# 第九十三章 JSONB使用边界
适合:
- 推荐理由快照
- 风险事件metadata
- 审计before/after
- 系统配置
- 小型多选条件
不适合:
> 核心业务实体全部JSON化。
---
# 第九十四章 数据库事务场景一:双向喜欢
事务建议:
```text
BEGIN
↓
插入A→B liked
↓
检查B→A liked
↓
如果存在:
创建match
创建conversation
创建2条conversation_member
COMMIT
↓
事务后发送通知
```
---
# 第九十五章 并发双向喜欢
A和B同时点击喜欢。
必须通过:
- unique relation约束
- unique match pair约束
- 捕获duplicate key后重新查询
保证最终:
> 只有一个Match。
---
# 第九十六章 数据库事务场景二:活动名额
审核通过报名时:
```text
BEGIN
↓
锁定活动/名额状态
↓
统计当前已通过人数
↓
判断性别名额
↓
更新registration
COMMIT
```
避免超额。
---
# 第九十七章 更高效的活动名额方案
如果未来活动量增大:
可在activities增加:
```text
approved_male_count
approved_female_count
approved_total_count
```
使用原子更新。
V1可以先用事务统计。
---
# 第九十八章 数据库事务场景三:审核领取
```sql
UPDATE review_tasks
SET status = 'processing',
assigned_operator_id = :operatorId
WHERE id = :id
AND status = 'pending';
```
检查影响行数:
```text
1 → 领取成功
0 → 已被其他审核员领取
```
不一定需要Redis锁。
数据库原子更新更简单。
---
# 第九十九章 数据库事务场景四:签到
唯一报名记录上更新:
```text
not_checked_in → checked_in
```
重复签到:
> 返回已签到,而不是插入第二条记录。
---
# 第一百章 数据归档策略
V1无需复杂冷热分层。
但应预留:
messages和audit_logs未来可能体量最大。
后续可按:
- 时间分区
- 月度分区
- 历史归档
扩展。
---
# 第一百零一章 messages未来分区建议
如果消息量明显增长:
可按:
```text
created_at
```
按月分区。
V1阶段无需提前复杂化。
---
# 第一百零二章 audit_logs未来分区
同样可按:
> 月份。
因为操作日志通常只追加。
---
# 第一百零三章 数据库迁移工具
必须使用迁移工具。
Java:
```text
Flyway / Liquibase
```
Node:
```text
Prisma migrations / TypeORM migrations
```
不能生产环境:
> 手工改表。
---
# 第一百零四章 migration命名
例如:
```text
V001__init_users.sql
V002__add_profiles.sql
V003__add_review_tables.sql
V004__add_match_tables.sql
```
每次变更可追溯。
---
# 第一百零五章 初始化字典数据
建议migration或seed初始化:
- 兴趣字典
- 教育类型
- 职业分类
- 审核原因
- 权限
- 默认角色
- 默认匹配规则
---
# 第一百零六章 测试数据策略
开发环境可提供:
> seed脚本。
生成:
- 男用户
- 女用户
- 不同年龄
- 不同婚姻状态
- 不同择偶条件
- 匹配/不匹配数据
方便测试匹配算法。
---
# 第一百零七章 生产数据严禁复制到开发环境
尤其不得直接复制:
- 实名信息
- 聊天记录
- 用户照片
- 举报证据
如确需问题复现:
> 使用脱敏数据。
---
# 第一百零八章 备份对象
至少备份:
```text
PostgreSQL
对象存储关键Bucket
系统配置
数据库迁移文件
```
Redis:
原则上不需要作为业务恢复唯一依赖。
---
# 第一百零九章 数据恢复优先级
第一:
> 用户与业务数据库。
第二:
> 用户照片与证据。
第三:
> 配置。
Redis可以:
> 重建。
---
# 第一百一十章 敏感数据访问原则
实名数据库字段不能被:
- 推荐模块
- 活动普通列表
- 红娘候选列表
- 统计模块
直接查询。
建议代码层也分Repository。
例如:
```text
IdentityRepository
```
只允许Verification/SensitiveAdmin服务使用。
---
# 第一百一十一章 数据库账号权限
生产环境至少拆:
```text
app_rw
migration_admin
backup_user
readonly_reporting
```
后台运营人员绝不能直接拥有数据库账号。
---
# 第一百一十二章 数据最小化
没有业务必要就不采集。
例如V1不采集:
- 房产证
- 车辆信息
- 详细收入
- 银行信息
- 精确住宅地址
数据库也不要“先预留几十个字段以后可能用”。
---
# 第一百一十三章 业务唯一约束汇总
必须有:
```text
users.wechat_openid UNIQUE
users.phone UNIQUE(有效账号)
user_identities.user_id UNIQUE
user_profiles.user_id UNIQUE
user_preferences.user_id UNIQUE
user_privacy_settings.user_id UNIQUE
user_interests(user_id, interest_code) UNIQUE
user_relations(user_id,target_user_id,relation_type) UNIQUE
matches(user_a_id,user_b_id) UNIQUE
conversations.match_id UNIQUE
conversation_members(conversation_id,user_id) UNIQUE
messages(sender_id,client_message_id) UNIQUE
activity_registrations(activity_id,user_id) UNIQUE
activity_feedback(activity_id,user_id) UNIQUE
```
---
# 第一百一十四章 外键策略
核心业务表建议使用外键。
好处:
> 防止孤儿数据。
但生产删除必须谨慎。
多数情况下:
> 不使用ON DELETE CASCADE。
因为婚恋业务需要历史审计。
---
# 第一百一十五章 不建议Cascade删除
例如删除User时绝不能自动删除:
- messages
- reports
- matches
- review_tasks
因此:
> 账号关闭优先状态化,而不是删除。
---
# 第一百一十六章 统计数据来源
早期后台统计从业务表聚合。
例如:
成功牵手:
```text
matches.status = successful
```
审核通过:
```text
user_profiles.profile_status = approved
```
不要前端自己累加。
---
# 第一百一十七章 后续可增加每日汇总表
例如:
```text
daily_platform_metrics
```
字段:
```text
date
new_users
verified_users
approved_users
likes
mutual_matches
chat_started
activity_registrations
successful_matches
reports
```
当实时统计性能不足时再启用。
---
# 第一百一十八章 推荐记录的数据价值
recommendation_records后续可以分析:
```text
曝光 → 查看
查看 → 喜欢
喜欢 → 双向
双向 → 聊天
```
这比一开始建设复杂AI推荐系统更重要。
---
# 第一百一十九章 未来AI匹配的数据基础
如果未来引入AI,现有表已经提供:
- 基础条件
- 兴趣
- 婚恋计划
- 推荐曝光
- 喜欢行为
- 双向匹配
- 解除匹配
- 成功牵手
可以作为后续推荐优化基础。
因此V1不需要提前做向量数据库。
---
# 第一百二十章 数据库V1明确不建设
暂不需要:
- ElasticSearch
- Neo4j
- Milvus
- ClickHouse
- Kafka存储
- 数据湖
- 特征库
- AI向量表
- 推荐模型训练表
- 支付订单表
- VIP表
- 礼物表
- 直播表
- 短视频表
---
# 第一百二十一章 核心表开发优先级
## P0
第一批:
```text
users
user_identities
user_profiles
user_preferences
user_interests
user_photos
user_privacy_settings
review_tasks
review_items
recommendation_records
user_relations
matches
conversations
conversation_members
messages
organizations
operators
roles
permissions
role_permissions
operator_roles
audit_logs
```
---
## P1
第二批:
```text
activities
activity_registrations
activity_feedback
matchmaker_services
matchmaker_recommendations
matchmaker_followups
notifications
```
---
## P2
第三批:
```text
reports
report_evidences
user_risk_profiles
risk_events
risk_actions
appeals
system_configs
match_rule_configs
sensitive_access_logs
```
风控功能本身仍是V1必做,这里的P2只是数据库实施排期顺序,不代表可以不上线。
---
# 第一百二十二章 数据库验收重点
必须验证:
### 用户
一个微信账号不能生成多个正常用户。
### 身份
一个认证身份证不能无限重复注册。
### 关系
不能自己喜欢自己。
### Match
一对用户不能生成重复Match。
### 消息
重复clientMessageId不能生成两条消息。
### 活动
同一用户不能重复报名同一活动。
### 审核
一个审核任务不能被多人同时完成。
### 权限
敏感信息不进入普通查询结果。
---
# 第一百二十三章 数据一致性验收
必须重点测试:
```text
喜欢成功但Match失败
Match成功但Conversation失败
活动报名成功但人数超限
举报处理后User状态未同步
用户拉黑后Conversation还能发送
成功牵手后仍进入推荐池
```
这些都必须通过事务和业务状态保证一致。
---
# 第一百二十四章 数据库ER文字总图
```text
User
│
├─ Identity
├─ Profile
│ ├─ Photos
│ ├─ Interests
│ ├─ Preference
│ └─ Privacy
│
├─ Reviews
│ └─ ReviewItems
│
├─ Relations
│ ↓
│ Match
│ ↓
│ Conversation
│ ↓
│ Messages
│
├─ ActivityRegistrations
│ ↓
│ Activities
│
├─ MatchmakerServices
│ ├─ Recommendations
│ └─ Followups
│
├─ Reports
│ └─ Evidences
│
├─ RiskProfile
│ ├─ RiskEvents
│ └─ RiskActions
│
└─ Notifications
Organization
│
└─ Operators
├─ Roles
│ └─ Permissions
└─ AuditLogs
```
---
# 第一百二十五章 数据库设计最终原则
数据库层必须做到:
> **真实身份和公开婚恋资料物理分层。**
> **所有双向关系都有数据库唯一约束。**
> **所有重要状态变化都有历史可追溯。**
> **聊天、举报、审核、风控不能因为用户状态变化被随意删除。**
> **匹配数据能够支持未来持续优化,但V1不提前复杂化。**
> **敏感数据永远不通过普通User查询顺手返回。**
> **每个高风险业务都依赖数据库约束兜底,而不是只相信前端。**
最终数据库不是为了“表越多越专业”,而是为了保证:
> **用户、匹配、聊天、活动、红娘、审核和安全这几条核心业务链在任何异常、并发和后续扩展场景下,都能保持数据清晰、状态一致、责任可追溯。**