# 数据库 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 满载跑批产出,待评审*
