---
name: Troubleshoot
description: 问题排查协助 - 当测试失败/服务异常/部署升级报错/MQTT收不到/UI元素找不到/SSH连不上/脚本跑挂/改了配置不生效等任意问题时，按"固化现象→切分排查域→排序假设→最小验证→定位根因"流程诊断，默认只读不动手，确认真失败再处置，绝不误判
---

当任意环节出问题（测试用例失败、远程服务异常、部署/升级报错、MQTT 收不到、UI 元素定位失败、SSH 连不上、PowerShell 脚本跑挂……）时，用这个流程做**结构化排查**。目标：在最短路径内定位**根因**而非只消症状，且**绝不误判、不误操作**。

## Usage

```
/Troubleshoot                      # 未给现象时，先问清楚再排查
/Troubleshoot <现象或报错>          # 直接基于描述排查
/Troubleshoot 只读                 # 只诊断 + 给方案，不执行任何改动（默认）
/Troubleshoot 并修复               # 诊断确认后，对"真失败"执行处置（高危动作仍需二次确认）
```

## 默认姿态（务必遵守）

- **默认只读**：诊断阶段不 restart、不重跑、不改配置、不删数据；给出"我建议怎么做"让用户拍板
- **区分三类状态，绝不混淆**：
  - `ERROR` 基础设施/环境问题（容器不存在、docker 缺失、SSH 断、ARM 不兼容）→ 不当服务故障，换路排查
  - `FAIL` 服务/功能真失败 → 连续确认后才考虑修复
  - `瞬态抖动` 偶发一次 → 重试确认，勿当真故障
- **修复前取证、修复后复验**：动手前采 `docker ps` + `logs --tail`；动手后等就绪再复测一遍
- **先查"已知坑"再动手**：很多现象已被记录为"假异常/已知坑"（见文末速查表），别把已解决的问题当新故障重新排查

## 排查六步法

### 1. 固化现象（最重要，别急着猜）
- 报错原文 / 截图 / 日志关键行 —— 原样抓取，别转述失真
- 在哪个环境（IP/服务器/分支）、哪个用例/脚本、什么时间、是否稳定复现
- **最近一次变更**（升级、改配置、改代码、重启过什么）—— 排查第一线索
- 现象不清就用 AskUserQuestion 问：现象/报错、环境、复现条件、最近变更

### 2. 切分排查域（自底向上逐层排除）
分层，下层挂了上层必然异常，先排除下层：
```
① 网络/基础设施  (SSH 通不通、端口、DNS、防火墙)
② 容器/中间件    (docker 容器状态、umysql/uredis/unacos/uemqx/utracker/unginx)
③ 服务/应用      (业务进程、Java 启动、HTTP 500、依赖链)
④ 测试代码/脚本  (用例本身、定位器、测试数据、编码)
⑤ 环境/数据      (配置文件、凭据、测试数据、版本不匹配)
```
**找第一个失败的层**：上层失败常是下层根因的表象（如 nacos 控制台登录坏 ≠ nacos 挂了）。

### 3. 形成假设并按概率排序
列 2-4 个候选根因，按"最近变更 + 该域历史故障率"排序，先验最可能的。

### 4. 最小验证（一次只动一个变量）
- 一次只改/查一个东西，否则分不清哪个起作用
- 优先非破坏性命令（看日志、探端口、curl 健康检查），最后才考虑 restart / 改数据

### 5. 定位根因
- 验证通过的假设即根因；要能解释**所有**现象，不能只解释一部分
- 区分"根因"和"触发条件"：根因是 why，触发是 when

### 6. 处置 + 回归 + 防复发
- 修复 → 原现象复测（回归）→ 相关联带功能也测一下
- 写一句话根因到报告/记忆，避免下次重复排查

## 高频排查场景与排查套路

> 以下套路提取自 346 条历史问题记录的共性模式，按历史故障率从高到低排列。

### 场景一：服务/容器启动失败或异常（历史占比最高 ≈38%）

**现象特征**：服务无法访问、页面 502/500、接口报错、容器 Exited

**标准排查链路：**
1. **容器存活检查** → `docker ps -a | grep <服务名>`，区分运行中 / Exited / 不存在
2. **看退出原因** → `docker logs --tail 100 <容器>`，找 ERROR / FATAL / Connection refused
3. **数据库连通性** → 大量启动失败根因是连不上数据库：
   - 数据库容器是否正常：`docker ps | grep umysql`
   - 数据库是否导入完成：磁盘 IO 慢（5-6 秒一张表）时服务先于数据库就绪 → 等待数据库完全就绪再重启服务
   - 凭据是否正确：`grep` 检查服务配置文件中的数据库密码是否和历史一致
4. **Redis 连通性** → `docker exec <ujava容器> curl uredis:6379`（容器内需能通），常见坑：服务器网口变更后 docker 网络隔离，需重启 docker
5. **Nacos 状态** → Nacos 启动失败常见两原因：
   - mysql 未就绪时 nacos 先启动，连不上库且不自动重连
   - Nacos 登录提示"账号密码错误" → 数据库被替换后凭据不一致，直接替换 nacos + 数据库
6. **磁盘空间** → `df -h`，删除的文件被进程持有未释放 → `lsof \| grep deleted`

**典型根因 Top 5：**
| 根因 | 现象 | 处置 |
|------|------|------|
| 数据库未就绪 / 连不上 | 服务启动后 500，日志打印 mysql connection refused | 等数据库完全导入后再重启服务；磁盘 IO 慢时设置 5 分钟等待 |
| 配置文件缺失/错误 | 后端 URL 指向 193（旧的 inner url） | 补充配置项 + 重启 |
| docker 网络隔离 | 容器内 curl 不通 uredis | 重启 docker 服务 |
| Nacos 不自动重连 | mysql 重启后 nacos 连不上且不恢复 | 配置监测脚本 + 定时自愈 |
| 服务器磁盘占满 | 所有服务不可用 | `df -h` 确认 + 清理日志 + 增加定时清理脚本 |

