# 2026-06-03 工作日志

## Docker Compose 部署编排完善
- 重写 `docker-compose.yml`：PostgreSQL 16-alpine + Redis 7-alpine + 后端 + Nginx 四服务
- 增强 nginx.conf：新增 WebSocket 路径代理、HTTPS 模板（含 SSL 安全配置和安全头）
- 创建 `.env.production`：生产环境变量模板（企微凭证、数据库、域名、SSL 路径）
- 创建 `scripts/build.sh`：一键构建两个前端（agent + h5）
- 创建 `scripts/deploy.sh`：一键部署（检查环境 → 构建前端 → 启动服务 → 健康检查）
- 后端容器端口不暴露（仅 Nginx 入口），数据库/Redis 端口默认不暴露（安全策略）
- 所有服务配置日志轮转（10-20MB/文件，3-5个文件）

## US-7 模型层准备（上下游互联）
- 创建 `Employee` 模型（`employees` 表）：corp_id + employee_id 复合唯一键，支持跨企业员工
- `Conversation` 模型新增 `corp_id` 字段 + 索引（默认空字符串，不破坏现有数据）
- 创建 Alembic 迁移 `001_add_employees_table.py`：创建 employees 表 + 为 conversations 添加 corp_id
- 更新 `models/__init__.py` 和 `alembic/env.py` 注册 Employee 模型
- 所有模型导入和应用创建验证通过

## 文档清理
- `现有系统复用评估报告.md` 已在之前的合并操作中删除（内容合并至团队沟通文档第7章）

## 摇人功能可行性评估
- 分析两种场景：
  - 情况1（创建企微群+拉员工入群）：技术上可行（appchat API），但聊天记录转发体验差、跨企业受限、群生命周期管理复杂。**用户暂缓确认。**
  - 情况2（坐席B进入同一会话协作）：纯内部扩展，成本低体验好。**用户确认优先开发。**
- 输出详细技术方案到 `docs/摇人-多坐席协作-技术方案.md`，覆盖模型/API/WS/前端全链路
- 核心设计：Conversation 新增 `collaborating_agent_ids` (JSON)，协作坐席可查看+回复但不能结单/转接，不占负载
- 预留情况1接口待用户确认

## 正式环境独立部署架构方案
- 用户要求以"影响最小、责任清晰、避免系统混搭"为原则，给出正式环境部署建议
- **核心决策：物理隔离 > 逻辑隔离**，修正了原复用评估中的共享建议
- 方案要点：
  - **独立服务器**：不共用 10.80.0.86，申请新 VM（4C8G/100GB 以上）
  - **独立数据库**：独立 PostgreSQL 16 容器（不复用旧 PG13 实例）
  - **独立 Redis**：独立 Redis 7 容器（不复用旧实例，避免 db 号隔离不彻底）
  - **独立 Nginx + 子域名**：`itdesk.dc.servyou-it.com`，变更不影响旧系统
  - **仅共享外部服务**：企微应用凭证（只读）、AI 服务（HTTP 调用）、SSL 证书（只读文件）
- 输出完整方案文档 `docs/正式环境独立部署架构方案.md`：含资源申请清单、网络拓扑、容器拓扑、部署步骤、回滚方案、运维责任矩阵、风险矩阵、退化方案

