# HANDOFF_服务监测 — 服务监测模块：检测项丰富化移植 + 移动端响应式优化 + 功能增强（报告对比 + 连续异常告警）

> 最后更新：2026-07-23（阶段九：报告对比功能 + 异常项趋势告警）｜ 分支：troubleshoot-ai-assistant ｜ 负责人：czj
> 专属交接文档：聚焦服务监测模块（service_monitor）的全部历史与当前状态

---

## 1. 我们在做什么

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

**已完成轮次**：
- **阶段一**：补全缺失项 + 重构 MySQL/Redis 深度检测脚本（见 2.5 节）
- **阶段二**：移植安全合规深度检测 + 中间件连接检测模块（见 2.7-2.11 节）
- **阶段三**：移植 P2 级检测模块：系统类型识别 + 平台服务检测 + 配置IP检测 + Console配置检测（见 2.12-2.15 节）
- **阶段四**：移植 P3 级检测模块：文件权限检测 + 定时任务预期检测
- **阶段五**：移植剩余 P3 级检测模块：FastDFS功能验证 + Android设备检测 + 数据备份检测 + 配置日志导出检测 + 修复能力检测
- **阶段六**：巡检防重复触发优化 + 数据备份检测超时修复
- **阶段七**：模块清理 + 5.44 验证 + 移动端响应式样式优化
- **阶段八**：移动端响应式深度优化 — 新增 480px 超小屏幕断点 + 修正布局溢出问题
- **阶段九（本次）**：功能增强 — 报告对比功能 + 异常项趋势告警

本轮会话完成了以下工作：

**P1：报告对比功能**：
1. 新增 `compare_service.py` 对比服务，支持 5 类差异分类（新增异常/恢复正常/值变化/新增项/消失项）
2. 新增 API：`GET /api/service-monitor/reports/{id1}/compare/{id2}` 和导出接口
3. 新增 `compare.html` 对比结果页，差异按模块分组展示
4. `report.html` 新增"对比历史报告"按钮和报告选择弹窗

**P1：异常项趋势告警**：
1. `report_service.py` 新增 `check_consecutive_abnormal()` 函数，检测连续异常
2. `runner_service.py` 集成连续异常告警，报告完成后自动检测并发送通知
3. `notification.html` 新增"连续异常告警"配置区（阈值/级别/渠道）
4. 冷却期机制：24 小时内同一检测项不重复告警

> **检测项丰富化移植全部完成**：阶段一至五共新增 15 个检测模块，full 套件模块数 30 → 42（移除 58 后）
>
> **设计原则**：所有原版依赖 Windows 本地工具(pscp.exe/adb.exe)的模块，均改造为远程巡检检测（检测就绪状态/能力，不执行本地文件操作）；所有修复类操作均改为只检测不修复

---

## 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 个测试全绿 ✅ 语法检查通过 ✅

### 2.16 文件权限检测模块 ✅

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

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

**核心实现**：
- `54_file_permission_check.sh`（新增）：文件权限检测脚本
  - 新平台：检查 12 个关键路径（run.sh 应可执行、rc.local 应可执行）
  - 传统平台：检查 7 个关键路径
  - 权限判定：`.sh` 文件和 `rc.local` 期望可执行（含 x 权限），配置文件期望不可执行
  - 兼容 GNU stat (`-c '%a'`) 和 BSD stat (`-f '%Lp'`)
  - **只检测不修复**
- `display_names.py`：新增 5 个 `FILE_PERM_*` KEY 映射
- `check_modules.py`：注册 `54_file_permission_check` 模块

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

### 2.17 定时任务预期检测模块 ✅

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

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

**核心实现**：
- `55_cron_expected.sh`（新增）：定时任务预期检测脚本
  - 通用预期：MySQL 每日自动备份脚本（`UbainsmysqlBakUp.sh`）
  - 新平台额外预期：ujava2 每3分钟守护启动脚本（`ujava2-startup.sh`）
  - 通过 `crontab -l` 获取当前定时任务，逐条 `grep -qF` 检查预期条目
  - **只检测不修改**
- `display_names.py`：新增 5 个 `CRON_EXPECTED_*` KEY 映射
- `check_modules.py`：注册 `55_cron_expected` 模块

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

### 2.18 FastDFS功能验证检测模块 ✅

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

