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

45 KiB
Raw Permalink Blame History

丹阳公益婚恋服务平台

V1.0 数据库详细设计与核心数据模型

版本:V1.0
数据库建议:PostgreSQL
适用范围:微信小程序用户端、公益红娘工作台、运营管理后台
设计目标:结构清晰、敏感数据分层、状态可追溯、支持事务、便于后续扩展


第一章 数据库设计原则

V1.0数据库设计必须坚持:

  • 账号数据与婚恋资料分离
  • 公共资料与敏感资料分离
  • 用户状态与关系状态分离
  • 审核数据可追溯
  • 风控数据独立
  • 聊天数据独立
  • 活动数据独立
  • 红娘服务独立
  • 权限与组织独立
  • 重要业务状态不通过删除实现
  • 核心业务使用数据库约束兜底
  • PostgreSQL作为业务真实数据源

不建议:

  • 一个users表塞所有字段
  • 使用大量JSON替代清晰结构
  • 核心关系依赖Redis
  • 不做唯一约束
  • 只依赖前端校验

第二章 核心数据域

V1.0数据库建议划分为:

01 用户账号域
02 实名认证域
03 婚恋资料域
04 人工审核域
05 推荐与关系域
06 聊天消息域
07 公益活动域
08 公益红娘域
09 举报风控域
10 系统通知域
11 组织权限域
12 审计日志域
13 配置与统计域

第三章 核心表总览

建议V1.0核心表:

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

第四章 通用字段规范

大部分业务表建议包含:

id
created_at
updated_at

需要软删除的表:

deleted_at

业务状态不要用:

is_deleted = 0/1

优先采用:

deleted_at IS NULL

第五章 ID设计

推荐:

UUID / UUIDv7

或:

应用生成的分布式唯一ID。

不要让外部接口直接暴露连续自增ID。

例如:

user_id = 018f...

或者:

usr_xxxxxxxxx

如果数据库内部使用BIGINT,也建议API层映射公共ID。


第六章 users 用户账号表

职责:

只保存账号和平台级状态。

建议结构:

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约束

唯一约束:

CREATE UNIQUE INDEX uk_users_openid
ON users(wechat_openid)
WHERE deleted_at IS NULL;

手机号:

CREATE UNIQUE INDEX uk_users_phone
ON users(phone)
WHERE phone IS NOT NULL
AND deleted_at IS NULL;

unionid:

如业务使用:

CREATE UNIQUE INDEX uk_users_unionid
ON users(wechat_unionid)
WHERE wechat_unionid IS NOT NULL
AND deleted_at IS NULL;

第八章 users状态约束

account_status:

normal
reviewing_risk
limited
suspended
banned
closed

relationship_status:

searching
getting_to_know
paused
matched_successfully
service_exited

建议使用:

VARCHAR + CHECK

而不是PostgreSQL ENUM。

原因:

后续状态扩展和数据库迁移更方便。


第九章 user_identities 实名认证表

高度敏感。

与users严格分离。

字段建议:

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)
);

第十章 实名敏感字段

real_name_enc
id_number_enc

必须:

加密后存储。

同时建议存:

id_number_hash

用于:

  • 防重复身份证注册
  • 查询重复身份

但不能通过Hash还原身份证。


第十一章 身份证重复约束

如果产品规则限制:

一个身份证只能一个有效账号

可建立:

CREATE UNIQUE INDEX uk_identity_hash
ON user_identities(id_number_hash)
WHERE verification_status = 'verified';

具体注销后是否允许重新注册,应由后续业务规则决定。


第十二章 user_profiles 婚恋资料表

职责:

用户可以参与匹配的主要资料。

建议:

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作为业务真相。

展示时:

age = 当前日期 - birth_date

如为了搜索性能需要,可建立表达式索引或预计算字段,但不要让前端修改age。


第十四章 profile_status

建议:

in_progress
completed
pending_review
approved
need_edit
rejected

但要注意:

Profile状态主要用于快速业务查询。

完整审核历史仍以review_tasks为准。


第十五章 user_preferences 择偶条件表

一用户一条主配置。

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:

region_scope
marital_statuses

因为本质是小型多选集合。

不建议把整个择偶条件全部放一个JSONB中。