**预防措施**：
- 自动化部署脚本中增加数据库导入完成检测（等待 + 超时提示）
- 增加定时检测脚本监测各服务进程（特别是 nacos）
- 检测脚本增加对磁盘空间、删除文件未释放的检测

---

### 场景二：设备终端问题——门口屏/无纸化/桌牌（历史占比 ≈31%）

**现象特征**：门口屏不显示会议、签到失败、绑定失败、同步异常、闪退

**排查心法**：设备端问题通常是「版本/配置/MQTT 连通」三板斧。

**标准排查链路：**
1. **先看版本** → 门口屏 apk/pak 版本是否最新？旧版本有已知 bug（如无默认 MQTT 账号密码、人脸同步逻辑已变更）
2. **看设备日志** → 门口屏 / 无纸化日志是关键，通常直接指向问题：
   - 提示"授权码不存在" → 数据库被重建，授权码变化
   - 提示"无效 token" / "JWT 校验失败" → 授权码绑定的是旧服务器
   - 提示"ip 无权限" → 出口 IP 不在企业微信可信域名
   - 提示"占位错误 404" → 版本不匹配
3. **MQTT 连通** → 设备通过 MQTT 与服务器通信：
   - 门口屏程序是否有默认 MQTT 账号密码？（旧版本没有 → 升级 pak）
   - EMQX 容器是否正常？`docker ps | grep uemqx`
   - MQTT 主题权限是否正确？
4. **重启清缓存** → 更新包后**必须重启**设备应用，否则旧数据仍在内存
5. **人脸/签到相关**：
   - 老版本门口屏同步所有人脸 → 升级后需**清空人脸数据重新同步**
   - 白名单功能是否已开启并授权

**典型根因 Top 5：**
| 根因 | 现象 | 处置 |
|------|------|------|
| 门口屏程序没有默认 MQTT 账号密码 | MQTT 连接错误 / 绑定失败 | 升级 pak（新版本已加默认凭据） |
| 版本更新后没重启设备 | 更新包后仍显示旧数据 | 重启门口屏 APP |
| 人脸数据未清空 | 版本升级后所有人脸都能签到 | 清空人脸库重新同步 |
| 授权码变化 | 老设备显示无效 token | 升级无纸化程序重新绑定新授权码 |
| 出口 IP 变更 | 门口屏获取信息失败，提示 IP 无权限 | 重新配置企业微信可信域名 |

**预防措施**：
- 门口屏最新版本默认内置 MQTT 账号密码
- 更新包后强制提示重启
- 服务自检脚本补充安卓端自检

---

### 场景二补充：安卓信息界面问题（WebSocket / 操作不生效）

**现象特征**：安卓信息界面点击无响应、wss 连接不上、刷新设备失败、设备不显示、操作后设备没反应

**排查三板斧：wss 连接 → config 配置 → 版本兼容**

**标准排查链路：**

1. **先看 Console（最快定位）** → F12 Console 面板看 wss 连接报错
   - 如果 WebSocket 连接失败 → 99% 是 config 配置中的 IP/端口不对
   - 如果 wss 正常但操作不生效 → 跳到第 3 步

2. **配置文件逐项排查（改 IP 后必查）**：
   - 前端 `config.js` 中 `mqttSetting` 配置项 — IP/端口/路径是否指向正确服务器
   - 后端 `config.json` 中 MQTT 相关配置 — **修改 IP 脚本可能漏了这里**
   - ⚠️ 常见坑：用脚本修改 IP 后，后端 config.json 的 MQTT IP 仍是旧地址 → 操作请求发到旧服务器

3. **刷新设备/操作不生效**：
   - 门口屏 4.0：web 端向设备主动发起刷新，N 个终端 = N 次并发 → Java 连接通道可能断开 → 报 `Client is not connected`
   - 门口屏 5.0：改为设备主动上报 → 升级可解决
   - 临时处理：判断版本号 ≥5 时，刷新接口直接返回"无需刷新"

4. **设备不显示/筛选不到**：
   - 旧版前端包未限制设备类型筛选 → 更新
   - 门口屏需"门口屏"标识才能被筛选 → 后端兼容空字段
   - 更新前端包后模块消失 → 版本不兼容需恢复

5. **签名/时区**：
   - 签名校验未关闭 → 重新打包
   - 门口屏时间显示不对 → 后端兼容上报时区

**典型根因速查：**

| 根因 | 现象 | 处置 |
|------|------|------|
| config.js 中 mqttSetting IP 错误 | 点击无响应 / wss 连接报错 | F12 Console 确认 → 修改 config.js |
| 后端 config.json 的 MQTT IP 未更新 | 修改 IP 后安卓信息操作仍失败 | 修改 config.json 的 MQTT 配置项 |
| 门口屏 4.0 web 端并发刷新 | 刷新设备接口报 Client is not connected | 升级门口屏 5.0（设备主动上报） |
| 签名校验未关闭 | 安卓信息页上报日志报错 | 重新打包关闭签名 |
| 后端未转换上报时区 | 安卓信息界面门口屏时间不正确 | 后端代码兼容上报时区 |

**预防措施**：
- 修改 IP 脚本需同时覆盖前端 config.js 和后端 config.json 的 MQTT 配置
- 门口屏 5.0 设备主动上报替代 web 端刷新
- 发布版本前确认签名校验已关闭

---

### 场景三：配置文件/参数问题（历史占比 ≈28%）

**现象特征**：改了配置不生效、某个功能开关关闭后仍执行、参数值不对、URL 指向错误

**排查步骤：**
1. **确认改的是生效文件** → 多份副本（挂载卷/模板/容器内），确认是服务实际加载的
2. **全文搜索同名配置项（核心坑）** → `grep -n "配置项名" <文件>`
   - 同文件两个相同 key → **后一个覆盖前一个**
   - 改了前面没改后面 = 改了等于没改