**参考来源**：`platform-auto-test/临时目录/服务监测/check_fdfs_arm.sh` + `check_fdfs_x86.sh`

**核心实现**：
- `56_fastdfs_check.sh`（新增）：FastDFS 功能验证检测脚本
  - 容器检测：自动检测 Tracker/Storage 容器名称
  - 端口检测：22122(Tracker) + 23000(Storage) + 8888(Nginx)
  - 进程检测：容器内 fdfs_tracked/fdfs_storaged 进程
  - 配置检测：/etc/fdfs/client.conf 存在性 + tracker_server 配置
  - 功能测试：创建测试文件 → 上传 → 下载 → 完整性验证（MD5） → 清理
  - HTTP 测试：8888 端口访问测试
  - **只检测不修复**
- `display_names.py`：新增 20 个 `FDFS_*` KEY 映射
- `check_modules.py`：注册 `56_fastdfs_check` 模块

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

### 2.19 Android设备检测模块 ✅

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

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

**核心实现**：
- `57_android_check.sh`（新增）：Android 设备检测脚本
  - ADB 工具检测：检查系统 PATH + 常见安装位置
  - ADB 版本获取：`adb version`
  - ADB 服务状态：检查 adb server 是否运行
  - 设备连接检测：`adb devices` 获取已连接设备列表
  - 网络 ADB 能力：检查 adb tcpip 支持情况
  - **只检测不操作**（原版依赖 adb.exe + Windows 本地操作，改造为远程检测）
- `display_names.py`：新增 12 个 `ANDROID_*` KEY 映射
- `check_modules.py`：注册 `57_android_check` 模块

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

### 2.20 数据备份检测模块 ✅

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

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

**核心实现**：
- `58_data_backup_check.sh`（新增）：数据备份检测脚本
  - 备份脚本检测：检查常见备份脚本路径是否存在
  - MySQL 备份检测：检查备份目录 + 最近备份文件时间/大小
  - mysqldump 能力：检查宿主机或容器内 mysqldump 命令
  - 应用备份检测：检查应用备份目录
  - 定时备份任务：检查 crontab 中是否有备份任务
  - 磁盘空间检测：检查备份目录所在分区可用空间
  - **只检测不执行**（原版依赖 pscp.exe 下载到本地，改造为检测备份能力）
- `display_names.py`：新增 21 个 `BACKUP_*` KEY 映射
- `check_modules.py`：注册 `58_data_backup_check` 模块

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

### 2.21 配置日志导出检测模块 ✅

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

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

**核心实现**：
- `59_log_export_check.sh`（新增）：配置日志导出检测脚本
  - 配置文件检测：新平台 15 个配置路径 + 传统平台 8 个配置路径
  - 日志目录检测：新平台 13 个日志路径 + 传统平台 5 个日志路径
  - 关键日志大小：检查 ERROR 日志文件大小
  - 日志轮转配置：检查 logrotate.d 中是否有应用配置
  - 导出能力检测：检查 tar + gzip 命令可用性
  - **只检测不导出**（原版依赖 pscp.exe 下载到本地，改造为检测日志可获取性）
- `display_names.py`：新增 21 个 `LOGEXP_*` KEY 映射
- `check_modules.py`：注册 `59_log_export_check` 模块

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

### 2.22 修复能力检测模块 ✅

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

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

**核心实现**：
- `60_repair_capability_check.sh`（新增）：修复能力检测脚本
  - 修复脚本检测：检查 issue_handler.sh 等修复脚本是否存在
  - DNS 修复能力：检查 resolv.conf 可写性 + NetworkManager
  - NTP 修复能力：检查 chrony/ntp 服务 + 配置文件可写性
  - 端口修复能力：检查 firewalld/iptables 工具
  - 权限修复能力：检查 chmod/chown + 关键目录可写性
  - Docker 修复能力：检查 docker 命令 + 权限
  - 服务重启能力：检查 systemctl/service 命令
  - 日志清理能力：检查 logrotate/lsof/find 工具
  - **只检测不修复**（原版是交互式修复工具，改造为检测修复工具链就绪状态）
- `display_names.py`：新增 25 个 `REPAIR_*` KEY 映射
- `parser.py`：补充新模块状态词映射（yes/no/上传失败/下载失败等）
- `check_modules.py`：注册 `60_repair_capability_check` 模块

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

