# 飞书通知与问答接入数据库设计 ## 文档信息 | 项目 | 内容 | | --- | --- | | 需求分析文档 | docs/1.需求分析/4.飞书通知与问答接入.md | | 功能设计文档 | docs/2.功能设计/4.飞书通知与问答接入.md | | 数据库类型 | SQLite / Django ORM | | 表名前缀 | ra_ | | 设计日期 | 2026-06-07 | | 设计版本 | V1.0 | --- ## 一、设计原则 | 原则 | 说明 | | --- | --- | | 统一通知抽象 | 三个工作流共用统一通知服务和通用通知记录,减少重复实现 | | 兼容现有表 | 现有法规通知、填表通知可保留;新增通用表作为后续统一入口 | | 可判重 | 通知记录必须支持同一批次、同一流程、同一状态只发送一次 | | 摘要入库 | 只保存发送摘要、状态、错误,不保存完整富文本 payload | | 映射可维护 | 系统用户与飞书用户映射独立建表,通过 Django Admin 维护 | | 问答可扩展 | 预留问答日志表,首期可不接事件回调 | | SQLite 兼容 | 使用 Django ORM 常规字段,避免数据库特有能力 | --- ## 二、ER 图 ```mermaid erDiagram AUTH_USER ||--o{ RA_FEISHU_USER_MAPPING : maps AUTH_USER ||--o{ RA_WORKFLOW_NOTIFICATION_RECORD : triggers RA_FEISHU_USER_MAPPING ||--o{ RA_WORKFLOW_NOTIFICATION_RECORD : resolves AUTH_USER ||--o{ RA_FEISHU_QUESTION_LOG : asks RA_WORKFLOW_NOTIFICATION_RECORD { bigint id string workflow_type bigint workflow_batch_id string workflow_status string dedupe_key string channel string target string send_status } RA_FEISHU_USER_MAPPING { bigint id bigint system_user_id string feishu_open_id string feishu_user_id string feishu_mobile boolean is_active } RA_FEISHU_QUESTION_LOG { bigint id bigint system_user_id string feishu_open_id string intent string query_object string status } ``` --- ## 三、表结构设计 ### 3.1 ra_feishu_user_mapping 系统用户与飞书用户标识映射表。首期通知发送给环境变量中配置的指定个人账号,本表通过 Django Admin 手工维护,用于后续按发起人私聊通知和飞书私聊问答身份识别。 | 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 | | --- | --- | --- | --- | --- | | id | BigAutoField | integer | 是 | 主键 | | system_user_id | ForeignKey | bigint | 是 | 关联 Django 用户 | | feishu_display_name | CharField(120) | varchar(120) | 否 | 飞书展示名,便于后台识别 | | feishu_open_id | CharField(120) | varchar(120) | 否 | 飞书 open_id,优先用于 @ | | feishu_user_id | CharField(120) | varchar(120) | 否 | 飞书 user_id,第二优先级 | | feishu_mobile | CharField(40) | varchar(40) | 否 | 飞书手机号,兜底 | | is_active | BooleanField | bool | 是 | 是否启用 | | remark | CharField(255) | varchar(255) | 否 | 备注 | | created_at | DateTimeField | datetime | 是 | 创建时间 | | updated_at | DateTimeField | datetime | 是 | 更新时间 | 约束: | 约束名 | 字段 | 说明 | | --- | --- | --- | | uq_ra_feishu_mapping_user | system_user_id | 一个系统用户首期只维护一条启用映射 | 索引: | 索引名 | 字段 | 说明 | | --- | --- | --- | | idx_ra_feishu_mapping_active | is_active | 后台筛选启用映射 | | idx_ra_feishu_mapping_open | feishu_open_id | 后续私聊事件反查用户 | | idx_ra_feishu_mapping_userid | feishu_user_id | 后续私聊事件反查用户 | | idx_ra_feishu_mapping_mobile | feishu_mobile | 手机号兜底查询 | 校验规则: | 规则 | 说明 | | --- | --- | | 至少一个飞书标识 | `feishu_open_id`、`feishu_user_id`、`feishu_mobile` 至少填写一个 | | @ 优先级 | `feishu_open_id -> feishu_user_id -> feishu_mobile` | --- ### 3.2 ra_workflow_notification_record 通用工作流通知记录表。用于记录自动汇总、法规核查、自动填表的飞书通知发送结果。现有专项通知表可继续保留,后续逐步收敛到本表。 | 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 | | --- | --- | --- | --- | --- | | id | BigAutoField | integer | 是 | 主键 | | workflow_type | CharField(40) | varchar(40) | 是 | file_summary、regulatory_review、application_form_fill | | workflow_batch_id | PositiveBigIntegerField | bigint | 是 | 对应工作流批次 ID | | workflow_batch_no | CharField(80) | varchar(80) | 是 | 批次编号冗余,便于展示 | | workflow_status | CharField(40) | varchar(40) | 是 | success、partial_success、failed 等 | | dedupe_key | CharField(160) | varchar(160) | 是 | 判重键 | | trigger_user_id | ForeignKey | bigint | 是 | 发起人或上传人 | | feishu_mapping_id | ForeignKey | bigint | 否 | 命中的飞书用户映射 | | channel | CharField(40) | varchar(40) | 是 | mock、feishu_api、disabled | | target | CharField(160) | varchar(160) | 否 | 指定个人账号名称、open_id、user_id 或目标标识 | | at_display_name | CharField(120) | varchar(120) | 否 | 被 @ 人展示名 | | at_identifier_type | CharField(30) | varchar(30) | 否 | open_id、user_id、mobile、missing | | at_identifier_masked | CharField(120) | varchar(120) | 否 | 脱敏后的 @ 标识 | | send_status | CharField(30) | varchar(30) | 是 | pending、success、failed、skipped_duplicate、disabled | | message_title | CharField(200) | varchar(200) | 是 | 通知标题 | | message_summary | TextField | text | 否 | 发送摘要,不保存完整 payload | | result_url | CharField(500) | varchar(500) | 否 | 系统结果入口 | | external_message_id | CharField(120) | varchar(120) | 否 | Webhook 一般为空,API 发送时保存 | | error_code | CharField(80) | varchar(80) | 否 | 飞书或客户端错误码 | | error_message | TextField | text | 否 | 失败原因 | | request_duration_ms | PositiveIntegerField | integer | 否 | HTTP 请求耗时 | | sent_at | DateTimeField | datetime | 否 | 成功发送时间 | | created_at | DateTimeField | datetime | 是 | 创建时间 | | updated_at | DateTimeField | datetime | 是 | 更新时间 | 唯一约束: | 约束名 | 字段 | 说明 | | --- | --- | --- | | uq_ra_notify_dedupe_key | dedupe_key | 同一批次、流程、状态只保留一个成功发送意图 | 索引: | 索引名 | 字段 | 说明 | | --- | --- | --- | | idx_ra_notify_workflow | workflow_type, workflow_batch_id | 批次详情页查询通知 | | idx_ra_notify_user_created | trigger_user_id, created_at | 用户通知历史 | | idx_ra_notify_status | send_status, created_at | 排查失败通知 | | idx_ra_notify_batch_no | workflow_batch_no | 按批次编号检索 | dedupe_key 生成规则: ```text {workflow_type}:{workflow_batch_id}:{workflow_status} ``` --- ### 3.3 ra_feishu_question_log 飞书问答日志预留表。首期可创建表但不接入事件回调;后续私聊问答 MVP 使用该表记录问题、意图、查询对象、回答摘要和错误信息。 | 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 | | --- | --- | --- | --- | --- | | id | BigAutoField | integer | 是 | 主键 | | system_user_id | ForeignKey | bigint | 否 | 识别出的系统用户 | | feishu_mapping_id | ForeignKey | bigint | 否 | 命中的飞书映射 | | feishu_open_id | CharField(120) | varchar(120) | 否 | 事件中的 open_id | | feishu_user_id | CharField(120) | varchar(120) | 否 | 事件中的 user_id | | source_type | CharField(30) | varchar(30) | 是 | private_chat、group_mention | | message_id | CharField(120) | varchar(120) | 否 | 飞书消息 ID | | question_text | TextField | text | 是 | 用户原始问题 | | intent | CharField(60) | varchar(60) | 否 | batch_status、risk_summary、export_summary 等 | | query_object | JSONField | text/json | 是 | 批次号、工作流类型、最近批次等查询对象 | | answer_summary | TextField | text | 否 | 回答摘要,不保存完整回答正文 | | permission_result | CharField(40) | varchar(40) | 否 | allowed、denied、unbound | | status | CharField(30) | varchar(30) | 是 | success、failed、ignored | | error_message | TextField | text | 否 | 异常说明 | | processed_at | DateTimeField | datetime | 否 | 处理完成时间 | | created_at | DateTimeField | datetime | 是 | 创建时间 | 索引: | 索引名 | 字段 | 说明 | | --- | --- | --- | | idx_ra_feishu_q_user_created | system_user_id, created_at | 用户问答历史 | | idx_ra_feishu_q_intent | intent, created_at | 按意图分析 | | idx_ra_feishu_q_status | status, created_at | 排查失败问答 | | idx_ra_feishu_q_message | message_id | 消息幂等 | --- ## 四、状态枚举 ### 4.1 WorkflowNotificationRecord.channel | 值 | 说明 | | --- | --- | | mock | 模拟通知 | | disabled | 真实通知未启用 | | feishu_api | 飞书官方智能体/企业自建应用消息 API | | feishu_webhook | 备选自定义机器人 Webhook,非首期主方案 | ### 4.2 WorkflowNotificationRecord.send_status | 值 | 说明 | | --- | --- | | pending | 待发送 | | success | 发送成功 | | failed | 发送失败 | | skipped_duplicate | 重复通知跳过 | | disabled | 未启用真实发送 | ### 4.3 FeishuQuestionLog.intent | 值 | 说明 | | --- | --- | | batch_status | 查询批次状态 | | risk_summary | 查询风险摘要 | | missing_summary | 查询缺失摘要 | | export_summary | 查询导出摘要 | | unknown | 未识别 | --- ## 五、与现有表的兼容关系 | 现有表 | 处理建议 | | --- | --- | | `ra_regulatory_notification_record` | 保留现有数据;法规核查真实飞书通知可新增写入通用表,后续再决定是否迁移 | | `ra_application_form_fill_notification_record` | 保留现有数据;自动填表通知状态展示可优先读通用表,兼容旧表 | | `ra_exported_summary_file` | 通知摘要中的导出文件数量来自该表 | | `ra_workflow_event` | 可记录通知节点事件,但不替代通知记录表 | | `auth_user` | 飞书映射通过外键关联系统用户 | --- ## 六、数据脱敏与安全 | 数据 | 入库策略 | | --- | --- | | App ID | 不入库,只在环境变量中维护 | | App Secret | 不入库,只在环境变量中维护 | | tenant_access_token | 不持久化入库,仅允许进程内短期缓存 | | 富文本完整 payload | 不入库 | | 手机号 | 映射表保存原值;通知记录只保存脱敏值 | | open_id/user_id | 映射表保存原值;通知记录保存脱敏值 | | 用户问题 | 问答日志保存原始问题,用于审计;不保存完整回答正文 | --- ## 七、迁移计划 | 步骤 | 说明 | | --- | --- | | 1 | 新增 `FeishuUserMapping` 模型和迁移 | | 2 | 新增 `WorkflowNotificationRecord` 模型和迁移 | | 3 | 新增 `FeishuQuestionLog` 预留模型和迁移 | | 4 | 注册 Django Admin 管理入口 | | 5 | 批次详情页查询通用通知记录展示 | | 6 | 保留现有专项通知表,不做破坏性迁移 | --- ## 八、验收 SQL 示例 查询某个批次通知状态: ```sql SELECT workflow_type, workflow_batch_no, workflow_status, channel, send_status, sent_at, error_message FROM ra_workflow_notification_record WHERE workflow_type = 'application_form_fill' AND workflow_batch_no = 'AFF-20260607-001' ORDER BY created_at DESC; ``` 查询未配置飞书映射的失败或降级通知: ```sql SELECT workflow_type, workflow_batch_no, trigger_user_id, send_status, message_summary FROM ra_workflow_notification_record WHERE at_identifier_type = 'missing' ORDER BY created_at DESC; ``` 查询飞书用户映射: ```sql SELECT u.username, m.feishu_display_name, m.feishu_open_id, m.feishu_user_id, m.feishu_mobile, m.is_active FROM ra_feishu_user_mapping m JOIN auth_user u ON u.id = m.system_user_id ORDER BY u.username; ```