3. **配置文件缺失** → 新版本代码需要新配置项，旧配置文件没有 → 补上
4. **配置字段名错误** → config.js 中字段名拼错 / 与代码中不一致

**实际案例速查：**
| 现象 | 根因 | 处置 |
|------|------|------|
| 后端 URL 指向错误 IP（如 193） | 配置文件缺少 `ubains.inner.server.url` 配置项 | 补充配置项 + 重启 |
| 关闭土建推送配置后仍推送 | 配置项代码中没有实际判断 | 后端增加配置项判断 |
| 信息播放系统终端在线数不一致 | 前端连 MQTT 端口、后端连 upython 端口，配置不同 | 统一配置端口 |
| 法语开关不生效 | config.js 中字段名写错 | 修正字段名 |
| 短信发送失败 | 数据库同时存在加密/不加密两条配置 | 删除不加密的那条 |

**铁律**：改配置项前先 `grep -n` 全文扫一遍，确认该项只出现一次。出现多次就是这个坑。

---

### 场景四：前端/UI 显示问题（历史占比 ≈26%）

**现象特征**：页面报错、显示异常、响应不生效、浏览器特定表现

**排查步骤：**
1. **F12 Console** → 看 JS 报错，通常直接指向问题
2. **浏览器差异** → Edge/Chrome 正常但 360 浏览器异常 = 代码使用了浏览器特有 API（如只能关闭代码打开的窗口）
3. **屏幕适配** → 小屏幕显示不全 → 增加响应式判断
4. **Vue 数据流** → prop 直接修改报错 → 在 data 中新建变量接收
5. **接口返回数据量** → 页面卡顿/崩溃 → 检查接口返回大小：
   - 启用 gzip 压缩：`server.compression.enabled=true`
   - 减少返回字段 / 分页
   - 考虑引入 Redis 缓存减少数据库交互

**典型根因：**
| 根因 | 现象 | 处置 |
|------|------|------|
| 接口返回数据太大（>4MB） | 页面卡顿/崩溃，无报错 | 启用 gzip 压缩 + 减少返回 |
| Vue prop 直接修改 | eslint 报错，页面异常 | data 中新建变量 |
| 浏览器安全策略 | 360 浏览器无法关闭窗口 | 改为打开新窗口 |
| CSS 适配问题 | 小屏幕显示不全 | 增加响应式判断 |

---

### 场景五：时区/时间问题（历史占比 ≈6%）

**现象特征**：会议时间偏差 8 小时、提示"开始时间不能小于当前时间"、时间显示不正确

**排查步骤：**
1. **服务器时区** → `date` 和 `timedatectl` 确认
2. **容器时区** → 容器内 `date`，确认是否映射了 `/etc/localtime`
3. **Python 容器时区** → 最简操作系统版可能缺少 `/usr/share/zoneinfo`，python 无法使用正确时区
4. **Java 启动参数** → 检查 run.sh 中是否有 `-Duser.timezone=` 参数
5. **数据库时区** → MySQL 时区设置

**处置：**
- 容器映射时区文件 + 拷贝 `/usr/share/zoneinfo` 目录
- Java 启动参数指定时区：`-Duser.timezone=Europe/Madrid`（海外项目）
- 程序默认读取容器环境时区（长远方案）

---

### 场景六：第三方系统对接问题（SMC/华为/腾讯/钉钉/Exchange）（历史占比 ≈13%）

**现象特征**：SMC 会议创建失败、腾讯会议呼叫失败、Exchange 会议不同步、钉钉集成异常

**排查步骤（通用流程）：**
1. **先隔离** → 直接在第三方平台创建/操作是否正常？（排除第三方自身问题）
2. **查配置** → SMC 服务区配置、腾讯会议 API 配置、Exchange EWS 配置是否正确
3. **查日志** → 第三方接口调用日志，看请求参数和响应
4. **查文档** → 第三方配置部署文档，逐项核对

**SMC 专项：**
| 现象 | 根因 | 处置 |
|------|------|------|
| 创建 SMC 会议提示"资源不足" | 会议室未分配终端时 SMC 服务区指向 default（不可用） | 后台编辑会议室，添加服务区为"111"的会场 |
| 腾讯会议呼叫失败 | SMC 上腾讯会议配置不正确 | 修改 SC 管理配置中的区域管理和查找规则 |
| SMC 签名校验报错 | 签名校验未关闭 | 重新打包关闭签名 |

**Exchange 专项：**
- Exchange 会议不同步 → 查同步日志 → 常见：账号密码错误、会议室邮箱绑定不对
- 查 EWS 配置和会议室邮箱绑定是否正确

---

### 场景七：邮件/短信通知问题（历史占比 ≈4%）

**现象特征**：预约会议后收不到邮件/短信通知、邮箱通知正常但短信不行

**排查步骤：**
1. **确认日志** → 后端日志是否打印了"发送成功"——打印了但收不到 = 第三方通道问题
2. **邮件**：检查 Exchange/邮件服务器配置
3. **短信**：
   - 阿里云短信：确认加密/不加密配置，数据库不能同时存在两条
   - 华为云短信：确认是否已对接
   - 腾讯会议短信：需绑定手机号才能发送

---

### 场景八：授权/激活问题（历史占比 ≈8%）

**现象特征**：激活码失效、token 无效、单点登录失败、证书过期

**排查步骤：**
1. **激活码变化** → 服务器重装/磁盘更换后激活码变化，需重新激活
2. **证书** → 单点登录失败查证书是否过期 → 更新证书
3. **JWT 校验** → 旧设备绑定旧授权码，新部署后授权码变化 → 设备端报无效 token → 重新绑定
4. **ARM 架构** → 授权需要手动分发：license.zip 上传后需手动解压 .uas 到各服务 license.txt

---

## Selenium / UI 自动化失败

