feat: 2026-07-11 全量更新 - 代办集成+会议室预定+知识迭代修复+UI统一+Bug修复
== 已部署上线 (9项) == - 代办事项真实数据源集成 (企微审批API 8bug修复链) - H5/坐席端 Logo样式统一+绿色背景 - 视频引导页修复 (localStorage key v2) - 坐席端 v9 Vue版本修复 (ElMessage._context) - 截图按钮 v10 修复 (getDisplayMedia user gesture) - 扫码样式恢复+H5扫码登录跳转修复 - H5截图快捷键提示 == 代码完成待部署 (3项) == - 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查) - 会议室预定-小鱼易联终端 (40文件, 40/40测试通过) - IT资产升级审批推送 (asset_service.py) == 需求文档 (2项) == - 坐席端AI辅助消息框-PRD (4项新功能确认) - 坐席端布局优化建议 v2.0 (7天计划) == 新增文档 == - 日报-2026-07-11.md - 知识迭代Bug修复报告-20260711.md - 会议室预定-部署指南.md - CHANGELOG.md 更新 == 测试 == - test_todo_integration.py: 40/40 - test_meetingroom.py: 40/40 - test_bugfix_ki_suggestions.py: 21/21
This commit is contained in:
+114
-4
@@ -1,10 +1,10 @@
|
||||
# 00 · 标准故障排查手册
|
||||
|
||||
> **版本**: v1.1 | **日期**: 2026-07-08 | **维护人**: 宋献 / 助理
|
||||
> **版本**: v1.4 | **日期**: 2026-07-10 | **维护人**: 宋献 / 助理
|
||||
> **定位**: 所有故障排查前**首先查看本手册**。
|
||||
> **最新**: 新增 CASE-20260708-01~06(OTP 路由404 / nginx 404 / 扫码角色 / 用户角色 / 员工端路由 / OTP列表结构)+ nginx 配错急救流程
|
||||
> **最新**: 方案 C(卷挂载)已上线,§1.4 更新为 volume 挂载验证 + CASE-20260710-02 标注根因已消除 + 错误码速查更新
|
||||
|
||||
| v1.1 | 2026-07-08 | 新增 6 天 7.8 案例 + nginx 急救流程 + 端到端验证更新 |
|
||||
| v1.4 | 2026-07-10 | 方案 C 上线:§1.4 改为 volume 挂载验证 + 错误码速查更新 + CASE-20260710-02 根因已消除标注 |
|
||||
> **前置阅读**: [运维手册(部署/回滚/备份/应急)](../01-项目总览/01-智能IT服务系统运维手册-20260704.md) · [SOP-04 应急响应](../10-项目管理/SOPs-标准流程/SOP-04-应急响应.md)
|
||||
|
||||
---
|
||||
@@ -24,13 +24,26 @@
|
||||
| 版本 | 日期 | 变更 |
|
||||
|------|------|------|
|
||||
| v1.0 | 2026-07-07 | 整合 9 份散落文档 + 新增 CASE-20260707-01(Redis urlparse 挂起)|
|
||||
| v1.1 | 2026-07-08 | 新增 CASE-20260708-01~06 + nginx 急救流程 + 端到端验证更新 |
|
||||
| v1.2 | 2026-07-10 | 新增 CASE-20260710-01(CSP 拦截内联 JS)+ Step 0 响应头检查 + sed -i inode 教训 |
|
||||
| v1.4 | 2026-07-10 | 方案 C 上线:§1.4 改为 volume 挂载验证 + 错误码速查更新 + CASE-20260710-02 根因已消除标注 |
|
||||
| v1.3 | 2026-07-10 | 新增 CASE-20260710-02(镜像缺文件导致 API 404)+ 判定矩阵扩展 + 部署前同步检查清单 |
|
||||
|
||||
---
|
||||
|
||||
## 1 快速诊断决策树
|
||||
|
||||
### 1.1 三步隔离法(通用)
|
||||
任何"页面打不开 / 网络连接失败 / 接口无响应 / 422"都先用三步隔离,定位是 nginx、后端、还是依赖(DB / Redis)的问题:
|
||||
任何"页面打不开 / 网络连接失败 / 接口无响应 / 422"都先用三步隔离,定位是 nginx、后端、还是依赖(DB / Redis)的问题。
|
||||
|
||||
#### Step 0:HTTP 响应头检查(页面 200 但功能异常时首先执行)
|
||||
|
||||
```bash
|
||||
# 检查响应头,特别关注 Content-Security-Policy / X-Frame-Options / X-Content-Type-Options
|
||||
curl -ksI https://itsupport.servyou.com.cn/h5/ | grep -iE 'content-security|x-frame|content-type'
|
||||
```
|
||||
|
||||
**为什么要有 Step 0**:页面返回 HTTP 200、HTML 正常加载,但 JavaScript 不执行——这类问题无法被三步隔离法捕获。根因通常是 CSP(Content-Security-Policy)头限制了 `script-src`,导致内联 `<script>` 被浏览器静默拦截。详见 CASE-20260710-01。
|
||||
|
||||
```bash
|
||||
# 第1步:nginx 层可达性(在服务器执行;浏览器走 HTTPS,故用 https 而非 localhost)
|
||||
@@ -56,6 +69,8 @@ docker compose exec postgres pg_isready -U wecom # 期望 accepting
|
||||
| 某端点 502 | ❌502 | ❌后端 down | — | 后端未起 / 缺 `PYTHONPATH=/app` |
|
||||
| /itdesk/ 200 但 /api/... 404 | ✅ | — | — | nginx 代理路径不匹配 |
|
||||
| 422 | ✅ | API 校验失败 | — | 请求体缺字段(见 §2)|
|
||||
| 页面 200 但 JS 不执行 | ✅200 | — | — | **CSP 拦截内联 JS**(检查 `Content-Security-Policy` 头的 `script-src` 是否含 `'unsafe-inline'`,见 Step 0 / CASE-20260710-01)|
|
||||
| API 404 但本地代码存在 | ✅200 | ❌404 | — | ~~镜像缺文件~~(方案 C 前根因)。卷挂载模式下检查 volume 挂载 + `./app/` 代码完整性(见 CASE-20260710-02 / §1.4)|
|
||||
|
||||
### 1.2 关键陷阱:URL 特殊字符导致依赖"静默挂起"
|
||||
详见案例 **CASE-20260707-01**。密码含 `@` `#` 时,`urlparse` 把它们当 URL 分隔符,连到不存在的 host,连接**无限挂起**(浏览器表现为"网络连接失败",curl 永远等不到返回)。这是最隐蔽的一类故障——容器全 Up、nginx 全 200、唯独业务接口卡死。
|
||||
@@ -70,6 +85,57 @@ python jms_ops.py exec -c "docker compose ps" -c "curl -ksI https://itsupport.se
|
||||
|
||||
> 原"服务器端跑诊断"的 3 种手工方式(PuTTY 跳堡垒机 / scp 上传 / 服务器下载)已不推荐,统一用上述 jumpserver-ops 自动化。
|
||||
|
||||
### 1.4 部署前检查清单(卷挂载模式 — 方案 C)
|
||||
|
||||
**触发条件**:任何涉及后端代码变更的部署(新增/修改 `.py` 文件、新增 Python 依赖)。
|
||||
|
||||
#### ⛔ 硬规则:后端代码部署方式(方案 C 卷挂载,2026-07-10 上线)
|
||||
|
||||
| 变更类型 | 正确命令 | ❌ 禁止操作 | 耗时 |
|
||||
|---------|---------|---------|------|
|
||||
| `.py` 文件变更 | `cd /opt/wecom-it-desk && docker compose restart backend` | ❌ `docker compose build` | ~15-30 秒 |
|
||||
| `requirements.txt` 变更 | `docker compose build backend && docker compose up -d backend` | — | ~60-90 秒 |
|
||||
| `.env` / `docker-compose.yml` 变更 | `docker compose up -d backend` | — | ~10 秒 |
|
||||
|
||||
> 代码通过 `./app:/app/app` volume 挂载到容器,不烘焙进镜像。改代码只需 restart 让 uvicorn 重新加载,无需重建镜像。`docker compose build` 只在 Python 依赖变化时才需要。
|
||||
|
||||
**背景**:方案 C(卷挂载)已于 2026-07-10 上线。代码不再烘焙进 Docker 镜像,而是通过 `./app:/app/app` volume 挂载到容器。`backend/app/` 旧代码目录已删除。部署前只需验证 `./app/` 代码目录完整性和 volume 挂载状态。
|
||||
|
||||
> ⚠️ **历史背景**(已消除):方案 C 前,服务器存在两份代码目录 `app/` 和 `backend/app/`,不同步导致镜像缺文件(见 CASE-20260710-02)。方案 C 消除了此根因。
|
||||
|
||||
**检查清单(经堡垒机执行)**:
|
||||
|
||||
```bash
|
||||
# 1. 验证 ./app/ 关键文件存在
|
||||
for f in app/__init__.py app/main.py app/api/auth.py; do
|
||||
[ -f "/opt/wecom-it-desk/$f" ] && echo "PASS: $f" || echo "FAIL: $f missing"
|
||||
done
|
||||
|
||||
# 2. 验证 volume 挂载正常(容器内可访问代码)
|
||||
docker exec wecom_it_backend ls /app/app/main.py
|
||||
|
||||
# 3. 验证代码一致性(宿主机与容器内 MD5 一致)
|
||||
HOST=$(md5sum /opt/wecom-it-desk/app/main.py | awk '{print $1}')
|
||||
CONTAINER=$(docker exec wecom_it_backend md5sum /app/app/main.py | awk '{print $1}')
|
||||
[ "$HOST" = "$CONTAINER" ] && echo "PASS: code match" || echo "FAIL: code mismatch"
|
||||
|
||||
# 4. 如有新依赖,检查 requirements.txt
|
||||
# diff 本地 requirements.txt 与服务器上的(如需要)
|
||||
|
||||
# 5. 代码更新后只需重启(无需重建镜像)
|
||||
cd /opt/wecom-it-desk && docker compose restart backend
|
||||
```
|
||||
|
||||
**简化版(用 jumpserver-ops 一键检查)**:
|
||||
```bash
|
||||
python jms_ops.py exec \
|
||||
-c "for f in app/__init__.py app/main.py app/api/auth.py; do [ -f /opt/wecom-it-desk/\$f ] && echo \"PASS: \$f\" || echo \"FAIL: \$f\"; done" \
|
||||
-c "docker exec wecom_it_backend ls /app/app/main.py && echo PASS_mount" \
|
||||
--reuse
|
||||
```
|
||||
|
||||
> ⚠️ 代码更新后 `docker compose restart` 即可(15-30 秒)。仅当 `requirements.txt` 有变化时才需要 `docker compose build`。
|
||||
|
||||
---
|
||||
|
||||
## 2 常见错误码速查(E5xx)
|
||||
@@ -82,6 +148,8 @@ python jms_ops.py exec -c "docker compose ps" -c "curl -ksI https://itsupport.se
|
||||
| **E403** | IP 白名单 / 无权限 | `grep allow /opt/wecom-it-desk/nginx/nginx.conf`;admin 角色不足 |
|
||||
| **E422** | 请求体校验失败(Pydantic)| 确认必填字段齐全(如登录需 `user_id`+`name`)|
|
||||
| **网络失败 / 连接挂起** | 依赖不可达(最常见 Redis 配置错)| 见 §1.2 / CASE-20260707-01 |
|
||||
| **页面 200 但 JS 不执行** | CSP 头 `script-src` 缺 `'unsafe-inline'`,内联 `<script>` 被浏览器静默拦截 | `curl -ksI <URL> \| grep content-security`;检查 `script-src` 是否含 `'unsafe-inline'`(见 CASE-20260710-01)|
|
||||
| **API 404 但本地代码存在** | ~~镜像缺文件~~(方案 C 前根因已消除)。卷挂载模式下检查:volume 是否正常挂载 + `./app/` 是否有该文件 | `docker exec <容器> ls /app/app/api/auth.py`;`docker exec <容器> md5sum /app/app/main.py` 对比宿主机(见 §1.4 / CASE-20260710-02)|
|
||||
|
||||
### 2.1 E500 常见根因速查
|
||||
- 数据库缺列 → `ALTER TABLE ... ADD COLUMN IF NOT EXISTS ...`
|
||||
@@ -133,6 +201,47 @@ docker logs wecom_it_backend | grep -i websocket
|
||||
|
||||
## 4 案例库(倒序,编号 CASE-YYYYMMDD-序号)
|
||||
|
||||
### CASE-20260710-02 · 坐席端/管理端登录"获取二维码失败"(Docker 镜像缺文件)⭐⭐
|
||||
- **现象**:坐席端和管理端登录均报"获取二维码失败",浏览器控制台显示 `WebSocket connection failed` + `/api/auth/qrcode` 返回 404。
|
||||
- **误判历程**:
|
||||
1. 误判为 Nginx 未 reload → `nginx -s reload` 后 `/auth_qrcode/create` 返回 200,但前端实际调用的是 `/auth/qrcode`(不同路由)
|
||||
2. 尝试 `docker cp` 临时复制 `auth.py` 到容器 → 重启后文件丢失(临时文件系统)
|
||||
3. 发现连锁依赖:`auth.py` 依赖 `schemas/auth.py` → `services/auth_service.py` → `models/login_log.py`,全部缺失
|
||||
4. **真正根因**:服务器存在两份代码——`/opt/wecom-it-desk/app/`(较新,开发用)和 `/opt/wecom-it-desk/backend/app/`(较旧,Docker 构建用)。新增的 `auth.py` 只放到了 `app/`,未同步到 `backend/app/`,导致镜像构建时打包的是旧代码。
|
||||
- **根因**:服务器上 `app/`(开发目录)与 `backend/app/`(Docker 构建目录)不同步,`docker compose build` 打包了旧代码。同时 `requirements.txt` 也未同步(新增 `neo4j` 依赖),首次重建后容器因缺包 crash。
|
||||
- **修复**:
|
||||
1. 用 Python 脚本同步 `/opt/wecom-it-desk/app/` → `/opt/wecom-it-desk/backend/app/`
|
||||
2. 上传最新 `requirements.txt`(含 `neo4j>=5.26.0`)到 `/opt/wecom-it-desk/backend/`
|
||||
3. `docker compose build backend`(约 65s)
|
||||
4. `docker compose up -d backend`
|
||||
5. 验证:后端日志显示 `GET /auth/qrcode → 200 OK`,容器状态 healthy
|
||||
- **⚠️ 教训**:
|
||||
1. **部署新功能时必须检查两份代码一致性**:`app/` 和 `backend/app/` 不同步 = 镜像缺文件。
|
||||
2. **`docker cp` 是临时方案**:容器重启后丢失。永久修复必须重建镜像。
|
||||
3. **全链路检查**:代码同步 → requirements 更新 → 镜像重建 → 容器重启,四步缺一不可。
|
||||
- **防护措施**:见 §1.4 部署前检查清单。
|
||||
- **✅ 根因已消除**(2026-07-10):方案 C(卷挂载)已上线。代码不再烘焙进 Docker 镜像,通过 `./app:/app/app` volume 挂载到容器。`backend/app/` 旧代码目录已删除,不再存在"两份代码不同步"问题。此案例保留作为历史参考。
|
||||
|
||||
### CASE-20260710-01 · 扫码登录成功页 JS 不执行(CSP 拦截内联脚本)⭐⭐
|
||||
- **现象**:企微扫码登录成功后,"登录成功"页面显示但不会自动关闭。页面停在"JS加载中…",所有内联 `<script>` 从未执行。后端返回 HTTP 200,HTML 内容正确。
|
||||
- **误判历程**(5 轮,供反思):
|
||||
1. 误判为未引入企微 JS-SDK → 添加后仍不工作
|
||||
2. 误判为 JSAPI 签名 URL 协议不一致(HTTP vs HTTPS)→ 修复 `X-Forwarded-Proto` 后仍不工作
|
||||
3. 误判为外部 JS-SDK 加载失败 → 移除外部依赖改用 `WeixinJSBridge` 仍不工作
|
||||
4. 误判为 `WeixinJSBridgeReady` 事件未触发 → 改为轮询检测仍不工作
|
||||
5. **真正根因**:Nginx CSP 头 `script-src 'self' 'unsafe-eval' https://res.wx.qq.com` 缺少 `'unsafe-inline'`,所有内联 `<script>` 被浏览器静默拦截。**JS 从未执行过**——前面 4 轮的代码修改全部白费。
|
||||
- **根因**:`Content-Security-Policy` 的 `script-src` 指令未包含 `'unsafe-inline'`,浏览器根据 CSP 规范静默拦截所有内联脚本(`<script>...</script>` 和 `onclick=` 等内联事件处理器)。页面 HTML 正常返回,但 JS 引擎从未解析执行任何脚本。
|
||||
- **修复**:Nginx 配置 CSP 头 `script-src` 加入 `'unsafe-inline'`:
|
||||
```
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://res.wx.qq.com; ..." always;
|
||||
```
|
||||
修改后 `docker restart wecom_it_nginx`(不能用 `reload`,见下方教训)。
|
||||
- **验证**:用户手机企微扫码 → 显示"登录成功"→ 1 秒后自动关闭返回企微 ✅
|
||||
- **⚠️ 教训**:
|
||||
1. **页面 200 + HTML 正确 ≠ JS 会执行**。排查前端功能异常时,Step 0 应检查 HTTP 响应头(CSP / X-Frame-Options),而非直接深入代码逻辑。
|
||||
2. **CSP 拦截是静默的**——浏览器控制台报错但页面无任何错误提示,后端日志也完全正常。如果不主动检查响应头,会一直在代码层面打转。
|
||||
3. **5 轮误判的共同盲点**:每次都在"JS 代码为什么不对"上做文章,从未怀疑"JS 根本没执行"。正确的排查路径应该是先在浏览器 F12 控制台输入 `1+1` 确认 JS 引擎可用,再看 Console 是否有 CSP 报错。
|
||||
|
||||
### 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` 时卡死。
|
||||
@@ -220,6 +329,7 @@ docker logs wecom_it_backend | grep -i websocket
|
||||
| 5 | 检查容器状态:`docker ps --filter name=wecom_it_nginx` |
|
||||
| 6 | 用 `agent-browser` 打开 URL 做端到端验证 |
|
||||
| 7 | 避免用 sed 修改 nginx 配置——`$uri`/`$host` 等变量会被 shell 解释;用 Python 脚本或直接上传文件 |
|
||||
| 8 | ⚠️ **`sed -i` 会创建新 inode**:Docker bind mount 不跟踪新 inode,导致容器内看到的仍是旧文件。用 `sed -i` 修改后必须 `docker restart`(而非 `nginx -s reload`),或改用 `sed` 不带 `-i` + 重定向写入原文件(保持 inode 不变) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user