Files
lehuo/docs/04_DATA_MODEL.md
T

14 KiB
Raw Blame History

04 数据模型

1. 通用字段规范

核心表统一包含:

  • id: bigint/uuid(二选一后固定;建议 bigint 内部主键 + public_id 随机ID)
  • public_id: varchar,外部暴露,禁止连续可枚举
  • created_at
  • updated_at
  • created_by(后台业务表适用)
  • updated_by
  • deleted_at(需要软删除的表)

时间统一数据库 UTC 存储,展示按 Asia/Shanghai。


2. 核心实体

users

微信/后台统一用户主体,避免重复身份系统。

  • id
  • public_id
  • status
  • user_type
  • mobile(加密/脱敏策略)
  • nickname
  • avatar_url
  • last_login_at

wechat_identities

  • user_id
  • openid_ciphertext(AES-256-GCM)
  • openid_hash(HMAC-SHA-256,仅用于等值查询)
  • unionid_ciphertext nullable
  • unionid_hash nullable
  • appid
  • created_at 唯一索引 (appid, openid_hash);日志和 API 均不得返回 openid/unionid。

staff_profiles

  • user_id
  • employee_no nullable
  • display_name
  • department
  • status

工作人员显示名不是服务对象敏感档案字段;严禁把其扩展为手机号、证件号或详细住址。服务对象称呼仍按下文要求加密。

roles / permissions / user_roles / role_permissions

标准 RBAC。Phase 1 预置 VOLUNTEER、STAFF、PROJECT_MANAGER、ADMIN、 SUPER_ADMIN,权限码由 shared contracts 统一定义。角色替换必须在事务中写审计,并禁止移除最后一名有效超级管理员。

admin_credentials

  • user_id(唯一)
  • username(唯一)
  • password_hash(Argon2id)
  • failed_login_count
  • locked_until nullable
  • password_changed_at

auth_sessions

  • public_id(随机 UUID)
  • user_id
  • channel(WECHAT/ADMIN)
  • token_hash(仅保存带 pepper 的 HMAC,不保存明文 token)
  • expires_at
  • last_used_at nullable
  • revoked_at nullable

同一用户、同一渠道重新登录时撤销旧会话;过期、撤销或非 ACTIVE 用户的会话均无效。


3. beneficiary_profiles

服务对象档案:

  • id
  • public_id
  • display_name_encrypted / display_name_hash
  • gender nullable
  • birth_year nullable
  • mobile_encrypted / mobile_hash nullable
  • region_code
  • address_detail_encrypted nullable
  • primary_category
  • key_care_flag
  • is_minor
  • status
  • notes_encrypted nullable
  • created_by
  • timestamps

不要默认收集完整身份证。


4. beneficiary_contacts

  • beneficiary_id
  • public_id
  • name_encrypted
  • relation
  • mobile_encrypted / mobile_hash
  • is_guardian
  • is_emergency_contact
  • consent_record_id nullable

5. beneficiary_tags / tags

标签:

  • 独居
  • 高龄
  • 行动不便
  • 定期探访
  • 困境儿童 等。

标签只能帮助服务,不应成为歧视性画像。


6. service_applications

  • id
  • public_id(随机 UUID,仅此字段对外暴露)
  • application_no(随机业务编号)
  • applicant_user_id
  • beneficiary_id nullable
  • consent_id
  • source_follow_up_task_id nullable(继续帮扶来源,唯一)
  • parent_application_id nullable(原申请)
  • client_request_id
  • request_fingerprint(域隔离 HMAC-SHA-256)
  • apply_mode
  • service_category
  • beneficiary_name_encrypted(AES-256-GCM)
  • contact_mobile_encrypted(AES-256-GCM)
  • contact_mobile_hash(域隔离 HMAC-SHA-256,仅用于等值查询)
  • region_code
  • description_encrypted(AES-256-GCM)
  • preferred_contact_time nullable
  • needs_home_visit nullable
  • mobility_difficulty nullable
  • status
  • source
  • submitted_at
  • accepted_at nullable
  • closed_at nullable
  • timestamps

