# HANDOFF_服务监测 — 服务监测模块：检测项丰富化移植（阶段三）

> 最后更新：2026-07-22（P2 级移植完成：系统类型识别 + 平台服务检测 + 配置IP检测 + Console配置检测）｜ 分支：troubleshoot-ai-assistant ｜ 负责人：czj
> 专属交接文档：聚焦服务监测模块（service_monitor）的全部历史与当前状态

---

## 1. 我们在做什么

服务监测模块（service_monitor）是运行维护平台的子模块，提供目标服务器巡检、报告管理、定时任务、通知推送等功能。

**已完成轮次**：
- **阶段一**：补全缺失项 + 重构 MySQL/Redis 深度检测脚本（见 2.5 节）
- **阶段二**：移植安全合规深度检测 + 中间件连接检测模块（见 2.7-2.11 节）
- **阶段三（本次）**：移植 P2 级检测模块：系统类型识别 + 平台服务检测 + 配置IP检测 + Console配置检测

本轮会话完成了四个新检测模块的移植：

1. **系统类型识别**（50_system_profile.sh）：识别目标服务器的平台类型、容器类型、ujava 变体
   - 平台类型：new（新统一平台）/ old（传统平台）
   - 容器类型：ujava / upython / upython_voice / unknown
   - ujava 变体：unified / meeting / none

2. **平台服务检测**（51_service_check.sh）：检测核心业务服务进程状态 + 容器端口监听
   - 新平台服务：auth/gateway/system/meeting/mqtt/quartz/message/extapi
   - 传统平台服务：nginx/meeting/extapi
   - upython/upython_voice 端口：8000/11211/8080/1883 等

3. **配置IP检测**（52_config_ip_check.sh）：扫描配置文件中的未授权 IP 地址
   - 扫描路径：服务配置目录 + 前端配置目录 + 中间件配置
   - 允许列表：服务器IP + 127.0.0.1 + 172.17.0.1

4. **Console配置检测**（53_config_console_check.sh）：检测 console=true 配置项未关闭问题
   - 只检测不修复（避免巡检工具自动改生产配置）
   - 发现问题标记为警告

---

## 2. 已经完成了什么

### 2.1 报告免登访问与操作鉴权 ✅

**需求文档**：`Docs/需求文档/服务监测/PRD_需求文档_报告免登访问与操作鉴权.md`

**核心实现**：
- `report_service.py`：报告生成时自动生成 `access_token`（格式 `rpt_<32位随机hex>`）和 `access_token_expires_at`（7天有效期）
- `runner_service.py`：通知链接拼接 `?token=rpt_xxx` 参数
- `routes.py`：`page_report()` 支持 token 验证，验证通过后渲染报告页并标记 `token_auth=True`（访客模式）；`api_export_report()` 支持 token 导出
- `auth.py`：GET/POST `/login` 支持 `next` 参数，登录成功后跳转回原始页面
- `decorators.py`：`page_login_required` 重定向时携带 `next` 参数
- `login.html`：表单提交携带 `next` 参数，登录成功后跳转
- `report.html`：访客模式显示提示条、隐藏管理操作、导出链接携带 token
- `base.html`：访客模式下导航菜单点击跳转登录页，顶部显示"访客 + 登录按钮"

**验证结果**：
- 未登录点击通知链接 → 直接查看报告 ✅
- 访客模式点击其他模块 → 弹窗提示并跳转登录 ✅
- 登录后回跳到原始页面 ✅
- 218 个单元测试全绿 ✅

### 2.2 监测统计模块 ✅

**需求文档**：`Docs/需求文档/服务监测/PRD_需求文档_监测统计模块.md`

**核心实现**：
- `statistics_service.py`（新增）：统计聚合服务，包含 5 个公开函数：
  - `get_overview()`：概览摘要（总报告数、异常率、最常异常项等）
  - `get_module_stats()`：模块健康统计
  - `get_item_trend()`：检测项趋势数据（时间序列 + 状态分布）
  - `get_abnormal_items()`：高频异常检测项 TOP 10
  - `get_item_keys_for_target()`：检测项列表（按模块分组，含 `has_numeric` 标记）
- `routes.py`：新增页面路由 `/service-monitor/statistics` + 5 个 API 端点
- `base.html`：侧边栏添加"📈 监测统计"菜单项
- `statistics.html`（新增）：完整统计页面，使用 ECharts 渲染图表
  - 筛选栏：目标选择器 + 日期范围（自定义）
  - 概览卡片：4 项摘要指标
  - 模块健康概览：堆叠条形图
  - 检测项趋势分析：模块/检测项两级联动选择器 + 折线图 + 饼图
  - 高频异常检测项：水平堆叠条形图