- **元素找不到 / 超时**：先 F12 确认定位器是否仍有效（UI 改版）；CSS 优先于 XPath（避免国际化）；查隐式等待是否够；多窗口场景查 window handle 切换
- **浏览器没起来**：ChromeDriver 版本与 Chrome 不匹配（最常见）→ 手动指定 driver 路径（见 `预定系统/Base/base.py:88`）
- **偶发失败**：网络慢/页面未加载完 → 加显式等待，别只靠隐式

## MQTT / EMQX 问题

**EMQX 排查标准流程（5.46 实战案例）：**

1. **容器状态检查**：`docker ps -a --filter name=uemqx`，看是否 `Exited`
2. **退出原因分析**：
   - 退出码 0 + 日志有 `received_terminate_signal` → **外部停止**（`docker stop` 或脚本），非服务崩溃
   - 退出码非 0 / OOMKilled true → 服务异常或内存溢出
   - 日志无 terminate_signal 直接消失 → 容器被 kill -9 或系统强制终止
3. **端口监听检查**：`ss -tln | grep 1883`（宿主机侧，**勿在容器内用 netstat**，EMQX 5.x 镜像无 netstat 会误判）
4. **服务状态验证**：`docker exec uemqx emqx ctl status` 或 `emqx_ctl status`
5. **连接测试**：`bash -c 'exec 3<>"/dev/tcp/127.0.0.1/1883"'` 测试 TCP 握手
6. **断电损坏场景**：直接断电可能导致 EMQX 容器文件损坏 → 日志无异常但连不上 → 重建 EMQX 容器

**常见问题**：
- **broker 连不上**：先 `nc -z <ip> 1883` 探端口；查 uemqx 容器状态；查认证（username/password）
- **消息收不到**：topic 是否一致（订阅/发布）；payload 结构；QoS；订阅端是否先于发布启动
- **监控脚本误判**：5.46 曾因监控脚本在容器内用 netstat 检测端口，EMQX 5.x 无 netstat 导致健康容器被误 stop
- **MQTT 连接报 `Client is not connected`**：门口屏刷新接口并发量过大时 Java 连接通道断开 → 升级门口屏 5.0（设备主动上报替代主动刷新）

**MQTT 认证失败排查（5.46 实战案例：`bad_username_or_password`）：**

当 MQTT 客户端连接 1883 返回认证失败时，按以下步骤排查：
1. **确认容器存活**：`docker ps --filter name=uemqx`，容器 Exited 则先 `docker start uemqx`
2. **查日志确认是认证问题**：`docker logs --tail 50 uemqx | grep authentication_failure`，看到 `bad_username_or_password` 即认证失败（非网络/端口问题）
3. **查 bootstrap 凭据文件**：`docker exec uemqx cat /opt/emqx/etc/auth-built-in-db-bootstrap.csv`，输出格式 `user_id,password,is_superuser`
4. **对比用户输入的密码与 bootstrap 文件中的密码**：常见坑是字符混淆（`O`字母 vs `0`数字、`l`字母 vs `1`数字、`I`大写i vs `l`小写L）
5. **确认认证链配置**：`docker exec uemqx emqx ctl conf show authentication`，确认 `backend = built_in_database` 且 `enable = true`

**已知 EMQX 业务账号凭据（5.46 实测）：**
| 用户名 | 密码 | 是否超级用户 |
|--------|------|-------------|
| `mqtt@cmdb` | `mqtt@webpassw0RD` | TRUE |
| `mqtt@client` | `client@passwORD` | FALSE |

> ⚠️ **注意**：`mqtt@client` 密码中 `ORD` 是**大写字母 O**，不是数字 0。这是 2026-06-29 排查确认的，勿再踩坑。

**MQTT 授权/ACL 权限排查（5.46 实战案例：用户能订阅"未授权"主题）：**

当用户能订阅/发布"不应该有权限"的主题时，按以下步骤排查：
1. **查授权配置来源**：`docker exec uemqx emqx ctl conf show authorization`，确认 `sources` 列表（file / built_in_database / http 等）
2. **查 ACL 文件规则**：`docker exec uemqx cat /opt/emqx/etc/acl.conf`，逐条检查是否有匹配该用户的 allow 规则
3. **理解规则匹配逻辑**：
   - EMQX ACL 规则**从上到下顺序匹配**，先匹配到的生效
   - `{allow, {username, "xxx"}, all, ["topic/#"]}` 表示允许该用户订阅+发布该主题
   - `{allow, all, all, ["topic/#"]}` 表示允许**所有用户**订阅+发布该主题（常被忽略）
   - `{deny, all}` 是默认拒绝规则，但放在它前面的 allow 规则已经生效则不会走到 deny
4. **检查"允许所有用户"规则**：`acl.conf` 中常有 `{allow, all, all, ["xxx/#"]}` 规则，会让所有用户都能访问该主题，即使没有单独授权
5. **检查超级用户**：`mqtt@cmdb` 是超级用户（`is_superuser=TRUE`），超级用户默认绕过 ACL 检查

**5.46 acl.conf 已授权规则速查（2026-06-29 实测）：**

| 用户 | 权限类型 | 主题 |
|------|---------|------|
| `mqtt@cmdb` | all（订阅+发布） | `ubains/#`, `/maintain_enc/room/#`, `/maintain/room/#`, `/ideatop/#`, `/meeting/#`, `/office/#`, `/wecom/#`, `/system/#`, `/material/client/#`, ... |
| `mqtt@cmdb` | all（订阅+发布） | `/iot/v1/#`, `/topic/qos0`, `/topic2/qos0`, `/MqttTest/`, `/androidMqtt/`, `/androidPanel/`, `/message/meetingAssistant/#`, ... |
| `mqtt@client` | publish | `/maintain_enc/room/master/device/#`, `/ideatop/#`, `/iot/v1/#`, `/topic/qos0`, `/topic2/qos0`, `/MqttTest/` |
| `mqtt@client` | subscribe | `ubains/#`, `/maintain_enc/room/#`, `/ideatop/#`, `/iot/v1/#`, `/topic/qos0`, `/topic2/qos0`, `/MqttTest/` |
| **所有用户** | all | `test/#`, `/maintain/room/#`, `/meeting/#`, `/iot/v1/#`, `/topic/qos0`, `/topic2/qos0`, `/MqttTest/`, `/c1_100_wifi_u/#`, `message/#` |