首次申请在 Phase 2 创建时不自动建立 beneficiary 档案;Phase 3 经人工核验后关联。Phase 4 的继续帮扶申请保留 parent_application_id 和 source_follow_up_task_id,并创建新的独立 consent,不修改或复用原申请授权。

索引:

  • application_no unique
  • (applicant_user_id, client_request_id) unique,保证数据库级幂等
  • applicant_user_id + submitted_at
  • status + submitted_at
  • service_category + submitted_at
  • contact_mobile_hash

7. service_application_status_history

  • application_id
  • from_status
  • to_status
  • operator_user_id
  • reason_code
  • created_at

不可更新,只追加。


8. service_cases

  • id
  • public_id
  • case_no
  • application_id
  • accept_request_id / accept_request_fingerprint(接单幂等)
  • beneficiary_id
  • owner_staff_id
  • category
  • priority
  • status
  • planned_start_at nullable
  • planned_end_at nullable
  • service_location_encrypted nullable
  • summary_encrypted
  • result_summary_encrypted nullable
  • follow_up_plan_encrypted nullable
  • completed_at nullable
  • complete_request_id / complete_request_fingerprint nullable(完成服务幂等)
  • timestamps

9. case_assignees

支持多人协作:

  • case_id
  • user_id
  • assignee_type STAFF/VOLUNTEER
  • role
  • assigned_at
  • removed_at nullable

10. service_records

  • id
  • public_id
  • case_id
  • beneficiary_id
  • service_start_at
  • service_end_at nullable
  • service_type
  • client_request_id / request_fingerprint(同一创建者范围内幂等)
  • content_encrypted
  • result_encrypted
  • follow_up_suggestion_encrypted nullable
  • beneficiary_confirm_status nullable
  • created_by
  • timestamps

service_record_revisions 在修改前保存加密快照、原 revision 和修改人;更新使用 expectedRevision 乐观锁,防止覆盖并支持审计追溯。


11. follow_up_tasks

  • id
  • public_id / follow_up_no(均不可预测)
  • beneficiary_id
  • case_id nullable
  • owner_staff_id
  • due_at
  • follow_up_type(SERVICE_COMPLETION / KEY_CARE / MANUAL)
  • status(PENDING / COMPLETED / CANCELLED)
  • source(AUTO_SERVICE_COMPLETION / STAFF_MANUAL / KEY_CARE_PLAN)
  • reason_encrypted
  • result_code nullable
  • result_note_encrypted nullable
  • create_request_id / create_request_fingerprint
  • complete_request_id / complete_request_fingerprint nullable
  • completed_at nullable
  • created_by / updated_by / timestamps

自动回访与服务完成在同一事务创建。人工回访以 (created_by, create_request_id) 唯一;完成请求保存指纹并安全重放。

12. follow_up_status_history

  • follow_up_task_id
  • from_status nullable
  • to_status
  • operator_user_id
  • reason_code
  • created_at

只追加,不更新。继续帮扶申请通过 service_applications.source_follow_up_task_id 回指来源,原申请历史不覆盖。


13. volunteers / volunteer_skills

volunteers:

  • user_id
  • real_name
  • mobile
  • region_code
  • availability_note
  • status
  • approved_by nullable
  • approved_at nullable

skills:

  • volunteer_id
  • skill_code

14. projects

  • id
  • public_id
  • project_no
  • client_request_id / request_fingerprint
  • name
  • category
  • summary
  • content
  • cover_file_id
  • manager_user_id
  • start_date
  • end_date
  • target_people nullable
  • status
  • is_public
  • sort_order
  • timestamps

(created_by_id, client_request_id) 唯一;project_status_history 只追加记录创建、发布和完成。service_cases.project_id 可空,用于把既有工单归集到项目但不改变工单事实。


