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

docs(service-monitor): 更新 HANDOFF 交接文档 + Toggle 样式优化 PRD 文档

- 更新 HANDOFF.md 记录本轮所有改动(钉钉通知优化、toggle 修复、时区问题、FLASK_DEBUG 修复)
- 新增 Toggle 开关样式优化需求文档和计划执行文档
Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 e1e812c9
# 计划执行:Toggle 开关样式优化
## 文档信息
- 创建时间:2026-07-21
- 关联需求文档:`Docs/需求文档/服务监测/PRD_需求文档_Toggle开关样式优化.md`
- 预计改动文件:1 个
---
## 执行步骤
### Step 1:修改 notification.html 的 toggle CSS
**文件**`skill/code/web/templates/service_monitor/notification.html`
**改动位置**:L20-25(toggle-switch 相关 CSS)
**改前**
```css
.toggle-switch { position: relative; width: 48px; height: 26px; cursor: pointer; }
.toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; }
.toggle-slider { position: absolute; cursor: pointer; inset: 0; background: var(--gray-300); border-radius: 13px; transition: .2s; pointer-events: none; }
.toggle-slider:before { position: absolute; content: ""; height: 20px; width: 20px; left: 3px; bottom: 3px; background: #fff; border-radius: 50%; transition: .2s; pointer-events: none; }
.toggle-switch input:checked + .toggle-slider { background: var(--primary); }
.toggle-switch input:checked + .toggle-slider:before { transform: translateX(22px); }
```
**改后**
```css
.toggle-switch { position: relative; width: 52px; height: 28px; cursor: pointer; display: inline-block; }
.toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; }
.toggle-slider {
position: absolute;
cursor: pointer;
inset: 0;
background: #d1d5db;
border-radius: 14px;
transition: all 0.2s ease;
pointer-events: none;
border: 1px solid #9ca3af;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 22px;
width: 22px;
left: 2px;
bottom: 2px;
background: #fff;
border-radius: 50%;
transition: all 0.2s ease;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.toggle-switch input:checked + .toggle-slider { background: var(--primary); border-color: var(--primary); }
.toggle-switch input:checked + .toggle-slider:before { transform: translateX(24px); }
.toggle-switch:hover .toggle-slider { box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2); }
.toggle-switch input:checked + .toggle-slider:hover { box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.3); }
```
---
### Step 2:本地测试
**命令**
```bash
cd skill/code && python -m pytest web/service_monitor/tests/ -v
```
**预期结果**:所有测试通过。
---
### Step 3:部署验证
**验证**
1. 访问通知配置页面
2. Toggle 开关在白色背景下清晰可见
3. 启用/禁用状态颜色差异明显
4. Hover 时有外发光反馈
5. 点击响应正常,配置表单展开
---
## 执行记录
| 步骤 | 状态 | 时间 | 备注 |
|------|------|------|------|
| Step 1: 修改 CSS | 待执行 | - | - |
| Step 2: 本地测试 | 待执行 | - | - |
| Step 3: 部署验证 | 待执行 | - | - |
\ No newline at end of file
# 需求文档:Toggle 开关样式优化
## 文档信息
- 创建时间:2026-07-21
- 需求来源:用户反馈
- 关联模块:service_monitor/notification
---
## 1. 背景与目标
### 1.1 背景
用户反馈通知配置页面的 toggle 开关"看不清楚",导致多次尝试点击都无法展开配置表单。
经分析,当前 toggle 开关存在以下视觉问题:
1. **对比度不足**:slider 背景色为 `var(--gray-300)`(浅灰色),与白色背景对比度低
2. **未启用状态不明显**:未启用时 slider 为灰色,启用时为蓝色,但颜色差异不够醒目
3. **缺少视觉引导**:用户不知道这是可点击的开关,容易误以为是静态装饰元素
4. **尺寸偏小**:48x26px 的尺寸在移动端或高分辨率屏幕上显得较小
### 1.2 目标
1. 提高 toggle 开关的视觉对比度和识别度
2. 清晰区分启用/禁用状态
3. 增强可点击暗示(光标、阴影、过渡效果)
4. 保持与整体 UI 风格一致
---
## 2. 需求详述
### 2.1 样式优化
#### 当前样式
```css
.toggle-switch { position: relative; width: 48px; height: 26px; cursor: pointer; }
.toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; }
.toggle-slider {
position: absolute;
cursor: pointer;
inset: 0;
background: var(--gray-300); /* 浅灰色,对比度低 */
border-radius: 13px;
transition: .2s;
pointer-events: none;
}
.toggle-slider:before { ... }
.toggle-switch input:checked + .toggle-slider {
background: var(--primary); /* 蓝色 */
}
```
#### 优化后样式
```css
.toggle-switch {
position: relative;
width: 52px; /* 加大尺寸 */
height: 28px;
cursor: pointer;
display: inline-block;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
position: absolute;
}
.toggle-slider {
position: absolute;
cursor: pointer;
inset: 0;
background: #d1d5db; /* 更深的灰色 */
border-radius: 14px;
transition: all 0.2s ease;
pointer-events: none;
border: 1px solid #9ca3af; /* 添加边框增加轮廓 */
}
.toggle-slider:before {
position: absolute;
content: "";
height: 22px;
width: 22px;
left: 2px;
bottom: 2px;
background: #fff;
border-radius: 50%;
transition: all 0.2s ease;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.1); /* 添加阴影增加立体感 */
}
.toggle-switch input:checked + .toggle-slider {
background: var(--primary);
border-color: var(--primary);
}
.toggle-switch input:checked + .toggle-slider:before {
transform: translateX(24px);
}
/* 添加 hover 效果 */
.toggle-switch:hover .toggle-slider {
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
}
.toggle-switch input:checked + .toggle-slider:hover {
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.3);
}
```
### 2.2 优化要点
| 属性 | 当前 | 优化后 | 目的 |
|------|------|--------|------|
| 尺寸 | 48x26px | 52x28px | 更容易被点击 |
| 未启用背景 | var(--gray-300) | #d1d5db | 更深灰色,对比度更高 |
| 边框 | 无 | 1px solid #9ca3af | 增加轮廓,更明显 |
| 圆点阴影 | 无 | box-shadow | 立体感,更像真实开关 |
| Hover 效果 | 无 | 外发光 | 暗示可点击 |
| 过渡效果 | .2s | all 0.2s ease | 平滑过渡 |
### 2.3 颜色对比
| 状态 | 背景色 | 边框色 | 说明 |
|------|--------|--------|------|
| 禁用 | #d1d5db | #9ca3af | 深灰色,清晰可见 |
| 启用 | var(--primary) | var(--primary) | 蓝色,醒目 |
| Hover(禁用) | #d1d5db + 外发光 | #9ca3af | 暗示可点击 |
| Hover(启用) | var(--primary) + 外发光 | var(--primary) | 增强反馈 |
---
## 3. 验收标准
| 标准 | 验证方法 |
|------|---------|
| Toggle 开关在白色背景下清晰可见 | 视觉检查 |
| 启用/禁用状态颜色差异明显 | 视觉检查 |
| Hover 时有视觉反馈 | 鼠标悬停测试 |
| 点击响应正常 | 点击展开配置表单 |
| 移动端显示正常 | Chrome DevTools 模拟移动设备 |
---
## 4. 影响范围
仅影响通知配置页面(`notification.html`)的 toggle 开关样式。不影响其他页面。
\ No newline at end of file
# HANDOFF — 定时任务执行状态展示 + 立即执行功能 # HANDOFF — 钉钉通知优化 + Toggle 开关修复 + 时区问题修复
> 最后更新:2026-07-21 16:30 | 分支:troubleshoot-ai-assistant | 负责人:czj > 最后更新:2026-07-21 19:30 | 分支:troubleshoot-ai-assistant | 负责人:czj
--- ---
## 1. 我们在做什么 ## 1. 我们在做什么
本轮解决用户反馈的"定时任务还是没执行"问题。经排查 `/api/health` 确认 `scheduler.running=true, job_count=3` —— **APScheduler 已启动、任务已注册**,问题不在"调度器没启动",而在"到点没触发(疑似时区)或触发后执行失败"。 本轮解决用户反馈的**钉钉通知功能完善**问题,涉及三个层面:
但页面**完全看不到执行情况**,用户无从判断。本次新增: 1. **钉钉配置与通知格式优化**:原来只有 `webhook` 字段,缺少 `webhook_url`/`secret` 区分;通知消息只展示汇总数字,不展示异常项详情;secret 可被复制粘贴。
1. **「上次执行」列**:展示时间 + 成功/失败/运行中徽章 + 报告链接 2. **Toggle 开关不可点击**:通知配置页面的邮件/钉钉/企业微信三组 toggle 开关在用户浏览器上无法点击展开,原因是 `toggle-slider`(span 元素)覆盖在 checkbox 上,`pointer-events` 未设置为 `none`,拦截了点击事件。
2. **「立即执行」按钮**:手动触发一次,后台线程执行、立即返回、状态自动刷新
3. **异常路径状态统一**`last_run_status``current_status` 保持一致
这套组合可定位"不执行"根因:点「立即执行」成功 → 执行链路没问题,问题在 cron 到点(对比"下次执行"时间,若晚 8 小时即确认时区问题);失败 → 徽章直接暴露错误 3. **定时任务不执行 & 时区偏差**:承接上一轮遗留问题,最终定位到 `FLASK_DEBUG` 默认值不一致(`'1'` vs `'0'`)、服务器 UTC 时区导致 cron 偏移 8 小时
> 历史背景:上一轮已完成"定时任务弹窗优化(commit 28f55365)+ 通知配置(fdb0ee63)+ 定时任务不执行三根因修复(9d887980)"。本轮在其基础上补执行可见性 > 历史背景:上两轮已完成"定时任务弹窗优化 + 通知配置 + 定时任务不执行三根因修复",本轮在此基础上完善钉钉通知和修复遗留问题
--- ---
## 2. 已经完成了什么 ## 2. 已经完成了什么
### ✅ 定时任务执行状态展示 + 立即执行(本轮 ### ✅ 钉钉通知优化(commit a7f7e175
**页面新增「上次执行」列** **配置字段更新**
- 表格从 7 列扩展为 8 列:`任务名称 | 目标 | 套件 | 调度周期 | 启用 | 上次执行 | 下次执行 | 操作` - `webhook``webhook_url`(更清晰,符合钉钉官方命名)
- 显示时间(`last_run_at`)、执行状态徽章(`current_status` 优先)、报告链接(`last_report_id` - 兼容旧字段加载:`_load_config()` 中自动迁移 `webhook``webhook_url`
- 徽章样式:🔄 执行中(蓝)、✅ 成功(绿)、❌ 失败/⚠️ 异常(红)、⏸ 待执行(灰)
- 响应式:平板端隐藏"套件""上次执行""下次执行"
**新增「立即执行」按钮** **通知格式升级**
- 操作列新增「执行」按钮(蓝色),点击后 POST `/api/service-monitor/schedules/<id>/run` - 消息类型从 `text` 改为 `markdown`
- 后台线程执行巡检(`run_now()` 函数),HTTP 立即返回,不阻塞 - 异常项分类展示:🔴 严重(最多 5 项)、🟡 警告(最多 3 项)
- 前端立即显示"执行中"徽章,启动 3 秒轮询直到完成 - 格式:`- 模块名:指标名 = 值(阈值:xxx)`
- 禁用状态下也可执行(手动触发是显式动作) - 报告链接使用 Markdown 格式:`[查看完整报告](url)`
**后端改动** **安全控制**
- `schedule_service.py`:抽 `_run_job_body()` 共享执行体、新增 `run_now()``create_schedule()``"current_status": "idle"`、异常路径状态统一为 `error` - `secret` 输入框允许粘贴、禁止复制/剪切/右键菜单
- `routes.py``page_schedule` 注入 `run_badge`/`run_label`;新增 `POST .../schedules/<id>/run` 路由
**验证** **验证**:51 用例全绿 ✅;钉钉测试消息发送成功 ✅
- 本地测试:167 用例 + service_monitor 51 用例全绿 ✅
- 新路由已注册 ✅
- 待 5.60 部署后验证实际执行效果
--- ### ✅ Toggle 开关修复(commit a19fdbb2, e1e812c9, ff9181c9)
### ✅ 定时任务弹窗交互优化(commit 28f55365) **点击无响应根因**
- `toggle-slider`(span 元素)覆盖在 checkbox 上,`pointer-events` 默认为 `auto`,拦截了点击事件
- 点击 slider 时事件无法传递到内部的 label 和 checkbox input
- 删除 Cron 输入框,改为**重复周期单选组**(每天/工作日/每周) **修复**
- 新增**时:分下拉选择器**(0-23 时,每5分钟) - `toggle-slider``toggle-slider:before``pointer-events: none`
- 新增**星期多选按钮组**(仅每周模式,至少选1个) - `toggle-switch``cursor: pointer`
- 新增**生效日期/失效日期**选择器
- 列表"Cron"列改为**"调度周期"**,显示自然语言描述(如"工作日 09:30")
- 后端新增 `_build_cron()` / `_describe_schedule()` 自动转换
- 数据模型新增 `repeat_mode`/`hour`/`minute`/`weekdays`/`start_date`/`end_date`
- **验证**:5.60 浏览器验证通过,创建任务正确显示,编辑回填正确
### ✅ 通知配置功能(commit fdb0ee63) **样式优化**
- 尺寸加大:48x26px → 52x28px
- 禁用色加深:`var(--gray-300)``#d1d5db`
- 添加边框:`1px solid #9ca3af`
- 圆点添加阴影:立体感
- 添加 hover 外发光效果
- 左侧菜单新增 🔔 通知配置(仅管理员可见,位于定时任务与目标管理之间) ### ✅ FLASK_DEBUG 默认值修复(commit 0d84df9f)
- 新增 `notification_service.py`:邮件/钉钉/企业微信配置 CRUD、测试发送、巡检后通知
- 新增 `notification.html`:三渠道配置卡片 + 触发条件 + 测试按钮
- `runner_service.py``run_inspection_sync()` 完成后调用通知服务
- 敏感字段用 Fernet 加密存储(复用 `crypto.py``encrypt_password`
- **验证**:5.60 浏览器验证页面功能正常
### ✅ 定时任务不执行修复(commit 9d90d646) **根因**`schedule_service.py:383``FLASK_DEBUG` 默认值为 `'1'`,与 `server.py:151``'0'` 不一致。systemd 启动时未设环境变量,`init_scheduler()` 判定为 debug 模式直接 return,APScheduler 未初始化。
**根因 1:Flask debug reloader**(核心) **修复**
- `server.py``FLASK_DEBUG` 默认值从 `'1'` 改为 `'0'` - `schedule_service.py:385``FLASK_DEBUG` 默认值改为 `'0'`
- `init_scheduler()` 增加 `WERKZEUG_RUN_MAIN` 环境变量检查 - `/etc/systemd/system/troubleshoot.service`:添加 `Environment=FLASK_DEBUG=0`
- `upload_to_server.py` 启动命令显式 `FLASK_DEBUG=0`
**根因 2:start_date 类型错误** **验证**`/api/health` 返回 `scheduler: {running: true, job_count: 3}`
- `_add_job()` 中字符串转 `datetime` 并附加 `timezone.utc`,end_date 设为 `23:59:59`
**根因 3:croniter 依赖缺失** ### ✅ 时区问题修复(commit 1147566e, 712feaa9)
- `requirements.txt` 增加 `apscheduler==3.11.0` + `croniter==6.2.4`
- `skill/code/requirements.txt` 增加对应依赖
- `upload_to_server.py``_REQUIRED_PACKAGES` 增加 `'croniter'`
**验证**`/api/health` 返回 `scheduler: {'running': true, 'job_count': 3}` **根因**:服务器 UTC 时区,`CronTrigger.from_crontab()` 默认使用本地时区解析 cron 表达式。用户配"每天 18:15"实际在 UTC 18:15(北京时间凌晨 02:15)触发。
### ✅ 其他改进 **修复**
- 新增 `SHANGHAI_TZ = pytz_timezone("Asia/Shanghai")` 常量
- `_add_job()``CronTrigger.from_crontab(..., timezone=SHANGHAI_TZ)`
- `_calc_next_run()`:使用 `datetime.now(SHANGHAI_TZ)` 作为基准
- `_now()`:返回北京时间字符串
- 所有 `datetime.now()` 改为 `datetime.now(SHANGHAI_TZ)``_now()`
- **编辑保存提示**`schedule.html` 成功后 `alert('保存成功')` **依赖补充**`pytz>=2024.1` 在三处同步声明
- **执行状态显示**`schedule_service.py` 新增 `_update_current_status()``current_status` 字段(idle/running/success/failed)
- **批量删除报告**`reports.html` checkbox + 全选 + 批量删除按钮;`routes.py` 新增 `DELETE /api/service-monitor/reports/batch`
- **health 接口增强**`routes/troubleshoot.py` 增加 `scheduler` 状态字段
### ✅ 提交并推送(commit 9d887980 / 633da268) **验证**`_calc_next_run('30 18 * * *')` 返回北京时间 18:30 ✅
5 个提交已推送到 `origin troubleshoot-ai-assistant` ### ✅ 定时任务执行状态展示 + 立即执行(commit d10c77e4)
```
28f55365 feat(service-monitor): 定时任务弹窗交互优化 - 表格新增「上次执行」列(时间 + 成功/失败徽章 + 报告链接)
fdb0ee63 feat(service-monitor): 通知配置功能(邮件/钉钉/企业微信) - 操作列新增「执行」按钮(后台线程执行,3 秒轮询自动刷新)
9d90d646 fix(service-monitor): 定时任务不执行修复 + 依赖补充 + 报告批量删除 - 响应式适配
9d887980 docs: 更新 HANDOFF 交接文档
633da268 docs(service-monitor): 定时任务弹窗优化 PRD 文档
```
--- ---
## 3. 当前卡在哪 ## 3. 当前卡在哪
**无卡点**代码改动完成、测试通过、待部署到 5.60 **无卡点**所有功能已实现、部署到 5.60、验证通过
**待验证项** **确认状态**
1. **本地验证**:起服务 → 定时任务页点「执行」→ 徽章刷新 → 报告链接跳转 - 定时任务已能正常触发和执行 ✅
2. **5.60 部署验证**:点「立即执行」若成功 → 对比"下次执行"时间判断是否时区问题 - 钉钉通知已能正常发送 ✅(用户确认"定时任务是会发送的")
- 通知配置页面 toggle 开关可点击 ✅
--- ---
## 4. 下一步计划 ## 4. 下一步计划
1. **部署到 5.60**(P0) 1. **创建 Merge Request**(P1)
- 执行 `! cd deploy && SSH_PASSWORD='***' python upload_to_server.py`
- 刷新页面确认「上次执行」列显示、「执行」按钮可见
2. **验证立即执行功能**(P0)
- 点「执行」→ 徽章变"执行中"→ 数秒后自动刷新为"成功/失败"
- 点「报告」链接跳转正常
3. **定位不执行根因**(P1)
- 若「立即执行」成功 → 问题在 cron 到点;对比"下次执行"时间,晚 8 小时即确认时区问题(需后续修 CronTrigger 时区)
- 若「立即执行」失败 → 徽章直接暴露错误,看日志定位
4. **创建 Merge Request**(P2)
- 访问 http://git.ubainsyun.com/bing/ubains-module-test/merge_requests/new?merge_request%5Bsource_branch%5D=troubleshoot-ai-assistant - 访问 http://git.ubainsyun.com/bing/ubains-module-test/merge_requests/new?merge_request%5Bsource_branch%5D=troubleshoot-ai-assistant
- 将 troubleshoot-ai-assistant 合并到 master - 将 troubleshoot-ai-assistant 合并到 master
2. **清理测试数据**(P2)
- 删除测试用的定时任务(工作日巡检、每周巡检)
- 在 5.60 上配置 `MONITOR_ENC_KEY` 环境变量(当前使用开发兜底密钥)
3. **企业微信通知格式优化**(P3)
- 与钉钉格式统一,增加异常项展示
--- ---
## 5. 踩过的坑(绝对不要再踩) ## 5. 踩过的坑(绝对不要再踩)
### 坑 0:定时任务"看起来没执行",实际是页面没展示执行状态(本轮) ### 坑 1:Toggle 开关点击无响应 - pointer-events 拦截(本轮)
**坑**用户反馈"还是没执行",但 `/api/health` 显示 `scheduler.running=true, job_count:3` —— 调度器在跑、任务已注册。真正问题是 `schedule.html` 的"状态"列只显示启用/禁用开关,`last_run_at`/`last_run_status`/`current_status`/`last_report_id` 这些字段数据都有但 UI 没渲染,用户无从判断 **坑**`toggle-slider`(span 元素)覆盖在 checkbox 上,`pointer-events` 默认为 `auto`,点击 slider 时事件被拦截,无法传递到内部的 label 和 checkbox input。用户点击 toggle 开关没反应,反复点击以为页面坏了
**避免方法** **避免方法**
- 定时任务功能必须有**执行可见性**:上次执行时间 + 成功/失败徽章 + 报告链接 - 覆盖层元素(如 slider)必须加 `pointer-events: none`
- 加「立即执行」按钮可主动验证执行链路,区分"到点没触发"和"触发后失败" - 自定义 checkbox 样式时,点击事件必须能穿透到 input 元素
- 测试时用 `elementFromPoint()` 检查点击命中目标
### 坑 0.5:疑似时区问题(待验证 ### 坑 2:FLASK_DEBUG 默认值不一致导致 scheduler 不初始化(最严重
**坑**`_add_job()``CronTrigger.from_crontab(cron)`,默认按服务器本地时区解析 cron 字段。若 5.60 系统时区是 UTC,用户配"每天 09:30"实际会在北京时间 17:30 才触发,看起来像"没执行" **坑**`schedule_service.py:383``FLASK_DEBUG` 默认值为 `'1'``server.py:151` 的默认值为 `'0'`。systemd 启动时未设环境变量,`init_scheduler()` 判定为 debug 模式直接 return 不初始化 APScheduler
**避免方法**:部署后对比"下次执行"时间与预期,晚 8 小时即确认。后续可显式锁定 `CronTrigger` 时区为 `Asia/Shanghai` **症状**`/api/health` 返回 `scheduler: {running: false, reason: "APScheduler 未初始化"}`,定时任务永远不执行。
### 坑 1:Flask debug 模式的 reloader 导致 APScheduler 失效(最严重)
**坑**`server.py` 默认 `FLASK_DEBUG='1'`,Flask debug 模式用 Werkzeug reloader fork 出子进程后,父进程中初始化的 APScheduler 被丢弃,子进程 `_scheduler` 全局变量重置为 None。症状是定时任务"看起来注册成功但永远不执行"。
**为什么踩**:开发环境默认开 debug 方便热重载,但 reloader 双进程机制与 APScheduler 单进程初始化冲突。
**避免方法** **避免方法**
- 生产环境**必须** `FLASK_DEBUG=0` - 新增环境变量默认值时,**必须检查所有使用该变量的地方是否一致**
- `init_scheduler()` 检查 `WERKZEUG_RUN_MAIN` 环境变量,仅在子进程初始化 - systemd 服务文件显式设置 `Environment=FLASK_DEBUG=0`(双保险)
- health 接口暴露 scheduler 状态快速诊断
### 坑 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` ### 坑 3:CronTrigger 默认使用系统时区
**避免方法**:用 `datetime.strptime(date_str, "%Y-%m-%d").replace(tzinfo=timezone.utc)` 转换。end_date 当天也要执行,设为 `23:59:59` **坑**`CronTrigger.from_crontab(cron)` 默认使用本地系统时区。服务器 UTC 时区时,用户配的"18:15"实际在 UTC 18:15(北京时间凌晨 02:15)才触发
### 坑 3:croniter 依赖未声明 **避免方法**:显式指定时区:`CronTrigger.from_crontab(cron, timezone=SHANGHAI_TZ)`
**坑**`apscheduler``upload_to_server.py``_REQUIRED_PACKAGES` 中声明了,`croniter` 没有。缺失导致 `next_run_at` 为 null,任务无法注册。 ### 坑 4:时间戳显示 UTC 时间误导用户
**避免方法****所有 Python 依赖必须在三处同步声明** **坑**`datetime.now()` 返回服务器本地时间(UTC),`last_run_at` 显示 `10:39` 用户以为是北京时间 10:39,实际是 UTC 10:39(北京时间 18:39)。
1. `requirements.txt`(容器化部署)
2. `skill/code/requirements.txt`(开发环境)
3. `deploy/upload_to_server.py``_REQUIRED_PACKAGES`(SSH 部署)
### 坑 4:notification_service 导入了不存在的函数名 **避免方法**:所有面向用户的时间戳统一使用 `datetime.now(SHANGHAI_TZ)` 或封装的 `_now()` 函数。
**坑**:导入了 `encrypt_value`/`decrypt_value`,但 `crypto.py` 实际函数名是 `encrypt_password`/`decrypt_password`,导致 `ImportError` 服务启动崩溃。 ### 坑 5:定时任务"看起来没执行",实际是页面没展示
**避免方法**:新增模块前检查依赖模块的实际函数签名,不要凭记忆写函数名 **坑**`schedule.html` 的"状态"列只显示启用/禁用开关,`last_run_at`/`last_run_status` 等字段数据都有但 UI 没渲染。用户反馈"没执行",实际可能已执行只是看不到
### 坑 5:5.60 服务器 pip install 需要特殊参数 **避免方法**:定时任务功能必须有执行可见性 + 立即执行按钮可主动验证。
**坑**:5.60 是 Ubuntu + Python 3.14,`pip install``externally-managed-environment`,需加 `--break-system-packages` ### 坑 6:SSH 后台启动进程随 channel 关闭被杀
**避免方法**`upload_to_server.py` 的依赖安装命令已加此参数;后续手动安装时也要加 **坑**:用 `nohup python3 server.py &` 后台启动,SSH channel 关闭后进程被 SIGHUP 杀掉
### 坑 6:APScheduler 验证要用 HTTP 接口而非新进程 **避免方法**:用 `setsid``screen` 创建新会话,或 `nohup cmd > log 2>&1 < /dev/null & disown`
**坑**:用 `python3 -c "from schedule_service import ..."` 验证 scheduler 状态时,这是**全新独立进程**`_scheduler` 当然是 None,不能反映运行中服务进程的真实状态。
**避免方法**:通过 HTTP 接口(health 加 scheduler 字段)检查运行中进程的 scheduler 状态,不要新开 Python 进程验证。
--- ---
## 6. 关键文件与命令速查 ## 6. 关键文件与命令速查
### 本次修改文件
| 文件 | 改动 |
|------|------|
| `skill/code/web/service_monitor/services/notification_service.py` | 配置字段 `webhook``webhook_url`、兼容旧字段、钉钉通知改 markdown 格式、异常项提取展示 |
| `skill/code/web/templates/service_monitor/notification.html` | toggle 点击修复 + 样式优化、secret 安全控制、字段名更新 |
| `skill/code/web/service_monitor/services/schedule_service.py` | FLASK_DEBUG 默认值修复、时区固定为 Asia/Shanghai、`_now()` 北京时间、`run_now()` 手动触发 |
| `skill/code/web/service_monitor/routes.py` | `page_schedule` 注入徽章、新增 `POST .../schedules/<id>/run` |
| `skill/code/web/templates/service_monitor/schedule.html` | 表格 7→8 列、执行状态徽章、「执行」按钮、3 秒轮询 |
| `requirements.txt` / `skill/code/requirements.txt` / `deploy/upload_to_server.py` | +`pytz>=2024.1` |
### 本次新增文件 ### 本次新增文件
| 文件 | 用途 | | 文件 | 用途 |
|------|------| |------|------|
| `skill/code/web/service_monitor/services/notification_service.py` | 通知服务(邮件/钉钉/企业微信) | | `Docs/需求文档/服务监测/PRD_需求文档_钉钉通知优化.md` | 钉钉通知优化需求 |
| `skill/code/web/templates/service_monitor/notification.html` | 通知配置页面 | | `Docs/需求文档/服务监测/PRD_计划执行_钉钉通知优化.md` | 钉钉通知优化计划 |
| `Docs/需求文档/服务监测/PRD_需求文档_定时任务弹窗优化.md` | 弹窗优化 PRD | | `Docs/需求文档/服务监测/PRD_需求文档_Toggle开关样式优化.md` | Toggle 开关样式优化需求 |
| `Docs/需求文档/服务监测/PRD_计划执行_定时任务弹窗优化.md` | 弹窗优化计划 | | `Docs/需求文档/服务监测/PRD_计划执行_Toggle开关样式优化.md` | Toggle 开关样式优化计划 |
| `Docs/需求文档/服务监测/PRD_需求文档_通知配置.md` | 通知配置 PRD | | `Docs/需求文档/服务监测/PRD_问题处理_定时任务时区问题.md` | 时区问题处理 |
| `Docs/需求文档/服务监测/PRD_计划执行_通知配置.md` | 通知配置计划 | | `Docs/需求文档/服务监测/PRD_计划执行_定时任务时区问题.md` | 时区问题修复计划 |
| `Docs/需求文档/服务监测/PRD_问题处理_定时任务与报告优化.md` | 问题处理文档 |
| `Docs/需求文档/服务监测/PRD_计划执行_定时任务与报告优化.md` | 问题修复计划 |
### 本次修改文件 ### 服务器配置
| 文件 | 改动要点 | | 配置 | 位置 |
|------|---------| |------|------|
| `skill/code/web/service_monitor/services/schedule_service.py` | 抽 `_run_job_body`、新增 `run_now``create_schedule``current_status:idle`、异常状态统一为 `error` | | `FLASK_DEBUG=0` | `/etc/systemd/system/troubleshoot.service` [Service] 段 |
| `skill/code/web/service_monitor/routes.py` | `page_schedule` 注入 `run_badge`/`run_label`;新增 `POST .../schedules/<id>/run` | | 通知配置 | `/opt/troubleshoot/web/service_monitor/data/notifications.json` |
| `skill/code/web/templates/service_monitor/schedule.html` | 表格 7→8 列、执行状态徽章 CSS、执行按钮、3 秒轮询自动刷新、响应式适配 | | 定时任务 | `/opt/troubleshoot/web/service_monitor/data/schedules.json` |
### 历史修改文件(前几轮)
| 文件 | 改动要点 |
|------|---------|
| `skill/code/web/server.py` | FLASK_DEBUG 默认值改为 `'0'` |
| `skill/code/web/service_monitor/services/schedule_service.py` | _build_cron/_describe_schedule/_update_current_status + reloader 防护 + 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
# 本地启动(开发 debug 模式) # 本地启动
FLASK_DEBUG=1 python skill/code/web/server.py
# 本地启动(生产模式,定时任务可用)
python skill/code/web/server.py python skill/code/web/server.py
# 部署到 5.60(用 ! 前缀在会话内执行,避免读不到 SSH_PASSWORD) # 部署到 5.60
! cd deploy && SSH_PASSWORD='***' python upload_to_server.py ! 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
# 查看定时任务数据
ssh ubains@192.168.5.60 "cat /opt/troubleshoot/web/service_monitor/data/schedules.json"
# 通知配置数据
ssh ubains@192.168.5.60 "cat /opt/troubleshoot/web/service_monitor/data/notifications.json"
# 服务日志
ssh ubains@192.168.5.60 "journalctl -u troubleshoot --since '30 min ago' --no-pager"
# 单元测试 # 单元测试
cd skill/code && python -m pytest -v cd skill/code && python -m pytest -v
cd skill/code && python -m pytest web/service_monitor/tests/ -v
``` ```
### 数据文件
| 数据 | 文件 |
|------|------|
| 监测目标 | `service_monitor/data/targets.json` |
| 巡检报告 | `service_monitor/data/reports/*.json` |
| 定时任务 | `service_monitor/data/schedules.json` |
| 通知配置 | `service_monitor/data/notifications.json` |
### 未提交文件 ### 未提交文件
- `HANDOFF.md`(本轮待提交)
- `skill/code/web/users.json`(仅登录统计更新,无需提交) - `skill/code/web/users.json`(仅登录统计更新,无需提交)
---
## 提交历史(本轮)
```
e1e812c9 style(service-monitor): toggle 开关样式优化
ff9181c9 fix(service-monitor): secret 允许粘贴但禁止复制
a19fdbb2 fix(service-monitor): toggle 开关点击无响应修复
a7f7e175 feat(service-monitor): 钉钉通知优化
712feaa9 fix(service-monitor): 所有时间戳统一使用北京时区
1147566e fix(service-monitor): 定时任务时区固定为 Asia/Shanghai
0d84df9f fix(service-monitor): init_scheduler FLASK_DEBUG 默认值与 server.py 统一为 '0'
d10c77e4 feat(service-monitor): 定时任务执行状态展示 + 立即执行按钮
```
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论