> ⚠️ **关键**：`mqtt@cmdb` 在 `acl.conf` 第 36 行已被明确授权 `/ideatop/#`，勿认为"未授权"。且 `mqtt@cmdb` 是超级用户（`is_superuser=TRUE`），默认绕过 ACL 检查。

**超级用户绕过 ACL 检查（5.46 实战案例：`mqtt@cmdb` 订阅无规则的 `/yunweitest`）：**

当用户能订阅/发布 ACL 中无明确授权的主题时，优先检查：
1. **查 is_superuser 字段**：`docker exec uemqx cat /opt/emqx/etc/auth-built-in-db-bootstrap.csv | grep <用户>`，`TRUE` 表示超级用户
2. **超级用户特性**：`is_superuser=TRUE` 的用户**绕过所有 ACL 检查**，可订阅/发布任意主题
3. **查 no_match 策略**：`docker exec uemqx emqx ctl conf show authorization | grep no_match`：
   - `no_match = allow` → 无匹配规则时默认允许（放行）
   - `no_match = deny` → 无匹配规则时默认拒绝（拦截）

**5.46 超级用户确认（2026-06-29 实测）：**
- `mqtt@cmdb` → `is_superuser=TRUE` → **绕过 ACL，可订阅/发布任意主题**
- `mqtt@client` → `is_superuser=FALSE` → 受 ACL 限制
- `authorization.no_match = allow` → 无规则匹配时默认放行（次要因素）

**工具**：`Base/Mqtt_Send.py`、`Mqtt_Universal_Tool`

## Appium 移动端（展厅巡检）

- **ADB 掉线**：`adb connect <ip>:5555` 重连；网络抖动常见，操作后自动断开是有意设计
- **appium 没响应**：确认 appium server 在跑；确认 device 在 `adb devices`

## 远程服务 / 容器异常

- 容器 down：`docker ps -a` 看状态 → `docker logs --tail 100 <容器>` 看退因
- 端口不通：容器内 `ss -tln` + 宿主 `nc -z`；注意 host 网络模式（uredis/utracker）
- 服务 500 / 预定失败：查业务容器日志；**注意 ARM Java 启动慢（约 12 分钟），勿因慢判挂**
- 中间件：直接用 `/MiddleVerify 只读` 验证六个 u 前缀中间件，确认真失败再修
- **服务器重启后服务没自动启动**：
  - 查 `rc.local` 权限和配置是否生效
  - 增加定时检测任务（每 2 分钟检测一次，掉线自启动）作为兜底

## 部署 / 升级失败

- **SSH 登录失败**：9.76 等 root 被禁，实际 **admin/Ubains@123 + sudo**（密码同）
- **解压 / 传输中断**：**严禁中途打断解压**，断了重来；网络不稳先确认带宽
- **升级后服务没更新**：`/UPDATE-SERVICES` 有 upython 子串误匹配坑 —— 涉及 cmdb 要手动补重启 upython 容器
- **ARM Nacos 反复崩溃**：cron 监控脚本竞态，查 cron 而非 nacos 本身
- **数据库导入慢导致启动失败**：磁盘 IO 爆满时数据库导入慢（5-6 秒/张表），其他服务先于数据库就绪 → 设置等待时间 + 超时提示
- **自动化部署脚本**：
  - 旧脚本可能含已废弃配置（如旧版 ntp）→ 屏蔽
  - 部署包缺少 chronyd.conf 的 `allow all` 配置 → NTP 同步失败
- **升级 openssh 后系统重启**：新版 openssh 与系统内核不兼容（如 10.2p2 在统信 1070e）→ 回退到官方版本
- **数据库密码修改**：等保测评改密码后通过脚本修改可能漏掉某些用户（如 mysql@172）→ 输出一键修改脚本覆盖所有系统

## PowerShell 脚本（AuxiliaryTool）

- 中文乱码：控制台默认 GBK，入口强制 `$OutputEncoding` / UTF-8
- 路径/引号：bash 里调 Windows 程序注意 MSYS 路径转换，用 `cmd.exe /c` + `MSYS_NO_PATHCONV=1`

## 配置文件 / 现场问题（改了配置不生效）

**典型现象**：修改了配置文件中的某项配置，重启/重载后**不生效**。

**排查标准流程：**

1. **先确认改的是生效文件**：很多配置文件有"挂载卷/模板/容器内"多份副本，改的可能不是服务实际加载的那份。确认路径是否是服务真正读取的（`docker inspect` 挂载、进程启动参数指向的路径）
2. **全文搜索同名配置项（核心坑）**：用 `grep -n "配置项名"` 列出**所有**匹配行，看是否存在**两个或多个相同 key**。多数配置加载器（YAML/properties/ini）遇到重复 key 时，**后一个覆盖前一个**：
   ```bash
   grep -n "^spring.datasource.url" application.yml   # 列全部，看行号
   grep -n "配置项名" /path/to/config | cat -n        # 计数 + 行号定位
   ```
3. **判断覆盖关系**：如果存在重复 key，确认服务实际生效的是**最后一个**。你改了前面的、没改后面的 → 被后面的值覆盖 → 改了等于没改
4. **处理方式（二选一）**：
   - ✅ 推荐：**删掉重复的 key**，只保留一份（最干净，杜绝再次踩坑）
   - 或者：把**所有**重复项都改成目标值（保证覆盖前后一致）
5. **改完复验**：重启/重载服务，确认配置生效（`actuator/env`、日志打印、功能验证）；再次 `grep` 确认无残留重复
6. **新增场景：配置项代码中未判断**：界面有关闭开关但代码中未读取该配置项 → 配置项不生效 → 后端增加判断逻辑