**验证结果**：
- 统计页面正常加载 ✅
- 各 API 返回正确数据结构 ✅
- 图表正常渲染 ✅
- 218 个单元测试全绿 ✅

### 2.3 检测项趋势图表显示问题修复 ✅

**问题文档**：`Docs/需求文档/服务监测/PRD_问题处理_检测项趋势图表显示问题.md`

**问题根因**：
- 部分检测项的值是状态文本（"正常"/"异常"）、yes/no、路径等，`_extract_numeric()` 无法提取数值
- 前端收到 `value_num=null` 后折线图无数据点，但未显示任何提示

**修复内容**：
- `statistics_service.py`：`get_item_keys_for_target()` 增加 `has_numeric` 字段，标记检测项是否有数值数据
- `statistics.html`：`fetchItemTrend()` 增加三种情况处理：
  - 无数据点：显示"暂无数据"
  - 有数据点但无数值：显示友好提示"该检测项为状态/文本类型，无法绘制数值趋势图，请查看右侧状态分布"，饼图正常渲染
  - 有数值数据：正常渲染折线图 + 饼图
- 检测项选择器标注：无数值能力的检测项标注 "(状态)" 后缀

**验证结果**：
- 选择 `CPU_USAGE` 等数值项，折线图正常 ✅
- 选择 `CPU_STATUS` 等状态项，显示友好提示 + 饼图正常 ✅
- 选择器中状态项标注 "(状态)" ✅

### 2.4 JavaScript 代码残留问题修复 ✅

**问题文档**：`Docs/需求文档/服务监测/PRD_问题处理_统计页面JS错误.md`

**问题根因**：
- 编辑 `fetchItemKeys` 函数时，旧代码尾部 4 行未被删除，残留在新函数之后
- 导致 `onModuleChange`、`fetchItemTrend`、`fetchAllData` 等函数被错误嵌套，全局作用域无法访问 `fetchAllData`

**修复内容**：
- 删除第 330-333 行、第 492-494 行的残留代码
- 补回被误删的 `onModuleChange` 函数体

**验证结果**：
- 花括号平衡（124 对）✅
- 9 个关键函数全部存在 ✅
- 查询按钮点击正常工作 ✅

### 2.5 检测项丰富化（参考目录移植 - 阶段一）✅

**需求文档**：`Docs/需求文档/服务监测/PRD_需求文档_检测项丰富化移植.md`

**参考目录**：`platform-auto-test/临时目录/服务监测/`（老版 PowerShell + Shell 巡检工具）

**已完成内容**：

**1. display_names.py 补全缺失 KEY** ✅
- 新增 `VM_PGMajFAULT`（主缺页错误数）
- 原有 `DOCKER_DAEMON_CPU/MEM`、`NGINX_CONTAINER_LEVEL`、`NACOS_CONTAINER_LEVEL` 已存在

**2. 重构 23_mysql_depth.sh** ✅
- 从 `echo "KEY:value"` 改为 `output_result "KEY" "value"` 标准格式
- KEY 加 `MYSQL_DEPTH_` 前缀，display_names.py 新增 30 个 KEY
- 去掉硬编码密码，使用 `MYSQL_PASSWORD` 环境变量
- 容器名使用 `CONTAINERS[mysql]` 模糊匹配
- thresholds.py 新增 7 个深度阈值（连接使用率/慢查询/缓冲池命中率/主从延迟/死锁/锁等待/碎片表）

**3. 重构 25_redis_depth.sh** ✅
- 从 `echo "KEY:value"` 改为 `output_result "KEY" "value"` 标准格式
- KEY 加 `REDIS_DEPTH_` 前缀，display_names.py 新增 22 个 KEY
- 去掉硬编码密码，使用 `REDIS_PASSWORD` 环境变量
- 容器名使用 `CONTAINERS[redis]` 模糊匹配
- thresholds.py 新增 5 个深度阈值（内存碎片/缓存命中/阻塞客户端/拒绝连接/慢日志）

**验证结果**：218 个测试全绿 ✅ 已部署到 5.60 ✅

### 2.6 参考目录分析完成

参考目录有 18 类全新检测项，当前项目未实现：
- **P1**：MySQL深度/Redis深度/安全合规深度/中间件连接（4类）
- **P2**：系统类型识别/平台服务检测/配置IP检测/Console配置（4类）
- **P3**：文件权限/定时任务预期/FastDFS/Android/数据备份/日志导出/修复能力（7类）