### 2.23 巡检防重复触发优化 ✅

**问题**：用户执行全量巡检后返回首页再次进来，又重新触发巡检

**问题根因**：
- `run.html:175` — `DOMContentLoaded` 无条件触发 `startInspection()`，页面每次加载都自动开始巡检
- 后端 `runner_service._runs` 按 `run_id` 索引，不按 `target_id` 索引，无法查询"该目标是否正在运行"
- 首页巡检按钮直接链接到 `/service-monitor/run/<id>`，每次都是全新页面导航

**修复内容**：

1. **后端 `runner_service.py`**：新增 `get_running_for_target(target_id)` 函数
   - 遍历 `_runs` 字典，查找 `target_id` 匹配且 `finished=False` 的运行记录
   - 返回 `{"run_id", "target_id", "suite", "done", "total", "current"}` 或 `None`

2. **后端 `routes.py`**：
   - 新增 `GET /api/service-monitor/run/status/<target_id>` API 端点
     - 返回 `{"running": true, ...}` 或 `{"running": false}`
   - SSE 流接口增加 409 防重检查：同一目标已有未完成巡检时返回 409 错误

3. **前端 `run.html`**：
   - `DOMContentLoaded` 改为先调用状态 API 检查
   - 如果已有巡检在运行：显示提示"该目标已有巡检正在运行 (x/y)，请等待完成或取消后重试"，不触发新巡检
   - 如果没有运行中巡检：正常调用 `startInspection()`

4. **前端 `index.html`**：
   - 巡检按钮增加 `checkBeforeRun(href, targetId)` 函数
   - 点击时先调用状态 API 检查，运行中则弹窗提示，否则正常跳转
   - 修复 `e.currentTarget` 在异步回调后为 null 导致的 TypeError

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

### 2.24 数据备份检测超时修复 ✅

**问题**：58_data_backup_check.sh 执行超过 5 分钟未完成

**问题根因**：
- `find "$backup_dir" -name "*.sql*"` 无深度限制（`-maxdepth`），在大型备份目录下遍历整个目录树
- `find "$app_backup_dir" -type f | wc -l` 统计所有文件数，目录大时很慢
- `find ... -printf` 是 GNU find 扩展，部分系统不支持

**修复内容**：
- 所有 `find` 命令添加 `-maxdepth 3` 限制搜索深度
- 兼容不支持 GNU `-printf` 的系统，用 `xargs ls -t` 备选方案查找最新文件

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

### 2.25 数据备份检测模块移除 ✅

**原因**：58_data_backup_check.sh 在实际运行中超时，且备份能力检测价值有限

**实现内容**：
- 删除 `58_data_backup_check.sh` 脚本文件（本地 + 5.60 服务器）
- 移除 `check_modules.py` 中 `58_data_backup_check` 模块注册
- 移除 `display_names.py` 中 21 个 `BACKUP_*` KEY 映射

**验证结果**：218 个测试全绿 ✅ **已部署到 5.60** ✅ full 套件模块数 43 → 42

### 2.26 FastDFS功能验证超时修复 ✅

**问题**：56_fastdfs_check.sh 执行超时，原脚本"功能测试"部分（上传→下载→MD5验证→清理）在异常情况下卡死

**问题根因**：
- `fdfs_upload_file` / `fdfs_download_file` 命令无超时机制
- 服务异常时会无限等待

**修复内容**：
- 重构 `56_fastdfs_check.sh`，移除上传下载功能测试，只保留状态检测
- 检测项：容器状态、端口检测（22122/23000/8888）、进程检测、配置文件检测、HTTP 探测
- HTTP 探测加 `--connect-timeout 3 --max-time 5` 超时保护
- 端口检测用宿主机 `ss` 替代容器内 `netstat`
- display_names.py 精简 KEY（移除上传/下载/完整性相关，保留 16 个）

**验证结果**：218 个测试全绿 ✅ **已部署到 5.60** ✅ 5.44 实测 11 项全正常

### 2.27 5.44 新模块实测验证 ✅

**验证方式**：在 5.44 服务器执行 full 套件巡检（报告 ID: 20260723_020607_63127b）

