# 数据库 ER 图 + 环境变量清点 **日期**: 2026-06-15 **审计人**: Claude(满载跑批) **关联**: [[技术架构]] / [[风险跟踪表]] / [[前端审计报告]] --- ## 📌 1. ER 图(ASCII + Mermaid) ### 1.1 实体清单(16 张表) | # | 表名 | 中文 | 模型文件 | 用途 | |---|---|---|---|---| | 1 | `conversations` | 会话 | `conversation.py` | 核心:员工-坐席咨询会话 | | 2 | `messages` | 消息 | `message.py` | 会话内的所有消息 | | 3 | `agents` | 坐席 | `agent.py` | IT 服务人员 | | 4 | `employees` | 员工 | `employee.py` | 通过 OAuth2 认证的员工 | | 5 | `agent_notes` | 坐席备注 | `agent_note.py` | 坐席对会话的备注 | | 6 | `system_configs` | 系统配置 | `system_config.py` | 动态配置(关键词/阈值) | | 7 | `config_change_logs` | 配置变更日志 | `config_change_log.py` | 配置项的审计 | | 8 | `quick_reply_templates` | 快速回复模板 | `quick_reply_template.py` | 坐席常用回复 | | 9 | `funny_phrases` | 趣味话术 | `funny_phrase.py` | 等候中的趣味话 | | 10 | `approval_links` | 审批流程链接 | `approval_link.py` | 各类审批入口 | | 11 | `software_downloads` | 软件下载 | `software_download.py` | 常用软件清单 | | 12 | `troubleshooting_templates` | 排障模板 | `troubleshooting_template.py` | 标准化排障路径 | | 13 | `todo_items` | 待办事项 | `todo_item.py` | 工单/审批/设备 | | 14 | `roles` | 角色 | `role.py` | RBAC 角色定义 | | 15 | `user_roles` | 用户-角色关联 | `user_role.py` | 多对多关联 | | 16 | `role_mapping_rules` | 角色映射规则 | `role_mapping_rule.py` | 自动分配规则 | ### 1.2 ER 图(Mermaid) ```mermaid erDiagram EMPLOYEES ||--o{ CONVERSATIONS : "创建(通过 corp_id+employee_id)" EMPLOYEES ||--o{ USER_ROLES : "拥有角色" CONVERSATIONS ||--o{ MESSAGES : "包含" CONVERSATIONS ||--o{ AGENT_NOTES : "有备注" CONVERSATIONS }o--|| AGENTS : "被分配给" AGENTS ||--o{ AGENT_NOTES : "写" AGENTS ||--o{ QUICK_REPLY_TEMPLATES : "提交" AGENTS ||--o{ CONFIG_CHANGE_LOGS : "改配置" ROLES ||--o{ USER_ROLES : "分配给" ROLES ||--o{ ROLE_MAPPING_RULES : "按规则映射" CONVERSATIONS ||--o| TODO_ITEMS : "关联待办" SYSTEM_CONFIGS ||--o{ CONFIG_CHANGE_LOGS : "被改" EMPLOYEES { string id PK "UUID" string corp_id "企业ID" string employee_id "企微UserID" string name "姓名" string department "部门(IDs)" string position "岗位" string mobile string email string avatar int status "1激活 2禁用 4未激活" string it_level "技能等级" string it_level_source json notes "坐席备注" datetime last_login_at datetime created_at datetime updated_at } AGENTS { string id PK string user_id UK "企微UserID" string name string status "online/offline/busy" int current_load int max_load string role "admin/agent" json skill_tags string otp_secret "TOTP密钥" boolean otp_enabled string password_hash "bcrypt" datetime created_at datetime updated_at } CONVERSATIONS { string id PK string corp_id string employee_id string employee_name string department string position string level string status "ai/queued/serving/resolved" boolean is_vip boolean is_pinned boolean is_todo int urgency_score "1-5" json tags string assigned_agent_id FK json collaborating_agent_ids json participants int ai_substantive_reply_count int impact_scope boolean is_blocking string emotion_state string dify_conversation_id datetime last_message_at string last_message_summary datetime created_at datetime updated_at } MESSAGES { string id PK string conversation_id FK string sender_type "employee/agent/ai/system" string sender_id string sender_name text content string msg_type "text/image/file/voice/system" string reply_to_id "引用" string media_id string media_url string file_name int file_size json extra_data boolean ai_suggestion string status "sending/sent/delivered/read" datetime recallable_until boolean is_read string suggestion_action "accepted/edited/ignored" datetime created_at } AGENT_NOTES { string id PK string conversation_id FK string agent_id text content datetime created_at datetime updated_at } SYSTEM_CONFIGS { string id PK string config_key UK text config_value string description datetime updated_at } CONFIG_CHANGE_LOGS { string id PK string config_key text old_value text new_value string changed_by datetime changed_at } QUICK_REPLY_TEMPLATES { string id PK string category string title text content json variables int sort_order string status "draft/pending/approved/rejected" int version string submitted_by datetime created_at datetime updated_at } FUNNY_PHRASES { string id PK string scene "shake/keyword/waiting/connected/timeout/vip" text content string tone int sort_order boolean is_active datetime created_at datetime updated_at } APPROVAL_LINKS { string id PK string category "IT/HR/行政/财务" string title text url int sort_order datetime created_at datetime updated_at } SOFTWARE_DOWNLOADS { string id PK string category "办公/开发/安全/工具" string name string version string platform text download_url int sort_order datetime created_at datetime updated_at } TROUBLESHOOTING_TEMPLATES { string id PK string name string category "vpn/email/system/account" json path_steps json flowchart boolean is_active datetime created_at datetime updated_at } TODO_ITEMS { string id PK string type "ticket/approval/device" string title string priority "urgent/high/normal" json description string status "pending/processing/resolved" string assigned_agent_id string corp_id datetime created_at datetime updated_at } ROLES { string id PK string name UK "user/agent/admin" string display_name text description json permissions boolean is_default datetime created_at datetime updated_at } USER_ROLES { string id PK string employee_id string role_id FK string source "auto/tag/ehr/manual" string assigned_by datetime assigned_at datetime expires_at } ROLE_MAPPING_RULES { string id PK string role_id FK string source_type "wecom_tag/ehr_position" string source_value int priority boolean is_active datetime created_at } ``` ### 1.3 ER 关系总结 ``` ┌────────────┐ │ EMPLOYEES │ │ (员工) │ └─────┬──────┘ │ corp_id + employee_id ┌─────────────┼─────────────┐ │ │ │ v v v ┌────────┐ ┌──────────────┐ ┌──────────────┐ │CONVER- │ │ USER_ROLES │ │TODO_ITEMS │ │SATIONS │ │ ↕ │ │(企业内待办) │ └──┬─────┘ │ ROLES │ └──────────────┘ │ │↕ │ │ │ROLE_MAPPING │ │ │_RULES │ │ └──────────────┘ │ 1:N v ┌────────┐ 1:N ┌────────┐ │MESSAGES│◄─────│AGENT_ │ └────────┘ │NOTES │ └────┬───┘ │ 写 v ┌────────┐ ┌──────────┐ │AGENTS │ │CONFIGS │ └───┬────┘ │ ↕ │ │ 改 │CHANGE │◄───────┘ │LOGS │ └──────────┘ ``` **关系数**: 13 个外键关联 + 3 个 JSON 数组(协作/参与者/技能) **外键关系**: 1. `conversations.employee_id` → 企微 ID(无 DB FK,跨企业灵活) 2. `conversations.assigned_agent_id` → `agents.id`(可空) 3. `messages.conversation_id` → `conversations.id` (CASCADE) 4. `agent_notes.conversation_id` → `conversations.id` (CASCADE) 5. `agent_notes.agent_id` → `agents.id`(无 CASCADE) 6. `user_roles.role_id` → `roles.id` (CASCADE) 7. `role_mapping_rules.role_id` → `roles.id` (CASCADE) 8. `config_change_logs.changed_by` → `agents.id`(无 FK) 9. `quick_reply_templates.submitted_by` → `agents.id`(可空) --- ## 📌 2. 字段-模块映射 | 业务模块 | 主要表 | 关键字段 | |---|---|---| | 鉴权登录 | `agents`, `employees`, `roles`, `user_roles` | `user_id`, `password_hash`, `otp_secret`, `role` | | 会话管理 | `conversations` | `status`, `urgency_score`, `assigned_agent_id`, `is_vip` | | 消息 | `messages` | `sender_type`, `content`, `msg_type`, `reply_to_id` | | AI 助手 | `conversations`, `system_configs` | `dify_conversation_id`, `ai_substantive_reply_count` | | 排障 | `troubleshooting_templates` | `path_steps`, `flowchart` | | 快速回复 | `quick_reply_templates` | `category`, `content`, `variables` | | 待办 | `todo_items` | `type`, `status`, `priority` | | 工具面板 | `approval_links`, `software_downloads`, `funny_phrases` | `category`, `scene` | | 动态配置 | `system_configs`, `config_change_logs` | `config_key`, `config_value` | | 审计 | `config_change_logs` | `changed_by`, `changed_at`, `old_value`, `new_value` | --- ## 📌 3. 数据规模评估(生产估算) | 表 | 日增(估) | 总量/年 | 备注 | |---|---|---|---| | `conversations` | 100-500 | 50K-100K | 视企业规模 | | `messages` | 1K-10K | 1M-3M | 高频 | | `employees` | 10-30 | 5K-20K | 增长慢 | | `agents` | 0-1 | 20-50 | 增长极慢 | | `agent_notes` | 50-200 | 30K-70K | 每会话 1-2 条 | | `quick_reply_templates` | 1-3 | 50-200 | 缓慢增长 | | `system_configs` | 0-1 | 50-100 | 极慢 | | `config_change_logs` | 5-20 | 5K-10K | 审计 | | `todo_items` | 50-200 | 30K-70K | 流转快 | | `troubleshooting_templates` | 0-1 | 30-50 | 缓慢 | | `funny_phrases` | 0 | 30-50 | 几乎不变 | | `approval_links` | 0-1 | 20-50 | 缓慢 | | `software_downloads` | 0-1 | 30-80 | 缓慢 | | `roles` | 0 | 3-10 | 几乎不变 | | `user_roles` | 5-15 | 5K-20K | 跟员工同步 | | `role_mapping_rules` | 0 | 5-15 | 几乎不变 | **总数据量估算**: 第 1 年 ~5-10 MB(纯数据), 含索引 ~20-50 MB **建议**: PostgreSQL 起步 10 GB 足够,3-5 年无需扩容 --- ## 📌 4. 环境变量清点(15 个 + 4 个文档化待补) ### 4.1 后端核心(`backend/app/config.py`) | # | 变量 | 类型 | 默认 | 必填 | 敏感 | 用途 | |---|---|---|---|---|---|---| | 1 | `WECOM_CORP_ID` | str | ww1234... | ✅ | ❌ | 企微企业 ID | | 2 | `WECOM_AGENT_ID` | str | 1000002 | ✅ | ❌ | 企微应用 ID | | 3 | `WECOM_SECRET` | str | your-agent-secret | ✅ | 🔴 高 | 企微应用 Secret | | 4 | `WECOM_TOKEN` | str | your-callback-token | ✅ | 🟠 中 | 回调 Token | | 5 | `WECOM_ENCODING_AES_KEY` | str | your-aes-key-43-... | ✅ | 🟠 中 | 回调 AES Key | | 6 | `DATABASE_URL` | str | postgresql://wecom:... | ✅ | 🔴 高(密码部分) | DB 连接 | | 7 | `REDIS_URL` | str | redis://localhost:6379/0 | ✅ | 🟠 中(密码) | Redis 连接 | | 8 | `BACKEND_HOST` | str | 0.0.0.0 | ❌ | ❌ | 监听地址 | | 9 | `BACKEND_PORT` | int | 8000 | ❌ | ❌ | 监听端口 | | 10 | `CORS_ORIGINS` | str(逗号分隔) | localhost:5173,5174 | 🟡 生产必填 | ❌ | CORS 白名单 | | 11 | `DIFY_API_URL` | str | "" | 🟡 启用 AI 必填 | ❌ | Dify Chat 端点 | | 12 | `DIFY_API_KEY` | str | "" | 🟡 启用 AI 必填 | 🔴 高 | Dify API Key | | 13 | `DIFY_TIMEOUT` | int | 30 | ❌ | ❌ | Dify 超时 | | 14 | `DIFY_WINGMAN_API_URL` | str | "" | ❌ | ❌ | Wingman 端点 | | 15 | `DIFY_WINGMAN_API_KEY` | str | "" | ❌ | 🔴 高 | Wingman Key | | 16 | `DIFY_WINGMAN_TIMEOUT` | int | 30 | ❌ | ❌ | Wingman 超时 | | 17 | `MOCK_LOGIN_ENABLED` | bool | false | ❌ | ❌ | Mock 登录开关 | **合计 17 个**(`Settings` 字段),**5 个敏感**(3 个 P0-高,2 个 P0-中) ### 4.2 部署相关(`deploy-server/.env` / `deploy-nas/.env.nas`) | # | 变量 | 用途 | |---|---|---| | 18 | `POSTGRES_USER` | DB 用户名 | | 19 | `POSTGRES_PASSWORD` | DB 密码(🔴) | | 20 | `POSTGRES_DB` | DB 名 | | 21 | `REDIS_PASSWORD` | Redis 密码(🔴) | ### 4.3 前端(Vue 4 个端) | 前端 | 变量 | 用途 | |---|---|---| | admin | `VITE_API_BASE_URL` | 后端地址 | | agent | `VITE_API_BASE_URL`, `VITE_WS_URL` | 后端 + WebSocket | | h5 | `VITE_API_BASE_URL`, `VITE_WS_URL` | 同上 | | portal | `VITE_API_BASE_URL`, `VITE_PORTAL_REDIRECT` | 入口跳转 | ### 4.4 漏配/待补 | # | 变量 | 状态 | 影响 | |---|---|---|---| | A | `LOG_LEVEL` | ❌ 缺失 | 日志粒度无法控制 | | B | `JWT_SECRET` / `SESSION_SECRET` | ❌ 缺失 | token 加密用,但还没用 JWT | | C | `WS_TOKEN_SECRET` | ❌ 缺失 | WS token 签名用 | | D | `DIFY_PROXY_URL` | ❌ 文档化但未用 | 公司有内部 Dify,本项目直连 | --- ## 📌 5. 敏感凭据安全审计 ### 5.1 现状 | # | 凭据 | 存储位置 | 风险 | |---|---|---|---| | 1 | WECOM_SECRET | `.env.production`(git?) | 🟠 中(看是否加 .gitignore) | | 2 | POSTGRES_PASSWORD | `.env.production` | 🟠 中 | | 3 | REDIS_PASSWORD | `.env.production` | 🟠 中 | | 4 | DIFY_API_KEY | `.env.production` | 🟠 中 | | 5 | 内部 Gitea tokens | wincred(✅) | 🟢 已修 | ### 5.2 待办(风险跟踪表 M-11) - [ ] `.env.production` 是否在 .gitignore?(需确认) - [ ] `.env.nas` 是否入仓?(文档明确说不入) - [ ] 公司有内部 Vault?目前直连 Dify - [ ] WECOM_TOKEN / AES_KEY 走 vault(下一轮) ### 5.3 短期方案(本周) ```bash # 1. 验证 .gitignore 覆盖 git check-ignore -v .env.production .env.nas backend/.env # 2. 验证仓里无 secret git log --all -p --source -- .env.production 2>/dev/null | head -20 # 3. 跑 gitleaks 扫描 bash scripts/security-audit.sh --secrets ``` ### 5.4 长期方案(下季度) 1. **NAS Vault**:用 Synology 的「密码保险箱」存关键 secret 2. **Server Keyring**:用 systemd-creds / HashiCorp Vault 3. **环境变量注入**:容器启动时从 vault 拉,不入镜像 --- ## 📌 6. 关联文档 - [[技术架构]] §3 数据层 - [[风险跟踪表]] M-11(凭据管理)/ D-3(DB 密码) - [[外部系统集成]] §1-4(火绒/联软/aTrust/eHR 凭据) - [[SOP-001-Gitea部署]] - token 走 wincred - [[Gitea部署指南]] - Gitea app.ini 凭据 --- *本清点是 2026-06-15 Claude 满载跑批产出,待评审*