**深度检测重构已完成（阶段一），P1 移植进行中（阶段二已完成 2/4）**

### 2.7 安全合规深度检测模块 ✅

**需求文档**：`Docs/需求文档/服务监测/PRD_需求文档_检测项丰富化移植.md`

**参考来源**：`platform-auto-test/临时目录/服务监测/modules/SecurityCheck.psm1`

**核心实现**：
- `46_security_deep.sh`（新增）：安全合规深度检测脚本，9 类安全检测
  - MySQL Root 访问范围：查询 `mysql.user` 表 root 用户的 `host` 字段，含 `%` 为严重
  - MySQL 空密码用户：查询 `authentication_string` 为空的用户
  - Redis 密码状态：检查 `requirepass` 配置，空或弱密码为严重
  - EMQX 默认密码：用 `admin:public` 测试 Dashboard API
  - Linux 空密码账户：解析 `/etc/shadow` 第 2 字段
  - SUID 文件：扫描系统 SUID 文件与白名单对比
  - Crontab：收集所有用户/系统级 crontab 与安全模式对比
  - SSH 暴力破解：分析 SSH 日志失败登录统计 IP 次数（>=50 次）
  - 异常端口：获取监听端口与平台端口白名单对比
- `display_names.py`：新增 20 个 `SECURITY_DEEP_*` KEY 映射
- `thresholds.py`：新增 6 个安全检测阈值
- `config.sh.template`：新增安全白名单配置（SUID 白名单、端口白名单、EMQX Dashboard 端口）

**验证结果**：218 个测试全绿 ✅ 语法检查通过 ✅ **已部署到 5.60** ✅

### 2.8 中间件连接检测模块 ✅

**需求文档**：`Docs/需求文档/服务监测/PRD_需求文档_检测项丰富化移植.md`

**参考来源**：`platform-auto-test/临时目录/服务监测/lib/shell/middleware_check.sh` + `MiddlewareCheck.psm1`

**核心实现**：
- `39_middleware_conn.sh`（新增）：中间件连接检测脚本，4 类连接检测
  - Redis 连接：容器存在 + 端口监听 + `redis-cli ping` + 版本/内存/连接数
  - MySQL 连接：容器存在 + 端口监听 + `mysqladmin ping` + 版本
  - EMQX 连接：MQTT 端口监听 + Dashboard 端口 + API 状态 + 进程检查
  - FastDFS 连接：容器存在 + 网络模式 + trackerd/storaged 进程检查
- `display_names.py`：新增 16 个 `MW_CONN_*` KEY 映射
- `config.sh.template`：新增 EMQX 端口配置（MQTT 端口、Dashboard 端口）

**验证结果**：218 个测试全绿 ✅ 语法检查通过 ✅ **已部署到 5.60** ✅

### 2.9 状态等级误判问题修复 ✅

**问题文档**：用户报告 Redis/MySQL/EMQX 连接状态"端口未监听"却显示"正常"

**问题根因**：
- LEVEL KEY 命名不一致：脚本输出 `MW_CONN_REDIS_LEVEL`，但 `_resolve_status` 查找 `MW_CONN_REDIS_STATUS_LEVEL`
- 状态词映射缺失：`"端口未监听"`、`"连接失败"` 等异常状态词不在 `_VALUE_STATUS_MAP` 中
- 优先级 1（LEVEL 字段）失败 → 优先级 3（文本映射）也失败 → 默认"正常"

**修复内容**：
- `39_middleware_conn.sh`：4 类连接的 LEVEL KEY 改为 `<STATUS_KEY>_LEVEL` 格式（8 处）
- `46_security_deep.sh`：7 类安全检测的 LEVEL KEY 改为 `<值KEY>_LEVEL` 格式（7 处）
- `display_names.py`：同步 11 个 LEVEL KEY 的显示名映射
- `parser.py`：`_VALUE_STATUS_MAP` 补充 11 个异常状态词（端口未监听/MQTT 端口未监听/连接失败/进程异常/未检测到容器/容器未运行/无密码/弱密码/使用默认密码/查询失败/无法读取）

**验证结果**：218 个测试全绿 ✅ 语法检查通过 ✅ **已部署到 5.60** ✅

### 2.10 中间件端口检测逻辑重构 ✅

**问题文档**：部署后 5.44 检测仍报"端口未监听"

**问题根因**：
- 脚本在**容器内**用 `ss -tlnp | grep ':6379'` 检查端口
- 5.44 的容器镜像（精简版）**没有 `ss` 和 `netstat` 命令**
- `ss` 命令失败 stderr 被 `2>/dev/null` 吞掉，`port_listening` 变空 → 误判"端口未监听"