## 摇人（情况2）多坐席协作 全链路实现
- **13个文件改动**，完整前后端实现：
  - **模型**：`conversation.py` 新增 `collaborating_agent_ids` (JSON, default list)
  - **Schema**：新增 `ConversationInvite`、`ConversationResponse` 扩展 `collaborating_agent_ids/names`、`is_collaborator`
  - **SessionService**：新增 `invite_collaborator()`（6步校验 + WS广播+定向推送）和 `leave_collaboration()`（4步校验 + WS广播）
  - **API**：新增 `POST /conversations/{id}/invite`（错误码3020-3024）、`POST /conversations/{id}/leave`（错误码3025-3026）；列表接口扩展协作字段
  - **前端 API**：`inviteCollaborator()`、`leaveCollaboration()`；Conversation 类型扩展
  - **Store**：新增 `collaboratingConversations` 计算属性、`inviteToConversation()`、`leaveConvCollaboration()`、`handleCollaboratorInvited()`、`handleCollaboratorChanged()` WS处理器
  - **WebSocket**：处理 `collaborator_invited`（弹窗通知被邀请坐席）、`collaborator_joined`、`collaborator_left` 事件
  - **新组件 InviteDialog.vue**：搜索在线坐席→选中→确认邀请，排除主责/协作坐席/自己
  - **ConversationList**：新增「协作会话」分区（排在「我的会话」之后），支持退出按钮
  - **ConversationItem**：新增 `showLeave` prop + 退出按钮样式
  - **ChatArea**：新增「🤝 摇人」按钮（仅 serving 且 is_mine/is_collaborator 时显示）、协作信息行（主责+协作坐席展示）、InviteDialog 集成
- **权限矩阵已落地**：主责坐席可做一切；协作坐席可查看+回复+再摇人，不能结单/转接/标记；不占协作坐席负载

## 应急预案（应急模式）— 方案B：纯应急 + 手动启停
- 决策：先选方案B（员工服务常态隐藏，需要时手动开启），后续条件成熟再升级方案C（自动降级）
- **后端**：
  - `main.py` 默认配置新增 `emergency_mode`（默认 false）
  - 新建 `app/api/system.py`：GET/PUT `/api/system/emergency-mode` 查询/切换应急模式
  - 在 `router.py` 注册系统管理路由
- **前端坐席端**：
  - 新建 `api/system.ts`：封装 `getEmergencyMode()` / `toggleEmergencyMode()`
  - `Workspace.vue` 顶部栏新增「启用应急模式」按钮（常态隐藏）；开启后显示红色应急横幅 + 「关闭应急模式」按钮
  - 开启/关闭均需二次确认，防止误操作
- Phase 2（待条件成熟）：服务挂掉时 H5 页面自动显示引导提示走员工服务

## H5员工端「摇人」→「双手敲桌子」改造
- 用户反馈：前端未发现「举手」和「摇人」功能变更 → 经核查，坐席端「摇人」已完整实现，H5员工端「举手」缺少专用按钮
- 用户决策：将 H5 员工端现有「摇人」按钮改为「双手敲桌子」
- **ShakeButton.vue 完全重写**：
  - 图标从 🔔 改为 👊👊 双拳
  - CSS 动画：交替敲击（左右拳各3轮，0.8s）+ 按钮水平震动（模拟桌子晃动）+ 静止时呼吸浮动
  - 按钮底色从 #FF6B35→#FF8F5E 改为 #FF5722→#FF7043（更深的紧急感）
  - 防抖逻辑保持不变
- **InputBar.vue**：引导条文案「急需 IT 支持？👊👊 敲桌子呼叫坐席」
- **ChatPanel.vue**：空状态提示「输入问题咨询，或 👊👊 敲桌子呼叫坐席」
- **后端 h5.py**：注释/日志从「摇人」改为「举手/敲桌子」
- **前端注释批量更新**：H5 api/conversation.ts、stores/conversation.ts、frontend-agent conversation.ts

## H5员工端「呼叫坐席」完整改造（三步流程 + 七种动画）

### 核心设计变更
- 用户决策：呼叫坐席必须有前置条件——用户先描述问题，AI 复述确认后再呼叫，避免无效转人工
- 动画触发方式：随机选择（方案C），每次点击随机出现7种动画之一，增加趣味性
- 话术与场景一一对应，不同紧急程度有不同表达

### 三步流程（CallAgentModal.vue）
1. **描述问题**：TextArea 输入（上限500字），引导员工说清楚问题
2. **AI 复述确认**：调后端 API 让 AI 用自己的话复述，用户确认无误后进入下一步
3. **播放动画 + 发请求**：随机选场景播放动画，同时发 shake 请求

