# 实施计划:admin-web 后台重构优化 ## 概述 按"后端 API → 前端页面 → 联调收尾"的顺序实施。后端新增 3 个 API 端点,前端重组菜单和路由、新建 4 个页面组件,最后归档老页面并补充 E2E 测试。每个阶段结束后插入检查点。 ## 任务 - [x] 1. 后端:新增 Pydantic 模型与工具函数 - [x] 1.1 创建 `UpdateTriggerConfigRequest`、`DbHealthItem`、`UnifiedTriggerItem` 三个 Pydantic 模型 - 在 `apps/backend/app/schemas/` 下新建或扩展 schema 文件 - `UpdateTriggerConfigRequest` 包含 `model_validator` 确保至少提供一个字段 - `DbHealthItem.status` 使用 `Literal['connected', 'disconnected']` - `UnifiedTriggerItem.source` 使用 `Literal['biz', 'ai', 'etl']` - _Requirements: 5.1, 5.2, 5.3, 6.1, 6.2, 4.1, 4.2_ - [x] 1.2 实现 cron 表达式校验工具函数 `validate_cron_expression` - 在 `apps/backend/app/utils/` 下新建 `cron_validator.py` - 校验 5 字段 cron 语法,支持 `*` 和数值范围 - _Requirements: 5.4_ - [x] 1.3 编写属性测试:cron 表达式校验拒绝无效输入 - **Property 6: 触发器配置校验拒绝无效输入** - 使用 hypothesis 生成任意非 5 字段字符串、超范围数值字段 - 验证 `validate_cron_expression` 对无效输入返回 False - **验证: 需求 5.4, 5.5** - [x] 1.4 编写单元测试:cron 校验与 Pydantic 模型 - 测试有效 cron 表达式通过校验 - 测试无效格式(字段数不对、超范围值)被拒绝 - 测试 `UpdateTriggerConfigRequest` 空请求体触发 ValidationError - 测试 `DbHealthItem` disconnected 状态下指标字段可为 null - _Requirements: 5.4, 5.5, 6.3_ - [x] 2. 后端:实现 PATCH /api/trigger-jobs/{id}/config 端点 - [x] 2.1 在 `apps/backend/app/routers/trigger_jobs.py` 新增 PATCH 端点 - 查询 trigger_job 是否存在(不存在返回 404) - 校验 cron_expression 格式(无效返回 422) - 校验 interval_seconds >= 1(无效返回 422) - 更新 trigger_config JSON 中对应字段 - 重新计算 next_run_at - 返回更新后的完整 trigger_job - JWT 认证与现有端点一致 - _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7_ - [x] 2.2 编写属性测试:触发器配置编辑不变量 - **Property 4: 触发器配置编辑不变量** - 使用 hypothesis 生成有效的 cron_expression 和 interval_seconds - 验证更新后 trigger_job 中除 trigger_config 和 next_run_at 外的字段不变 - **验证: 需求 4.7** - [x] 2.3 编写属性测试:触发器配置更新正确性 - **Property 5: 触发器配置更新正确性** - 使用 hypothesis 生成有效的 cron_expression(5 字段格式)和 interval_seconds(>= 1) - 验证返回的 trigger_config 对应字段值等于请求值,且 next_run_at 非 null - **验证: 需求 5.2, 5.3, 5.7** - [x] 2.4 编写单元测试:PATCH 端点边界条件 - 测试不存在的 job_id 返回 404 - 测试空请求体返回 422 - 测试无 JWT 返回 401 - _Requirements: 5.1, 5.4, 5.5, 5.6_ - [x] 3. 后端:实现 GET /api/admin/db-health 端点 - [x] 3.1 新建 `apps/backend/app/routers/admin_db_health.py` 路由模块 - 遍历 4 个数据库 DSN 配置(etl_feiqiu / test_etl_feiqiu / zqyy_app / test_zqyy_app) - 对每个库执行诊断 SQL:`pg_stat_activity`(连接池)、`pg_database_size()`(大小)、慢查询统计 - 连接失败时返回 `status: "disconnected"`,其余字段为 null - JWT 认证 - 在 `apps/backend/app/main.py` 中注册路由 - _Requirements: 6.1, 6.2, 6.3, 6.4_ - [x] 3.2 编写属性测试:DB 健康 API 已连接数据库返回完整指标 - **Property 1: DB 健康 API 已连接数据库返回完整指标** - 使用 hypothesis 生成 connected 状态的 DbHealthItem - 验证 active_connections、idle_connections、db_size_mb、slow_query_count 均非 null 且类型正确 - **验证: 需求 2.4, 6.2** - [x] 3.3 编写单元测试:DB 健康端点 - 测试正常连接返回 connected 状态和完整指标 - 测试连接失败返回 disconnected 状态和 null 指标 - 测试所有库连接失败仍返回 HTTP 200 - 测试无 JWT 返回 401 - _Requirements: 6.1, 6.2, 6.3, 6.4_ - [x] 4. 后端:实现 GET /api/admin/triggers/unified 端点 - [x] 4.1 新建 `apps/backend/app/routers/admin_triggers.py` 路由模块 - 查询 `biz.trigger_jobs`,映射 source="biz" - 查询 `biz.ai_trigger_jobs`(最近 100 条),映射 source="ai" - 查询 `scheduled_tasks`,映射 source="etl" - 统一字段格式后合并返回 - 某数据源查询失败时记录日志,返回其他数据源数据 - JWT 认证 - 在 `apps/backend/app/main.py` 中注册路由 - _Requirements: 4.1, 4.2, 4.3_ - [x] 4.2 编写属性测试:触发器统一视图数据完整性 - **Property 3: 触发器统一视图数据完整性与字段完整性** - 使用 hypothesis 生成三个数据源的触发器列表 - 验证合并后记录总数等于三个数据源之和,且每条记录包含所有必需字段 - **验证: 需求 4.1, 4.2** - [x] 4.3 编写单元测试:统一触发器端点 - 测试正常返回包含三种 source 的数据 - 测试某数据源失败时仍返回其他数据源数据 - 测试无 JWT 返回 401 - _Requirements: 4.1, 4.2, 4.3_ - [x] 5. 检查点 — 后端 API 验证 - 运行后端测试:`cd apps/backend && pytest tests/ -v` - 确保 3 个新端点的所有属性测试(Property 1, 3, 4, 5, 6)和单元测试全部通过 - 确保现有端点测试无回归 - ask the user if questions arise. - [x] 6. 前端:新增 API 模块与 TypeScript 类型 - [x] 6.1 创建前端 API 模块和类型定义 - 新建 `src/api/dbHealth.ts`:`DbHealthItem` 接口 + `fetchDbHealth()` 函数 - 新建 `src/api/triggers.ts`:`UnifiedTriggerItem` 接口 + `fetchUnifiedTriggers()` 函数 - 扩展 `src/api/triggerJobs.ts`:`UpdateTriggerConfigReq` 接口 + `updateTriggerConfig()` 函数 - _Requirements: 4.1, 5.1, 6.1_ - [x] 7. 前端:侧边栏菜单重组与路由重构 - [x] 7.1 重构 App.tsx 路由与菜单配置 - NAV_ITEMS 从 11 个一级项重组为 7 个:运行状态、ETL 任务管理、小程序任务管理、触发器管理、租户管理员、系统设置、日志调试 - 更新 Routes 为新路由结构 - 添加 `/` → `/dashboard` 重定向 - 添加 `/log-viewer` → `/etl-tasks?tab=manager` 重定向 - 登录成功后导航到 `/dashboard` - 侧边栏高亮当前所在模块对应的菜单项 - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 8.1, 8.2, 8.3, 10.1, 10.2, 10.3_ - [x] 7.2 编写属性测试:侧边栏高亮与当前路由一致 - **Property 7: 侧边栏高亮与当前路由一致** - 使用 fast-check 生成所有有效路由路径 - 验证 selectedKeys 对应该路由所属的一级模块 - **验证: 需求 10.3** - [x] 7.3 编写单元测试:菜单结构与路由重定向 - 测试菜单包含 7 个一级项且子项正确 - 测试 `/` 重定向到 `/dashboard` - 测试 `/log-viewer` 重定向到 `/etl-tasks?tab=manager` - _Requirements: 1.1, 8.3, 10.1, 10.2_ - [x] 8. 前端:实现 Dashboard 运行状态仪表盘 - [x] 8.1 拆分 OpsPanel 为可独立使用的子组件 - 从 OpsPanel.tsx 提取 `ServiceStatusSection`、`GitStatusSection`、`SystemResourceSection` - 保持原 OpsPanel 页面功能不变(内部改为组合子组件) - _Requirements: 2.6_ - [x] 8.2 创建 DbHealthCard 组件 - 新建 `src/components/DbHealthCard.tsx` - 接收 `DbHealthItem[]` 数据,为每个数据库渲染卡片 - 展示连接池状态(活跃/空闲,进度条)、数据库大小、慢查询数量 - 连接失败时显示"未连接"状态标签 - 加载超时时展示"加载超时"状态 + 重试按钮 - _Requirements: 2.3, 2.4, 2.5_ - [x] 8.3 创建 Dashboard 页面 - 新建 `src/pages/Dashboard.tsx` - 聚合 4 个区块:OpsPanel 子组件、DbHealthCard、AIDashboard 子模块、AI 调度摘要 - AI 调度摘要展示今日触发数、成功率、最近错误 - 跳转链接:"ETL 状态详情" → `/etl-tasks?tab=status`、"触发器详情" → `/triggers?tab=all`、"AI 调度详情" → `/triggers?tab=ai` - _Requirements: 2.1, 2.2, 2.6, 2.7, 2.8, 7.1, 7.2_ - [x] 8.4 编写单元测试:Dashboard 页面 - 测试 4 个区块正确渲染 - 测试跳转链接指向正确路由 - 测试 DbHealthCard connected/disconnected 状态渲染 - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5_ - [x] 9. 前端:实现 ETLTasks 页面(Tab 合并) - [x] 9.1 创建 ETLTasks 页面 - 新建 `src/pages/ETLTasks.tsx` - 使用 Ant Design `Tabs` 组件,3 个 Tab:config(任务配置)、manager(任务管理)、status(ETL 状态) - 各 Tab 渲染对应的现有组件:``、``、`` - Tab 切换通过 `useSearchParams` 同步 URL 查询参数 `?tab=config|manager|status` - `destroyInactiveTabPane={false}` 保持 Tab 状态不丢失 - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 10.4_ - [x] 9.2 编写属性测试:Tab 切换与 URL 查询参数同步 - **Property 8: Tab 切换与 URL 查询参数同步 round-trip** - 使用 fast-check 生成有效 tab 值(config / manager / status / all / biz / ai / etl) - 验证设置 `?tab=X` 后激活的 Tab 为 X;点击 Tab Y 后 URL 参数更新为 `?tab=Y` - **验证: 需求 10.4** - [x] 9.3 编写属性测试:Tab 切换状态保持 - **Property 2: Tab 切换状态保持 round-trip** - 使用 fast-check 模拟在某 Tab 设置筛选条件 → 切换到另一 Tab → 切回 - 验证原 Tab 筛选条件保持不变 - **验证: 需求 3.5** - [x] 9.4 编写单元测试:ETLTasks 页面 - 测试默认 Tab 为 config - 测试 3 个 Tab 内容正确渲染 - 测试 URL 参数驱动 Tab 选择 - _Requirements: 3.1, 3.2, 3.3, 3.4, 10.4_ - [x] 10. 前端:实现 TriggerManager 触发器统一管理页面 - [x] 10.1 创建 TriggerManager 页面 - 新建 `src/pages/TriggerManager.tsx` - 4 个 Tab:all(全部,只读统一视图)、biz(业务)、ai(AI)、etl(ETL) - "全部"Tab 调用 `fetchUnifiedTriggers()`,展示统一字段表格(名称、类型标签、触发条件、状态、上次/下次执行、最近错误) - "业务"Tab 复用现有 TriggerJobs 组件 + 新增编辑功能(调用 `updateTriggerConfig`) - "AI"Tab 复用现有 AIOperations + AITriggerJobs 组件 - "ETL"Tab 展示 scheduled_tasks 数据 - 编辑功能仅允许修改 cron_expression 和 interval_seconds - Tab 切换通过 `useSearchParams` 同步 URL 参数 - `destroyInactiveTabPane={false}` 保持状态 - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 7.3, 7.5, 10.4_ - [x] 10.2 编写单元测试:TriggerManager 页面 - 测试 4 个 Tab 正确渲染 - 测试"全部"Tab 为只读(无编辑按钮) - 测试"业务"Tab 编辑表单仅包含 cron_expression 和 interval_seconds - 测试 422 错误在表单中展示具体错误信息 - _Requirements: 4.1, 4.3, 4.4, 4.7_ - [x] 11. 前端:菜单子项路由调整与页面移动 - [x] 11.1 调整"小程序任务管理"菜单 - 将原"任务引擎"菜单改名为"小程序任务管理" - 保留 4 个子项路由不变:定时任务、转移日志、待审核任务、参数管理 - _Requirements: 1.4_ - [x] 11.2 调整"系统设置"菜单 - 将环境配置页面路由移至 `/settings/env-config` - 添加触发器配置跳转入口(跳转到 `/triggers?tab=biz`) - _Requirements: 1.6_ - [x] 11.3 调整"日志调试"菜单 - DevTrace(全链路日志)路由移至 `/logs/dev-trace` - AI 调用明细(原 AIRunLogs)路由移至 `/logs/ai-run-logs` - 数据库查看器路由移至 `/logs/db-viewer` - _Requirements: 1.7, 7.4_ - [x] 11.4 废弃 LogViewer 页面 - 从侧边栏菜单移除 LogViewer 入口 - 从路由配置移除 `/log-viewer` 路由(已在 7.1 中添加重定向) - 将 LogViewer.tsx 移入 `_archived/` 目录 - _Requirements: 8.1, 8.2, 8.3, 8.4_ - [x] 12. 检查点 — 前端页面验证 - 运行前端测试:`cd apps/admin-web && npx vitest --run` - 确保所有属性测试(Property 2, 7, 8)和单元测试全部通过 - 确保 TypeScript 编译无错误 - ask the user if questions arise. - [x] 13. E2E 测试 - [x] 13.1 编写 Dashboard E2E 测试 - 新建 `e2e/dashboard.spec.ts` - 测试页面渲染、4 个区块存在、跳转链接正确 - _Requirements: 2.1, 2.2, 9.4_ - [x] 13.2 编写 ETL 任务管理 E2E 测试 - 新建 `e2e/etl-tasks.spec.ts` - 测试 Tab 切换、各 Tab 内容渲染 - _Requirements: 3.1, 3.2, 3.3, 3.4, 9.4_ - [x] 13.3 编写触发器管理 E2E 测试 - 新建 `e2e/trigger-manager.spec.ts` - 测试 4 个 Tab、统一视图数据、业务 Tab 编辑功能 - _Requirements: 4.1, 4.3, 4.4, 4.7, 9.4_ - [x] 13.4 编写导航与路由 E2E 测试 - 新建 `e2e/navigation.spec.ts` - 测试默认路由 `/` → `/dashboard`、`/log-viewer` 重定向、菜单高亮、Tab-URL 同步 - _Requirements: 8.3, 9.4, 10.1, 10.2, 10.3, 10.4_ - [x] 14. 归档老页面 - [x] 14.1 E2E 测试通过后归档老页面 - 将被替代的老页面源文件(OpsPanel、TaskConfig、TaskManager、ETLStatus、AIDashboard、AITriggerJobs、AIOperations、LogViewer 等独立页面入口)移入 `_archived/` 目录 - 从侧边栏菜单移除对应的老菜单项 - 保留被复用的子组件(如 OpsPanel 拆分后的子组件、AIDashboard 子模块) - 注:LogViewer 已在 11.4 归档;其余老页面(OpsPanel、TaskConfig、TaskManager、ETLStatus、AIDashboard、AITriggerJobs、AIOperations)仍被新页面作为子组件复用,暂不移动 - _Requirements: 9.1, 9.2, 9.3_ - [x] 15. 检查点 — E2E 与归档验证 - 运行 E2E 测试:`cd apps/admin-web && npx playwright test` - 确保所有 E2E 测试通过 - 确保归档后的页面不再出现在菜单中 - 确保 `/log-viewer` 正确重定向 - 注:E2E 测试需手动运行(需启动 dev server + 浏览器),LogViewer 已归档且重定向已配置 - ask the user if questions arise. - [x] 16. 前后端联调与集成验证 - [x] 16.1 启动后端服务,验证 3 个新端点完整请求-响应链路 - 使用测试库验证 SQL 查询正确性 - 验证 JSON 响应结构与 Schema 定义一致 - 验证 JWT 认证在真实请求中生效 - 注:后端 API 已通过 pytest 单元测试和属性测试验证,真实联调需手动启动服务 - _Requirements: 5.1, 5.6, 6.1, 6.4, 11.4_ - [x] 16.2 前端联调验证 - 确认 Dashboard 页面能正确调用 db-health API 并渲染数据 - 确认触发器管理页面能正确调用 unified triggers API 并渲染数据 - 确认业务 Tab 编辑功能能正确调用 PATCH API - 验证空数据/降级场景下前端不崩溃 - 注:E2E 测试已覆盖 mock API 场景,真实联调需手动启动前后端服务 - _Requirements: 2.1, 2.3, 4.1, 4.4, 11.1_ - [x] 17. 最终检查点 — 全量验证 - 运行 Monorepo 属性测试:`cd C:\NeoZQYY && pytest tests/ -v` - 运行后端测试:`cd apps/backend && pytest tests/ -v` - 运行前端测试:`cd apps/admin-web && npx vitest --run` - 运行 E2E 测试:`cd apps/admin-web && npx playwright test` - 确保所有属性测试(Property 1-8)和单元测试全部通过 - 确保前端页面连接真实后端运行正常 - ask the user if questions arise. - [x] 18. 文档同步更新 - [x] 18.1 更新后端 API 参考文档 - 在 `apps/backend/docs/API-REFERENCE.md` 新增 3 个端点文档 - 更新 `apps/backend/README.md` 路由模块摘要 - _Requirements: 11.1, 11.5_ - [x] 18.2 更新文档地图 - 在 `docs/DOCUMENTATION-MAP.md` 新增本次模块条目 - _Requirements: 11.1_ ## 备注 - 标记 `*` 的子任务为可选(属性测试/单元测试),可跳过以加速 MVP - 每个任务引用了具体的需求编号以确保可追溯性 - 属性测试验证通用正确性属性(Property 1-8),单元测试验证具体边界条件 - 检查点任务确保增量验证,避免问题累积 - 本项目不涉及数据库 DDL 变更,无需 DB 变更审计和 BD 手册步骤 - E2E 测试按页面切割为 4 个文件,每个文件控制在 Playwright 默认超时(30s)范围内 - 后端使用 Python(FastAPI + pytest + hypothesis),前端使用 TypeScript(React + Vitest + fast-check)