**为什么会被覆盖：**
- 配置合并顺序：命令行参数 > 环境变量 > application.yml > application-xxx.yml > 默认值
- 同文件内重复 key：后定义者胜出（last-write-wins）
- Spring Boot 多 profile 激活时，profile 配置覆盖主配置

**关联场景**：改达梦/数据库连接、改端口、改日志级别、改超时时间等"明明改了却不生效"，优先怀疑重复配置覆盖。

> ⚠️ **铁律**：改配置项前先 `grep -n` 全文扫一遍，确认该项只出现一次。出现多次就是这个坑。

---

### 场景九：人脸/签到问题（历史占比 ≈10%）

**现象特征**：人脸同步失败、签到无反应、所有人可签到、人脸特征生成异常

**排查步骤：**
1. **确认门口屏版本和人脸同步策略**：
   - 旧版：同步所有人脸 → 升级后需清空人脸库重新同步
   - 门口屏 5.0：只同步参会人人脸 → 无会议时无法人脸开门（需开启白名单）
2. **人脸同步失败**：查用户表特征和 URL 是否为空、用户角色是否满足同步条件（`r.role_type in ('admin','attendant','attendantMonitor')`）、区域 ID 是否匹配
3. **人脸签到不生效**：门口屏时间不对 → 会议和人脸未及时同步 → 配置 NTP
4. **ARM 架构**：人脸服务激活失败不影响主流程 → 关闭配置项即可

| 根因 | 现象 | 处置 |
|------|------|------|
| 旧人脸数据未清空 | 升级后所有人脸都能签到 | 清空人脸库重新同步 |
| 门口屏 5.0 只同步参会人 | 无会议时无法人脸开门 | 开启白名单功能 |
| 区域 ID 变更 | 人脸同步到错误门ロ屏 | 核对 ubains.auth.area 配置 |
| ARM 人脸服务激活失败 | 预定启动失败（如果配置了人脸） | 关闭人脸激活配置 |

---

### 场景十：数据同步问题（OA/飞书/钉钉/企业微信/Welink）（历史占比 ≈6%）

**现象特征**：第三方数据不同步、同步数据不一致、同步数量不对

**排查步骤：**
1. **查同步日志**：`grep "同步"` / `grep "封装后的用户数量"` / `grep "会议详情"` — 日志不够先补充
2. **curl 直调验证**：将接口参数转成 curl 在服务器上直接调用第三方接口 → 确认第三方是否正常返回
3. **对比接口返回**：会议列表 vs 会议详情返回是否一致（不一致以列表为准）
4. **常见根因**：接口权限不足 → 部分部门/用户查不到；手机号重复 → 同步失败；第三方自身故障；解密不一致

| 根因 | 现象 | 处置 |
|------|------|------|
| OA 接口权限不足 | 部分会议不同步 | 扩大 key 权限 + curl 直调验证 |
| 详情接口返回时间错误 | 会议时间与 OA 不一致 | 改用列表接口返回值 |
| 手机号重复 | 用户同步数量对不上 | 确认唯一标识字段 |
| 飞书拒绝日程不发通知 | 取消后预定仍显示 | 增加定时轮询对比 |

---

### 场景十一：安全漏洞/等保整改（历史占比 ≈5%）

**现象特征**：安全扫描报告漏洞、等保测评要求整改、弱密码、未授权访问

**排查步骤：**
1. **Nacos 未授权访问**：开启 `nacos.core.auth.enabled=true`，密钥 ≥32 位；开启后其他服务也需配账号密码
2. **EMQX 弱密码/RCE**：升级版本 + 开启认证
3. **MQTT 无密码访问**：开启认证配置 + 重建 EMQX 容器
4. **数据库改密**：等保测评后需覆盖所有系统（预定/运维/新统一平台/统一平台），输出一键改密脚本
5. **openssh 漏洞**：升级前确认内核兼容性（新版可能与统信不兼容 → 回退）

| 根因 | 现象 | 处置 |
|------|------|------|
| Nacos 未开启安全校验 | 未授权访问漏洞 | 开启认证 + 密钥 ≥32 位 |
| EMQX 版本过低 | 弱密码 / RCE 漏洞 | 升级 + 开启认证 |
| 等保改密脚本漏改 | 部分服务连不上数据库 | 输出全系统一键改密脚本 |
| openssh 与内核不兼容 | 升级后系统重启 | 回退官方版本 |

---

### 场景十二：版本兼容问题（历史占比 ≈3%）

**现象特征**：升级后功能异常、新旧版本混用、回退版本后问题仍在

**排查步骤：**
1. **确认所有组件版本**：前端包 / 后端包 / 门口屏 apk / pak / 容器镜像是否匹配
2. **常见不兼容**：后端包新但门口屏旧（时间格式不匹配）、MySQL 5.x vs 8.x、旧数据库缺少表/字段、加密文件版本不一致
3. **回退操作**：记录当前版本号 → 回退到已知正常版本 → 验证核心功能

| 根因 | 现象 | 处置 |
|------|------|------|
| 后端时间格式与门口屏 2.0 不兼容 | 更新后端后门口屏不显示 | 后端向下兼容时间戳格式 |
| 旧后端包用 MySQL 5.x | 连接 MySQL 8.x 失败 | 使用匹配的容器镜像 |
| 加密文件版本不一致 | 超管登录失败 | 更新加密文件 |
| 现场只部署了基础版本 | 功能不全 / 已修复 bug 仍存在 | 更新到已测试版本 |

---

## 常用排查命令速查

## 常用排查命令速查