**修复内容**：
- `39_middleware_conn.sh` 重构检测逻辑：
  - Redis：`redis-cli ping` 优先（成功=运行中，NOAUTH=需密码认证，其他=连接失败）
  - MySQL：`mysqladmin ping` 优先（在容器内执行，连内部端口）
  - EMQX：宿主机 `ss` 检 MQTT 端口 + 容器内进程检查（官方镜像有 `ps`）
  - FastDFS：进程检查（不变，此前已正确处理 host 网络模式）

**验证结果**：
- Redis：运行中，版本 8.2.2，内存 2.23M，连接数 33 ✅
- MySQL：运行中，版本 8.0.28 ✅
- EMQX：运行中，MQTT 端口 1883 正常 ✅
- FastDFS：运行中，host 网络模式 ✅

### 2.11 5.44 目标凭据配置 ✅

**问题**：Redis/MySQL ping 连接失败（NOAUTH / Access denied）

**根因**：5.44 目标的 `credential_overrides` 为空，检测时无密码可用

**修复方式**：
- 通过 API 注入加密密码：`mysql_password` / `redis_password` = `dNrprU&2S`
- 密码使用 Fernet 加密存储，运行时解密注入 config.sh

**验证结果**：
- 凭据加密存储正确 ✅
- 解密后注入正确 ✅
- full 套件检测 442 项，4 正常 / 4 警告 / 11 严重 ✅

**额外发现的安全问题**（因密码配置后才检测出来）：
- MySQL Root 访问范围：`%,localhost` → **严重**（允许任意 IP）
- EMQX 默认密码：使用默认密码 → **严重**（admin:public）
- 异常端口数：57 个 → **严重**（大量非白名单端口）

### 2.12 系统类型识别模块 ✅

**需求文档**：`Docs/需求文档/服务监测/PRD_需求文档_P2级移植.md`

**参考来源**：`platform-auto-test/临时目录/服务监测/modules/ServerProfile.psm1`

**核心实现**：
- `50_system_profile.sh`（新增）：系统类型识别脚本，3 类识别
  - 平台类型：检测 `/data/services` 目录存在 → new，否则 → old
  - 容器类型：`docker ps` 容器名正则匹配（ujava/upython/upython_voice/unknown）
  - ujava 变体：检测 `/var/www/java/unifiedPlatform` 目录 → unified，否则 → meeting
- `display_names.py`：新增 6 个 `SYS_PROFILE_*` KEY 映射
- `check_modules.py`：注册 `50_system_profile` 模块

**验证结果**：218 个测试全绿 ✅ 语法检查通过 ✅

### 2.13 平台服务检测模块 ✅

**需求文档**：`Docs/需求文档/服务监测/PRD_需求文档_P2级移植.md`

**参考来源**：`platform-auto-test/临时目录/服务监测/modules/ServiceCheck.psm1` + `check_server_health.sh`

**核心实现**：
- `51_service_check.sh`（新增）：平台服务检测脚本
  - 新平台 ujava 容器内服务：auth/gateway/system/meeting/mqtt/quartz/message（7 个 jar 进程匹配）
  - 新平台宿主机服务：extapi（jar 进程匹配）
  - 传统平台 ujava 容器服务：nginx + meeting
  - 传统平台宿主机服务：extapi（路径匹配 `/var/www/java/external-meeting-api`）
  - upython/upython_voice 端口监听：用进程检查代替 ss（避免容器内无 ss/netstat 误判）
- `display_names.py`：新增 20+ 个 `SVC_CHECK_*` KEY 映射
- `config.sh.template`：新增服务匹配模式 + 端口列表配置
- `check_modules.py`：注册 `51_service_check` 模块

**设计要点**：
- 服务匹配模式可配置化（`UJAVA_SERVICES` 等环境变量）
- 端口检测用进程检查代替 ss（吸取坑 9 教训）
- 内部自行检测平台/容器类型，不依赖 50 模块执行顺序

**验证结果**：218 个测试全绿 ✅ 语法检查通过 ✅

### 2.14 配置IP检测模块 ✅

**需求文档**：`Docs/需求文档/服务监测/PRD_需求文档_P2级移植.md`

**参考来源**：`platform-auto-test/临时目录/服务监测/modules/ConfigIPCheck.psm1` + `lib/shell/config_check.sh`