**验证结果**：
- 检测项总数：521（上次 510，新增 11 项）
- 正常 500，警告 8，严重 13
- 10 个新模块全部正常执行，无超时/报错

| 模块 | 结果 |
|------|------|
| 50 系统类型识别 | ✅ 3项全正常 |
| 51 平台服务检测 | ⚠️ 2项警告（MQTT/Message 未监听，真实状态） |
| 52 配置IP检测 | ⚠️ 1项警告（有未授权IP） |
| 53 Console配置检测 | ✅ 4项全正常 |
| 54 文件权限检测 | ✅ 4项全正常 |
| 55 定时任务预期检测 | ⚠️ 1项警告（定时任务缺失） |
| 56 FastDFS功能验证 | ✅ 11项全正常（修复后首次验证通过） |
| 57 Android设备检测 | ✅ 8项全正常 |
| 59 配置日志导出检测 | ⚠️ 1项警告（logrotate 未配置） |
| 60 修复能力检测 | ⚠️ 1项警告（修复脚本不存在） |

### 2.28 移动端响应式样式优化 ✅

**问题**：移动端排版在多个页面存在拥挤、溢出、文字竖向换行等问题

**优化内容**（9 个页面）：

| 页面 | 优化点 |
|------|--------|
| base.html | 顶部栏 padding 收窄、用户名溢出省略、退出按钮缩小、侧边栏菜单紧凑化 |
| index.html | 目标卡片 padding 收窄、巡检按钮纵向堆叠 |
| reports.html | 筛选栏纵向排列、报告行紧凑化、移动端隐藏【查看】按钮（点名称进入） |
| report.html | 报告头纵向排列、导出按钮全宽、异常项/明细行紧凑化、检测项状态文字 `white-space:nowrap` 防竖向换行 |
| statistics.html | 筛选栏纵向、概览卡片缩小、图表 padding 收窄、趋势筛选纵向 |
| schedule.html | 表格卡片化、操作按钮缩小、弹窗全宽、重复周期纵向 |
| targets.html | 表格卡片化、操作按钮缩小、弹窗全宽 |
| notification.html | 表单紧凑化、输入框缩小、保存按钮全宽 |
| run.html | 巡检卡片紧凑化、取消按钮全宽、模块列表字号缩小 |

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

### 2.29 移动端响应式深度优化 ✅

**问题**：上一轮优化仅覆盖 768px 断点，超小屏幕（<480px）仍存在溢出、触摸目标过小等问题

**本轮优化**：

| 问题类型 | 影响页面 | 修复方案 |
|---------|---------|---------|
| 缺少 480px 断点 | 全部 9 页面 | 新增 `@media (max-width: 480px)` 覆盖小屏手机 |
| statistics.html 筛选栏溢出 | statistics.html | 768px 断点改为完全纵向堆叠（原 `flex-wrap` 仍可溢出） |
| notification.html 端口输入框固定宽度 | notification.html | `flex: 0 0 120px` → 自适应，避免窄屏溢出 |
| report.html 模块标题溢出 | report.html | 添加 `word-break: break-word` |
| run.html 进度条高度未响应式 | run.html | 三级递减 12→10→8px |

**各页面 480px 断点优化详情**：

| 页面 | 优化内容 |
|------|--------|
| base.html | 顶栏高度/字号缩小、用户名截断宽度收紧、汉堡按钮/侧边栏紧凑化 |
| index.html | 卡片间距/padding/icon 收窄、section-head 纵向堆叠、目标管理按钮全宽 |
| reports.html | 筛选下拉框/报告行/复选框/批量栏全部缩小 |
| report.html | 汇总卡片字号缩小、模块标题 word-break、异常项/检测项行紧凑化、进程表字号递减 |
| statistics.html | 筛选栏及趋势选择器完全纵向堆叠、概览卡片/图表/padding 全面缩小 |
| schedule.html | 表格行/操作按钮/弹窗/星期按钮/时间选择器紧凑化 |
| targets.html | 表格行/操作按钮/弹窗紧凑化 |
| notification.html | 开关/输入框/标签字号缩小、端口输入框自适应 |
| run.html | 进度条高度响应式（12→10→8px）、巡检卡片/模块列表字号递减、完成横幅长文字 word-break |

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

---

## 3. 当前卡在哪

**无卡点**。阶段一至九全部完成。

