提交 9d887980 authored 作者: 陈泽健's avatar 陈泽健

docs: 更新 HANDOFF 交接文档

Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 9d90d646
# HANDOFF — 服务监测模块深度扩展与 SSH 连接问题修复 # HANDOFF — 定时任务弹窗优化 + 通知配置 + 定时任务不执行修复
> 最后更新:2026-07-20 18:26 | 分支:troubleshoot-ai-assistant | 负责人:czj > 最后更新:2026-07-21 15:07 | 分支:troubleshoot-ai-assistant | 负责人:czj
--- ---
## 1. 我们在做什么 ## 1. 我们在做什么
项目是「运维辅助平台」(原"问题排查 AI 助手"),Flask Web 服务跑在 `192.168.5.60:8088` 次会话承接上一轮的"左侧菜单改造 + 定时自动巡检"功能,继续推进服务监测模块的完善,共完成三项任务:
本次会话有两项主要任务: **任务一:定时任务弹窗交互优化**。原弹窗使用 Cron 表达式(如 `0 8 * * *`)配置调度周期,非技术人员无法理解。改为"每天/工作日/每周"三种重复周期 + 时间选择器 + 星期多选 + 起止日期的用户友好界面。来源 PRD:`Docs/需求文档/服务监测/PRD_需求文档_定时任务弹窗优化.md`
### 任务一:SSH 连接测试失败问题修复 **任务二:通知配置功能**。定时巡检完成后用户无法及时获知结果,新增"通知配置"菜单页,支持邮件(SMTP)、钉钉机器人、企业微信机器人三种通知渠道,可选择触发条件和测试发送。来源 PRD:`Docs/需求文档/服务监测/PRD_需求文档_通知配置.md`
用户在服务监测平台新建远程目标服务器(192.168.5.44),点击"测试连接"提示"SSH 连接失败"。经排查发现服务器 5.60 上**未安装 paramiko**,且错误信息过于笼统,无法区分网络/认证/依赖问题。 **任务三:定时任务不执行问题修复**。用户反馈设置了定时任务但到了时间不执行、不输出报告。排查发现三个根因:①Flask debug 模式的 reloader 机制导致 APScheduler 在错误进程初始化;②start_date/end_date 传字符串而非 datetime 对象给 APScheduler;③croniter 依赖未在 requirements.txt 声明。来源:`Docs/需求文档/服务监测/PRD_问题处理_定时任务与报告优化.md`
### 任务二:服务监测模块深度扩展 ---
在核心子集(7个模块)基础上,扩展更多检测能力: ## 2. 已经完成了什么
- 补充常用系统模块:OOM检测、进程检测、网络检测、计划任务、端口检测
- 补充服务深度检测:Docker深度、MySQL深度、Redis深度
- 新增业务容器监测:UStorage、UTracker
需求文档位置: ### ✅ 定时任务弹窗交互优化
- `Docs/需求文档/服务监测/PRD_需求文档_服务监测模块.md`
- `Docs/需求文档/服务监测/PRD_计划执行_服务监测模块.md`
--- - 删除 Cron 表达式输入框和预设按钮,改为**重复周期单选组**(每天/工作日/每周)
- 新增**时:分下拉选择器**(0-23 时,每5分钟)
- 新增**星期多选按钮组**(仅每周模式显示,至少选1个)
- 新增**生效日期/失效日期**选择器
- 列表"Cron"列改为**"调度周期"**,显示自然语言描述(如"工作日 09:30"、"每周一、周五 10:00")
- 后端 `schedule_service.py` 新增 `_build_cron()` / `_describe_schedule()` 函数,自动转换和描述
- 数据模型新增字段:`repeat_mode` / `hour` / `minute` / `weekdays` / `start_date` / `end_date`
- 部署到 5.60 验证通过:创建"工作日巡检"显示"工作日 09:30",编辑回填正确
## 2. 已经完成了什么 ### ✅ 通知配置功能
### 任务一:SSH 连接问题修复 ✅ - 左侧菜单新增 🔔 通知配置(在"定时任务"和"目标管理"之间,仅管理员可见)
- 新增 `notification_service.py`:支持邮件/钉钉/企业微信三种渠道的配置 CRUD、测试发送、巡检完成后通知
- 新增 `notification.html`:三个渠道配置卡片(启用开关 + 表单 + 测试按钮)+ 触发条件配置
- `runner_service.py``run_inspection_sync()` 完成后调用通知服务
- 敏感字段(SMTP 密码、钉钉密钥)用 Fernet 加密存储(复用 `crypto.py``encrypt_password`
- 部署到 5.60 验证通过:页面功能正常,邮件配置展开后表单完整
1. **部署 paramiko** ### ✅ 定时任务不执行修复
- 在服务器 5.60 执行 `pip3 install --break-system-packages paramiko` 安装 5.0.0
- 重启服务验证 SSH 连接成功
2. **增强错误分类** **根因 1:Flask debug reloader**(核心问题)
- `executor.py` 新增 `DependencyError``SSHConnectionError` 异常类 - 修复:`server.py``FLASK_DEBUG` 默认值从 `'1'` 改为 `'0'`
- 新增 `classify_ssh_error()` 函数,按异常类型返回 6 种错误码 - 修复:`schedule_service.init_scheduler()` 增加 `WERKZEUG_RUN_MAIN` 环境变量检查,仅在 reloader 子进程初始化
- `BaseExecutor.test_connection()` 改为抛出分类后的异常 - 修复:`deploy/upload_to_server.py` 启动命令显式设置 `FLASK_DEBUG=0`
3. **API 返回详细错误** **根因 2:start_date/end_date 类型错误**
- `target_service.test_connection()` 返回 `detail` + `error_code` 字段 - 修复:`_add_job()` 中将字符串转为 `datetime` 对象并附加 `timezone.utc`
- `routes.py` 直接返回完整结果
- 前端 `targets.html` 显示详细错误,鼠标悬停查看详情
4. **上传脚本增加依赖检查** **根因 3:croniter 依赖缺失**
- `deploy/upload_to_server.py` 新增 `_check_and_install_deps()` 函数 - 修复:`requirements.txt` 增加 `apscheduler==3.11.0` + `croniter==6.2.4`
- 部署时自动检查并安装 paramiko/cryptography - 修复:`skill/code/requirements.txt` 增加 `apscheduler>=3.10.0` + `croniter>=2.0.0`
- 修复:`deploy/upload_to_server.py``_REQUIRED_PACKAGES` 增加 `'croniter'`
5. **输出 PRD 文档** **验证**`/api/health` 返回 `scheduler: {'running': true, 'job_count': 3}`,3 个定时任务全部注册
- `Docs/需求文档/服务监测/PRD_问题处理_SSH连接测试失败.md`
- `Docs/需求文档/服务监测/PRD_计划执行_SSH连接测试失败修复.md`
**验证结果** ### ✅ 编辑保存提示
- 认证失败返回 `"认证失败:用户名或密码错误"` + `AUTH_FAILED`
- 不可达 IP 返回 `"连接被拒绝:目标端口未开放或 SSH 服务未运行"` + `CONNECTION_REFUSED`
- 连接成功返回 `"连接成功"`
### 任务二:模块扩展 ✅ - `schedule.html``saveSchedule()` 成功后增加 `alert('保存成功')` / `alert('创建成功')`
1. **迁移 system 类模块** ### ✅ 执行状态显示
-`临时目录/服务器监测/lib/system/` 迁移 5 个模块到 `assets/system/`
- 改造 `LIB_DIR` 从写死改为可注入模式
- 模块:05_oom_check、06_process_check、07_network_check、11_scheduled_tasks、12_port_check
2. **迁移 service 深度模块** - `schedule_service.py` 新增 `_update_current_status()` 函数
-`临时目录/服务器监测/lib/service/` 迁移 3 个模块到 `assets/service/` - `_execute_scheduled_job()` 开始时标记 `running`,成功标记 `success`,失败标记 `failed`
- 模块:21_docker_deep、23_mysql_depth、25_redis_depth - `schedules.json` 增加 `current_status` 字段
3. **新增业务容器监测** ### ✅ 批量删除报告
- 新建 `37_ustorage_check.sh`:容器状态、健康检查、存储容量与IO
- 新建 `38_utracker_check.sh`:容器状态、健康检查、处理能力
4. **更新代码配置** - `reports.html` 增加 checkbox、全选、批量删除按钮
- `check_modules.py`:ALL_MODULES 从 7→17 个 - `routes.py` 新增 `DELETE /api/service-monitor/reports/batch` 接口
- `display_names.py`:新增 ~150 个 KEY 的中文显示名
- `config.sh.template`:添加 ustorge/utracker 容器匹配模式
5. **修复空值问题** ### ✅ health 接口增强
- `parser.py` 新增 `_INVALID_VALUES` 过滤 N/A、无法获取、未知等无效值
- `01_system_basic.sh` 修复 `check_command` 函数不存在的问题
- `02_cpu_check.sh` 修复 `SCHEDULER_RUNQUEUE` 取值逻辑
**验证结果** - `routes/troubleshoot.py``/api/health` 增加 `scheduler` 状态字段,暴露 APScheduler 运行状态
- 快速巡检:5 模块,61 项
- 全量巡检:17 模块,193 项(全部有效值,无空值/N/A)
- 已部署到 5.60 并验证通过
--- ---
## 3. 当前卡在哪 ## 3. 当前卡在哪
**无卡点**。所有任务已完成,等待用户下一步指令 **无卡点**。所有功能已实现并部署到 5.60
工作区有未提交的改动(本次会话的修改),建议提交后再结束 但有一个**待验证项**:定时任务到时间后是否真正执行并生成报告。APScheduler 已正确初始化(health 接口确认),但尚未等一个完整周期验证报告输出。建议创建一个 2 分钟后触发的定时任务来快速验证
--- ---
## 4. 下一步计划 ## 4. 下一步计划
按优先级排序: 1. **验证定时任务实际执行**(P0)
- 在页面上创建一个 2 分钟后触发的定时任务
- 等待执行后检查巡检报告是否生成
- 检查 `schedules.json``last_run_at``last_report_id` 是否更新
1. **提交代码**(P0) 2. **提交代码**(P0)
- 当前有 11 个修改文件 + 10 个新增文件未提交 - 当前有 13 个修改文件 + 7 个新增文件未提交
- 建议拆分为 3 个提交: - 建议拆分为 3-4 个提交:
- `fix: SSH 连接测试错误分类与依赖检查` - `feat(service-monitor): 定时任务弹窗交互优化(友好调度配置替代 Cron 表达式)`
- `feat: 服务监测模块扩展(新增 10 个检测模块)` - `feat(service-monitor): 通知配置功能(邮件/钉钉/企业微信)`
- `fix: 过滤无效检测值,修复 bash 脚本检测逻辑` - `fix(service-monitor): 定时任务不执行修复 + 依赖补充 + 报告批量删除`
- 所有 `Docs/需求文档/` 下的新增 PRD 文件也需要提交
2. **添加更多检测模块**(P1,可选 3. **清理 5.60 上的测试定时任务**(P1
- 参考 PRD,还有 system 类(40综合诊断/43安全合规/44系统日志/45时间同步 - 删除之前创建的测试任务(如不需要
- 以及 service 类(EMQX/Java/Python/Nginx/Nacos/FastDFS) - 确保 schedules.json 数据干净
3. **远程目标巡检测试**(P2) 4. **配置实际通知渠道**(P2)
- 新建 192.168.5.44 远程目标,执行快速/全量巡检 - 配置邮件/钉钉/企业微信的真实凭据
- 验证 SSH 远程执行全链路 - 发送测试验证
5. **systemd 服务文件补充 FLASK_DEBUG=0**(P2)
- 当前服务文件缺少此环境变量,依赖 server.py 的默认值
- 建议手动在 `/etc/systemd/system/troubleshoot.service``[Service]` 段加 `Environment=FLASK_DEBUG=0`
--- ---
## 5. 踩过的坑(绝对不要再踩) ## 5. 踩过的坑(绝对不要再踩)
### 坑 1:服务器端 paramiko 未安装 ### 坑 1:Flask debug 模式的 reloader 导致 APScheduler 失效(最严重)
**坑**:点击测试连接返回"SSH 连接失败",但实际原因是服务器上没装 paramiko,`import paramiko` 失败被吞掉。
**原因**:HANDOFF 文档写了"待安装依赖",但部署时跳过了。`upload_to_server.py` 只上传代码不检查依赖。
**避免** **坑**`server.py` 默认 `FLASK_DEBUG='1'`,Flask debug 模式使用 Werkzeug reloader,fork 出子进程后,父进程中初始化的 APScheduler 被丢弃,子进程中 `_scheduler` 全局变量重置为 None。定时任务注册在即将被丢弃的父进程中,实际处理请求的子进程没有调度器。
- `upload_to_server.py` 已增加依赖检查步骤,部署时自动安装
- 错误信息现在会明确提示"paramiko 未安装"
### 坑 2:Windows 换行符导致 bash 脚本无法执行 **为什么会踩**:开发环境默认开启 debug 方便热重载,但 reloader 的双进程机制与 APScheduler 的单进程初始化冲突。症状是定时任务"看起来注册成功但永远不执行"。
**坑**:bash 脚本从 Windows 上传后含 `\r\n`,Linux 执行报 `$'\r': command not found` **避免方法**
- 生产环境**必须**设置 `FLASK_DEBUG=0`
- `init_scheduler()` 要检查 `WERKZEUG_RUN_MAIN` 环境变量,仅在子进程中初始化
- health 接口暴露 scheduler 状态,方便快速诊断
**原因**:Git 默认在 Windows 上检出 CRLF,SFTP 上传时没转换。 ### 坑 2:APScheduler 的 start_date/end_date 需要带时区的 datetime
**避免** **坑**:直接把 `"2026-07-20"` 字符串赋值给 `CronTrigger.start_date`,报错 `'str' object has no attribute 'astimezone'`。改为 `datetime` 对象后又报错 `can't compare offset-naive and offset-aware datetimes`
- `upload_to_server.py` 已增加 `_sftp_put_unix_lines()` 自动转换 `.sh`/`.template` 文件
- 未来上传脚本文档要强调换行符问题
### 坑 3:templates/service_monitor 子目录未上传 **避免方法**:用 `datetime.strptime(date_str, "%Y-%m-%d").replace(tzinfo=timezone.utc)` 转换为 UTC 时区的 datetime 对象。end_date 当天也要执行,需设为 `23:59:59`
**坑**:部署后模板目录为空,页面 404。 ### 坑 3:croniter 依赖未声明
**原因**`DIRS_TO_UPLOAD` 只上传一层文件,`templates/service_monitor/` 子目录被忽略 **坑**`apscheduler``upload_to_server.py``_REQUIRED_PACKAGES` 中声明了,但 `croniter` 没有。`croniter``schedule_service.py` 的间接依赖(`_calc_next_run()``create_schedule()` 中使用),缺失导致 `next_run_at` 为 null
**避免**:已修改上传脚本,子目录递归上传。 **避免方法****所有 Python 依赖必须在三个位置同步声明**
1. `requirements.txt`(容器化部署)
2. `skill/code/requirements.txt`(开发环境)
3. `deploy/upload_to_server.py``_REQUIRED_PACKAGES`(SSH 部署)
### 坑 4:检测项值为空/N/A 判定为"正常" ### 坑 4:notification_service 导入了不存在的函数名
**坑**报告显示大量 N/A 值被判定为"正常",误导用户 **坑**导入了 `encrypt_value` / `decrypt_value`,但 `crypto.py` 中的实际函数名是 `encrypt_password` / `decrypt_password`
**原因**:Parser 不过滤无效值,所有 KEY:VALUE 都解析成检测项 **避免方法**:新增模块前检查依赖模块的实际函数签名,不要凭记忆写函数名
**避免**`parser.py` 已增加 `_INVALID_VALUES` 过滤,N/A、无法获取、未知等不再输出。 ### 坑 5:5.60 服务器 pip install 需要特殊参数
### 坑 5:bash 脚本调用不存在的函数 **坑**:5.60 服务器是 Ubuntu + Python 3.14,`pip install``externally-managed-environment` 错误,需要加 `--break-system-packages` 参数。
**坑**`check_command ulimit` 函数不存在,导致 `ULIMIT_INFO` 输出"无法获取"。 **避免方法**`upload_to_server.py` 的依赖安装命令已加此参数,后续手动安装时也要加。
**原因**:参考脚本中有 `check_command` 函数,但 `common.sh` 没有迁移。
**避免**:迁移脚本时检查函数依赖,用 `command -v` 替代自定义函数。
--- ---
## 6. 关键文件与命令速查 ## 6. 关键文件与命令速查
### 本次修改的核心文件 ### 本次新增文件
| 文件 | 用途 | | 文件 | 用途 |
|------|------| |------|------|
| `service_monitor/utils/executor.py` | SSH 连接错误分类 | | `skill/code/web/service_monitor/services/notification_service.py` | 通知服务(邮件/钉钉/企业微信) |
| `service_monitor/utils/parser.py` | 过滤无效检测值 | | `skill/code/web/templates/service_monitor/notification.html` | 通知配置页面 |
| `service_monitor/utils/check_modules.py` | 模块清单(7→17) | | `Docs/需求文档/服务监测/PRD_需求文档_定时任务弹窗优化.md` | 弹窗优化 PRD |
| `service_monitor/utils/display_names.py` | KEY 中文显示名 | | `Docs/需求文档/服务监测/PRD_计划执行_定时任务弹窗优化.md` | 弹窗优化计划 |
| `service_monitor/assets/system/*.sh` | 新增 5 个系统检测模块 | | `Docs/需求文档/服务监测/PRD_需求文档_通知配置.md` | 通知配置 PRD |
| `service_monitor/assets/service/*.sh` | 新增 5 个服务检测模块 | | `Docs/需求文档/服务监测/PRD_计划执行_通知配置.md` | 通知配置计划 |
| `deploy/upload_to_server.py` | 依赖检查 + 换行符转换 | | `Docs/需求文档/服务监测/PRD_问题处理_定时任务与报告优化.md` | 问题处理文档 |
| `Docs/需求文档/服务监测/PRD_计划执行_定时任务与报告优化.md` | 问题修复计划 |
### 本次修改文件
| 文件 | 改动要点 |
|------|---------|
| `skill/code/web/server.py` | FLASK_DEBUG 默认值改为 `'0'` |
| `skill/code/web/service_monitor/services/schedule_service.py` | 新增 _build_cron/_describe_schedule/_update_current_status;init_scheduler 增加 reloader 防护;_add_job 修复 start_date 类型 |
| `skill/code/web/service_monitor/services/runner_service.py` | run_inspection_sync 完成后调用通知服务 |
| `skill/code/web/service_monitor/utils/paths.py` | 增加 NOTIFICATIONS_FILE |
| `skill/code/web/service_monitor/routes.py` | 新增通知配置 6 个路由 + 批量删除 API |
| `skill/code/web/routes/troubleshoot.py` | health 接口增加 scheduler 状态 |
| `skill/code/web/templates/service_monitor/base.html` | 左侧菜单增加"通知配置" |
| `skill/code/web/templates/service_monitor/schedule.html` | 弹窗全面重写(友好调度)+ 保存提示 |
| `skill/code/web/templates/service_monitor/reports.html` | 增加 checkbox + 批量删除 |
| `requirements.txt` | 增加 apscheduler==3.11.0 + croniter==6.2.4 |
| `skill/code/requirements.txt` | 增加 apscheduler>=3.10.0 + croniter>=2.0.0 |
| `deploy/upload_to_server.py` | _REQUIRED_PACKAGES 增加 croniter;启动命令加 FLASK_DEBUG=0 |
### 常用命令 ### 常用命令
```bash ```bash
# 单元测试(51 用例,应全绿 # 本地启动(开发环境,debug 模式
cd skill/code && python -m pytest web/service_monitor/tests/ -v FLASK_DEBUG=1 python skill/code/web/server.py
# 部署到 5.60 # 本地启动(生产模式,定时任务可用)
cd deploy && SSH_PASSWORD='***' python upload_to_server.py python skill/code/web/server.py
# 登录密码 # 部署到 5.60(用 ! 前缀在会话内执行)
admin / Ubains@1357 ! cd deploy && SSH_PASSWORD='***' python upload_to_server.py
# 健康检查 # 健康检查(含 scheduler 状态)
curl -s http://192.168.5.60:8088/api/health curl -s http://192.168.5.60:8088/api/health
# 快速巡检 # 单元测试
curl -s -b cookies.txt "http://192.168.5.60:8088/api/service-monitor/run/stream?target_id=local&suite=quick" cd skill/code && python -m pytest -v
# 全量巡检
curl -s -b cookies.txt "http://192.168.5.60:8088/api/service-monitor/run/stream?target_id=local&suite=full"
``` ```
### 参考目录 ### 服务器凭据
- 参考脚本:`临时目录/服务器监测/lib/` | 账号 | 用户名 | 密码 |
- 需求文档:`Docs/需求文档/服务监测/` |------|--------|------|
- 会话交接:`HANDOFF.md` | SSH | ubains | 见环境变量 SSH_PASSWORD |
| Web 管理员 | admin | 见 .env |
### 数据文件
| 数据 | 文件 |
|------|------|
| 监测目标 | `service_monitor/data/targets.json` |
| 巡检报告 | `service_monitor/data/reports/*.json` |
| 定时任务 | `service_monitor/data/schedules.json` |
| 通知配置 | `service_monitor/data/notifications.json` |
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论