**核心实现**：
- `52_config_ip_check.sh`（新增）：配置IP检测脚本
  - 新平台扫描 12 个配置目录（API 服务 + 前端 + 中间件）
  - 传统平台扫描 6 个配置目录
  - IP 提取：`grep -oE` 匹配 IP 正则
  - 允许列表：SERVER_IP + 127.0.0.1 + 172.17.0.1
  - 输出未授权 IP 明细（JSON 数组：文件路径 + 行号 + IP）
- `display_names.py`：新增 5 个 `CFG_IP_*` KEY 映射
- `config.sh.template`：新增 `SERVER_IP` 配置
- `check_modules.py`：注册 `52_config_ip_check` 模块

**验证结果**：218 个测试全绿 ✅ 语法检查通过 ✅

### 2.15 Console配置检测模块 ✅

**需求文档**：`Docs/需求文档/服务监测/PRD_需求文档_P2级移植.md`

**参考来源**：`platform-auto-test/临时目录/服务监测/check_server_health.sh`（第1582-1708行）

**核心实现**：
- `53_config_console_check.sh`（新增）：Console配置检测脚本
  - 扫描配置文件中 `console[:=]true` 的行（不区分大小写）
  - **只检测不修复**（避免巡检工具自动改生产配置的风险）
  - 复用配置 IP 检测的扫描路径
  - 输出问题文件列表（JSON 数组）
- `display_names.py`：新增 5 个 `CFG_CONSOLE_*` KEY 映射
- `check_modules.py`：注册 `53_config_console_check` 模块

**验证结果**：218 个测试全绿 ✅ 语法检查通过 ✅

---

## 3. 当前卡在哪

**无卡点**。阶段一（补全缺失项 + 重构 depth 脚本）、阶段二（安全合规深度 + 中间件连接）、阶段三（P2 级移植）已完成。

**当前状态**：
- 服务健康检查：status: ok, scheduler: running ✅
- 报告免登访问正常工作 ✅
- 统计模块正常工作 ✅
- 检测项趋势图表友好提示正常 ✅
- display_names.py 已补全缺失 KEY（539+52+36+35=662 条）✅
- 23_mysql_depth.sh / 25_redis_depth.sh 已重构为 output_result 格式 ✅
- 46_security_deep.sh 安全合规深度检测模块 ✅
- 39_middleware_conn.sh 中间件连接检测模块 ✅
- 50_system_profile.sh 系统类型识别模块 ✅
- 51_service_check.sh 平台服务检测模块 ✅
- 52_config_ip_check.sh 配置IP检测模块 ✅
- 53_config_console_check.sh Console配置检测模块 ✅
- full 套件 34 个模块（原 30 + 新 4）✅
- 218 个单元测试全绿 ✅
- **已部署到 5.60** ✅（阶段一+二，阶段三待部署）
- **5.44 实际运行验证通过**（full 套件，中间件全部"运行中"，安全模块发现真实问题）✅
- 状态等级误判已修复（LEVEL KEY 对齐 + 状态词映射兜底）✅
- 中间件端口检测逻辑已修复（服务 ping 优先，不依赖容器内 ss）✅
- 5.44 目标凭据已配置（MySQL/Redis 密码加密注入）✅

---

## 4. 下一步计划

| 优先级 | 任务 | 说明 |
|--------|------|------|
| ~~P1~~ | ~~Web 界面验证实际运行~~ | ✅ 已完成（5.44 full 套件检测通过，发现问题项正确标记严重） |
| ~~P1~~ | ~~统计页面验证~~ | ✅ 已完成（新检测项出现在统计页面选择器中） |
| ~~P2~~ | ~~继续移植 P2 优先级模块~~ | ✅ 已完成（系统类型识别/平台服务检测/配置IP检测/Console配置检测） |
| P2 | 部署阶段三到 5.60 + 5.44 实际运行验证 | 验证新模块在真实环境运行正确 |
| P3 | 继续移植 P3 优先级模块 | 文件权限、定时任务预期、FastDFS功能验证等 |
| 持续 | 更新 HANDOFF_服务监测文档 | 每次会话结束前更新进度 |

---

## 5. 踩过的坑（绝对不要再踩）

### 坑 1：JavaScript 编辑时代码残留

**坑**：使用 Edit 工具替换函数时，`old_string` 未精确匹配完整范围，只替换了函数开头，尾部代码残留。导致后续函数定义在错误的闭包内，全局作用域无法访问。

**避免方法**：
- 编辑前完整阅读原代码，确保 `old_string` 匹配完整范围
- 编辑后检查花括号平衡
- 本地测试后再部署

### 坑 2：状态类检测项折线图空白无提示

**坑**：`_extract_numeric()` 对非数值（"正常"/"异常"/"yes"/"no"）返回 `(None, value)`，前端收到 `value_num=null` 后折线图无数据点，但未显示任何提示。用户以为"图表坏了"。

