# 00 · 标准故障排查手册 > **版本**: v1.1 | **日期**: 2026-07-08 | **维护人**: 宋献 / 助理 > **定位**: 所有故障排查前**首先查看本手册**。 > **最新**: 新增 CASE-20260708-01~06(OTP 路由404 / nginx 404 / 扫码角色 / 用户角色 / 员工端路由 / OTP列表结构)+ nginx 配错急救流程 | v1.1 | 2026-07-08 | 新增 6 天 7.8 案例 + nginx 急救流程 + 端到端验证更新 | > **前置阅读**: [运维手册(部署/回滚/备份/应急)](../01-项目总览/01-智能IT服务系统运维手册-20260704.md) · [SOP-04 应急响应](../10-项目管理/SOPs-标准流程/SOP-04-应急响应.md) --- ## 0 文档说明与版本 ### 0.1 为什么要有这本手册 原先"故障排查"主题散落在 9 份文档中(500 诊断、服务器端诊断、故障排查指南、4 份修复记录、通讯链路、deploy/02 手册、调试验证指南),内容重复且存在断链。任何故障都应**先翻这一本**,按决策树定位,再查案例库。 ### 0.2 ⛔ 验证完成硬规则(最重要) **宣布"已修复 / 已完成"之前,必须提供真实可验证证据**,不得仅凭 curl / 日志 / "我认为": - **前端 / 登录类问题**:真实浏览器登录或操作截图(用真实 Chromium / Playwright 打开页面、输入凭据、完成动作、进入目标页的截图)。 - **API / 后端类问题**:端到端调用证据(curl 真实返回 + 必要时代码层拦截响应体)。 - **禁止**:只因 `docker logs` 无报错就断言修复;只因"我认为应该好了"就宣布完成。 ### 0.3 版本历史 | 版本 | 日期 | 变更 | |------|------|------| | v1.0 | 2026-07-07 | 整合 9 份散落文档 + 新增 CASE-20260707-01(Redis urlparse 挂起)| --- ## 1 快速诊断决策树 ### 1.1 三步隔离法(通用) 任何"页面打不开 / 网络连接失败 / 接口无响应 / 422"都先用三步隔离,定位是 nginx、后端、还是依赖(DB / Redis)的问题: ```bash # 第1步:nginx 层可达性(在服务器执行;浏览器走 HTTPS,故用 https 而非 localhost) curl -ksI https://itsupport.servyou.com.cn/itadmin/ | head -5 curl -ksI https://itsupport.servyou.com.cn/api/health | head -5 # 第2步:直连后端(绕过 nginx,确认后端本身) docker compose exec backend curl -s http://localhost:8000/health # 或容器外: docker exec wecom_it_backend curl localhost:8000/health # 第3步:依赖可达性 docker compose exec redis redis-cli ping # 期望 PONG docker compose exec postgres pg_isready -U wecom # 期望 accepting ``` **判定矩阵**: | 现象 | 第1步 | 第2步 | 第3步 | 定位 | |------|------|------|------|------| | 浏览器"网络连接失败"、curl 永远不返回 | ✅200 | ✅200 | ❌挂起 | **依赖挂起**(如 Redis 连到错误 host)| | 全站 500 | ❌500 | ✅/❌ | — | 后端异常,看 backend 日志 | | 某端点 502 | ❌502 | ❌后端 down | — | 后端未起 / 缺 `PYTHONPATH=/app` | | /itdesk/ 200 但 /api/... 404 | ✅ | — | — | nginx 代理路径不匹配 | | 422 | ✅ | API 校验失败 | — | 请求体缺字段(见 §2)| ### 1.2 关键陷阱:URL 特殊字符导致依赖"静默挂起" 详见案例 **CASE-20260707-01**。密码含 `@` `#` 时,`urlparse` 把它们当 URL 分隔符,连到不存在的 host,连接**无限挂起**(浏览器表现为"网络连接失败",curl 永远等不到返回)。这是最隐蔽的一类故障——容器全 Up、nginx 全 200、唯独业务接口卡死。 ### 1.3 在服务器跑诊断的 3 种方式(经堡垒机) 公司服务器只能经堡垒机(`sxn@10.212.189.210:2222` → `ssh sxn@10.90.5.110`)操作,无法本地 scp。推荐用 jumpserver-ops 工具自动执行: ```powershell # 本地(Windows)用 jumpserver-ops 跑(自动复用会话,~2-3s/条): python jms_ops.py exec -c "docker compose ps" -c "curl -ksI https://itsupport.servyou.com.cn/api/health" --reuse ``` > 原"服务器端跑诊断"的 3 种手工方式(PuTTY 跳堡垒机 / scp 上传 / 服务器下载)已不推荐,统一用上述 jumpserver-ops 自动化。 --- ## 2 常见错误码速查(E5xx) | 错误码 | 含义 | 首选排查 | |--------|------|---------| | **E500** | 后端未捕获异常 / 缺列 / 缺依赖 | `docker compose logs backend --tail=200 \| grep -i error`;查数据库缺列 / 缺 Python 依赖 | | **E502** | nginx 连不到后端 | 后端容器 `unhealthy`?`docker logs wecom_it_backend`;是否缺 `PYTHONPATH=/app` | | **E503** | 服务过载 / 维护 | `docker stats`;`docker inspect ... Health` | | **E403** | IP 白名单 / 无权限 | `grep allow /opt/wecom-it-desk/nginx/nginx.conf`;admin 角色不足 | | **E422** | 请求体校验失败(Pydantic)| 确认必填字段齐全(如登录需 `user_id`+`name`)| | **网络失败 / 连接挂起** | 依赖不可达(最常见 Redis 配置错)| 见 §1.2 / CASE-20260707-01 | ### 2.1 E500 常见根因速查 - 数据库缺列 → `ALTER TABLE ... ADD COLUMN IF NOT EXISTS ...` - 缺 Python 依赖 → `requirements.txt` 补依赖后重构建(如 `wordfilter`) - 代码签名不匹配(如缺 `current_agent` 参数)→ 修函数签名 - `import aioredis` 与 Python 3.12 冲突 → 改 `redis.asyncio`,设 `PYTHONPATH=/app` ### 2.2 E422 登录场景 登录端点 `/api/agents/login` 要求 `user_id`(必填) + `name`(必填);缺字段直接 422。前端 `admin.ts` 用 `name: inputUserId` 发送。 --- ## 3 诊断脚本与命令 ### 3.1 一键系统状态 ```bash #!/bin/bash echo "==== 容器状态 ===="; docker compose ps echo "==== 端口 ===="; netstat -tlnp | grep -E "80|443|5432|6379|8000" echo "==== 前端文件 ===="; ls -la /opt/wecom-it-desk/html/itdesk/ 2>/dev/null | head echo "==== backend 错误 ===="; docker compose logs --tail=20 backend 2>&1 | grep -i error echo "==== Redis ===="; docker compose exec redis redis-cli ping echo "==== PG ===="; docker compose exec postgres pg_isready -U wecom ``` ### 3.2 500 错误快速对照 | 现象 | 诊断 | |------|------| | `ls .../frontend-h5/dist/` No such file | 部署包未含 dist | | nginx 容器内 `ls /usr/share/nginx/html/itdesk/` 失败 | 挂载路径错 | | curl /itdesk/ 返回 500 | 后端代理或 SPA 内部错 | | /itportal/ 200 但 /itdesk/ 500 | H5 端特定问题 | | nginx 日志有 `proxy_pass` 错 | 后端未起 / 端口不通 | | nginx 日志 `rewrite ... cycle` | try_files 死循环,修 nginx 配置 | ### 3.3 通讯链路检查点(用户 ↔ 坐席 ↔ 企微) - 用户→系统:企微回调 `/wecom/callback` → `message_router` → 消息入库 → 坐席 WS / 轮询 - 系统→用户:坐席 POST `/conversations/{id}/messages` → `wecom_service.send_text_message()`(errcode=0)→ 用户收到 - 已知风险:非文本消息(图片/文件)不推送;`dev_mode` 跳过企微推送;企微 API 失败静默(仅日志) - 检查点文件:`wecom_callback.py` / `message_router.py` / `messages.py` / `wecom_service.py` / `ws_manager.py` ### 3.4 WebSocket 失败 ```bash grep -r 'websocket' /opt/wecom-it-desk/nginx/nginx.conf # 需 proxy_http_version 1.1 + Upgrade/Connection docker logs wecom_it_backend | grep -i websocket ``` --- ## 4 案例库(倒序,编号 CASE-YYYYMMDD-序号) ### CASE-20260707-01 · 管理后台登录"网络连接失败"(Redis 密码 URL 解析挂起)⭐ - **现象**:浏览器登录 `/itadmin/` 一直转圈 / "网络连接失败";API 永远不返回;curl 超时。 - **根因**:`REDIS_URL=redis://:R3d!s@2026#Secure@redis:6379/0`,密码含 `@` 和 `#`。`urlparse()` 把 `#` 当 fragment、`@` 当 host 分隔符 → 解析出 host=`2026`、password=`R3d!s` → 连到不存在的 host → **无限挂起**。后端 `token_service.create_token()` 调 `redis.setex` 时卡死。 - **修复**: 1. `docker-compose.yml` 后端 `REDIS_URL` 改为 URL-encoded:`redis://:R3d%21s%402026%23Secure@redis:6379/0` 2. `backend/app/config.py` 的 `create_redis_client` 增加 `unquote()` 解码 + `socket_connect_timeout=5` / `socket_timeout=5` 3. redis 服务 `--requirepass` 与 healthcheck **保持明文** `R3d!s@2026#Secure`(与后端解码后的明文一致) 4. 重建 backend + redis 容器 - **验证**:Redis `PING→PONG`;`curl` 登录 `/api/agents/login` 返回 `HTTP 200, 0.64s, role:admin`;**真实浏览器登录截图进入 dashboard 成功**(见 §5)。 - **⚠️ 同类复发防护**:本项目 Redis 密码含特殊字符,**改 docker-compose 密码时两处必须一致**(后端 `REDIS_URL` 用 encoded,redis `--requirepass` 用明文);且 `config.py` 必须 `unquote`。 ### CASE-20260705-01 · 502 Bad Gateway(后端启动失败 / aioredis + PYTHONPATH) - **现象**:坐席端登录失败 `502`,后端容器 `unhealthy`。 - **根因**:旧镜像 `import aioredis` 与 Python 3.12 冲突(`TypeError: duplicate base class TimeoutError`);且未设 `PYTHONPATH=/app` 致 `ModuleNotFoundError: No module named 'app.core'`。 - **修复**:Dockerfile 改 `import redis.asyncio as aioredis`;`docker-compose.yml` 设 `PYTHONPATH=/app`;重建后端。 ### CASE-20260705-02 · 坐席端 4 个问题(消息列表 500 / 页面抖动 / 发送失败 / 文档缺失) - #1 消息列表 500:`list_messages()` 缺 `current_agent: Agent = Depends(get_current_agent)` 参数。修 `messages.py` + 重启。 - #2 页面短暂不可用:容器重启波动,自愈。 - #3 发送失败 `ModuleNotFoundError: wordfilter`:`requirements.txt` 缺 `wordfilter==0.2.7`,容器内 `pip install` 临时修 + 同步 requirements。 - #4 文档补"Python 依赖管理"章节(服务器部署手册)。 ### CASE-20260613-01 · H5 消息 500(缺列 + AIHandler 签名) - **现象**:`POST /api/h5/.../messages` 500:`column conversations.impact_scope does not exist` + `AIHandler.__init__() missing 'ai_service'`。 - **根因**:DB 缺 4 列(`impact_scope`/`is_blocking`/`emotion_state`/`dify_conversation_id`);`dependencies.py` 两处 `AIHandler()` 未传 `ai_service`。 - **修复**:`ALTER TABLE` 补列;`dependencies.py` 改 `AIHandler(ai_service=AIService())`。 ### 附:企微工作台"加载失败 / 无限加载"(2026-07-04) - 根因1:nginx 未正确挂载 `nginx.conf` → API 404,重建 nginx 容器。 - 根因2:后端 `h5.py` 存在 `NameError: _require_wework_ua` → 代码未同步最新,复制最新 `h5.py` + 重启。 --- ### CASE-20260708-01 · 后端路由 404 — OTP 统一路由未注册 ⭐ - **现象**:`POST /api/auth/otp-bind` 返回 404;前端 OTP 绑定面板密钥和二维码不显示。 - **根因**:部署 `otp.py` 时漏部署 `router.py`,`api_router.include_router(otp_router)` 未执行。本地 `router.py` 包含服务器不存在的模块(`knowledge_iteration` / `approval_queue` / `vision` / `ragflow_ingestion` / `automation`),导入失败导致整个 `router.py` 加载失败。 - **修复**:上传 `router.py`,注释掉服务器上不存在的模块导入;同时修复 `otp.py` 中 `@require_role("admin")` 装饰器与显式 `current_user` 参数的冲突(服务器旧版 `require_role` 会自动追加 `current_user`)。 - **教训**:修改路由时务必同步部署 `router.py`,否则新端点虽然代码存在但永远不会注册。 ### CASE-20260708-02 · 管理端 API 全部 404 — nginx 正则 location proxy_pass 缺 rewrite - **现象**:`/api/admin/roles`、`/api/admin/dashboard/overview` 等全部返回 404。 - **根因**:nginx 配置中 `location ~ ^/api/admin/`(正则匹配)内 `proxy_pass http://backend_api;` 不带尾部斜杠,导致 `/api/` 前缀未剥离,后端收到 `/api/admin/roles` 而非 `/admin/roles`。带尾部斜杠又会报错 `"proxy_pass" cannot have URI part in location given by regular expression`。 - **修复**:去掉嵌套正则 location,改为统一 `location /api/` → `proxy_pass http://backend_api/;`(尾部斜杠剥离 /api/ 前缀)。 - **教训**:nginx 中正则 location 不能直接用 `proxy_pass` 带 URI;需要时用 `rewrite` 剥离前缀。 ### CASE-20260708-03 · sxn 扫码登录无 admin 权限 — QR 扫描写死 `roles=["agent"]` - **现象**:管理后台扫码登录后,OTP 管理/角色管理返回 403/无权限提示。`sxn` 账密登录正常。 - **根因**:`auth_qrcode.py` 扫码自动确认逻辑中,`create_token` 写死了 `roles=["agent"]`,未调用 `get_user_roles()`。 - **修复**:扫码确认改为调用 `RoleMappingService.get_user_roles()` 获取真实角色。 - **教训**:所有登录路径(账密/扫码/OAuth)的角色获取必须统一走 `get_user_roles()`。 ### CASE-20260708-04 · 用户角色列表为空 — `get_user_roles()` 只查 `user_roles` 表 - **现象**:管理后台角色管理页"用户角色分配"表格为空;OTP 管理 API 403。 - **根因**:`get_user_roles()` 仅查询 `user_roles` 表,但旧数据(包括 sxn 的 admin)只存在于 `agents.role` 字段。`user_roles` 表为空时返回 `["user"]`。 - **修复**:`get_user_roles()` 增加 `agents.role` 回退查询;新增 `GET /admin/roles/user-roles` 端点;前端 `Roles.vue` 加载用户角色分配数据。 - **教训**:新旧数据迁移时需确保角色数据完整同步到 `user_roles` 表。 ### CASE-20260708-05 · 企微工作台点"IT支持服务"进坐席登录页 — 员工端口缺失 - **现象**:企微工作台 → IT支持服务 → 显示坐席扫码登录页,而非员工 H5 页面。 - **根因**:(1) nginx `/itdesk/` 被错误配置为 301 重定向到 `/itagent/`;(2) H5 构建文件未挂载到容器(`docker-compose.yml` 缺少 `./html/h5` 挂载);(3) 改重定向到 `/h5/` 后仍失败,因为 H5 `vite base` 是 `/itdesk/`,OAuth 回调依赖此路径。 - **修复**:`/itdesk/` 直接 alias 到 H5 构建目录(`/usr/share/nginx/html/h5/`),不再 301 跳转;`docker-compose.yml` 添加 h5 volume 挂载;根路径 `/` 改为 302 → `/h5/`。 - **nginx 关键配置**(alias + try_files SPA 模式): ``` location /itdesk/ { alias /usr/share/nginx/html/h5/; index index.html; try_files $uri $uri/ /index.html; } ``` 注意:fallback 用 `/index.html` 而非 `/itdesk/index.html`——alias 会自动映射。 - **教训**:前端 `vite base` 路径必须与 nginx 服务路径一致;OAuth 回调路径不能用 301 重定向。 ### CASE-20260708-06 · 管理端 OTP 用户列表返回错误结构 - **现象**:OTP 管理页面提示加载失败,`/auth/otp-admin-users` 返回 403。 - **根因**:(1) `admin_list_otp_users` 返回普通数组,前端期望 `{total, items}` 结构;(2) `@require_role("admin")` 因 token 不含 admin 角色返回 403(见 CASE-03/04)。 - **修复**:后端改为分页查询返回 `{total, items}`;增加 keyword/bound/page/page_size 参数支持。 ### 附:nginx 配错急救流程(2026-07-08 实战总结) | 步骤 | 操作 | |------|------| | 1 | 备份:`sudo cp /opt/wecom-it-desk/nginx/nginx.conf /tmp/nginx.bak` | | 2 | 修改宿主文件后必须 `docker stop nginx && docker start nginx`(`reload` 有时不生效) | | 3 | 验证容器内配置已同步:`docker exec wecom_it_nginx grep 关键字 /etc/nginx/nginx.conf` | | 4 | 确认语法:`docker exec wecom_it_nginx nginx -t` | | 5 | 检查容器状态:`docker ps --filter name=wecom_it_nginx` | | 6 | 用 `agent-browser` 打开 URL 做端到端验证 | | 7 | 避免用 sed 修改 nginx 配置——`$uri`/`$host` 等变量会被 shell 解释;用 Python 脚本或直接上传文件 | --- ## 5 端到端验证完成标准(原《调试验证指南》整合) > 宣布完成前,按 §0.2 提供真实证据。 ### 5.1 管理后台验证(最常见) | 步骤 | 操作 | 预期 | |------|------|------| | 1 | 浏览器开 `https://itsupport.servyou.com.cn/itadmin/` | 登录页 | | 2 | 输入 `sxn` / `test123` 登录 | 进入 dashboard,右上角显示"宋" | | 3 | 仪表盘数据渲染 | 在线坐席 / 今日会话 / 平均响应 / AI 命中率 有值 | | 4 | API 拦截 `POST /api/agents/login` | HTTP 200 + `role:admin` + token | ### 5.2 通用验证清单(P0 必须通过) - [ ] H5 登录流程正常(企微 OAuth 跳转 → 回跳 → 欢迎) - [ ] 坐席登录正常,可接单 - [ ] 消息收发双向正常(文本 / 图片 / 文件) - [ ] 邀请功能闭环 - [ ] 管理后台可访问且数据正常 ### 5.3 真实浏览器证据获取(推荐 Playwright) 本机 Windows 可直接访问服务器(TCP 443 通)。用 `playwright-core` 驱动已安装的 Chromium(路径 `~/.agent-browser/browsers/chrome-*/chrome.exe`),拦截 API 响应作为证据,避免 CLI 工具 IPC 不稳。 --- ## 6 升级与应急(交叉引用,不重复) - **回滚方案** → 见 [运维手册·第六章](../01-项目总览/01-智能IT服务系统运维手册-20260704.md#六回滚方案) - **备份恢复** → 见 [运维手册·第七章](../01-项目总览/01-智能IT服务系统运维手册-20260704.md#七备份恢复) - **应急响应(P0/P1 分级、止血、通知)** → 见 [SOP-04 应急响应](../10-项目管理/SOPs-标准流程/SOP-04-应急响应.md) - 本手册只负责"定位 + 修复",变更管理与事故流程以上述文档为准。 --- ## 7 参考文档索引 | 文档 | 说明 | |------|------| | `01-项目总览/01-智能IT服务系统运维手册-20260704.md` | 部署 / 回滚 / 备份 / 应急(故障排查章已并入本手册)| | `10-项目管理/SOPs-标准流程/SOP-04-应急响应.md` | 应急响应 SOP | | `09-部署运维/deploy/01-部署指南.md` | 部署操作 | | `09-部署运维/deploy/03-版本记录.md` | 版本与修复记录索引 | | `09-部署运维/deploy/服务器部署手册.md` | 服务器部署细节 | | `06-测试质量/testing-测试/E2E-CHECKLIST-v0.7.0.md` | E2E 验收清单 | --- > **维护说明**: 本手册为故障排查唯一入口。新增案例请按 `CASE-YYYYMMDD-序号` 倒序追加到 §4;改动需同步本文件版本号与日期。