**当前状态**：
- 服务健康检查：status: ok, scheduler: running ✅
- 报告免登访问正常工作 ✅
- 统计模块正常工作 ✅
- 检测项趋势图表友好提示正常 ✅
- display_names.py 已补全缺失 KEY（移除 BACKUP_*/FDFS_* 部分 KEY）✅
- 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配置检测模块 ✅
- 54_file_permission_check.sh 文件权限检测模块 ✅
- 55_cron_expected.sh 定时任务预期检测模块 ✅
- 56_fastdfs_check.sh FastDFS功能验证检测模块 ✅（已重构，移除超时功能测试）
- 57_android_check.sh Android设备检测模块 ✅
- ~~58_data_backup_check.sh~~ ❌ 已移除
- 59_log_export_check.sh 配置日志导出检测模块 ✅
- 60_repair_capability_check.sh 修复能力检测模块 ✅
- full 套件 42 个模块（原 30 + 新增 12）✅
- 218 个单元测试全绿 ✅
- **已部署到 5.60** ✅（阶段一至七全部部署）
- **5.44 实际运行验证通过**（阶段一至五，full 套件，10 个新模块全部正常）✅
- 状态等级误判已修复（LEVEL KEY 对齐 + 状态词映射兜底）✅
- 中间件端口检测逻辑已修复（服务 ping 优先，不依赖容器内 ss）✅
- 5.44 目标凭据已配置（MySQL/Redis 密码加密注入）✅
- **移动端响应式优化完成**（9 个页面全部适配）✅
- **移动端响应式深度优化完成**（新增 480px 断点 + 修正溢出问题）✅
- **报告对比功能完成**（对比服务 + API + 页面 + 导出）✅
- **异常项趋势告警完成**（连续异常检测 + 告警通知 + 配置页）✅
- **导出 PDF/Excel 格式完成**（Excel openpyxl + PDF weasyprint/HTML降级）✅

---

## 4. 下一步计划

| 优先级 | 任务 | 说明 |
|--------|------|------|
| ~~P1~~ | ~~Web 界面验证实际运行~~ | ✅ 已完成（5.44 full 套件检测通过） |
| ~~P1~~ | ~~统计页面验证~~ | ✅ 已完成 |
| ~~P2~~ | ~~继续移植 P2 优先级模块~~ | ✅ 已完成 |
| ~~P3~~ | ~~继续移植 P3 优先级模块~~ | ✅ 已完成 |
| ~~P1~~ | ~~巡检防重复触发优化~~ | ✅ 已完成 |
| ~~P1~~ | ~~数据备份检测超时修复~~ | ❌ 已移除该模块 |
| ~~P1~~ | ~~合并 troubleshoot-ai-assistant 分支到 master~~ | ❌ 不合并，长期独立分支维护 |
| ~~P2~~ | ~~在 5.44 实际运行验证新模块~~ | ✅ 已完成（10 个新模块全部正常） |
| ~~P1~~ | ~~移动端响应式优化~~ | ✅ 已完成（9 个页面全部适配） |
| ~~P1~~ | ~~移动端响应式深度优化~~ | ✅ 已完成（新增 480px 断点 + 修正溢出） |
| ~~P1~~ | ~~报告对比功能~~ | ✅ 已完成（阶段九） |
| ~~P1~~ | ~~异常项趋势告警~~ | ✅ 已完成（阶段九） |
| ~~P2~~ | ~~导出 PDF/Excel 格式~~ | ✅ 已完成（阶段九） |
| 持续 | 更新 HANDOFF_服务监测文档 | 每次会话结束前更新进度 |

---

## 4.1 阶段九进度：功能增强

### ✅ P1：报告对比功能（已完成）

**实现内容**：
- `compare_service.py`：对比服务，5 类差异分类（新增异常/恢复正常/值变化/新增项/消失项）
- API：`GET /api/service-monitor/reports/{id1}/compare/{id2}`
- 导出 API：`GET /api/service-monitor/reports/{id1}/compare/{id2}/export?format=md|html`
- `compare.html`：对比结果页，差异按模块分组展示，可折叠
- `report.html`：新增"对比历史报告"按钮 + 报告选择弹窗
- 值变化阈值：默认 > 20%
- 数值提取：正则匹配，支持带单位的值（如 45%、100MB）