**避免方法**：
- 前端处理数据前检查数值情况，无数值时显示友好提示
- 饼图使用 `status` 字段，不依赖数值

### 坑 3：检测项选择器选项过多难找

**坑**：419 个检测项平铺在一个下拉框，用户难以找到目标项。

**避免方法**：
- 用两级联动：第一级选模块，第二级选检测项
- 检测项选择器按模块分组（optgroup）
- 标注无趋势能力的检测项

### 坑 4：部署后健康检查误报

**坑**：`upload_to_server.py` 结尾的 `[FAIL] Service may not be running` 是误报。服务启动需要加载索引（357 条），sleep 3 秒不够。

**避免方法**：
- 用 `curl http://192.168.5.60:8088/api/health` 权威验证
- 不要信赖部署脚本的自动检查

### 坑 5：Shell 脚本硬编码密码

**坑**：参考目录的 `23_mysql_depth.sh` 和 `25_redis_depth.sh` 硬编码了密码（`MYSQL_PASSWORD="dNrprU&2S"`），直接复制到项目会泄露凭据。

**避免方法**：
- 使用 `MYSQL_PASSWORD` 环境变量（通过 config.sh 注入）
- 容器名使用 `CONTAINERS[name]` 模糊匹配而非硬编码
- 所有输出使用 `output_result "KEY" "value"` 标准函数

### 坑 6：PowerShell 移植到 Bash 的差异

**坑**：参考目录是 PowerShell 模块，直接翻译到 Bash 时需注意：
- PowerShell 的 `@()` 数组 → Bash 用空格分隔字符串 + `for` 循环
- PowerShell 的 `$Config.Key` → Bash 用 `${CONTAINERS[key]}` 关联数组
- PowerShell 的 `Invoke-WebRequest` → Bash 用 `curl`
- PowerShell 的 `Select-Object` → Bash 用 `awk`/`cut`
- PowerShell 的 `-match` → Bash 用 `grep -E`

**避免方法**：
- 移植时只保留核心检测逻辑，简化边缘功能
- 容器名用 `grep -iE` 模糊匹配替代精确名
- 凭据用 `__占位符__` + executor 渲染替代硬编码

### 坑 7：部署路径与验证方式

**坑**：部署后想验证新模块，发现：
- `/tmp/check_modules/lib/` 目录为空 —— 这是**检测运行时**临时工作目录，脚本只在运行巡检时才上传到这里
- `/data/web/service_monitor/assets/` 不存在 —— 实际部署路径是 `/opt/troubleshoot/web/service_monitor/`
- 统计 API `item-keys` 返回空 —— 因为统计页面基于**历史报告数据**聚合，新模块没运行过就没有数据
- 登录接口 `/login` 返回 415 —— 后端期望 JSON 格式（`Content-Type: application/json`），不是 form-urlencoded

**避免方法**：
- 部署路径：`/opt/troubleshoot/web/service_monitor/`（不是 /data/web）
- 验证模块注册：SSH 检查 `check_modules.py` 包含新模块 ID
- 验证脚本：SSH 检查 `assets/system/` 或 `assets/service/` 下文件存在 + `bash -n` 语法检查
- 触发实际运行：必须通过 Web 界面 SSE 流式接口 `/api/service-monitor/run/stream`（需管理员权限）
- Windows 没有 `sshpass`，用 paramiko（项目已依赖）执行 SSH 命令

### 坑 8：LEVEL KEY 命名不一致导致状态误判

**坑**：检测脚本输出 `MW_CONN_REDIS_LEVEL`，但 `_resolve_status` 查找 `MW_CONN_REDIS_STATUS_LEVEL`。LEVEL 字段对不上 → 文本映射也缺失 → 默认"正常"。明明"端口未监听"却显示"正常"。

**避免方法**：
- LEVEL KEY 必须严格遵循 `<值KEY>_LEVEL` 格式（`f"{key}_LEVEL"` 精确匹配）
- 文本状态词映射作为双重保险，补充常见异常词
- 新模块开发时先确认已有模块的命名约定

### 坑 9：容器内无 ss/netstat 导致端口检测误判

**坑**：脚本在容器内用 `ss -tlnp | grep ':6379'` 检查端口。但精简镜像（Alpine/slim）没装 `iproute2`，`ss` 命令失败 stderr 被 `2>/dev/null` 吞掉，结果变量为空 ≠ "yes" → 误判"端口未监听"。