### 七种呼叫场景（权重随机）

| # | 场景 | 话术 | 权重 | 核心动画 |
|---|------|------|------|----------|
| 1 | 🙋 举手 | "看这里！…我有个问题！" | 3.0 | 右手臂上下挥动 + 气泡 |
| 2 | 🪑 拍桌子 | "快快快！我等不及了！" | 3.0 | 双拳交替敲击 + 桌面震动 |
| 3 | 💀 劈稻草人 | "不解决我要爆炸了💥" | 1.5 | 挥刀 + 稻草人抖动 + 爆炸光效 |
| 4 | 🍉 砍西瓜 | "IT救我！卡住了🍉" | 1.5 | 刀砍 + 汁水飞溅 |
| 5 | 🔔 摇铃铛 | "叮叮叮！有人吗！" | 1.0 | 双铃铛摆动 + 声波扩散 |
| 6 | 💣 大炮发射 | "开炮！必须解决了！" | 1.5 | 引信燃烧 + 炮弹飞行 + 爆炸+靶子抖动 |
| 7 | 🚀 导弹发射 | "发射！呼叫IT特种部队！" | 1.5 | 导弹上升 + 尾焰闪烁 + 烟雾扩散 + 按钮闪烁 |

### 技术实现
- **CallAgentModal.vue**：全新组件，Teleport 到 body，三步骤状态机
- **ShakeButton.vue** 重构：从直接发请求 → 只触发弹窗（emit 'trigger'）
- **ChatPanel.vue**：承载弹窗，监听 call-agent 事件
- **InputBar.vue**：向上传递 trigger 事件
- 所有 SVG 场景内联绘制，CSS @keyframes 驱动动画，无外部图片依赖
- 构建验证通过（CSS 从 26.52 kB → 30.16 kB，ChatView JS 从 48.38 kB → 53.49 kB）

## 呼叫坐席流程重设计：按钮条件显隐 + 弹窗简化（2026-06-03 下午）

### 需求
1. 初始隐藏「呼叫坐席」按钮，AI 实质性回复 >= 3 次后才出现
2. 打招呼（你好/hi等）和直接呼叫人工（人工/转人工等）不计数，AI 回复引导话术
3. CallAgentModal 简化为单步动画，去掉"描述问题"和"AI复述确认"步骤

### 后端改动
- **conversation.py**：新增 `ai_substantive_reply_count` 字段（Integer, default=0）
- **h5.py**：
  - `_get_current_employee()`：新增 `X-Employee-Id` 头 fallback（开发降级）
  - 新增 `_is_greeting()` / `_is_call_human()` 检测函数（关键字匹配）
  - `h5_send_message()` 完全重写：检测消息类型→生成AI回复→计数→返回 `{user_message, ai_reply, is_guidance, ai_reply_count, can_call_agent}`
  - `GET /h5/conversations/current`：返回 `can_call_agent` 和 `ai_substantive_reply_count`
  - `POST /h5/conversations/current/shake`：新增前置校验 `ai_substantive_reply_count >= 3`（含无会话场景兜底），不满足返回错误码 1003
- **.env**：`DATABASE_URL` 改为绝对路径 `sqlite+aiosqlite:///C:/Users/simon/wecom_it_smart_desk/backend/it_smart_desk.db`

### 前端改动
- **api/conversation.ts**：新增 `SendMessageResponse` 类型，`sendMessage()` 返回双消息结构
- **stores/conversation.ts**：新增 `canCallAgent` ref；`sendNewMessage()` 处理双消息响应；`fetchCurrentConversation()` 同步 canCallAgent
- **InputBar.vue**：`ShakeButton` `v-if="store.canCallAgent"` 条件渲染；底部文案动态切换（默认提示→橙色脉冲「呼叫坐席通道已开启」）
- **CallAgentModal.vue**：完全重写为单步动画模式，`watch(visible)` 自动触发 shake，4秒后自动关闭