15. activities

  • id
  • public_id
  • activity_no
  • client_request_id / request_fingerprint
  • project_id nullable
  • title
  • summary
  • content
  • cover_file_id
  • start_at
  • end_at
  • registration_deadline
  • location_text
  • capacity nullable
  • status
  • published_at nullable
  • timestamps

(created_by_id, client_request_id) 唯一;activity_status_history 只追加记录活动状态变化。


16. activity_registrations

  • activity_id
  • user_id
  • volunteer_id
  • public_id / client_request_id / request_fingerprint
  • status
  • check_in_status
  • registered_at
  • reviewed_by / reviewed_at / review_note_encrypted nullable
  • checked_in_at nullable
  • checked_in_by nullable
  • service_minutes nullable
  • service_minutes_recorded_at / service_minutes_recorded_by nullable

唯一 (activity_id,user_id) 防重复报名。 activity_registration_history 只追加保存报名审核、取消、签到和服务时长变更前后事实,并以全局请求 UUID 唯一约束支持安全重放。

志愿者资料的姓名、手机号、可参与时间和审核备注加密;手机号与姓名仅保存域隔离 HMAC 查询值。volunteer_status_history 只追加,并与志愿者角色授予/撤销事务一致。


17. content_posts

  • id
  • public_id
  • type
  • title
  • summary
  • content
  • cover_file_id nullable
  • status DRAFT/REVIEW/PUBLISHED/ARCHIVED
  • author_user_id
  • reviewer_user_id nullable
  • published_at nullable
  • timestamps

18. files

只保存元数据:

  • id
  • public_id
  • storage_provider
  • bucket
  • object_key
  • original_filename_encrypted
  • mime_type
  • size
  • sha256
  • business_type
  • uploader_user_id
  • status
  • created_at

service_record_files 关联服务记录与已确认文件。对象 key 使用随机值,上传确认时复核大小、MIME 和可选 SHA-256;不匹配文件进入隔离状态。

Phase 5 允许 files.case_id 为空,并通过 business_type=PROJECT_COVER/ACTIVITY_COVER 关联公开封面;封面仍经同一私有对象存储 Adapter 生成短时读取地址,不保存永久公网 URL。

前端不得直接拼接永久公网 URL。


19. consents

  • id
  • public_id
  • user_id nullable
  • beneficiary_id nullable
  • consent_type
  • version
  • granted_by
  • granted_at
  • revoked_at nullable
  • evidence_json(仅保存必要证明)
  • ip_hash nullable
  • created_at

Phase 2 每次申请显式要求 privacyConsent=true,记录隐私同意版本、授权主体、授权时间、最小证据和 IP HMAC;不得在证据中重复保存申请敏感明文。


20. audit_logs

只追加:

  • id
  • request_id
  • actor_user_id
  • actor_role
  • action
  • resource_type
  • resource_public_id
  • result
  • metadata_json(不得含敏感明文)
  • ip_hash
  • user_agent_summary
  • created_at

21. notifications / notification_deliveries

通知实体与发送记录分开,支持重试和追踪。

Phase 6 已落地 content_posts、只追加的 content_post_status_history、notifications、notification_preferences 和 notification_deliveries。消息以 (user_id,event_key) 唯一防重,微信投递以 (notification_id,channel) 唯一,失败最多重试五次后进入 DEAD。

Phase 7 不建立可人工修改的统计快照;九项 V1 指标直接从申请、工单、服务记录、服务对象、志愿者、活动和报名服务分钟事实计算。


22. 数据库规则

  1. 必须使用 FK 或由 ORM/服务保证等价完整性;禁止无约束“凭感觉关联”。
  2. 唯一业务编号必须 unique。
  3. 常查字段建组合索引。
  4. 禁止对大文本字段滥建索引。
  5. 金额如未来出现用 decimal,禁止 float。
  6. 所有枚举在 contracts 中有单一事实来源。
  7. migration 不允许 destructive change 直接上线。
  8. 删除列采用“停用 → 迁移 → 验证 → 后续版本删除”。