核心匹配字段仍应独立列。


第十七章 Priority约束

required
preferred

所有支持硬条件/软条件的字段统一使用。


第十八章 user_interests 兴趣关系表

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 兴趣字典

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
);

例如:

travel
reading
sports
fitness
photography
movie
music
food
cooking
outdoor
pets
games
culture
charity

第二十章 user_photos 用户照片表

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:

avatar
lifestyle

第二十一章 主头像约束

建议应用层保证:

一个用户只有一个有效avatar。

数据库可建立部分唯一索引:

CREATE UNIQUE INDEX uk_user_avatar
ON user_photos(user_id)
WHERE photo_type = 'avatar'
AND deleted_at IS NULL;

第二十二章 user_privacy_settings 隐私设置

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 审核任务

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)
);

第二十四章 审核任务索引

CREATE INDEX idx_review_tasks_status_time
ON review_tasks(status, submitted_at);

审核员:

CREATE INDEX idx_review_tasks_operator
ON review_tasks(assigned_operator_id, status);

第二十五章 review_items 审核明细

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:

avatar
lifestyle_photos
basic_profile
self_intro
relationship_profile

第二十六章 review_reasons 审核原因字典

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
);

例如:

AVATAR_NOT_CLEAR
PHOTO_NOT_SELF
CONTACT_INFO_INCLUDED
AD_CONTENT
PROFILE_CONFLICT
INAPPROPRIATE_CONTENT

第二十七章 recommendation_records 推荐记录

用于:

  • 避免重复推荐
  • 计算冷却期
  • 统计曝光
  • 分析推荐效果
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)
);

第二十八章 推荐索引

CREATE INDEX idx_recommend_user_time
ON recommendation_records(user_id, shown_at DESC);

双方关系:

CREATE INDEX idx_recommend_pair
ON recommendation_records(user_id, target_user_id);

第二十九章 user_relations 用户单向关系

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唯一约束

同方向同一关系类型:

CREATE UNIQUE INDEX uk_user_relation
ON user_relations(user_id, target_user_id, relation_type);

relation_type:

liked
passed
blocked

第三十一章 blocked优先级

业务逻辑中:

blocked > liked / passed

一旦拉黑:

  • 推荐层排除
  • 聊天层禁止
  • 红娘层排除

是否物理删除旧liked,可不删除。

保持行为历史更利于审计。


第三十二章 matches 双向匹配表

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标准化顺序

应用层必须保证:

user_a_id < user_b_id

或者基于UUID字符串排序。

并建立:

CREATE UNIQUE INDEX uk_match_pair
ON matches(user_a_id, user_b_id);

如果未来允许同一双方历史多次重新匹配,可以改为:

活跃关系部分唯一索引。

V1建议一个pair一条match记录,通过状态保存历史。


第三十四章 match status

active
getting_to_know
unmatched
successful

第三十五章 relationship_success_requests

用于:

成功牵手必须双方确认。

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 会话表

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

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 消息表

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)
);

第三十九章 消息幂等唯一索引

CREATE UNIQUE INDEX uk_message_client_id
ON messages(sender_id, client_message_id)
WHERE client_message_id IS NOT NULL;

第四十章 消息查询索引

CREATE INDEX idx_messages_conversation_time
ON messages(conversation_id, created_at DESC);

第四十一章 message_type

text
image
system
risk_notice

第四十二章 聊天内容是否加密

数据库层面建议至少:

磁盘/云数据库加密 + 严格访问权限。

如果后续提高安全等级,可对聊天正文做应用层加密。

但要注意:

  • 风控检测
  • 举报取证
  • 消息搜索

会增加实现复杂度。

V1先以严格访问控制和审计为主。


第四十三章 activities 活动表

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

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

pending
approved
rejected
cancelled

checkin_status:

not_checked_in
checked_in

第四十六章 activity_feedback

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 公益红娘服务

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)
);

第四十九章 红娘服务索引

CREATE INDEX idx_matchmaker_status
ON matchmaker_services(status, created_at);

按红娘:

CREATE INDEX idx_matchmaker_operator
ON matchmaker_services(assigned_operator_id, status);

第五十章 matchmaker_recommendations

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

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 举报表

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)
);

