# 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`:只追加积分流水,记录获得/人工校正、变更值、变更后余额、来源捐赠/规则版本、幂等指纹、加密原因和操作人;来源捐赠唯一,防止重复计分。 捐赠完成、证书签发、积分账户更新、积分流水和审计在同一事务提交。积分没有货币价值,不建立兑换订单、商品、转让、提现、到期或排行榜数据结构。