### 修复的 Bug
1. `_get_current_employee` 只支持 Bearer Token → 添加 `X-Employee-Id` 开发降级 fallback
2. `.env` 相对路径 `./it_smart_desk.db` → 改为绝对路径
3. shake 端点无会话时直接创建新会话绕过阈值 → 统一拒绝 code=1003
4. 编辑 cut-paste 残留垃圾代码 → 清理修复

### 验证结果
- 后端 API 全链路测试通过（打招呼引导 + 计数递增 + can_call_agent 阈值 + shake 拒绝/接受）
- 前端 `npm run build` 通过（ChatView JS: 53.49 kB → 48.82 kB）
- 本地环境：后端 `:8000` + 前端 `:5173` 运行中
- 测试指南：`TESTING_CALL_AGENT.md`

## 部署就绪性完善（2026-06-03 晚）

### 修复的问题
1. **nginx.conf 端口 80 重复监听**：两个 server 块都 `listen 80; server_name _;` → 重写为单一 HTTP server 块 + 注释模板 HTTPS server 块
2. **frontend-agent ConversationList.vue 重复 import**：`import type { Conversation }` 出现两次 → 删除重复行
3. **alembic.ini 日志格式错误**：`[%(name)]` 缺 `s` → 修正为 `[%(name)s]`；中文注释导致 Windows GBK 解码失败 → 改为英文注释
4. **alembic 迁移目录缺失**：env.py 不存在，`docker compose up` 会因 `alembic upgrade head` 失败 → 创建完整 alembic 环境

### 新建文件
- **alembic/env.py**：从环境变量读取 DATABASE_URL，自动转换异步驱动→同步驱动（aiosqlite→sqlite, asyncpg→psycopg2）
- **alembic/script.py.mako**：标准迁移脚本模板
- **alembic/versions/6d5520491644_initial_all_tables.py**：初始迁移（9张表 + 所有索引）
- **scripts/deploy.sh**：一键部署脚本（--build/--up/--down/--status 四种模式）
- **docs/DEPLOY_NAS.md**：群晖 NAS 部署指南（SSH + Container Manager 两种方式）

### 构建验证
- frontend-h5: `vite build` 通过（10 个文件）
- frontend-agent: `vite build` 通过（8 个文件，1.2MB JS 含 Element Plus）
- alembic migration: `upgrade head` 执行成功，9 张表全部创建

### 部署架构决策
- 基于日均 37 次会话的负载分析，现有 4 容器方案（PG + Redis + Backend + Nginx）完全够用
- 暂无需拆分为更复杂的微服务架构

## 共享域名部署适配（2026-06-03 晚）

### 需求
- 与 IT 数据查询平台共享域名 `http://it-dataquery.dc.servyou-it.com/`
- 路径路由：`/itdesk/`(H5员工端) + `/itagent/`(坐席端) + `/api/`(后端) + `/`(数据平台)

### 前端改动
- **frontend-h5/vite.config.ts**：添加 `base: '/itdesk/'`
- **frontend-h5/src/router/index.ts**：`createWebHistory('/h5/')` → `createWebHistory('/itdesk/')`
- **frontend-h5/src/stores/employee.ts**：OAuth2 回调 URI `/h5/` → `/itdesk/`
- **frontend-agent/vite.config.ts**：添加 `base: '/itagent/'`
- **frontend-agent/src/router/index.ts**：`createWebHistory()` → `createWebHistory('/itagent/')`
- **frontend-agent/index.html**：favicon 路径 `/vite.svg` → `/itagent/vite.svg`
- 两个前端 dist 重新构建验证通过

### Nginx 改动
- **nginx.conf** 完全重写：
  - `location /itdesk/` → H5 SPA（alias + try_files fallback）
  - `location /itagent/` → Agent SPA（alias + try_files fallback）
  - `location /api/` → backend:8000 反代
  - `location /ws/` → WebSocket 反代
  - `location /` → dataquery:80 反代（兜底到数据平台）