23. financial_disclosures

  • id / public_id(随机 UUID)
  • client_request_id / request_fingerprint(创建幂等)
  • title / summary nullable
  • rich_text(后端白名单清洗后的富文本)
  • status:DRAFT / PUBLISHED / ARCHIVED
  • is_pinned / sort_order / revision
  • published_at nullable
  • created_by / updated_by / timestamps

financial_disclosure_status_history 只追加保存创建、发布和归档事实,请求 UUID 全局唯一。已发布文档禁止覆盖编辑,只能归档;草稿编辑使用 revision 乐观锁。


24. 旧物善用 Phase 1

  • old_item_donations:随机 public_id、非连续公益编号、捐赠人、幂等键、品类、新旧程度、数量、方式、状态、revision、定向需求、投递点、人工分配人员和真实帮扶人数。
  • old_item_donation_status_history:只追加保存初始提交和每次状态变化,request_id 全局唯一。
  • old_item_donation_files:关联最多六张已确认的 OLD_ITEM_DONATION_IMAGE 私有文件。
  • old_item_needs:随机公开 ID、品类、区域、受助群体、需求量、已满足量、优先级、排序、发布状态和 revision。
  • old_item_need_status_history:只追加保存需求发布与下架事实。
  • old_item_drop_off_points:后台维护的公开投递名称、区域、地址、开放时间、启停和排序。

联系人、手机号、上门取件地址、物品描述、驳回原因和内部轨迹备注加密保存;手机号另存域隔离 HMAC。列表不公开敏感明文,管理端详情读取写审计。全部外键使用 RESTRICT,公开资源不暴露自增主键。


25. 旧物善用 Phase 2 物资台账

  • old_item_inventory_imports:记录随机公开 ID、创建人幂等键、请求指纹、加密原文件名和导入行数;不保存原 Excel 文件。
  • old_item_inventory_items:随机公开 ID、非连续台账编号、可选来源捐赠/导入批次、品类、名称、数量、单位、仓位、状态、定向需求、可选受助对象、帮扶区域、加密备注、落地时间和 revision。每个来源捐赠最多绑定一个物资批次,避免重复入库。
  • old_item_inventory_events:只追加保存入库、分拣、消毒、出库、落地节点,请求 UUID 全局唯一,记录前后状态、时间、位置、加密备注和操作人。

台账状态严格为 IN_STOCK → SORTED → DISINFECTED → OUTBOUND → DELIVERED,不允许跳级、回退或覆盖事件。Excel 批量入库在一个事务中创建导入批次、所有物资行、入库事件和审计,任一行无效时整批回滚。受助对象只保存既有内部外键;小程序仅返回流转状态、时间和出库/落地的粗粒度地区。


26. 旧物善用 Phase 3 荣誉证书与积分

  • old_item_reward_configs:不可变规则版本,保存证书标题/正文/颁发方/法律声明、四类物资每件积分、单笔积分上限和当前激活标识;新版本不覆盖历史版本。
  • old_item_certificates:随机公开 ID、非连续证书编号、来源捐赠、规则版本、加密收件人称呼、完整模板快照、签发/撤销幂等指纹、签发与撤销操作者及时间。每笔捐赠最多一张当前有效证书,补发会撤销旧证书但不删除历史。
  • old_item_point_accounts:每个用户一个荣誉积分账户,保存余额和 revision;余额不得小于零。
  • old_item_point_entries:只追加积分流水,记录获得/人工校正、变更值、变更后余额、来源捐赠/规则版本、幂等指纹、加密原因和操作人;来源捐赠唯一,防止重复计分。

捐赠完成、证书签发、积分账户更新、积分流水和审计在同一事务提交。积分没有货币价值,不建立兑换订单、商品、转让、提现、到期或排行榜数据结构。