第五十三章 举报索引

CREATE INDEX idx_reports_status_risk
ON reports(status, risk_level, created_at);

按被举报用户:

CREATE INDEX idx_reports_target
ON reports(target_user_id, created_at DESC);

第五十四章 report_evidences

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

一用户一条风险摘要。

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

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 风控处置

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:

warning
feature_limit
temporary_suspend
ban
manual_investigation

第五十八章 appeals 申诉表

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 系统通知

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)
);

第六十章 通知索引

CREATE INDEX idx_notifications_user
ON notifications(user_id, read_status, created_at DESC);

第六十一章 organizations 组织表

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:

women_federation
trade_union
charity_center
volunteer_team
civil_affairs_partner
other

第六十二章 operators 工作人员表

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)
);

第六十三章 工作人员唯一索引

CREATE UNIQUE INDEX uk_operator_username
ON operators(username);

手机号如果作为登录凭证,可对phone_hash建立唯一索引。


第六十四章 roles 角色表

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()
);

第六十五章 默认角色

super_admin
organization_admin
reviewer
matchmaker
activity_manager
risk_operator
volunteer

第六十六章 permissions 权限表

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

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

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 操作审计

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

专门记录敏感资料查看。

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

轻量系统配置。

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()
);

第七十三章 推荐配置示例

daily_recommendation_limit
max_profile_photos
max_interest_tags
recommendation_cooldown_days
activity_enabled
matchmaker_enabled
chat_image_enabled

第七十四章 match_rule_configs

推荐独立结构:

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
);

第七十五章 匹配规则版本化

不要直接覆盖原权重。

建议:

version 1
version 2
version 3

只有一条:

status = active

这样以后可以追溯:

某次推荐使用哪套规则。

recommendation_records可以增加:

rule_version

第七十六章 ER核心关系概览

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

User A
  │
  ├── liked ──────> User B
  │
User B
  │
  └── liked ──────> User A
          │
          ↓
        Match
          │
          ↓
     Conversation
          │
          ↓
       Messages

第七十八章 红娘关系ER

User
 ↓
MatchmakerService
 ↓
Operator / Matchmaker
 ↓
MatchmakerRecommendation
 ↓
TargetUser

红娘推荐不会直接写入:

user_relations.liked

必须由用户本人操作。


第七十九章 活动关系ER

Activity
  │
  ├── ActivityRegistration ── User
  │
  └── ActivityFeedback ───── User
                               │
                               └── interested_user_id

第八十章 审核关系ER

User
 ↓
ReviewTask
 ↓
ReviewItem

一次资料修改:

新建新的ReviewTask。

不要覆盖历史审核任务。


第八十一章 数据保留原则

以下数据不能因为状态结束就删除:

  • 匹配记录
  • 红娘服务记录
  • 审核记录
  • 举报记录
  • 风控处置
  • 活动报名
  • 操作日志

原因:

服务质量、纠纷处理、统计、审计都需要历史。


第八十二章 软删除建议

适合soft delete:

  • 用户照片
  • 活动
  • 某些运营配置

不建议简单soft delete:

  • messages
  • audit_logs
  • reports
  • review_tasks

这些属于业务记录。


第八十三章 用户注销后的数据策略

用户注销后:

users.account_status = closed

同时:

  • recommendation_enabled = false
  • search_visibility = false
  • 停止匹配
  • 停止新聊天

根据数据生命周期策略:

对公开资料匿名化或进入待清理流程。

具体删除周期需在隐私合规设计章节明确。


第八十四章 数据脱敏查询层

数据库Entity不直接返回前端。

必须经过DTO转换。

例如:

PublicProfileDTO

不会包含:

real_name_enc
id_number_enc
phone

第八十五章 公共用户资料DTO

建议包含:

userId
displayName
age
height
region
education
occupation
maritalStatus
childrenStatus
interests
photos
relationshipGoal
marriagePlan
verified
reviewed
recommendReasons

第八十六章 自己查看自己DTO

SelfProfileDTO可增加:

phoneMasked
reviewStatus
profileCompletion
privacySettings

仍然不要默认返回完整身份证。


第八十七章 红娘DTO

MatchmakerUserDTO:

可查看:

  • 公开资料
  • 择偶条件
  • 服务状态
  • 推荐记录
  • 跟进记录

不可包含:

  • 身份证
  • 完整聊天正文
  • 风控证据

第八十八章 管理员DTO

AdminUserDTO根据权限动态裁剪。

敏感数据接口应独立:

GET /admin/users/{id}/sensitive-identity

而不是:

用户详情接口默认带完整实名信息。


第八十九章 数据库索引重点

重点索引建议:

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不要做全量暴力评分。

数据库先筛:

性别
审核通过
账号正常
婚恋状态正常
隐私允许
年龄粗范围
地区

然后应用层继续:

双方required条件
preferred条件
权重评分

第九十一章 年龄查询建议

使用birth_date转换条件。

例如用户要求:

28~35岁。

换算成:

birth_date BETWEEN 某两个日期

这样比数据库逐条计算年龄更利于索引。


第九十二章 大字段控制

以下字段不要放主列表查询:

  • self_intro
  • 活动description
  • 举报description
  • 红娘followup全文
  • 消息content

列表页只查询必要字段。


第九十三章 JSONB使用边界

适合:

  • 推荐理由快照
  • 风险事件metadata
  • 审计before/after
  • 系统配置
  • 小型多选条件

不适合:

核心业务实体全部JSON化。


第九十四章 数据库事务场景一:双向喜欢

事务建议:

BEGIN
↓
插入A→B liked
↓
检查B→A liked
↓
如果存在:
  创建match
  创建conversation
  创建2条conversation_member
COMMIT
↓
事务后发送通知

第九十五章 并发双向喜欢

A和B同时点击喜欢。

必须通过:

  • unique relation约束
  • unique match pair约束
  • 捕获duplicate key后重新查询

保证最终:

只有一个Match。


第九十六章 数据库事务场景二:活动名额

审核通过报名时:

BEGIN
↓
锁定活动/名额状态
↓
统计当前已通过人数
↓
判断性别名额
↓
更新registration
COMMIT

避免超额。


第九十七章 更高效的活动名额方案

如果未来活动量增大:

可在activities增加:

approved_male_count
approved_female_count
approved_total_count

使用原子更新。

V1可以先用事务统计。


第九十八章 数据库事务场景三:审核领取

UPDATE review_tasks
SET status = 'processing',
    assigned_operator_id = :operatorId
WHERE id = :id
AND status = 'pending';

检查影响行数:

1 → 领取成功
0 → 已被其他审核员领取

不一定需要Redis锁。

数据库原子更新更简单。


第九十九章 数据库事务场景四:签到

唯一报名记录上更新:

not_checked_in → checked_in

重复签到:

返回已签到,而不是插入第二条记录。


第一百章 数据归档策略

V1无需复杂冷热分层。

但应预留:

messages和audit_logs未来可能体量最大。

后续可按:

  • 时间分区
  • 月度分区
  • 历史归档

扩展。


第一百零一章 messages未来分区建议

如果消息量明显增长:

可按:

created_at

按月分区。

V1阶段无需提前复杂化。


第一百零二章 audit_logs未来分区

同样可按:

月份。

因为操作日志通常只追加。


第一百零三章 数据库迁移工具

必须使用迁移工具。

Java:

Flyway / Liquibase

Node:

Prisma migrations / TypeORM migrations

不能生产环境:

手工改表。


第一百零四章 migration命名

例如:

V001__init_users.sql
V002__add_profiles.sql
V003__add_review_tables.sql
V004__add_match_tables.sql

每次变更可追溯。


第一百零五章 初始化字典数据

建议migration或seed初始化:

  • 兴趣字典
  • 教育类型
  • 职业分类
  • 审核原因
  • 权限
  • 默认角色
  • 默认匹配规则

第一百零六章 测试数据策略

开发环境可提供:

seed脚本。

生成:

  • 男用户
  • 女用户
  • 不同年龄
  • 不同婚姻状态
  • 不同择偶条件
  • 匹配/不匹配数据

方便测试匹配算法。


第一百零七章 生产数据严禁复制到开发环境

尤其不得直接复制:

  • 实名信息
  • 聊天记录
  • 用户照片
  • 举报证据

如确需问题复现:

使用脱敏数据。