### Docker Compose 改动
- **docker-compose.yml** 重写：
  - nginx 挂载 `frontend-h5/dist → /usr/share/nginx/html/itdesk`
  - nginx 挂载 `frontend-agent/dist → /usr/share/nginx/html/itagent`
  - 添加 `it-desk-internal` 内部网络（PG + Redis + Backend + Nginx）
  - 添加 `it-platform-net` 外部网络（与数据平台互联）
  - nginx 暴露 `18080:80`（临时端口，供数据平台反代或直接测试）

### 部署文件
- **scripts/deploy.sh**：更新输出信息 + 添加 `--pack` 打包模式
- **docs/DEPLOY_NAS.md**：重写为远程服务器部署指南（含两种网络接入方式）
- **.env.production**：域名改为 `it-dataquery.dc.servyou-it.com`

## T02 后端核心服务 — AI 回复集成（Dify 接入）

### 修改文件清单（11 个文件）

**配置层：**
- `backend/app/config.py` — 新增 3 个 Dify 配置项：`dify_api_url`、`dify_api_key`、`dify_timeout`
- `backend/.env` — 新增 DIFY_API_URL/KEY/TIMEOUT 环境变量
- `backend/.env.example` — 新建环境变量模板
- `.env.production` — 新增 DIFY 配置段
- `docker-compose.yml` — backend 容器新增 DIFY_* 环境变量传递

**模型层：**
- `backend/app/models/conversation.py` — 新增 `dify_conversation_id` 字段（String 128，nullable），用于 Dify 多轮对话上下文

**服务层（核心）：**
- `backend/app/services/message_router.py` — 完整重写，接入 Dify AI：
  - `__init__` 新增 `ai_service` 参数（可选，None 时跳过 AI）
  - `route_message` 流程重排：举手优先判断（跳过AI）→ AI 回复（仅 ai_handling 状态）→ 标记检测 → 评分
  - 新增 `_try_ai_reply` 方法：调 Dify → 命中则通过企微发回复 + 创建 AI 消息记录 + ai_substantive_reply_count++，未命中则转 queued + 发引导文案
  - `_find_or_create_conversation`：新会话默认 `ai_handling`（非 queued），活跃会话查找包含 ai_handling
- `backend/app/services/ai_service.py` — 修复配置读取：`getattr(settings, ...)` → `settings.dify_api_url`（直接用 pydantic 属性）
- `backend/app/services/session_service.py` — 会话排序新增 `ai_handling` (权重 25)，介于 queued(30) 和 serving(20) 之间

**API 层：**
- `backend/app/api/wecom_callback.py` — 注入 AIService 到 MessageRouter，回调结束时关闭 ai_service
- `backend/app/api/h5.py` — H5 消息发送重写：
  - 会话查找包含 ai_handling 状态（4处）
  - `h5_send_message`：实质问题调用 Dify API 替代硬编码模板；打招呼/呼叫人工保持引导话术；Dify 异常降级到模板回复
  - 响应新增 `conversation_status` 字段
- `backend/app/api/conversations.py` — status 过滤描述新增 ai_handling

### 前端适配评估
- 坐席工作台：已完整支持 ai_handling 状态——ConversationList 有「AI处理区」分区，ChatArea 有状态标签和颜色，Store 有状态排序权重
- H5 员工端：无需额外改动——通过 can_call_agent 和 ai_reply_count 驱动 UI，状态变化对 H5 透明

### AI 回复流程（全链路）
```
员工发消息（企微/H5）
  → 新会话 → ai_handling
  → 举手？ → 跳过AI，直接 queued
  → 调 Dify API
    → 命中 → 企微发回复 + 消息入库 + ai_count++ + 保持 ai_handling
    → 未命中 → 发引导文案 + 转 queued
    → 异常 → 降级处理 + 转 queued
  → ai_count >= 3 → H5 显示「呼叫坐席」按钮
```

