chore(repo): 补 dist.bak.*/ 忽略规则 + 记忆目录脱管 + 坐席工作台原型 v1.3 入仓

- .gitignore: 新增 dist.bak.*/ 规则,覆盖 7 个 Vite 构建备份目录
  (原规则 dist_bak*/ 只匹配下划线变体,点号变体仍显示为未跟踪噪声)
- .workbuddy/memory/MEMORY.md: git rm --cached 脱管
  (.gitignore:144 已声明「记忆目录个人上下文,不入仓」,该文件为规则生效前遗留;
   磁盘文件保留不受影响)
- docs: 坐席工作台原型 v1.3(右栏重组) 纳入版本控制,接续 v1.0-v1.2 版本链
This commit is contained in:
Simon
2026-08-03 23:55:19 +08:00
parent a8da4da0db
commit 502551ad9c
3 changed files with 2708 additions and 118 deletions
+1
View File
@@ -257,6 +257,7 @@ data/
# 补充忽略 (2026-08-03 仓库重组): 历史 dist 备份 + node_modules_old
# 这些是早期部署流程误将 dist 目录 commit 的残留,每个 50MB+,必须不入仓
dist.bak/
dist.bak.*/
dist.old/
dist_bak*/
dist-clean/
-118
View File
@@ -1,118 +0,0 @@
# IT智能服务台 - 项目记忆
## 测试铁律
- **e2e 测试 poll 不能依赖 `after_id`(UUID 排序 ≠ 时间排序)**messages/poll 的 `after_id` 参数使用数据库主键 UUID 排序,与 `created_at` 时间戳排序不一致。跨会话复用用户时可能 poll 到修复前的旧消息。应使用 `created_at` 时间戳过滤 + 唯一测试用户。
- **宣布修复前必须端到端验证**:容器内测试通过 ≠ 外部用户可用。需经过 Mock 登录 → 发消息 → 查回复 全链路验证。
## 设计决策(锁定)
- AI交互:小段多回合;术语:**员工端"人工坐席"按钮** = 用户呼叫坐席(统一命名);"摇人"=坐席呼叫坐席
- 原型:坐席v5.3 + H5 v1.1UI:企微浅色扁平,accent=#07C160
- 统一入口 `/itportal/` → user/agent/adminadmin需OTP
- H5 布局铁律:人工坐席按钮在"发送键和语音按钮上方";截图说明 PC 显示/移动端隐藏
## 技术架构
- 前端:坐席(Vue3+Element Plus) / H5(Vue3+Vant4) / 管理后台(Vue3+Element+Tailwind)
- 后端:FastAPI + SQLAlchemy + PostgreSQL + Redis(代码在 `app/`
- 字段映射:后端`id`/`sender_type` → 前端`message_id`/`message_type``conversation.ts``mapMessage()`
- WS双连接池:`active_connections`(agent) + `employee_connections`(H5)
- H5 参与者系统已完整存在:`ParticipantStrip`/`ParticipantList`/`InviteParticipantSheet` + `useParticipantDisplay` composable`ROLE_BORDER_COLORS`/`ROLE_LABELS` 常量;`canInvite`=发起人 OR 已在会话中的参与者
## 部署铁律(必读)
- 正式服务器:itsupport.servyou.com.cn (10.90.5.110),出口IP `218.75.34.87`
- 堡垒机:sxn@10.212.189.210:2222 (OTP),脚本 `C:\Users\simon\.workbuddy\skills\jumpserver-ops\scripts\jms_ops.py`
- **服务器项目根路径**`/opt/wecom-it-desk/`
- 后端卷挂载:`./app:/app/app``.py`变更→`docker compose restart backend`env变更→`docker compose up -d backend`restart不重载env
- **docker-compose.yml environment 铁律**2026-07-13 第二次踩坑导致 P0 `[object Object]`):`.env`变量不会自动传入容器,必须在`docker-compose.yml``environment:`显式声明每个变量(如 `DIFY_NATIVE_BASE_URL=${DIFY_NATIVE_BASE_URL:-}`)。后端重启时若发现 env 为空但 `.env` 文件有值,首先检查 docker-compose.yml 是否声明。
- **前端部署铁律**:所有前端 dist 均为 ro bind mount,**只能在宿主机源路径操作**,不可在容器内修改
- H5`/opt/wecom-it-desk/frontend-h5/dist``/usr/share/nginx/html/h5` (ro) [新增于 2026-07-13,此前仅 mount 到 `itdesk` 用于重定向]
- H5重定向:`/opt/wecom-it-desk/frontend-h5/dist``/usr/share/nginx/html/itdesk` (ro) [用于 `/itdesk/``/h5/` 301重定向]
- Agent`/opt/wecom-it-desk/frontend-agent/dist``/usr/share/nginx/html/itagent` (ro)
- Admin`/opt/wecom-it-desk/frontend-admin/dist``/usr/share/nginx/html/itadmin` (ro)
- Portal`/opt/wecom-it-desk/frontend-portal/dist``/usr/share/nginx/html/itportal` (ro)
- Terminal`/opt/wecom-it-desk/frontend-terminal/dist``/usr/share/nginx/html/itterminal` (ro)
- nginx.conf`/opt/wecom-it-desk/nginx/nginx.conf``/etc/nginx/nginx.conf` (ro)
- 部署命令模板:`H5_DIR=/opt/wecom-it-desk/frontend-h5/dist && cp -r $H5_DIR ${H5_DIR}_bak && rm -rf $H5_DIR/* && tar -xzf /tmp/h5-dist-vX.tar.gz -C $H5_DIR/ && docker compose up -d nginx`(**注意:容器重建,不是 nginx -s reload**
- 服务器 nginx `/h5/`:静态 `root /usr/share/nginx/html; try_files $uri /h5/index.html;` + `/h5/api/` 反代后端
- Docker bind mount铁律:rm后重建必须重启容器(或 `nginx -s reload` 热重载)
- **dist 分块上传技巧(2026-07-13 验证)**:大文件上传时先排除 video 等不变目录(8.35MB→210KB),写 Python 脚本导入 `jms_ops` 模块复用 `PlinkSession` 做单会话 base64 分块上传(3800 chars/line × 15 lines/batch here-doc),单会话完成上传+部署+验证,避免 session cache 过期后 Chrome/Playwright 启动失败。脚本模板在 `.workbuddy/tmp/upload_and_deploy_h5.py`
## Admin 前端拦截器 Bug2026-07-13 修复)
- **根因**`api/index.ts` 拦截器 `return res.data` 剥离了 axios 外壳,但 10 个文件仍用 `response.data.data` 旧模式
- **影响**:管理后台所有 API 调用 TypeError 被 catch 吞掉 → 集成页面"测试连接"报"请求失败,请检查网络连接"
- **修复**10 文件 25 处 `xxx.data.data``xxx`3 种变量名模式:response/res/leakResp
- **铁律**:新增 API 调用时直接用返回值(`const data = await getXxx()`),不要 `response.data.data`
- **Vite 构建铁律**`dist/assets` 超 50 文件时 safe-delete 会阻止构建,需先重命名旧 dist 为 dist_bak 再构建
- **Vite `@` 别名铁律(2026-07-13**Windows 上 `resolve.alias: { '@': '/src' }` 会解析为 FS 根路径(`D:\src\`),必须用 `fileURLToPath(new URL('./src', import.meta.url))`
- **`.d.ts` vs `.ts` 铁律(2026-07-13)**:含运行时值(如 `const TONE_LABELS = {...}`)的文件不能用 `.d.ts` 扩展名,Vite 构建时会报 `does not provide an export named`。应使用 `.ts`
## 文件上传(关键经验教训)
- ⚠️ **elFinder 上传二进制**:旧版手动上传 tar.gz 后 MD5 不匹配(差字节)。**2026-07-13 验证**`jms_ops.py upload` 命令对 >=100KB 文件走 elFinder Web UI 通道,上传 7.36MB `tar.gz` MD5 完全匹配 ✅,推荐优先使用。如必须用 base64 通道,则参考下一条铁律。
- ⚠️ **JumpServer PTY 4096 字节行缓冲铁律(2026-07-13 严重事故)**base64 单行字符数若超 4096 会被 PTY 静默丢弃,导致 gzip 流损坏、线上瘫痪。脚本 `.workbuddy/tmp/chunked_upload_v2.py` **必须按 base64 字符数分块 `B64_CHUNK=3800`**(非字节数)。subprocess timeout 设 1800s、`--cmd-timeout` 15s。脚本 print 用 ASCII(禁用 emoji,否则 GBK 崩溃)。上传后比对远程 md5sum 与本地。
- pscp/plink -T不可用;elFinder上传后需手动mvhttpx.Timeout须含default
- JumpServer v2.28 变更:登录新增图片验证码(CAPTCHA)connection-token 端点改为 `/api/v1/authentication/connection-token/`
## 外部集成
- 企微通讯录:通讯录同步Secret `BM6iosc3gKnPqkEXmsQN3ErJUpfO-whfMUN646eezB8` 已废弃(2022年8月后新增IP不能调department/list和user/listerrcode=48009,非IP白名单问题)。改用**自建应用Secret + 可见范围设为全公司**,`.env``WECOM_CONTACT_SECRET=` 留空触发降级。验证:651部门/7098员工
- Dify`http://yw-dify.dc.servyou-it.com/v1/chat-messages`
- 审批意图Key `app-7jkRkAzvX4QM9v9SM3P8mMEO`(✅200,结构化JSON
- 分诊Key `app-z3S9AEUUAVPbtR2rioxpiIvp`(✅200,结构化JSON
- `app-UaTWYdBSwN6VktKQlbh5YN5H`(✅200 纯文本,已禁用)
- `app-J3s8sHarZQ2SCaNF3xCppliL`(❌400 "Model credentials not initialized"
- **Dify App ID**Console):`8f0f3d62-f63d-4cf3-815e-b10529c66f1d`(智能IT支持-员工咨询,advanced-chat85节点)
- ⚠️ **Dify 发布铁律**`advanced-chat`应用改工作流后**必须浏览器手动点「发布」**,否则API返回400 `"Workflow not published"`;此版本 publish 端点不支持 Console API 调用。
- **Dify 原生 API 直连**v2.1):后端 `_call_dify_native()` 直连 `/v1/chat-messages`Bearer+blocking),优先于代理;`config.py``dify_native_base_url`/`dify_native_api_key`**docker-compose.yml 须显式声明**`.env` 定义不会自动传入容器)。
- ⚠️ **两次踩坑**:第一次是配置读取时未意识到容器内为空;第二次是 `[object Object]` 根因正是此变量未声明导致原生 API 不可用、回退到 buggy 代理路径。
- ⚠️ **第三次踩坑(2026-07-13 坐席扫码)**`WECOM_SSO_CALLBACK_BASE` 未声明导致 `qrcode_service.py` 抛异常 `ValueError: 请在环境变量中配置 WECOM_SSO_CALLBACK_BASE`
- **必须确保 `environment:` 部分有**(新增变量需同时加入 `.env``docker-compose.yml`):
```yaml
- DIFY_NATIVE_BASE_URL=${DIFY_NATIVE_BASE_URL:-}
- DIFY_NATIVE_API_KEY=${DIFY_NATIVE_API_KEY:-}
- BAIDU_ASR_APP_ID=${BAIDU_ASR_APP_ID:-}
- BAIDU_ASR_API_KEY=${BAIDU_ASR_API_KEY:-}
- BAIDU_ASR_SECRET_KEY=${BAIDU_ASR_SECRET_KEY:-}
- WECOM_SSO_CALLBACK_BASE=${WECOM_SSO_CALLBACK_BASE:-https://itsupport.servyou.com.cn}
```
- **Dify Prompt 自动更新**v2.2):Console API Token(`localStorage.console_token`) → `/console/api/apps/{app_id}/workflows/draft` GET/POST 批量改 LLM 节点 System Prompt;发布仍需手动。
- RAGFlow:生产 `http://10.80.0.85:8080/` / API `:9380`
- 映射策略:联软(主) > aTrust(VPN) > eHR(静态)
## 企微JS-SDK技术
- 双鉴权:`wx.config()`(jsapi_ticket) + `wx.agentConfig()`(agent_config_ticket),签名算法相同但**不能混用**
- `wx.invoke('thirdPartyOpenPage', {oaType:'10001', templateId, thirdNo, extData})` 原生打开审批表单
- 后端端点:`GET /wecom/jsapi-config?url=...&with_agent_config=true`
- 前端composable`frontend-h5/src/composables/useWecomApproval.ts`(懒加载+全降级+超时保护)
- 既有bug`EmergencyDispatcher.vue` 第99-119行 复用jsapi签名给agentConfig(靠3秒超时兜底)
## 已上线功能模块
- 群聊(摇人/邀请/四角色) / 审批(12类型18流程/三级意图) / 代办(getapprovalinfo+Semaphore/缓存45s)
- IT资产推送(模板`Bs7ucT...`) / 语音转文字(手机JS-SDK/PC百度ASR) / 截图拍照 / 复杂场景P0~P3
- 会议室预定(终端`/itterminal/`,企微会议室Secret待申请)
- 上下文感知智能诊断→修复闭环(三层诊断+三段排队+答题插队+五场景关闭);H5 QueueWaiting/RightPanel双Tab/InputBar三态人工按钮/ResolveConfirmCard;坐席pending_close结单
- 单通道统一消息架构:Dify输出JSON `{text,action,options}` → 后端发双WS(ai_reply+dynamic_recommend) → 文字到气泡/卡片到侧边栏;坐席端 ai_thinking 指示器 + ai_structured/byod_card 渲染
## H5 版本部署记录
- v4:人工按钮三态"人工坐席",直接调 `store.shakeAgent()`
- v5RightPanel v2.1 删软件安装/资源权限标签,智能推荐直接展示
- **v7(已部署 2026-07-13**:① InputBar 删除📷截图图标+说明(工具栏仅表情/文件)② ParticipantStrip 加 hover tooltip(姓名+角色+部门,仅PC) + `+邀请`按钮(canInvite) + InviteParticipantSheet 引用 ③ 头像条位于会话框正上方 `v-if="store.participants.length>0"` 保持;④ 修复 CSP 头(添加 `'unsafe-inline'` 和 `https://at.alicdn.com`)⑤ 修复 docker-compose mount(新增 `h5` 目录 bind mount);构建 index-BFvX9hTe.js / index-DLWx4YQ2.css
- **v11(已部署 2026-07-13**:修复"IT资产升级"审批报错"审批模板ID不正确"——原企微审批模板 `Bs7ucTGsPuFhxfk8pn8EydxrWxkVetB4JR8Pb6PHS` 已失效,改用 ITSM 工单系统 `https://devops.dc.servyou-it.com/ITSM/workflow/service/createTicket?name=IT%E8%AE%BE%E5%A4%87%E5%8D%87%E7%BA%A7%E4%B8%8E%E7%A1%AC%E4%BB%B6%E7%BB%B4%E4%BF%AE`
- **2026-07-17)零信任VPN卡片免登录修复**:将ITSM跳转URL从 `devops.dc.servyou-it.com` 改为 `itsm.servyou.com.cn`(企微应用主页域名,从企微工作台打开时免登录)
## 免登录跳转域名选择(2026-07-17)
- **ITSM系统**:使用 `itsm.servyou.com.cn`(企微应用主页域名)可实现免登录
- **旧域名** `devops.dc.servyou-it.com` 需要扫码登录
- **原因**`itsm.servyou.com.cn` 已配置为企微OAuth2.0应用主页,从企微工作台打开时自带身份信息
## 组织架构树(2026-07-13 重构)
- **层级树**`build_org_tree()` 用企微 `department/list` 的 `parentid` 构建真正层级树(非扁平分组)
- 部门节点 ID 加 `dept_` 前缀避免与员工 UserID 冲突;员工出现在其所属的所有部门下
- 三层缓存:`wecom:org_directory`(1800s) + `wecom:dept_list`(1800s) + `wecom:org_tree:{endpoint}`(1800s)
- `get_org_directory()` 用 `asyncio.gather` 并行调用企微 API
- 降级模式:部门列表不可用时 `_build_flat_tree_by_name()` 按名称分组
- H5 前端:多层级扁平化渲染(非 van-collapse),深度缩进 + 展开折叠
- Agent 前端:el-tree 原生支持多层级,无需改代码
- 测试:`backend/tests/test_org_tree.py` 47 个单元测试
## 运维工具
- SOP`docs/10-项目管理/IT智能服务台-标准作业流程SOP.md`
- 故障排查:`docs/09-部署运维/00-标准故障排查手册.md`
- 全栈改造实施计划:`docs/02-产品需求/AI对话链路全栈改造实施计划-v1.0.md`
File diff suppressed because one or more lines are too long