**避免方法**：
- 端口检测**不要依赖容器内的 ss/netstat**
- 优先用**服务自带工具**（redis-cli ping / mysqladmin ping / curl API）
- 端口检测用**宿主机 ss**（适用于有端口映射的服务）
- 进程检查用 `ps aux`（几乎所有镜像都有）

### 坑 10：目标凭据未配置导致安全检测失效

**坑**：46_security_deep.sh 检测 Redis 密码状态时，因 `REDIS_PASSWORD` 未配置，`redis-cli INFO` 报 `NOAUTH`，脚本误判"未检测到容器"或"查询失败"。实际 Redis 有密码保护，但密码没配进系统，安全漏洞检测不出来。

**避免方法**：
- 添加远程目标时必须配置数据库密码（credential_overrides）
- 密码通过 API 加密存储，config.sh 模板用 `__REDIS_PASSWORD__` 占位符
- 部署后验证：跑一次 full 套件，确认安全模块能输出"已设置"而非"查询失败"

---

## 6. Git 提交记录

| 日期 | Commit | 说明 |
|------|--------|------|
| 2026-07-22 | 待提交 | feat(service-monitor): P2级移植-系统类型识别/平台服务检测/配置IP检测/Console配置检测 |
| 2026-07-22 | `c0200115` | feat(service-monitor): 新增安全合规/中间件检测模块并修复状态等级误判 |
| 2026-07-14 | `3f48af4c` | feat(service-monitor): 报告免登访问 + 监测统计模块 + 多项修复 |

**最近提交详情（c0200115）**：

```
feat(service-monitor): 新增安全合规/中间件检测模块并修复状态等级误判

检测项丰富化移植（阶段二）：
- 新增 39_middleware_conn.sh 中间件连接检测
- 新增 46_security_deep.sh 安全合规深度检测（9类）
- check_modules.py 注册 2 个新模块
- display_names.py 追加 36 个 KEY 映射
- thresholds.py 追加 6 个安全阈值
- config.sh.template 追加安全白名单 + EMQX 端口配置

状态等级误判修复：
- 39/46 脚本 LEVEL KEY 对齐为 <值KEY>_LEVEL 格式
- 23_mysql_depth / 25_redis_depth 重构为 output_result 格式
- parser.py _VALUE_STATUS_MAP 补充 11 个异常状态词映射

中间件端口检测逻辑重构：
- 改用服务自带工具 ping 优先（redis-cli/mysqladmin/curl API）
- 避免容器内无 ss/netstat 导致误判"端口未监听"

服务管理模块（附带）：
- routes/service_manage.py 新增授权/升级/信息页面路由
- templates/service_manage/ 新增 4 个模板

文档：更新 HANDOFF_服务监测.md + 检测项丰富化 PRD
```

**分支状态**：已推送到 `origin/troubleshoot-ai-assistant`，待合并到 master。

---

## 7. 关键文件与命令速查

### 本次修改文件（阶段一：检测项丰富化）

| 文件 | 改动 |
|------|------|
| `service_monitor/utils/display_names.py` | 补全 `VM_PGMajFAULT`；新增 `MYSQL_DEPTH_*` 30 个 KEY、`REDIS_DEPTH_*` 22 个 KEY（共 52 条） |
| `service_monitor/utils/thresholds.py` | 新增 MySQL 深度阈值 7 个、Redis 深度阈值 5 个 |
| `service_monitor/assets/service/23_mysql_depth.sh` | 重构为 output_result 格式，30 个检测项，加 MYSQL_DEPTH_ 前缀 |
| `service_monitor/assets/service/25_redis_depth.sh` | 重构为 output_result 格式，22 个检测项，加 REDIS_DEPTH_ 前缀 |

### 本次修改文件（阶段二：新检测模块移植）

| 文件 | 改动 |
|------|------|
| `service_monitor/assets/system/46_security_deep.sh` | 新建 - 安全合规深度检测脚本（9 类安全检测） |
| `service_monitor/assets/service/39_middleware_conn.sh` | 新建 - 中间件连接检测脚本（4 类连接检测），修订 - 端口检测逻辑重构（服务 ping 优先） |
| `service_monitor/assets/config.sh.template` | 追加安全白名单配置（SUID/端口）+ EMQX 端口配置 |
| `service_monitor/utils/check_modules.py` | 追加 2 个 CheckModule 条目（46_security_deep、39_middleware_conn） |
| `service_monitor/utils/display_names.py` | 追加 36 个 KEY 映射（`SECURITY_DEEP_*` 20 个 + `MW_CONN_*` 16 个） |
| `service_monitor/utils/thresholds.py` | 追加 6 个安全检测阈值 |
| `service_monitor/utils/parser.py` | 追加 11 个异常状态词映射（`_VALUE_STATUS_MAP`） |

