Files
wecom_it_smart_desk/docs/项目经验与教训-可复用规则手册.md
Simon facc04aa65 chore: docs 结构整改 + compose 双目录对齐(合并重建提交)
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交
(5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。

一、docs 结构整改(整改 #14)
根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入,
随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录
树并存于 docs/,共 791 文件、双分类体系冲突。

修复动作:
- b2 同名异主题文件改名迁移保全 9 个
- C 类 39 个孤立文件按主题正确归类
- A/B1 类 222 个重复文件删除(新结构已有内容副本)
- 9 个旧独有空目录删除
- 270 处内部引用按 verified 映射改写
- 整改记录 #14 登记于 04-运维文档/部署运维

结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。
残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。

二、compose 双目录对齐(消除踩坑 A)
- docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist
  改为 src/frontend-*/dist(h5 / agent / admin / terminal)
- docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/
- 效果:本地 docker compose up 不再把根目录 stale dist 挂回,
  与线上一致,分叉隐患消除(已 docker compose config 校验通过)

防复发铁律:
- 重构须提交;仓库修复须 git stash -u 或先 commit
- 新结构须 git add 并提交,避免再次 untracked 复活
- H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
2026-08-07 22:31:32 +08:00

18 KiB
Raw Permalink Blame History

项目经验与教训 · 可复用规则手册

用途:作为每个项目 / 任务启动时的预设提示(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 / networkAPI 用 curl 真实返回;禁只信 docker logs
  4. 关键字段 / 配置修改后立即 Grep / Read 验证是否真生效——Edit 大批量可能静默失败。→ 教训:v3.0 的 18 字段未真正写入。
  5. 删除死代码 / 孤儿组件 / 死路由前,必须前后端双向核对消息类型与调用关系,避免"前端等 A 后端发 B"断链。→ 教训:结单确认断链。
  6. "验真"机制:标注完成的功能必须真实验证,防风注释掉的路由、[待AI生成] 占位、空实现伪装完成。
  7. AI 生成内容须断言非占位符 + 低置信度门控:低于阈值标记失败、不写脏数据。

B. 测试

  1. e2e 分页 / poll 不能依赖主键顺序UUID ≠ 时间);用 created_at 过滤 + 唯一测试用户。→ 教训:复用用户 poll 到旧消息误判修复。
  2. 容器化 dev 栈跑 E2EVite 代理 target 用 compose 服务名(backend:8000)非 localhost(容器内 localhost 是容器自身 → ECONNREFUSED)。
  3. 测试脚本(单元 / 集成)是最有效验证手段;改完先跑测试再部署。
  4. pytest fixture 用 generator 须 yieldreturn,否则 setup ERROR。
  5. mock patch 路径须是被测模块实际 import 的模块,否则 patch 无效。
  6. 外部依赖(Neo4j / PGfixture 自动降级内存 mock,无环境也可跑测试。
  7. 核心逻辑变更须同步更新测试辅助函数(如 _login_and_get_token 适配新响应)。
  8. 验收用浏览器无痕模式避缓存;人眼看到才算,不只信 curl。
  9. 第三方库升级(starlette / pytest-asyncio)可能破坏 monkeypatch 签名,conftest 加 encoding=None 兼容。
  10. 宣传卖点须真实数据验证(如"7 步达解"抽 100 问题验轮数),否则推广反噬信任。

C. 部署与配置

  1. .env 变量不会自动传入容器compose environment: 须显式声明(VAR=${VAR:-});新增变量同时加 .env 和 compose。
  2. 前端 dist 只读 bind mount 只能在宿主机改,容器内改无效;rm 后重建须重启容器。
  3. RO bind mount 目录 docker cp 会假成功,必须走宿主机 cp
  4. sed -i 改 nginx 会换新 inodebind mount 不跟踪 → 须 docker restart 非 reload;或 sed + 重定向保 inode。
  5. compose override.yml 自动合并并覆盖主文件同名项;部署前 docker compose config | grep 校验关键配置。
  6. POSTGRES_PASSWORD 首次写入数据卷后不可改,改须删卷重建。
  7. 改 FastAPI 路由须同步部署 router.pyinclude_router);nginx 正则 location 不能 proxy_pass 带 URI,须 rewrite 剥离 /api/ 前缀。
  8. SPA 用 try_files $uri $uri/ /index.html 防刷新 404vite base 须与 nginx 路径一致;OAuth 回调禁用 301 重定向
  9. WS 反代须 proxy_http_version 1.1 + Upgrade / Connection 头;浏览器传 token 用 Sec-WebSocket-Protocol 时后端 accept() 必须回显子协议
  10. /health 必须真实验证 DB + Redis/health(liveness) 与 /ready(readiness) 分离。
  11. 新依赖须同步 requirements.txt 并重建镜像Dockerfile 设 PYTHONDONTWRITEBYTECODE=1__pycache__ 污染挂载目录。
  12. 大文件(>100KB)用稳定通道上传 + MD5 校验JumpServer / PTY base64 单行 ≤ 3800 字符(超 4096 被静默丢弃),传输分块可 ~8000 字符 / 块。
  13. 部署后核对"跑的是这版":暴露 /version 端点返回 git hash / build time
  14. pydantic-settings 带前缀变量(如 WECOM_X)须显式 validation_alias,否则匹配不上、配置静默为空。
  15. WAF / 代理后 nginx 须还原真实 IPset_real_ip_from + real_ip_header X-Forwarded-For + real_ip_recursive on,否则 IP 白名单误拦 403。
  16. 改生产配置标准流程:脚本(python heredoc 避 sed 转义 / 换 inode)改 → 备份 → 人眼确认 → nginx -t → reload → 浏览器验证,每步可回滚。
  17. compose 多项目须 -p <project>,否则容器名冲突。
  18. 临时放宽的安全配置(如 IP 白名单 0.0.0.0/0)须排期收窄,不可遗留生产。
  19. 跨主机 Docker 网络不互通:反代用远程 IP,勿建 docker network。
  20. 推送临时 token 用完即移除 remote URL(安全)。
  21. 重服务(AI)部署前确认可用内存,必要时停非必要容器。

D. 重构与变更节奏

  1. 高风险重构 / 变更留清醒时段;先止血(降超时 + 兜底)再大改;凌晨避 Dify / Prompt 类变更。→ 教训:凌晨改 Prompt 三次返工。
  2. 重构后端后部署前做三重本地验证AST 结构 + 调用一致性 + py_compile
  3. 单例 / 连接池:模块级懒加载单例,避免每次新建连接池泄漏。
  4. 新功能发布前保留旧路径自动降级(兼容模式),零中断切换。→ 本案:主 Dify 结果缺 intent_type → 自动降级旧路径。

E. 代码质量(后端)

  1. 异步函数必须 await,否则 coroutine never awaited 且静默失效。
  2. 生产日志用 info,否则排查时无日志。
  3. 相同外部调用禁止复制多遍,抽通用函数 + grep 验证零命中;死分支须有兜底(返回全量非 None)。
  4. Neo4j CONTAINS 方向写 i.name CONTAINS $kw;文件解压 / 部署目录须与容器 mount 路径一致。→ 教训:解压到 /opt/.../backend/ 但 mount /app/,日志仍显示旧行为。
  5. FastAPI 装饰器工厂必须 @require_role("x") 装饰路由;误用 Depends(require_role("x")) 会被当请求参数 → 全接口 422 鉴权失效(P0)。
  6. 路由参数未用 Query / Path 装饰会被当请求体 → 422;第三方 OAuth 回调用 GET 走 ?code=&state=,接口须认 state 且支持 GET / POST。
  7. Python re\b / \w 对中文按单词字符处理;中文边界用 (?<!\w) / [^\d] / 显式 \d 替代。
  8. 角色判断勿硬编码(如写死 ["agent"]),须动态获取,否则越权 / 漏权。
  9. 容器内读日志用"应用写文件 + bind mount 宿主机目录"禁止挂 docker.sock(逃逸)/ 读 /var/lib/docker(随机 ID
  10. 日志 / 文件读取端点目录不可读须容错返回空 / 错误码,禁止 500
  11. Pydantic 响应用 exclude 剔除不应返回字段(避免泄露 null / 敏感)。

F. 前端与交互

  1. React 受控输入框浏览器自动化:用「剪贴板 + 真实 Ctrl+V」;不可用 fill / insertText / paste 事件。
  2. 自定义 handlePasteclipboardData.items 为空时不得 preventDefault / return 阻断默认,否则纯文本 Ctrl+V 静默失效。
  3. 企微 JS-SDK wx.configappId 传 corp_idagent_idnonceStr 驼峰映射,beta:true 必须,签名 URL 去 #
  4. Web Speech API 须 HTTPS / localhostcontinuous + interimResults 同 true 实现边说边出字;onresultevent.resultIndex 遍历。
  5. 浏览器截图 / 摄像头 API 须 HTTPS / localhost;内嵌浏览器可能受限须 fallbackhtml2canvas)。
  6. 设计令牌(色彩 / 字体 / 圆角 / 阴影 / 动效)一套三端共享;深色模式文字对比度 ≥ WCAG 4.5:1。
  7. 辅助功能(语音 / 截图)须降级策略:不支持隐藏按钮、错误 Toast 不阻塞、失败保留已有内容。

G. 架构与依赖

  1. 新增功能优先复用现有技术栈 / 存储,不新建表、不加新依赖(铁律)。
  2. 大文件 / 日志下载用 StreamingResponse 流式,避内存爆。
  3. 异构数据源用规范化层(composable / adapter)统一模型,组件只消费统一结构。

H. 安全与可观测性

  1. CORS 生产禁用 *精确白名单CSP 先在 Report-Only 观察约两周再 enforce。
  2. .dockerignore 排除 .env / .git;容器跑非 rootUSERCI 加 Trivy 镜像漏洞 + gitleaks secret 扫描exit-code 1 阻断)。
  3. 安全头全套:HSTS / X-Frame-Options DENY / Referrer-Policy / Permissions-Policy / server_tokens off
  4. 日志结构化:JSON formatter + request_id 中间件;错误响应统一带 trace_id,前端 axios 拦截器映射错误码。
  5. 安全敏感值用无特殊字符随机串;轮换选低峰期、停机 30–60s、通知重登录、新旧双备、仅存 .env

I. 版本 / 回滚 / 知识沉淀

  1. 大改动前先 commit 基线 + 打 git tag 回滚点;不用 git add -A(误加 node_modules_old),精确 add 路径。
  2. 部署前备份配置(.bak.<时间戳>+ 打 docker tag;回滚硬触发:health 连续 3 次失败 / 修复超 5 分钟 / 用户报打不开。
  3. 一键部署每步配 rollback + 备份镜像 tag 与 alembic 版本号(回滚前记 version_num)。
  4. 排查 > 30 分钟新案例必须沉淀故障手册;可复用脚本归档工具箱并登记 README;手册 = 作战手册 / 工具箱 = 弹药库 / SOP = 军规,职责不混。
  5. BugFix 复盘:简单单文件不复盘;重复 / 复杂 / 有改进点必须复盘并回流 SOP

J. 任务执行 SOP

  1. 部署三问(部署前必确认):影响业务吗 / 重启服务吗 / 怎么验证。
  2. 多步任务先路由再执行;路由阶段只传结论不传思考(上下文隔离)。
  3. 前端改造分批灰度发布,降全量风险。
  4. 功能未真可用前不推广(推广半成品透支信任);先打牢基础再做体验跃迁。

K. 安全软件 / 环境

  1. 企业安全软件(火绒)会异步隔离解释器类 exepython / bash),遇"exe 消失"先查隔离区;可改 node execFile 替代。

L. 前端运行时与消息通道(2026-07-26 新增,源自 CASE-20260726-01~08

  1. Pinia setup store 外部访问不加 .valuesetup store 返回的 ref 在组件 / store 外部访问时被 auto-unwrap,已是数组 / 对象本身;加 .valueundefined → 调用 .includes() 等方法抛 TypeError → 组件静默崩溃(不白屏但功能消失)。→ 教训:CASE-20260726-04 ✓选中导致 AI 消息消失。
  2. 双通道(WS + 轮询)必须有去重:WS 实时推送和轮询可能同时到达同一消息,任一通道无去重都会导致重复渲染。用 Set<message_id> 记录已处理 ID两通道共用同一去重集合。→ 教训:CASE-20260726-06 坐席端消息重复。
  3. WS 推送消息不应更新轮询游标:轮询 after_message_id 若用 UUID 字典序过滤,WS 推送的 AI 回复 UUID 可能大于后续员工消息 UUID,导致员工消息被跳过。WS 推送走独立通道,不更新轮询游标。(补充第 8 条"UUID≠时间"的具体场景)→ 教训:CASE-20260726-05 选项消息延迟出现。
  4. Edit 工具大批量修改可能写入损坏字符:Edit 工具写入中文注释时偶发编码损坏(如注释变乱码),TypeScript 编译可能通过但运行时语法异常导致白屏。改后必须 Grep / Read 验证关键文件内容完整性,不能只看构建结果。(补充第 4 条"Edit 静默失败"的编码损坏维度)→ 教训:CASE-20260726-01 H5 白屏。
  5. 构建成功 ≠ 运行时正常,白屏优先怀疑 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 backendenv 变更 up -d backend);后端必须 --workers 1ws_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-messagesadvanced-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
  • 既有 bugEmergencyDispatcher.vue 复用 jsapi 签名给 agentConfig(靠 3 秒超时兜底)

关键架构决策(锁定,勿随意改)

  • 术语:员工端「人工坐席」= 用户呼叫坐席;「摇人」= 坐席呼叫坐席
  • UI:企微浅色扁平,accent #07C160;统一入口 /itportal/ → user / agent / adminadmin 需 OTP
  • 单通道消息架构:Dify 输出 JSON {text,action,options} → 后端发双 WS(ai_reply + dynamic_recommend) → 文字到气泡 / 卡片到侧边栏
  • WS 双连接池:active_connections(agent) + employee_connections(H5)
  • 后端字段 id / sender_type → 前端 message_id / message_typeconversation.tsmapMessage()
  • 角色边框色硬编码:主责蓝 #3b82f6 / 协作绿 #07C160 / 发起人橙 #FF9800 / 自己高亮环;H5 超员 N=4、坐席 N=6

功能与版本(摘要)

  • 群聊(摇人/邀请/四角色) / 审批(12 类型 18 流程 / 三级意图) / 代办 / IT资产推送 / 语音转文字 / 截图拍照 / 复杂场景 P0~P3 / 诊断修复闭环 / D1 路由合并
  • 运维手册:docs/04-运维文档/部署运维/00-标准故障排查手册.mdSOPdocs/07-项目管理/IT智能服务台-标准作业流程SOP.md