```bash
# 远程（经 SSH，9.76 用 admin+sudo）
docker ps -a                              # 容器状态（含已退出）
docker logs --tail 100 <容器>             # 最近日志
docker inspect <容器> | grep -i status    # 存活/重启次数
ss -tln                                   # 端口监听
nc -z <ip> <port>                         # 端口连通
curl -sk https://<ip>/健康检查            # HTTP 健康
free -h ; df -h                           # 内存/磁盘（OOM、磁盘满常是根因）
ps -ef | grep <关键字>                    # 进程
du -sh /* 2>/dev/null | sort -rh | head -10  # 磁盘空间大户
lsof | grep deleted                       # 删除但未释放的文件（磁盘满隐藏原因）
date ; timedatectl                        # 时区检查
docker exec <容器> date                   # 容器内时间
docker exec <容器> curl -s uredis:6379    # 容器间 Redis 连通性
docker exec <容器> curl -s umysql:3306    # 容器间 MySQL 连通性
cat /etc/rc.local                         # 开机自启脚本
systemctl list-unit-files | grep enabled  # 开机自启服务
```

## 已知坑 / 假异常速查（动手前先对照）

| 现象 | 真相 | 处置 |
|------|------|------|
| nacos 控制台登录 user not found | 部署改密破坏认证链，**业务面正常** | 勿 restart，属部署债 |
| ARM ujava2 启动后预定 500 / 未就绪 | Java 启动约 12 分钟 | 再等 2-3 分钟，勿判授权失败 |
| ARM nacos 反复被杀 | cron 监控竞态 | 查 cron，非 nacos 故障 |
| 升级 cmdb 后 upython 服务没更新 | upython 子串误匹配到 upython_voice | 手动补 restart cmdb 的 upython |
| 9.76 SSH root Authentication failed | root 被禁 | 改用 admin + sudo |
| uemqx dashboard Admin@2026 登录失败 | 实际是默认 admin/public | 用实测凭据 |
| ARM 讯飞转录接口失败 | 架构未适配，**预期失败** | 勿当 bug 修 |
| **5.46 uemqx 容器 Exited 退出码 0** | **外部 docker stop 或脚本停止，非服务崩溃** | `docker start uemqx`，排查停止来源 |
| **EMQX 监控脚本误判容器异常** | **EMQX 5.x 镜像无 netstat，容器内端口检测永远失败** | 端口检测改宿主机侧 ss/netstat /dev/tcp |
| **MQTT 认证失败 bad_username_or_password** | **密码混淆：`O`字母 vs `0`数字 等；查 bootstrap 文件对照** | 查 `/opt/emqx/etc/auth-built-in-db-bootstrap.csv` 确认正确密码 |
| **MQTT 用户能订阅"未授权"主题** | **acl.conf 中已有 allow 规则，或超级用户绕过 ACL** | 查 `acl.conf` 逐条对照，查 `is_superuser` 字段 |
| **MQTT 用户能订阅无 ACL 规则的主题** | **超级用户绕过 ACL，或 `no_match=allow` 默认放行** | 查 `is_superuser=TRUE` 和 `authorization.no_match` |
| **改了配置项不生效** | **同文件存在两个相同 key，后一个覆盖前一个；改了前面的没改后面的** | `grep -n "key"` 全文确认无重复，删重复项或全部改齐 |
| **关闭配置项开关仍执行** | **代码中未读取/判断该配置项** | 后端增加配置项判断逻辑 |
| **数据库导入后服务启动失败** | **磁盘 IO 差，数据库 5-6 秒/表，服务先于数据库完全导入就绪** | 脚本增加等待时间 + 超时提示 |
| **服务器重启后服务没启动** | **rc.local 不生效 / 服务缺少自启机制** | 增加定时检测脚本（每 N 分钟检测 + 自启动） |
| **门口屏绑定 MQTT 失败** | **旧版门口屏程序没有默认 MQTT 账号密码** | 升级门口屏 pak（新版已内置默认凭据） |
| **更新门口屏包后仍显示旧数据** | **更新后没有重启设备应用** | 更新后强制重启 APP |
| **人脸签到所有人可开** | **升级门口屏版本后未清空旧人脸数据重新同步** | 清空人脸库 → 重新同步 |
| **旧设备报无效 token / JWT 校验失败** | **服务器重装/换盘后授权码变化，设备绑定旧授权码** | 重新绑定新授权码 |
| **服务日志显示 mysql 连接失败** | **数据库未完全就绪 / 密码错误 / 网口变更后 docker 网络隔离** | 分场景：等数据库就绪 → 重启 docker → 查密码 |
| **磁盘空间满但 df 看不出** | **删除的文件被进程持有未释放** | `lsof \| grep deleted` → kill 进程 → 重启 |
| **SMC 创建会议提示"资源不足"** | **会议室未分配终端时 SMC 服务区默认指向 default（不可用）** | 后台编辑会议室添加可用服务区 |
| **讯飞容器时区不对（晚 8 小时）** | **精简版容器缺少 /usr/share/zoneinfo** | 拷贝上海时区文件到容器 + 重新制作容器 |
| **openssh 升级后 SSH 连上就重启** | **新版 openssh 与统信 1070e 内核不兼容** | 回退到官方 openssh 版本 |
| **EMQX 容器断电后异常** | **直接断电导致容器文件损坏** | 重建 EMQX 容器；考虑备份机制 |
| **Nacos 启动后登录提示账号密码错误** | **数据库被替换后与 nacos 凭据不一致** | 替换 nacos 和对应数据库 |

→ 细节见下方关联记忆。

## 输出

排查完给一份简短诊断纪要：
- **现象**：一句话
- **根因**：一句话 + 证据（哪条命令/日志证明）
- **处置**：已做 / 建议做（标明是否高危）
- **回归**：复测结果
- 若是新出现的、可能复现的问题 → 写一条记忆，避免下次重复排查

## 问题追踪文档（必须执行）

**每次处理问题都必须更新对应的问题追踪文档**，无论问题是否已完全解决：

- **文档命名规范**：`Docs/PRD/<项目名>/_ISSUE_<IP或标识>_问题追踪.md`
  例如：`Docs/PRD/达梦数据库/_ISSUE_192.168.5.40_问题追踪.md`
