44e77dcb0e
**重构前**(旧编号 02-11): - docs/02-产品需求/ → 00 产品规划/PRD - docs/03-技术架构/ → 01-05 子目录散落 - docs/04-原型设计/ → 01-02 产品设计(HTML 原型) - docs/05-原型设计/ → screens/ - docs/06-测试素材/ → 02-E2E / 03-功能 / 04-版本测试 - docs/07-项目管理/ → 任务说明书/日报/计划 - docs/08-安全审计/ → 审计报告 - docs/09-堡垒运维/ → toolbox / deploy - docs/10-项目管理/ → 任务说明书(重复) - docs/11-历史归档/ → deploy-nas-archived **重构后**(新编号 00-07,语义化): - docs/00-产品开发流程与文档管理规范.md - docs/00-版本迭代总览.md - docs/01-产品文档/ (PRD/原型/认证/会话/AI 服务/坐席/集成) - docs/02-技术文档/ (技术方案/架构图/重构记录/前端改造/实现配置) - docs/03-测试文档/ (E2E/功能用例/版本报告/缺陷单) - docs/04-运维文档/ (部署运维/运维指南) - docs/05-运营文档/ (品牌推广/用户手册) - docs/06-安全审计/ (审计报告) - docs/07-项目管理/ (任务说明书/日报/计划/看板) **净收益**: - 目录编号与产品文档管理规范对齐(按文档阶段 01-07 编号) - 消除 02-产品需求 与 10-项目管理 的编号重叠 - 子目录按文档类型分组(如 01-产品文档/00-产品规划、01-产品文档/01-认证与登录) - 把运维/安全/项目管理从 0X 散落改为 04/06/07 合计 494 文件 + 78495 行 / - 14076 行
18 KiB
18 KiB
项目经验与教训 · 可复用规则手册
用途:作为每个项目 / 任务启动时的预设提示(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. 验证与修复(最高优先级)
- 宣布"已修复 / 已完成"前必须端到端验证并给真实证据;不得仅因日志无报错或主观判断。→ 教训:
[object Object]事故。 - 容器内测试通过 ≠ 外部用户可用;必须走完整链路(Mock 登录 → 发消息 → 查回复)。
- 验证按分层选工具:前端 / 登录 / 交互用真实浏览器截图 + console / network;API 用 curl 真实返回;禁只信
docker logs。 - 关键字段 / 配置修改后立即 Grep / Read 验证是否真生效——Edit 大批量可能静默失败。→ 教训:v3.0 的 18 字段未真正写入。
- 删除死代码 / 孤儿组件 / 死路由前,必须前后端双向核对消息类型与调用关系,避免"前端等 A 后端发 B"断链。→ 教训:结单确认断链。
- "验真"机制:标注完成的功能必须真实验证,防风注释掉的路由、
[待AI生成]占位、空实现伪装完成。 - AI 生成内容须断言非占位符 + 低置信度门控:低于阈值标记失败、不写脏数据。
B. 测试
- e2e 分页 / poll 不能依赖主键顺序(UUID ≠ 时间);用
created_at过滤 + 唯一测试用户。→ 教训:复用用户 poll 到旧消息误判修复。 - 容器化 dev 栈跑 E2E:Vite 代理 target 用 compose 服务名(
backend:8000)非localhost(容器内 localhost 是容器自身 → ECONNREFUSED)。 - 测试脚本(单元 / 集成)是最有效验证手段;改完先跑测试再部署。
- pytest fixture 用 generator 须
yield非return,否则 setup ERROR。 - mock patch 路径须是被测模块实际 import 的模块,否则 patch 无效。
- 外部依赖(Neo4j / PG)fixture 自动降级内存 mock,无环境也可跑测试。
- 核心逻辑变更须同步更新测试辅助函数(如
_login_and_get_token适配新响应)。 - 验收用浏览器无痕模式避缓存;人眼看到才算,不只信 curl。
- 第三方库升级(starlette / pytest-asyncio)可能破坏 monkeypatch 签名,conftest 加
encoding=None兼容。 - 宣传卖点须真实数据验证(如"7 步达解"抽 100 问题验轮数),否则推广反噬信任。
C. 部署与配置
.env变量不会自动传入容器;composeenvironment:须显式声明(VAR=${VAR:-});新增变量同时加.env和 compose。- 前端 dist 只读 bind mount 只能在宿主机改,容器内改无效;rm 后重建须重启容器。
- RO bind mount 目录
docker cp会假成功,必须走宿主机cp。 sed -i改 nginx 会换新 inode,bind mount 不跟踪 → 须docker restart非 reload;或 sed + 重定向保 inode。- compose
override.yml自动合并并覆盖主文件同名项;部署前docker compose config | grep校验关键配置。 POSTGRES_PASSWORD首次写入数据卷后不可改,改须删卷重建。- 改 FastAPI 路由须同步部署
router.py(include_router);nginx 正则location不能proxy_pass带 URI,须rewrite剥离/api/前缀。 - SPA 用
try_files $uri $uri/ /index.html防刷新 404;vitebase须与 nginx 路径一致;OAuth 回调禁用 301 重定向。 - WS 反代须
proxy_http_version 1.1+ Upgrade / Connection 头;浏览器传 token 用Sec-WebSocket-Protocol时后端accept()必须回显子协议。 /health必须真实验证 DB + Redis;/health(liveness) 与/ready(readiness) 分离。- 新依赖须同步
requirements.txt并重建镜像;Dockerfile 设PYTHONDONTWRITEBYTECODE=1防__pycache__污染挂载目录。 - 大文件(>100KB)用稳定通道上传 + MD5 校验;JumpServer / PTY base64 单行 ≤ 3800 字符(超 4096 被静默丢弃),传输分块可 ~8000 字符 / 块。
- 部署后核对"跑的是这版":暴露
/version端点返回 git hash / build time。 - pydantic-settings 带前缀变量(如
WECOM_X)须显式validation_alias,否则匹配不上、配置静默为空。 - WAF / 代理后 nginx 须还原真实 IP:
set_real_ip_from+real_ip_header X-Forwarded-For+real_ip_recursive on,否则 IP 白名单误拦 403。 - 改生产配置标准流程:脚本(python heredoc 避 sed 转义 / 换 inode)改 → 备份 → 人眼确认 →
nginx -t→ reload → 浏览器验证,每步可回滚。 - compose 多项目须
-p <project>,否则容器名冲突。 - 临时放宽的安全配置(如 IP 白名单
0.0.0.0/0)须排期收窄,不可遗留生产。 - 跨主机 Docker 网络不互通:反代用远程 IP,勿建 docker network。
- 推送临时 token 用完即移除 remote URL(安全)。
- 重服务(AI)部署前确认可用内存,必要时停非必要容器。
D. 重构与变更节奏
- 高风险重构 / 变更留清醒时段;先止血(降超时 + 兜底)再大改;凌晨避 Dify / Prompt 类变更。→ 教训:凌晨改 Prompt 三次返工。
- 重构后端后部署前做三重本地验证:AST 结构 + 调用一致性 +
py_compile。 - 单例 / 连接池:模块级懒加载单例,避免每次新建连接池泄漏。
- 新功能发布前保留旧路径自动降级(兼容模式),零中断切换。→ 本案:主 Dify 结果缺
intent_type→ 自动降级旧路径。
E. 代码质量(后端)
- 异步函数必须
await,否则coroutine never awaited且静默失效。 - 生产日志用
info级,否则排查时无日志。 - 相同外部调用禁止复制多遍,抽通用函数 + grep 验证零命中;死分支须有兜底(返回全量非 None)。
- Neo4j
CONTAINS方向写i.name CONTAINS $kw;文件解压 / 部署目录须与容器 mount 路径一致。→ 教训:解压到/opt/.../backend/但 mount/app/,日志仍显示旧行为。 - FastAPI 装饰器工厂必须
@require_role("x")装饰路由;误用Depends(require_role("x"))会被当请求参数 → 全接口 422 鉴权失效(P0)。 - 路由参数未用 Query / Path 装饰会被当请求体 → 422;第三方 OAuth 回调用 GET 走
?code=&state=,接口须认state且支持 GET / POST。 - Python
re中\b/\w对中文按单词字符处理;中文边界用(?<!\w)/[^\d]/ 显式\d替代。 - 角色判断勿硬编码(如写死
["agent"]),须动态获取,否则越权 / 漏权。 - 容器内读日志用"应用写文件 + bind mount 宿主机目录",禁止挂 docker.sock(逃逸)/ 读 /var/lib/docker(随机 ID)。
- 日志 / 文件读取端点目录不可读须容错返回空 / 错误码,禁止 500。
- Pydantic 响应用
exclude剔除不应返回字段(避免泄露 null / 敏感)。
F. 前端与交互
- React 受控输入框浏览器自动化:用「剪贴板 + 真实 Ctrl+V」;不可用 fill / insertText / paste 事件。
- 自定义
handlePaste在clipboardData.items为空时不得preventDefault/ return 阻断默认,否则纯文本 Ctrl+V 静默失效。 - 企微 JS-SDK
wx.config:appId 传corp_id非agent_id,nonceStr驼峰映射,beta:true必须,签名 URL 去#。 - Web Speech API 须 HTTPS / localhost;
continuous+interimResults同 true 实现边说边出字;onresult从event.resultIndex遍历。 - 浏览器截图 / 摄像头 API 须 HTTPS / localhost;内嵌浏览器可能受限须 fallback(html2canvas)。
- 设计令牌(色彩 / 字体 / 圆角 / 阴影 / 动效)一套三端共享;深色模式文字对比度 ≥ WCAG 4.5:1。
- 辅助功能(语音 / 截图)须降级策略:不支持隐藏按钮、错误 Toast 不阻塞、失败保留已有内容。
G. 架构与依赖
- 新增功能优先复用现有技术栈 / 存储,不新建表、不加新依赖(铁律)。
- 大文件 / 日志下载用 StreamingResponse 流式,避内存爆。
- 异构数据源用规范化层(composable / adapter)统一模型,组件只消费统一结构。
H. 安全与可观测性
- CORS 生产禁用
*走精确白名单;CSP 先在 Report-Only 观察约两周再 enforce。 .dockerignore排除.env/.git;容器跑非 root(USER);CI 加 Trivy 镜像漏洞 + gitleaks secret 扫描(exit-code 1 阻断)。- 安全头全套:HSTS / X-Frame-Options DENY / Referrer-Policy / Permissions-Policy /
server_tokens off。 - 日志结构化:JSON formatter +
request_id中间件;错误响应统一带trace_id,前端 axios 拦截器映射错误码。 - 安全敏感值用无特殊字符随机串;轮换选低峰期、停机 30–60s、通知重登录、新旧双备、仅存
.env。
I. 版本 / 回滚 / 知识沉淀
- 大改动前先 commit 基线 + 打 git tag 回滚点;不用
git add -A(误加 node_modules_old),精确 add 路径。 - 部署前备份配置(
.bak.<时间戳>)+ 打 docker tag;回滚硬触发:health 连续 3 次失败 / 修复超 5 分钟 / 用户报打不开。 - 一键部署每步配 rollback + 备份镜像 tag 与 alembic 版本号(回滚前记
version_num)。 - 排查 > 30 分钟新案例必须沉淀故障手册;可复用脚本归档工具箱并登记 README;手册 = 作战手册 / 工具箱 = 弹药库 / SOP = 军规,职责不混。
- BugFix 复盘:简单单文件不复盘;重复 / 复杂 / 有改进点必须复盘并回流 SOP。
J. 任务执行 SOP
- 部署三问(部署前必确认):影响业务吗 / 重启服务吗 / 怎么验证。
- 多步任务先路由再执行;路由阶段只传结论不传思考(上下文隔离)。
- 前端改造分批灰度发布,降全量风险。
- 功能未真可用前不推广(推广半成品透支信任);先打牢基础再做体验跃迁。
K. 安全软件 / 环境
- 企业安全软件(火绒)会异步隔离解释器类 exe(python / bash),遇"exe 消失"先查隔离区;可改 node execFile 替代。
L. 前端运行时与消息通道(2026-07-26 新增,源自 CASE-20260726-01~08)
- Pinia setup store 外部访问不加
.value:setup store 返回的 ref 在组件 / store 外部访问时被 auto-unwrap,已是数组 / 对象本身;加.value→undefined→ 调用.includes()等方法抛 TypeError → 组件静默崩溃(不白屏但功能消失)。→ 教训:CASE-20260726-04 ✓选中导致 AI 消息消失。 - 双通道(WS + 轮询)必须有去重:WS 实时推送和轮询可能同时到达同一消息,任一通道无去重都会导致重复渲染。用
Set<message_id>记录已处理 ID,两通道共用同一去重集合。→ 教训:CASE-20260726-06 坐席端消息重复。 - WS 推送消息不应更新轮询游标:轮询
after_message_id若用 UUID 字典序过滤,WS 推送的 AI 回复 UUID 可能大于后续员工消息 UUID,导致员工消息被跳过。WS 推送走独立通道,不更新轮询游标。(补充第 8 条"UUID≠时间"的具体场景)→ 教训:CASE-20260726-05 选项消息延迟出现。 - Edit 工具大批量修改可能写入损坏字符:Edit 工具写入中文注释时偶发编码损坏(如注释变乱码),TypeScript 编译可能通过但运行时语法异常导致白屏。改后必须 Grep / Read 验证关键文件内容完整性,不能只看构建结果。(补充第 4 条"Edit 静默失败"的编码损坏维度)→ 教训:CASE-20260726-01 H5 白屏。
- 构建成功 ≠ 运行时正常,白屏优先怀疑 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),出口 IP218.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 400Workflow 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/09-部署运维/00-标准故障排查手册.md;SOP:docs/10-项目管理/IT智能服务台-标准作业流程SOP.md