# 项目经验与教训 · 可复用规则手册 > **用途**:作为每个项目 / 任务启动时的预设提示(prompt)导入。 > **结构**:第一部分 = 跨项目通用铁律(任何开发任务适用,建议原样复用);第二部分 = 本项目(IT智能服务台)专属经验(仅本项目相关,其他项目可整段删除)。 > **修订依据**:本版已通读 `docs/09-部署运维`(含补充部署指南/OTP/NAS/本地AI/一键包/toolbox)、`docs/10-项目管理`、`docs/06-测试质量`(02-E2E/03-功能用例/04-版本测试报告)、`docs/08-安全审计`、`docs/03-技术架构`、`docs/02-产品需求` 并补全缺口(11-历史归档为已取代旧版,跳过)。 > **复用建议**:导入其他项目时,保留「第一部分」,删除「第二部分」,即可大幅降低 token 消耗。 ## 给 AI 的执行约定 - 第一部分为**硬规则**,凡涉及「修复 / 部署 / 声明完成 / 删除代码 / 改配置 / 上线」动作,先对照对应条目。 - 宣布"已修复 / 已完成"前,必须能给出**真实可验证证据**(见 A 类),不得仅凭日志无报错或"我认为"。 - 本项目相关规则以 `docs/09-部署运维`、`docs/10-项目管理` 为准;修订本手册须通读这两类文档。 - 第二部分仅在当前项目生效,提供上下文,不改变第一部分的约束。 --- ## 第一部分 · 跨项目通用开发铁律 ### A. 验证与修复(最高优先级) 1. 宣布"已修复 / 已完成"前必须**端到端验证**并给真实证据;不得仅因日志无报错或主观判断。→ 教训:`[object Object]` 事故。 2. **容器内测试通过 ≠ 外部用户可用**;必须走完整链路(Mock 登录 → 发消息 → 查回复)。 3. 验证按分层选工具:前端 / 登录 / 交互用真实浏览器截图 + console / network;API 用 curl 真实返回;禁只信 `docker logs`。 4. 关键字段 / 配置修改后立即 **Grep / Read 验证是否真生效**——Edit 大批量可能静默失败。→ 教训:v3.0 的 18 字段未真正写入。 5. 删除死代码 / 孤儿组件 / 死路由前,必须**前后端双向核对**消息类型与调用关系,避免"前端等 A 后端发 B"断链。→ 教训:结单确认断链。 6. **"验真"机制**:标注完成的功能必须真实验证,防风注释掉的路由、`[待AI生成]` 占位、空实现伪装完成。 7. AI 生成内容须**断言非占位符 + 低置信度门控**:低于阈值标记失败、不写脏数据。 ### B. 测试 8. e2e 分页 / poll **不能依赖主键顺序**(UUID ≠ 时间);用 `created_at` 过滤 + 唯一测试用户。→ 教训:复用用户 poll 到旧消息误判修复。 9. 容器化 dev 栈跑 E2E:Vite 代理 target 用 compose 服务名(`backend:8000`)非 `localhost`(容器内 localhost 是容器自身 → ECONNREFUSED)。 10. 测试脚本(单元 / 集成)是最有效验证手段;改完先跑测试再部署。 11. pytest fixture 用 generator 须 **`yield` 非 `return`**,否则 setup ERROR。 12. mock patch 路径须是**被测模块实际 import 的模块**,否则 patch 无效。 13. 外部依赖(Neo4j / PG)fixture 自动**降级内存 mock**,无环境也可跑测试。 14. 核心逻辑变更须**同步更新测试辅助函数**(如 `_login_and_get_token` 适配新响应)。 15. 验收用浏览器**无痕模式**避缓存;人眼看到才算,不只信 curl。 16. 第三方库升级(starlette / pytest-asyncio)可能破坏 monkeypatch 签名,conftest 加 `encoding=None` 兼容。 17. 宣传卖点须**真实数据验证**(如"7 步达解"抽 100 问题验轮数),否则推广反噬信任。 ### C. 部署与配置 18. `.env` 变量**不会自动传入容器**;compose `environment:` 须显式声明(`VAR=${VAR:-}`);新增变量同时加 `.env` 和 compose。 19. 前端 dist 只读 bind mount 只能在**宿主机改**,容器内改无效;rm 后重建须重启容器。 20. RO bind mount 目录 **`docker cp` 会假成功**,必须走宿主机 `cp`。 21. `sed -i` 改 nginx 会换新 inode,bind mount 不跟踪 → 须 `docker restart` 非 reload;或 sed + 重定向保 inode。 22. compose `override.yml` 自动合并并**覆盖主文件同名项**;部署前 `docker compose config | grep` 校验关键配置。 23. `POSTGRES_PASSWORD` 首次写入数据卷后**不可改**,改须删卷重建。 24. 改 FastAPI 路由须**同步部署 `router.py`**(`include_router`);nginx 正则 `location` 不能 `proxy_pass` 带 URI,须 `rewrite` 剥离 `/api/` 前缀。 25. SPA 用 `try_files $uri $uri/ /index.html` 防刷新 404;vite `base` 须与 nginx 路径一致;**OAuth 回调禁用 301 重定向**。 26. WS 反代须 `proxy_http_version 1.1` + Upgrade / Connection 头;浏览器传 token 用 `Sec-WebSocket-Protocol` 时后端 `accept()` **必须回显子协议**。 27. `/health` 必须**真实验证 DB + Redis**;`/health`(liveness) 与 `/ready`(readiness) 分离。 28. 新依赖须**同步 `requirements.txt` 并重建镜像**;Dockerfile 设 `PYTHONDONTWRITEBYTECODE=1` 防 `__pycache__` 污染挂载目录。 29. 大文件(>100KB)用稳定通道上传 + **MD5 校验**;JumpServer / PTY base64 **单行 ≤ 3800 字符**(超 4096 被静默丢弃),传输分块可 ~8000 字符 / 块。 30. 部署后核对"跑的是这版":暴露 **`/version` 端点返回 git hash / build time**。 31. pydantic-settings 带前缀变量(如 `WECOM_X`)须显式 **`validation_alias`**,否则匹配不上、配置静默为空。 32. WAF / 代理后 nginx 须**还原真实 IP**:`set_real_ip_from` + `real_ip_header X-Forwarded-For` + `real_ip_recursive on`,否则 IP 白名单误拦 403。 33. 改生产配置标准流程:脚本(python heredoc 避 sed 转义 / 换 inode)改 → 备份 → 人眼确认 → `nginx -t` → reload → 浏览器验证,每步可回滚。 34. compose 多项目须 **`-p `**,否则容器名冲突。 35. 临时放宽的安全配置(如 IP 白名单 `0.0.0.0/0`)须**排期收窄**,不可遗留生产。 36. 跨主机 Docker 网络不互通:反代用远程 IP,勿建 docker network。 37. 推送临时 token **用完即移除 remote URL**(安全)。 38. 重服务(AI)部署前**确认可用内存**,必要时停非必要容器。 ### D. 重构与变更节奏 39. 高风险重构 / 变更留**清醒时段**;先止血(降超时 + 兜底)再大改;凌晨避 Dify / Prompt 类变更。→ 教训:凌晨改 Prompt 三次返工。 40. 重构后端后部署前做**三重本地验证**:AST 结构 + 调用一致性 + `py_compile`。 41. **单例 / 连接池**:模块级懒加载单例,避免每次新建连接池泄漏。 42. 新功能发布前保留**旧路径自动降级**(兼容模式),零中断切换。→ 本案:主 Dify 结果缺 `intent_type` → 自动降级旧路径。 ### E. 代码质量(后端) 43. 异步函数必须 **`await`**,否则 `coroutine never awaited` 且静默失效。 44. 生产日志用 **`info` 级**,否则排查时无日志。 45. 相同外部调用禁止复制多遍,抽通用函数 + grep 验证零命中;死分支须有兜底(返回全量非 None)。 46. Neo4j `CONTAINS` 方向写 `i.name CONTAINS $kw`;文件解压 / 部署目录须与容器 **mount 路径一致**。→ 教训:解压到 `/opt/.../backend/` 但 mount `/app/`,日志仍显示旧行为。 47. FastAPI 装饰器工厂必须 **`@require_role("x")` 装饰路由**;误用 `Depends(require_role("x"))` 会被当请求参数 → 全接口 422 鉴权失效(P0)。 48. 路由参数未用 Query / Path 装饰会被当请求体 → 422;第三方 OAuth 回调用 GET 走 `?code=&state=`,接口须认 `state` 且支持 GET / POST。 49. Python `re` 中 **`\b` / `\w` 对中文按单词字符处理**;中文边界用 `(?`)+ 打 docker tag**;回滚硬触发:health 连续 3 次失败 / 修复超 5 分钟 / 用户报打不开。 71. 一键部署每步配 rollback + 备份镜像 tag 与 **alembic 版本号**(回滚前记 `version_num`)。 72. 排查 > 30 分钟新案例必须**沉淀故障手册**;可复用脚本归档**工具箱并登记 README**;手册 = 作战手册 / 工具箱 = 弹药库 / SOP = 军规,职责不混。 73. BugFix 复盘:简单单文件不复盘;重复 / 复杂 / 有改进点必须复盘并**回流 SOP**。 ### J. 任务执行 SOP 74. **部署三问**(部署前必确认):影响业务吗 / 重启服务吗 / 怎么验证。 75. 多步任务**先路由再执行**;路由阶段只传结论不传思考(上下文隔离)。 76. 前端改造**分批灰度发布**,降全量风险。 77. 功能未真可用前**不推广**(推广半成品透支信任);先打牢基础再做体验跃迁。 ### K. 安全软件 / 环境 78. 企业安全软件(火绒)会**异步隔离解释器类 exe**(python / bash),遇"exe 消失"先查隔离区;可改 node execFile 替代。 ### L. 前端运行时与消息通道(2026-07-26 新增,源自 CASE-20260726-01~08) 79. **Pinia setup store 外部访问不加 `.value`**:setup store 返回的 ref 在组件 / store 外部访问时被 auto-unwrap,已是数组 / 对象本身;加 `.value` → `undefined` → 调用 `.includes()` 等方法抛 TypeError → 组件静默崩溃(不白屏但功能消失)。→ 教训:CASE-20260726-04 ✓选中导致 AI 消息消失。 80. **双通道(WS + 轮询)必须有去重**:WS 实时推送和轮询可能同时到达同一消息,任一通道无去重都会导致重复渲染。用 `Set` 记录已处理 ID,**两通道共用同一去重集合**。→ 教训:CASE-20260726-06 坐席端消息重复。 81. **WS 推送消息不应更新轮询游标**:轮询 `after_message_id` 若用 UUID 字典序过滤,WS 推送的 AI 回复 UUID 可能大于后续员工消息 UUID,导致员工消息被跳过。WS 推送走独立通道,**不更新轮询游标**。(补充第 8 条"UUID≠时间"的具体场景)→ 教训:CASE-20260726-05 选项消息延迟出现。 82. **Edit 工具大批量修改可能写入损坏字符**:Edit 工具写入中文注释时偶发编码损坏(如注释变乱码),TypeScript 编译可能通过但运行时语法异常导致白屏。改后必须 **Grep / Read 验证**关键文件内容完整性,不能只看构建结果。(补充第 4 条"Edit 静默失败"的编码损坏维度)→ 教训:CASE-20260726-01 H5 白屏。 83. **构建成功 ≠ 运行时正常,白屏优先怀疑 JS 运行时错误**:Vite 构建通过只代表语法 / 类型检查通过,不代表运行时无异常(如未 import 的 `ref`、Pinia `.value` 崩溃、闭包值无法响应)。白屏排查路径:F12 控制台看 `ReferenceError` / `TypeError` → 检查 import → 检查 store 访问方式。→ 教训:CASE-20260726-01 / 03 / 04 三起白屏 / 组件崩溃。 --- ## 第二部分 · IT智能服务台项目专属经验 ### 服务器与部署 - 正式服务器 `itsupport.servyou.com.cn` (10.90.5.110),出口 IP `218.75.34.87` - 堡垒机 `sxn@10.212.189.210:2222` (OTP);脚本 `jms_ops.py` - 项目根 `/opt/wecom-it-desk/`;**代码唯一源 `/opt/wecom-it-desk/app/`**(卷挂载),重建镜像只更新依赖;代码变更 `restart`、依赖变更才 `build` - 后端 mount `./app:/app/app`(`.py` 变更 `restart backend`,env 变更 `up -d backend`);**后端必须 `--workers 1`**(ws_manager 进程内单例,多 worker 致约 50% WS 消息静默丢失) - 前端 dist 只读 bind 到 nginx `/usr/share/nginx/html` 下:`h5` / `itagent` / `itadmin` / `itportal` / `itterminal` / `itdesk` - 容器名用下划线 `wecom_it_nginx`(非横杠) - **部署顺序**:后端 → 前端 4 端 → nginx → DB 迁移 → 验收 - 免登录跳转:ITSM 用 `itsm.servyou.com.cn`(企微应用主页域名,从工作台打开免登录);`devops.dc.servyou-it.com` 需扫码 - 蓝绿部署 Blue / Green 共用同一 PG / Redis,切换后用户需重登录、WS 断开;**DB migration 只能逐版降**(`alembic downgrade -1` 多次) ### 外部集成(关键端点) - 企微通讯录 Secret 已废弃(errcode 48009)→ 改用**自建应用 Secret + 全公司可见范围**;`WECOM_CONTACT_SECRET=` 留空触发降级(验证 651 部门 / 7098 员工) - Dify `http://yw-dify.dc.servyou-it.com/v1/chat-messages`:advanced-chat 改工作流后**必须浏览器手动「发布」**,否则 API 400 `Workflow not published`;原生直连优先于代理,compose 须显式声明 `DIFY_NATIVE_*` / `BAIDU_ASR_*` / `WECOM_SSO_CALLBACK_BASE` - Dify App ID `8f0f3d62-f63d-4cf3-815e-b10529c66f1d`(智能IT支持-员工咨询,85 节点);审批意图 `app-7jkRkAzvX4QM9v9SM3P8mMEO`;分诊 `app-z3S9AEUUAVPbtR2rioxpiIvp` - RAGFlow 生产 `http://10.80.0.85:8080/` / API `:9380`;映射策略:联软(主) > aTrust(VPN) > eHR(静态) - **勿硬编码外部系统模板 / ID**:企微审批模板 ID 会失效,已统一改用 ITSM 工单系统(通用教训:外部模板 ID 配置化,勿写死代码) ### 企微登录与扫码 - 企微**扫码登录**:App 无确认 UI,扫码成功 = 直接登录(去 confirm),否则生产走不通 - **iOS WebView 无法解析相对路径回调 URL**,后端须返回绝对 URL(含域名) - **H5 员工端仍走企微 OAuth**,不进扫码 / MFA 体系,与其他端不同 - OTP **连续 5 次错误锁 5 分钟**、二维码 **120 秒过期**;`pyotp` 锁版本 `==2.9.0` - 后端 **JS-SDK 签名接口已存在**(`/api/wecom/jsapi-config`),语音等前端能力无需后端改动 ### 企微 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` - 既有 bug:`EmergencyDispatcher.vue` 复用 jsapi 签名给 agentConfig(靠 3 秒超时兜底) ### 关键架构决策(锁定,勿随意改) - 术语:员工端「人工坐席」= 用户呼叫坐席;「摇人」= 坐席呼叫坐席 - UI:企微浅色扁平,accent `#07C160`;统一入口 `/itportal/` → user / agent / admin(admin 需 OTP) - 单通道消息架构:Dify 输出 JSON `{text,action,options}` → 后端发双 WS(`ai_reply` + `dynamic_recommend`) → 文字到气泡 / 卡片到侧边栏 - WS 双连接池:`active_connections`(agent) + `employee_connections`(H5) - 后端字段 `id` / `sender_type` → 前端 `message_id` / `message_type`(`conversation.ts` 的 `mapMessage()`) - 角色边框色硬编码:主责蓝 `#3b82f6` / 协作绿 `#07C160` / 发起人橙 `#FF9800` / 自己高亮环;H5 超员 N=4、坐席 N=6 ### 功能与版本(摘要) - 群聊(摇人/邀请/四角色) / 审批(12 类型 18 流程 / 三级意图) / 代办 / IT资产推送 / 语音转文字 / 截图拍照 / 复杂场景 P0~P3 / 诊断修复闭环 / D1 路由合并 - 运维手册:`docs/04-运维文档/部署运维/00-标准故障排查手册.md`;SOP:`docs/07-项目管理/IT智能服务台-标准作业流程SOP.md`