24 KiB
24 KiB
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:新增
showLeaveprop + 退出按钮样式 - 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)
- 描述问题:TextArea 输入(上限500字),引导员工说清楚问题
- AI 复述确认:调后端 API 让 AI 用自己的话复述,用户确认无误后进入下一步
- 播放动画 + 发请求:随机选场景播放动画,同时发 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 下午)
需求
- 初始隐藏「呼叫坐席」按钮,AI 实质性回复 >= 3 次后才出现
- 打招呼(你好/hi等)和直接呼叫人工(人工/转人工等)不计数,AI 回复引导话术
- 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_countPOST /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:新增
canCallAgentref;sendNewMessage()处理双消息响应;fetchCurrentConversation()同步 canCallAgent - InputBar.vue:
ShakeButtonv-if="store.canCallAgent"条件渲染;底部文案动态切换(默认提示→橙色脉冲「呼叫坐席通道已开启」) - CallAgentModal.vue:完全重写为单步动画模式,
watch(visible)自动触发 shake,4秒后自动关闭
修复的 Bug
_get_current_employee只支持 Bearer Token → 添加X-Employee-Id开发降级 fallback.env相对路径./it_smart_desk.db→ 改为绝对路径- shake 端点无会话时直接创建新会话绕过阈值 → 统一拒绝 code=1003
- 编辑 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 晚)
修复的问题
- nginx.conf 端口 80 重复监听:两个 server 块都
listen 80; server_name _;→ 重写为单一 HTTP server 块 + 注释模板 HTTPS server 块 - frontend-agent ConversationList.vue 重复 import:
import type { Conversation }出现两次 → 删除重复行 - alembic.ini 日志格式错误:
[%(name)]缺s→ 修正为[%(name)s];中文注释导致 Windows GBK 解码失败 → 改为英文注释 - 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(临时端口,供数据平台反代或直接测试)
- nginx 挂载
部署文件
- 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_timeoutbackend/.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_servicebackend/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(
- 关键 API 限制:appchat ≤1000群/天、appchat/send ≤2万人次/分、message/send ≤账号上限×200人次/天
- 群聊 API 要求:仅自建应用、可见范围必须根部门、只能操作本应用创建的群
- 之前"摇人功能评估"(情况1:创建企微群)已分析过 appchat 方案,用户当时暂缓确认
用户修正(关键纠错)
- 员工可以看到自己发的消息 — 企微1对1应用聊天窗口中,员工自己发的和应用回复的都在同一窗口(我之前错误判断为看不到)
- 方案B交互路径修正 — 不是"每次咨询都创建群聊",而是:
- 主流程:员工↔自建应用1对1交互,AI+坐席都走
/message/send→ 同一窗口 - 群聊(appchat)仅在坐席需要外援时创建 → 新窗口,非常态
- 主流程:员工↔自建应用1对1交互,AI+坐席都走
- 方案A的跨平台移植便利性 — H5可嵌入企微/钉钉/飞书/浏览器,一次开发多处部署
- 方案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个需修复问题。
已完成的修复(主理人直接执行)
- Task 9: 启动时校验关键配置非占位符 —
main.py新增_validate_config(),启动时检查 wecom_corp_id/wecom_secret/wecom_token/wecom_encoding_aes_key 是否仍为占位符值,醒目警告 - Task 7: 坐席登录安全加固 —
agents.py的agent_login新增企微通讯录验证:调用 WecomService.get_user_info() 校验 user_id 是否存在,验证通过后用企微返回的真实姓名覆盖前端输入(防冒用);企微API不可达时降级放行+警告日志;Login.vue 提示文案更新 - 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验证