- **文档内容至少包含**：
  1. **已完成修复** — 已解决的问题列表，每项含问题描述、根因、修复方案、状态
  2. **待修复问题** — 尚未解决的问题，含阻塞原因和依赖
  3. **环境信息** — 服务器 IP、容器、数据库凭据等
  4. **执行记录** — 每次操作的时间、操作内容、结果（用表格记录）
  5. **下一步操作** — 明确的后续步骤和 SQL/命令
- **更新时机**：每次执行排查/修复操作后**立即更新**，不等到全部完成

## 关联记忆

- `server-976-ssh-admin` — 9.76 用 admin+sudo（root 被禁）
- `server-976-middleware-credentials` — 9.76 实测凭据全集
- `arm-ubuntu-java-startup-12min` — Java 启动慢勿误判
- `arm-nacos-cron-race-condition` — nacos 反复被杀真因
- `update-services-upython-keyword-bug` — cmdb upython 误匹配
- `middleware-verify-tool` — 中间件验证工具（假异常门控）
- `feedback-deploy-no-interrupt` — 部署禁中断解压
- `arm-ubuntu-voice-not-supported` — ARM 讯飞未适配（预期失败）
- `server-546-emqx-monitor-netstat-bug` — 5.46 EMQX 监控脚本 netstat 误判，V2.2 已修复（改宿主机侧检测）
- `server-546-emqx-external-stop` — 5.46 uemqx Exited(0) 系外部 docker stop 非服务崩溃，排查流程：docker ps -a → 退出码 → 日志 received_terminate_signal → 查 cron/手动操作来源 → docker start 恢复 → 三层验证
- `server-546-emqx-mqtt-auth-password-confusion` — 5.46 mqtt@client 认证失败，密码 `client@passwORD`（字母O）勿输成 `client@passw0RD`（数字0），排查：查 bootstrap 文件对照
- `server-546-emqx-acl-authorization-check` — 5.46 mqtt@cmdb 能订阅 `/ideatop/#` 系 acl.conf 第36行已授权，排查：查 authorization.sources → acl.conf → 规则匹配顺序 → 超级用户绕过 ACL
- `server-546-emqx-superuser-bypass-acl` — 5.46 mqtt@cmdb 能订阅无规则的 `/yunweitest` 系 `is_superuser=TRUE` 绕过 ACL，`no_match=allow` 默认放行，排查：查 bootstrap 文件 is_superuser 字段
- `config-duplicate-key-overrides` — 改配置项不生效：同文件存在两个相同 key，后一个覆盖前一个；改了前面没改后面=改了等于没改。铁律：改前 `grep -n` 全文确认只出现一次，重复则删多余项或全部改齐

## 关联文件

- `code/Troubleshoot助手.html` — 交互式问题排查知识库网页工具，可独立在浏览器中打开使用。与 SKILL.md 内容同源（基于 346 条历史问题记录提炼），覆盖 18 类问题分类，支持选分类→填现象→输出排查思路+典型根因+处置方案
- `code/web/` — **Web 排查助手**（新一代）：
  - `server.py` — Flask 后端服务，渐进式两阶段请求（搜索 + AI 分析），默认端口 8088
  - `search_engine.py` — TF-IDF + 关键词匹配搜索引擎，基于 357 条历史问题记录
  - `safety_filter.py` — 三层安全过滤器（危险命令黑名单 + 相关性检查 + 白名单验证）
  - `templates/index.html` — 前端页面，支持项目名称输入 + 系统类型选择 + APK 产品输入 + 问题提交入库
  - `config.json` — 配置文件（Claude API 地址/密钥/模型、系统类型列表）
- `code/xlsx_to_md.py` — Excel→Markdown 知识库转换脚本
- `code/build_index.py` — 搜索索引生成脚本
- `code/start.bat` — Windows 启动脚本

## 后续开发规范

后续如有开发需求或问题处理需求，应在 `Docs/PRD/问题知识库/` 目录下创建对应的文档：

- **需求文档**：`Docs/PRD/问题知识库/PRD_需求文档_<功能名>.md` — 描述功能需求、背景、验收标准
- **计划执行文档**：`Docs/PRD/问题知识库/PRD_计划执行_<功能名>.md` — 描述实施步骤、技术方案、执行记录

所有与 Troubleshoot 技能相关的代码实现应放在 `code/` 目录下。

## Web 排查助手（开发完成 2026-07-11）

Web 排查助手是一个基于 Flask 的 Web 应用，为现场同事提供智能问题排查服务。

### 启动方式

```bash
cd .claude/skills/Troubleshoot/code/web
# 依赖安装（首次）
pip install -r ../requirements.txt
# 启动服务
python server.py
# 访问：http://localhost:8088
# Windows 启动脚本：双击 ../start.bat
```

### 功能架构

```
用户输入（项目名称 + 系统类型 + APK产品 + 问题描述）
       ↓
阶段一：搜索引擎匹配相似案例（< 3 秒返回）
       ↓
阶段二：AI 深度分析生成排查步骤（20-30 秒）
       ↓
安全过滤器（三层过滤确保输出安全）
       ↓
展示排查结果 + 问题入库闭环
```

### API 接口

| 接口 | 方法 | 说明 |
|------|------|------|
| `/` | GET | 主页面 |
| `/api/projects` | GET | 获取项目列表 |
| `/api/search` | POST | 仅搜索匹配案例（阶段一，快速） |
| `/api/analyze` | POST | AI 深度分析（阶段二，较慢） |
| `/api/troubleshoot` | POST | 一键排查（搜索+分析，兼容旧接口） |
| `/api/submit` | POST | 提交问题记录入库 |

### 配置说明

编辑 `code/web/config.json`：
- `claude_api_base`：Claude API 地址
- `claude_api_key`：API 密钥（必填，否则使用 mock 模式）
- `claude_model`：模型名称