### ✅ P1：异常项趋势告警（已完成）

**实现内容**：
- `report_service.py`：`check_consecutive_abnormal()` 函数
- `_get_abnormal_items()`：提取异常检测项（支持仅严重/警告+严重）
- `_is_consecutive_abnormal()`：连续性检查
- 冷却期机制：`_ALERT_COOLDOWN` 内存字典，24 小时冷却
- `runner_service.py`：集成告警检查，报告完成后自动触发
- `_send_consecutive_alert()`：告警通知发送（邮件/钉钉/企微）
- `notification.html`：新增"连续异常告警"配置区
- 配置字段：`alert_on_consecutive.enabled/threshold/alert_level/channels`

### ✅ P2：导出 PDF/Excel 格式（已完成）

**实现内容**：
- `report_service.py`：`export_excel()` 函数（使用 openpyxl）
  - Sheet 1：汇总信息（目标、套件、时间、统计）
  - Sheet 2：检测明细（全量表格 + 条件格式）
  - 状态列颜色标记（正常=绿、警告=黄、严重=红）
  - 表头冻结
- `report_service.py`：`export_pdf()` 函数
  - 使用 weasyprint 将 HTML 转 PDF
  - 降级策略：weasyprint 不可用时返回可打印 HTML
  - `_build_report_html()`：打印友好 HTML 模板
  - @page 分页规则 + 中文字体支持
- `routes.py`：`api_export_report()` 增加 `pdf` 和 `excel` 格式
- `report.html`：导出菜单新增"导出 PDF"、"导出 Excel"
- PDF 检测：通过 `%PDF` 魔数判断，降级时添加打印按钮

---

## 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 套件，确认安全模块能输出"已设置"而非"查询失败"

### 坑 11：定时任务并发执行导致 run_id 冲突

**坑**：两个定时任务在同一秒触发（如都在 09:00），`run_id` 使用时间戳+"sched"后缀生成，导致 `run_id` 完全相同。第二个任务覆盖第一个的工作目录和 `_runs` 字典，只生成一份报告。

**避免方法**：
- `run_id` 必须加入 uuid 或其他唯一标识（已修复：使用 `target_id[:4]` + uuid[:6]）
- 并发执行场景必须确保资源隔离（工作目录、内存状态）

---

## 6. Git 提交记录

| 日期 | Commit | 说明 |
|------|--------|------|
| 2026-07-24 | `3cb1cf62` | fix(service-monitor): 修复定时任务并发执行导致 run_id 冲突 |
| 2026-07-23 | `7b0e7dcd` | feat(service-monitor): 导出 PDF/Excel 格式 |
| 2026-07-23 | `069f9fa2` | feat(service-monitor): 报告对比功能 + 异常项趋势告警 |
| 2026-07-23 | `8e7a5b3a` | style(service-monitor): 移动端响应式深度优化-新增480px超小屏幕断点 |
| 2026-07-23 | `dfe01cae` | feat(service-monitor): P3级检测模块补全+巡检防重复触发+移动端响应式优化 |
| 2026-07-22 | `664d318d` | feat(service-monitor): P3级移植-文件权限检测/定时任务预期检测 |
| 2026-07-22 | `dc42cffe` | 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 配置 |

### 本次修改文件（阶段五：P3 级补全）

| 文件 | 改动 |
|------|------|
| `service_monitor/assets/service/56_fastdfs_check.sh` | 新建 - FastDFS功能验证检测脚本（上传/下载/完整性测试） |
| `service_monitor/assets/service/57_android_check.sh` | 新建 - Android设备检测脚本（adb可用性+设备连接状态） |
| `service_monitor/assets/system/58_data_backup_check.sh` | 新建 - 数据备份检测脚本（备份脚本+备份时间+备份能力） |
| `service_monitor/assets/system/59_log_export_check.sh` | 新建 - 配置日志导出检测脚本（配置文件+日志目录+导出能力） |
| `service_monitor/assets/system/60_repair_capability_check.sh` | 新建 - 修复能力检测脚本（修复工具链就绪状态） |
| `service_monitor/utils/check_modules.py` | 追加 5 个 CheckModule 条目（56/57/58/59/60） |
| `service_monitor/utils/display_names.py` | 追加 99 个 KEY 映射（FDFS/ANDROID/BACKUP/LOGEXP/REPAIR） |
| `service_monitor/utils/parser.py` | 追加新模块状态词映射 |