第一百零八章 备份对象

至少备份:

PostgreSQL
对象存储关键Bucket
系统配置
数据库迁移文件

Redis:

原则上不需要作为业务恢复唯一依赖。


第一百零九章 数据恢复优先级

第一:

用户与业务数据库。

第二:

用户照片与证据。

第三:

配置。

Redis可以:

重建。


第一百一十章 敏感数据访问原则

实名数据库字段不能被:

  • 推荐模块
  • 活动普通列表
  • 红娘候选列表
  • 统计模块

直接查询。

建议代码层也分Repository。

例如:

IdentityRepository

只允许Verification/SensitiveAdmin服务使用。


第一百一十一章 数据库账号权限

生产环境至少拆:

app_rw
migration_admin
backup_user
readonly_reporting

后台运营人员绝不能直接拥有数据库账号。


第一百一十二章 数据最小化

没有业务必要就不采集。

例如V1不采集:

  • 房产证
  • 车辆信息
  • 详细收入
  • 银行信息
  • 精确住宅地址

数据库也不要“先预留几十个字段以后可能用”。


第一百一十三章 业务唯一约束汇总

必须有:

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

因此:

账号关闭优先状态化,而不是删除。


第一百一十六章 统计数据来源

早期后台统计从业务表聚合。

例如:

成功牵手:

matches.status = successful

审核通过:

user_profiles.profile_status = approved

不要前端自己累加。


第一百一十七章 后续可增加每日汇总表

例如:

daily_platform_metrics

字段:

date
new_users
verified_users
approved_users
likes
mutual_matches
chat_started
activity_registrations
successful_matches
reports

当实时统计性能不足时再启用。


第一百一十八章 推荐记录的数据价值

recommendation_records后续可以分析:

曝光 → 查看
查看 → 喜欢
喜欢 → 双向
双向 → 聊天

这比一开始建设复杂AI推荐系统更重要。


第一百一十九章 未来AI匹配的数据基础

如果未来引入AI,现有表已经提供:

  • 基础条件
  • 兴趣
  • 婚恋计划
  • 推荐曝光
  • 喜欢行为
  • 双向匹配
  • 解除匹配
  • 成功牵手

可以作为后续推荐优化基础。

因此V1不需要提前做向量数据库。


第一百二十章 数据库V1明确不建设

暂不需要:

  • ElasticSearch
  • Neo4j
  • Milvus
  • ClickHouse
  • Kafka存储
  • 数据湖
  • 特征库
  • AI向量表
  • 推荐模型训练表
  • 支付订单表
  • VIP表
  • 礼物表
  • 直播表
  • 短视频表

第一百二十一章 核心表开发优先级

P0

第一批:

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

第二批:

activities
activity_registrations
activity_feedback

matchmaker_services
matchmaker_recommendations
matchmaker_followups

notifications

P2

第三批:

reports
report_evidences
user_risk_profiles
risk_events
risk_actions
appeals

system_configs
match_rule_configs
sensitive_access_logs

风控功能本身仍是V1必做,这里的P2只是数据库实施排期顺序,不代表可以不上线。


第一百二十二章 数据库验收重点

必须验证:

用户

一个微信账号不能生成多个正常用户。

身份

一个认证身份证不能无限重复注册。

关系

不能自己喜欢自己。

Match

一对用户不能生成重复Match。

消息

重复clientMessageId不能生成两条消息。

活动

同一用户不能重复报名同一活动。

审核

一个审核任务不能被多人同时完成。

权限

敏感信息不进入普通查询结果。


第一百二十三章 数据一致性验收

必须重点测试:

喜欢成功但Match失败
Match成功但Conversation失败
活动报名成功但人数超限
举报处理后User状态未同步
用户拉黑后Conversation还能发送
成功牵手后仍进入推荐池

这些都必须通过事务和业务状态保证一致。


第一百二十四章 数据库ER文字总图

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查询顺手返回。

每个高风险业务都依赖数据库约束兜底,而不是只相信前端。

最终数据库不是为了“表越多越专业”,而是为了保证:

用户、匹配、聊天、活动、红娘、审核和安全这几条核心业务链在任何异常、并发和后续扩展场景下,都能保持数据清晰、状态一致、责任可追溯。