## 产物文档合并与部署架构修正（2026-06-03 晚）

### 产物文档合并
- 新建 **README.md**：按阅读对象组织（新人/开发/运维/测试），含项目背景、实现进度、快速启动、API概览、已知问题
- 文档体系分层：README（入口）→ ARCHITECTURE.md（架构细节）→ docs/（专题文档）

### 部署架构偏差修正
用户指出预生产实际部署与文档描述存在偏差，已调整：

**关键偏差**：文档假设智能咨询系统与数据平台在同一 Docker 主机（通过 `it-platform-net` 共享网络互联），但预生产实际是**不同主机、仅共用域名**。正式环境会迁移到 K8s。

**修正内容**（7个文件）：
- **README.md**：部署章节明确「预生产独立主机，正式环境 K8s」，部署前必须先改 DATAQUERY_HOST
- **docker-compose.yml**：移除 `it-platform-net` 外部网络（Docker 网络无法跨主机），backend 和 nginx 仅连 `it-desk-internal`
- **nginx/nginx.conf**：header 注释重写为「预生产·独立主机版」，`upstream dataquery` 改为 `DATAQUERY_HOST` 占位符（需替换为数据平台实际 IP），注释说明远程反代替代 Docker 网络
- **docs/01-项目总览与部署手册.md**：2.1 节新增「预生产 vs 正式环境」对比表，架构图标注跨主机代理；6.2 节「创建共享网络」→「配置数据平台反代地址」；常见问题更新
- **docs/DEPLOY_NAS.md**：网络互联部分重写，移除 it-platform-net 步骤
- **docs/团队沟通文档-架构消息知识库.md**：部署架构描述补充「预生产独立主机，正式 K8s」
- **ARCHITECTURE.md**：部署模式说明更新

### Simon→宋献 署名统一（6个文件）
- README.md、docs/01-项目总览与部署手册.md、docs/正式环境独立部署架构方案.md、docs/团队沟通文档-架构消息知识库.md、PRD.md 中所有署名「Simon」→「宋献」
- node_modules/ 中第三方库的 Simon 引用不修改（与项目署名无关）

### 新建 ai_service.py
- `backend/app/services/ai_service.py`：封装 Dify API 调用（非流式+流式），含知识库命中检测、错误降级回复

## 企微原生群聊方案可行性分析

### 背景
用户提出：能否用企微原生应用创建群聊/推送消息替代现有 H5 嵌入式员工端

### 初步结论（已修正）
- **完全可行**，企微提供两套原生 API：
  - 群聊会话 API（`/cgi-bin/appchat/*`）：创建/修改/获取群聊 + 群内推送消息
  - 应用消息 API（`/cgi-bin/message/send`）：1对1 推送（项目已在用），消息出现在与该应用的1对1聊天窗口中
- 关键 API 限制：appchat ≤1000群/天、appchat/send ≤2万人次/分、message/send ≤账号上限×200人次/天
- 群聊 API 要求：仅自建应用、可见范围必须根部门、只能操作本应用创建的群
- 之前"摇人功能评估"（情况1：创建企微群）已分析过 appchat 方案，用户当时暂缓确认

### 用户修正（关键纠错）
1. **员工可以看到自己发的消息** — 企微1对1应用聊天窗口中，员工自己发的和应用回复的都在同一窗口（我之前错误判断为看不到）
2. **方案B交互路径修正** — 不是"每次咨询都创建群聊"，而是：
   - 主流程：员工↔自建应用1对1交互，AI+坐席都走 `/message/send` → 同一窗口
   - 群聊(appchat)仅在坐席需要外援时创建 → 新窗口，非常态
3. **方案A的跨平台移植便利性** — H5可嵌入企微/钉钉/飞书/浏览器，一次开发多处部署
4. **方案A可跨主体企微支持** — 非静默登录时切换其他认证方式（手机号+验证码/SSO），原生方案无法跨主体