### 本次修改文件（阶段八：移动端响应式深度优化）

| 文件 | 改动 |
|------|------|
| `templates/service_monitor/base.html` | 新增 480px 断点：顶栏/侧边栏/用户名紧凑化 |
| `templates/service_monitor/index.html` | 新增 480px 断点：卡片/按钮缩小，目标管理按钮全宽 |
| `templates/service_monitor/reports.html` | 新增 480px 断点：筛选框/报告行/批量栏紧凑化 |
| `templates/service_monitor/report.html` | 新增 480px 断点 + 模块标题 `word-break` + 异常项/检测项紧凑化 |
| `templates/service_monitor/statistics.html` | 修正 768px 筛选栏纵向堆叠 + 新增 480px 断点 |
| `templates/service_monitor/schedule.html` | 新增 480px 断点：弹窗/星期按钮/时间选择器紧凑化 |
| `templates/service_monitor/targets.html` | 新增 480px 断点：表格行/操作按钮/弹窗紧凑化 |
| `templates/service_monitor/notification.html` | 修正 768px 端口输入框自适应 + 新增 480px 断点 |
| `templates/service_monitor/run.html` | 新增进度条响应式高度（12→10→8px）+ 480px 断点 |

### 本次修改文件（阶段九：报告对比 + 连续异常告警）

| 文件 | 改动 |
|------|------|
| `service_monitor/services/compare_service.py` | **新增** — 对比服务（5 类差异分类 + 导出 Markdown/HTML） |
| `service_monitor/services/report_service.py` | 新增连续异常检测函数（check_consecutive_abnormal + 冷却期） |
| `service_monitor/services/runner_service.py` | 集成连续异常告警 + 告警消息发送（邮件/钉钉/企微） |
| `service_monitor/services/__init__.py` | 导出 compare_service |
| `service_monitor/routes.py` | 新增对比/导出 API 路由 + 对比页面路由 |
| `templates/service_monitor/compare.html` | **新增** — 对比结果页模板 |
| `templates/service_monitor/report.html` | 新增"对比历史报告"按钮 + 报告选择弹窗 |
| `templates/service_monitor/notification.html` | 新增"连续异常告警"配置区 |

### 本次修改文件（阶段九-P2：导出 PDF/Excel）

| 文件 | 改动 |
|------|------|
| `service_monitor/services/report_service.py` | 新增 export_excel()、export_pdf()、_build_report_html() |
| `service_monitor/routes.py` | api_export_report() 增加 pdf/excel 格式，重构避免重复代码 |
| `templates/service_monitor/report.html` | 导出菜单新增"导出 PDF"、"导出 Excel" |

### 新增文档

| 文件 | 说明 |
|------|------|
| `Docs/需求文档/服务监测/PRD_需求文档_检测项丰富化移植.md` | 检测项丰富化需求文档 |
| `Docs/需求文档/服务监测/PRD_需求文档_报告对比功能.md` | 报告对比功能需求文档 |
| `Docs/需求文档/服务监测/PRD_需求文档_异常项趋势告警.md` | 异常项趋势告警需求文档 |
| `Docs/需求文档/服务监测/PRD_计划执行_报告对比功能.md` | 报告对比功能计划执行文档 |
| `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/` | **运行时**临时目录（巡检执行时才生成，勿手动查） |

### 未提交文件

- **服务管理模块**（非本次开发，应单独提交）：
  - `Docs/需求文档/服务管理/HANDOFF_服务管理.md`
  - `Docs/需求文档/服务管理/PRD_需求文档_服务管理模块.md`
  - `Docs/需求文档/服务管理/PRD_计划执行_服务管理模块.md`
  - `skill/code/web/routes/service_manage.py`
  - `skill/code/web/templates/service_manage/`
- **其他**：
  - `skill/code/web/users.json`（仅登录统计更新）
  - `HANDOFF_服务监测.md`（本轮待提交）

> 注：
> - 检测项丰富化移植（阶段一至四）已全部提交并部署到 5.60
> - 5.44 目标的 `credential_overrides`（MySQL/Redis 密码）已通过 API 写入 5.60 服务器的 `targets.json`（加密），不在代码仓库内