### 本次修改文件（阶段三：P2 级移植）

| 文件 | 改动 |
|------|------|
| `service_monitor/assets/system/50_system_profile.sh` | 新建 - 系统类型识别脚本（6 个检测项） |
| `service_monitor/assets/service/51_service_check.sh` | 新建 - 平台服务检测脚本（~20 个检测项） |
| `service_monitor/assets/system/52_config_ip_check.sh` | 新建 - 配置IP检测脚本（5 个检测项） |
| `service_monitor/assets/system/53_config_console_check.sh` | 新建 - Console配置检测脚本（5 个检测项） |
| `service_monitor/utils/check_modules.py` | 追加 4 个 CheckModule 条目（50/51/52/53） |
| `service_monitor/utils/display_names.py` | 追加 35 个 KEY 映射（`SYS_PROFILE_*` 6 个 + `SVC_CHECK_*` 20+ 个 + `CFG_IP_*` 5 个 + `CFG_CONSOLE_*` 5 个） |
| `service_monitor/utils/parser.py` | 追加 2 个状态词映射（监听/未监听） |
| `service_monitor/assets/config.sh.template` | 追加服务匹配模式 + 端口列表 + SERVER_IP 配置 |

### 新增文档

| 文件 | 说明 |
|------|------|
| `Docs/需求文档/服务监测/PRD_需求文档_检测项丰富化移植.md` | 检测项丰富化需求文档 |

### 参考目录

```
platform-auto-test/临时目录/服务监测/
├── lib/shell/          # Shell 检测脚本（10个）
├── modules/            # PowerShell 模块（20个）
├── check_mysql_deep.sh # MySQL深度检测参考
├── check_redis.sh      # Redis深度检测参考
├── check_server_health.sh  # 综合健康检查参考
└── SecurityCheck.psm1  # 安全合规检测参考（下一阶段移植目标）
```

### 常用命令

```bash
# 本地启动
python skill/code/web/server.py

# 单元测试
cd skill/code && python -m pytest -v

# 部署到 5.60（用 ! 前缀执行）
! cd deploy && SSH_PASSWORD='***' python upload_to_server.py

# 健康检查
curl -s http://192.168.5.60:8088/api/health

# 测试报告免登访问
curl -s "http://192.168.5.60:8088/service-monitor/report/<id>?token=rpt_xxx"

# SSH 验证部署文件（Windows 用 paramiko，见坑 7）
# 部署路径：/opt/troubleshoot/web/service_monitor/
# 语法检查：bash -n <script.sh>
```

### 服务器部署路径速查

| 路径 | 说明 |
|------|------|
| `/opt/troubleshoot/web/` | 应用部署根目录 |
| `/opt/troubleshoot/web/service_monitor/` | 服务监测子包 |
| `/opt/troubleshoot/web/service_monitor/assets/system/` | 系统类检测脚本（如 46_security_deep.sh） |
| `/opt/troubleshoot/web/service_monitor/assets/service/` | 服务类检测脚本（如 39_middleware_conn.sh） |
| `/tmp/check_modules/lib/` | **运行时**临时目录（巡检执行时才生成，勿手动查） |

### 未提交文件

- **阶段三新增文件**：
  - `skill/code/web/service_monitor/assets/system/50_system_profile.sh`
  - `skill/code/web/service_monitor/assets/service/51_service_check.sh`
  - `skill/code/web/service_monitor/assets/system/52_config_ip_check.sh`
  - `skill/code/web/service_monitor/assets/system/53_config_console_check.sh`
- **阶段三修改文件**：
  - `skill/code/web/service_monitor/utils/check_modules.py`
  - `skill/code/web/service_monitor/utils/display_names.py`
  - `skill/code/web/service_monitor/utils/parser.py`
  - `skill/code/web/service_monitor/assets/config.sh.template`
- **文档**：
  - `Docs/需求文档/服务监测/PRD_需求文档_P2级移植.md`
  - `Docs/需求文档/服务监测/PRD_计划执行_P2级移植.md`
  - `HANDOFF_服务监测.md`
- **其他**：
  - `skill/code/web/users.json`（仅登录统计更新）
  - `skill/code/web/routes/service_manage.py`（服务管理模块，见 git status）
  - `skill/code/web/templates/service_manage/`（服务管理模块模板）

> 注：5.44 目标的 `credential_overrides`（MySQL/Redis 密码）已通过 API 写入 5.60 服务器的 `targets.json`（加密），不在代码仓库内。