### 修正后结论
- 方案B可行性**大幅提升**：主流程无需群聊，不需要会话存档权限，员工体验最佳
- 方案A的独特价值**被低估**：跨平台移植和跨主体支持是原生方案无法替代的
- 方案C是方案B的子集，不存在独立选型意义
- **推荐 A+B 渐进式**：先上方案B做MVP（改动极小，已在用回调+message/send），H5保留为扩展层

## 方案B文档纳入（4个文件更新）

### PRD.md §3 方案可行性判断
- 新增"方式五：企微原生1对1 + 外援群聊"到方案对比表
- 新增 §3.2 方式五详解：架构原理、交互路径、API清单、与方式四对比、关键结论
- 更新 §3.3 最终方案：从"方式四"改为"方式四+五混合演进"，含选型决策逻辑

### 01-项目总览与部署手册.md §7 运维管理
- 新增 §7.5 应急预案可选技术项
  - §7.5.1 备用方案概述：5种应急场景→备用方案动作映射
  - §7.5.2 备用方案技术架构：交互流程图 + 已有能力 + 仅需新增
  - §7.5.3 切换流程：H5→原生（5步，前3步零代码）/ 原生→H5（3步）
  - §7.5.4 企微API限制与容量评估：4项API限额 vs 当前业务量
  - §7.5.5 备用方案局限性与适用边界：5项局限 + 决策建议

### ARCHITECTURE.md §1.2 核心技术挑战
- 表格新增第9项"员工端架构选型"：主方案H5，备选原生1对1
- 新增 §1.2.1 员工端架构双方案设计：方案A/B对比、API清单、决策建议

### 团队沟通文档-架构消息知识库.md §3.6
- 新增员工端架构双方案对比表、方案B交互路径、选型决策、运维应急引用

## 共享基础设施代码修复（2026-06-03 下午-2）

### 背景
用户决定暂不选择A/B方案，先做两方案共享的基础设施工作。审计4个领域（回调服务器/后端服务/坐席前端/AI集成），发现11个需修复问题。

### 已完成的修复（主理人直接执行）
1. **Task 9: 启动时校验关键配置非占位符** — `main.py` 新增 `_validate_config()`，启动时检查 wecom_corp_id/wecom_secret/wecom_token/wecom_encoding_aes_key 是否仍为占位符值，醒目警告
2. **Task 7: 坐席登录安全加固** — `agents.py` 的 `agent_login` 新增企微通讯录验证：调用 WecomService.get_user_info() 校验 user_id 是否存在，验证通过后用企微返回的真实姓名覆盖前端输入（防冒用）；企微API不可达时降级放行+警告日志；Login.vue 提示文案更新
3. **Message 模型扩展**（Task 4前置） — `message.py` 新增5个字段：media_id(企微媒体ID)、media_url(本地存储URL)、file_name、file_size、extra_data(JSON扩展元数据)；新建 Alembic 迁移 `002_add_media_fields.py`

### 企微消息XML结构调研
- 回调支持6种消息类型：text/image/voice/video/location/link
- 文件消息(file)不在回调文档中（企微可能不支持接收file类型回调）
- MediaId 仅3天有效，需收到后立即下载保存
- 所有消息都有MsgId字段（可用于去重）

### 工程师（寇豆码）进行中的任务
- Task 1: 修复H5端AI降级回复误计数
- Task 2: 统一AI调用逻辑为共享服务
- Task 3: 修复资源泄漏（callback/h5改用DI）

### 待处理任务
- Task 4: 补全回调非文本消息处理
- Task 5: 添加消息去重（MsgId检查）
- Task 6: 修复ScoringService硬编码关键词+需介入检测逻辑
- Task 8: 补全会话状态机校验+消除绕过
- Task 10: 补全回调事件处理业务逻辑
- Task 11: QA验证
