# 实现计划：Web 管理后台（admin-web-console）

## 概述

将现有 PySide6 桌面 GUI 替换为 BS 架构的 Web 管理后台。后端在 `apps/backend/` 上扩展 FastAPI API，前端在 `apps/admin-web/` 下使用 React + Vite + Ant Design 构建。实现按"后端基础设施 → 核心 API → 前端骨架 → 功能模块逐个对接"的顺序推进。

## 任务

- [x] 1. 后端基础设施搭建
  - [x] 1.1 创建数据库迁移脚本，在 `zqyy_app` 库中创建 4 张新表（admin_users、task_queue、task_execution_log、scheduled_tasks），所有表包含 site_id 字段
    - 迁移脚本放在 `db/zqyy_app/migrations/`，日期前缀命名
    - 包含索引创建（site_id 相关的复合索引）
    - 包含种子数据：插入一个默认管理员账号
    - _Requirements: 1.1, 4.1, 5.1_

  - [x] 1.2 实现 JWT 认证模块（`apps/backend/app/auth/`）
    - `jwt.py`：JWT 令牌生成（access_token + refresh_token）、验证、解码，payload 包含 user_id 和 site_id
    - `dependencies.py`：FastAPI 依赖注入函数 `get_current_user`，从 JWT 提取用户信息和 site_id
    - 新增依赖：`python-jose[cryptography]`、`passlib[bcrypt]` 到 `apps/backend/pyproject.toml`
    - _Requirements: 1.1, 1.2, 1.3, 1.4_

  - [x] 1.3 实现认证路由（`apps/backend/app/routers/auth.py`）
    - `POST /api/auth/login`：验证用户名密码，返回 JWT 令牌对
    - `POST /api/auth/refresh`：用刷新令牌换取新的访问令牌
    - Pydantic schemas：`LoginRequest`、`TokenResponse`
    - _Requirements: 1.1, 1.2, 1.4_

  - [x] 1.4 编写认证模块属性测试
    - **Property 2: 无效凭据始终被拒绝**
    - **Property 3: 有效 JWT 令牌授权访问**
    - **Validates: Requirements 1.2, 1.3**

  - [x] 1.5 扩展 `apps/backend/app/database.py`，新增 ETL 数据库只读连接函数
    - `get_etl_readonly_connection(site_id)`：连接 ETL 数据库，设置 `SET LOCAL app.current_site_id`
    - 配置项从 .env 读取 ETL 数据库连接参数
    - _Requirements: 7.4, 7.5_

  - [x] 1.6 在 `apps/backend/app/main.py` 中注册所有路由和中间件，配置 CORS 允许前端开发服务器访问
    - _Requirements: 10.2_

- [x] 2. 检查点 — 确保认证模块测试通过
  - 确保所有测试通过，如有问题请向用户确认。

- [x] 3. 任务配置与执行 API
  - [x] 3.1 迁移 CLIBuilder 到后端（`apps/backend/app/services/cli_builder.py`）
    - 从 `gui/utils/cli_builder.py` 迁移核心逻辑
    - 适配新的 TaskConfigSchema，自动注入 `--store-id` 参数
    - 支持 7 种 Flow 和 4 种处理模式
    - _Requirements: 2.6_

  - [x] 3.2 实现任务注册表 API（`apps/backend/app/routers/tasks.py`）
    - `GET /api/tasks/registry`：返回按业务域分组的任务列表
    - `GET /api/tasks/dwd-tables`：返回按业务域分组的 DWD 表定义
    - `GET /api/tasks/flows`：返回 7 种 Flow 定义和 4 种处理模式定义
    - `POST /api/tasks/validate`：验证 TaskConfig 并返回生成的 CLI 命令预览
    - Pydantic schemas 在 `apps/backend/app/schemas/tasks.py`
    - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 11.1, 11.2_

  - [x] 3.3 编写 TaskConfig 属性测试
    - **Property 1: TaskConfig 序列化往返一致性**
    - **Property 6: 时间窗口验证**
    - **Property 7: TaskConfig 到 CLI 命令转换完整性**
    - **Validates: Requirements 2.3, 2.5, 2.6, 11.1, 11.2, 11.3**

  - [x] 3.4 实现 TaskExecutor 服务（`apps/backend/app/services/task_executor.py`）
    - 使用 `asyncio.create_subprocess_exec` 启动 ETL_CLI 子进程
    - 逐行读取 stdout/stderr，存储到内存缓冲区并广播到 WebSocket
    - 记录退出码、执行时长到 task_execution_log 表
    - 支持取消（发送 SIGTERM）
    - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_

  - [x] 3.5 实现 TaskQueue 服务（`apps/backend/app/services/task_queue.py`）
    - `enqueue(config, site_id)`：入队，自动分配 position
    - `dequeue(site_id)`：取出 position 最小的 pending 任务
    - `reorder(task_id, new_position, site_id)`：调整顺序
    - `delete(task_id, site_id)`：删除 pending 任务
    - `process_loop()`：后台协程，自动取出并执行
    - _Requirements: 4.1, 4.2, 4.3, 4.4_

  - [x] 3.6 实现执行与队列路由（`apps/backend/app/routers/execution.py`）
    - `POST /api/execution/run`：直接执行任务
    - `GET /api/execution/queue`：获取当前队列（按 site_id 过滤）
    - `POST /api/execution/queue`：添加到队列
    - `PUT /api/execution/queue/reorder`：重排
    - `DELETE /api/execution/queue/{id}`：删除
    - `POST /api/execution/{id}/cancel`：取消
    - `GET /api/execution/history`：执行历史（按 site_id 过滤，limit 参数）
    - `GET /api/execution/{id}/logs`：获取历史日志
    - _Requirements: 3.1, 3.5, 4.1, 4.2, 4.3, 4.4, 4.5_

  - [x] 3.7 编写队列属性测试
    - **Property 8: 队列 CRUD 不变量**
    - **Property 9: 队列出队顺序**
    - **Property 10: 队列重排一致性**
    - **Property 11: 执行历史排序与限制**
    - **Validates: Requirements 4.1, 4.2, 4.3, 4.4, 4.5, 8.2**

- [x] 4. 检查点 — 确保任务配置与执行 API 测试通过
  - 确保所有测试通过，如有问题请向用户确认。

- [x] 5. 调度与辅助 API
  - [x] 5.1 实现 Scheduler 服务（`apps/backend/app/services/scheduler.py`）
    - `check_and_enqueue()`：查询 enabled=true 且 next_run_at <= now 的调度任务，将其 TaskConfig 入队
    - `start()`：启动后台 asyncio 循环，每 30 秒检查一次
    - 在 FastAPI lifespan 中启动/停止
    - _Requirements: 5.2_

  - [x] 5.2 实现调度路由（`apps/backend/app/routers/schedules.py`）
    - `GET /api/schedules`：列表（按 site_id 过滤）
    - `POST /api/schedules`：创建
    - `PUT /api/schedules/{id}`：更新
    - `DELETE /api/schedules/{id}`：删除
    - `PATCH /api/schedules/{id}/toggle`：启用/禁用
    - Pydantic schemas 在 `apps/backend/app/schemas/schedules.py`
    - _Requirements: 5.1, 5.3, 5.4, 5.5, 5.6_

  - [x] 5.3 编写调度属性测试
    - **Property 12: 调度任务 CRUD 往返**
    - **Property 13: 到期调度任务自动入队**
    - **Property 14: 调度任务启用/禁用状态**
    - **Validates: Requirements 5.1, 5.2, 5.3, 5.4**

  - [x] 5.4 实现环境配置路由（`apps/backend/app/routers/env_config.py`）
    - `GET /api/env-config`：读取 .env，敏感值掩码
    - `PUT /api/env-config`：验证并写入 .env
    - `GET /api/env-config/export`：导出去敏感值的配置文件
    - 敏感键列表：PASSWORD、TOKEN、SECRET、DSN
    - _Requirements: 6.1, 6.2, 6.3, 6.4_

  - [x] 5.5 编写环境配置属性测试
    - **Property 15: .env 解析与敏感值掩码**
    - **Property 16: .env 写入往返一致性**
    - **Validates: Requirements 6.1, 6.2, 6.3**

  - [x] 5.6 实现数据库查看器路由（`apps/backend/app/routers/db_viewer.py`）
    - `GET /api/db/schemas`：返回 Schema 列表
    - `GET /api/db/schemas/{name}/tables`：返回表列表和行数
    - `GET /api/db/tables/{schema}/{table}/columns`：返回列定义
    - `POST /api/db/query`：只读 SQL 执行（写操作拦截、1000 行限制、30 秒超时）
    - 使用 `get_etl_readonly_connection(site_id)` 确保 RLS 隔离
    - _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5, 7.6_

  - [x] 5.7 编写数据库查看器属性测试
    - **Property 17: SQL 写操作拦截**
    - **Property 18: SQL 查询结果行数限制**
    - **Validates: Requirements 7.4, 7.5**

  - [x] 5.8 实现 ETL 状态路由（`apps/backend/app/routers/etl_status.py`）
    - `GET /api/etl-status/cursors`：查询 etl_admin.etl_cursor 表，返回各任务游标
    - `GET /api/etl-status/recent-runs`：查询 task_execution_log 表，返回最近 50 条记录
    - _Requirements: 8.1, 8.2, 8.3_

  - [x] 5.9 实现 WebSocket 日志推送（`apps/backend/app/ws/logs.py`）
    - `WS /ws/logs/{execution_id}`：实时推送任务执行日志
    - TaskExecutor 执行时广播日志行到已连接的 WebSocket 客户端
    - _Requirements: 9.1, 9.4_

- [x] 6. 检查点 — 确保所有后端 API 测试通过
  - 确保所有测试通过，如有问题请向用户确认。

- [x] 7. 前端项目初始化
  - [x] 7.1 在 `apps/admin-web/` 下初始化 React + Vite + TypeScript 项目
    - `pnpm create vite . --template react-ts`
    - 安装核心依赖：`antd`、`@ant-design/icons`、`react-router-dom`、`axios`、`zustand`
    - 配置 `vite.config.ts`：API 代理到后端 `http://localhost:8000`
    - 配置中文 locale（antd ConfigProvider）
    - _Requirements: 10.1, 10.2_

  - [x] 7.2 实现 API 客户端（`src/api/client.ts`）
    - 创建 axios 实例，baseURL 指向 `/api`
    - 请求拦截器：自动附加 JWT Authorization header
    - 响应拦截器：401 时尝试刷新令牌，刷新失败跳转登录页
    - _Requirements: 1.3, 1.4, 1.5_

  - [x] 7.3 实现认证状态管理（`src/store/authStore.ts`）和登录页（`src/pages/Login.tsx`）
    - Zustand store：存储 token、user info、site_id
    - 登录页：Ant Design Form，用户名 + 密码
    - 登录成功后存储令牌到 localStorage 并跳转首页
    - _Requirements: 1.1, 1.2_

  - [x] 7.4 实现主布局（`src/App.tsx`）和路由配置
    - Ant Design Layout：Sider（侧边栏导航）+ Content + Footer（状态栏）
    - react-router-dom：6 个功能页面路由 + 登录页路由
    - 路由守卫：未登录重定向到登录页
    - 侧边栏导航项：任务配置、任务管理、环境配置、数据库、ETL 状态、日志
    - _Requirements: 10.1, 10.2, 10.3_

- [x] 8. 前端功能页面 — 任务配置
  - [x] 8.1 实现任务配置页面（`src/pages/TaskConfig.tsx`）
    - Flow 选择器：Radio Group，7 种 Flow，选择后动态显示/隐藏任务区域
    - 处理模式选择器：3 种模式 + 校验附加选项
    - 时间窗口配置：回溯模式（lookback_hours + overlap_seconds）/ 自定义模式（DatePicker）
    - 窗口切分选项：不切分 / 按天（1/10/30）
    - 高级选项折叠面板：dry-run、force-full 等 Checkbox
    - _Requirements: 2.2, 2.3, 2.5_

  - [x] 8.2 实现 TaskSelector 组件（`src/components/TaskSelector.tsx`）
    - 从 `/api/tasks/registry` 获取任务列表
    - 按业务域分组展示（Collapse + Checkbox Group）
    - 根据当前 Flow 包含的层过滤可见任务
    - 全选/反选功能
    - _Requirements: 2.1, 2.2_

  - [x] 8.3 实现 DwdTableSelector 组件（`src/components/DwdTableSelector.tsx`）
    - 从 `/api/tasks/dwd-tables` 获取 DWD 表定义
    - 按业务域分组展示（Collapse + Checkbox Group）
    - 仅在 Flow 包含 DWD 层时显示
    - _Requirements: 2.4_

  - [x] 8.4 实现任务提交和 CLI 命令预览
    - 提交前调用 `/api/tasks/validate` 预览生成的 CLI 命令
    - 提交到队列（`/api/execution/queue`）或直接执行（`/api/execution/run`）
    - 提交成功后跳转到任务管理页面
    - _Requirements: 2.6, 3.1, 4.1_

  - [x] 8.5 编写 Flow 层级过滤前端单元测试
    - **Property 21: Flow 层级与任务兼容性**
    - 使用 Vitest 测试过滤逻辑函数
    - **Validates: Requirements 2.2**

- [x] 9. 前端功能页面 — 任务管理
  - [x] 9.1 实现任务管理页面（`src/pages/TaskManager.tsx`）
    - Ant Design Tabs：队列 + 调度 + 历史
    - 队列 Tab：Table 展示当前队列，支持拖拽排序、删除、取消
    - 历史 Tab：Table 展示执行历史，点击行查看详情和日志
    - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5_

  - [x] 9.2 实现调度管理 Tab
    - 调度任务列表 Table：名称、调度描述、启用状态 Switch、下次执行时间、执行次数
    - 创建/编辑调度任务 Modal：任务选择 + 调度配置（类型、间隔、时间等）
    - 删除确认
    - _Requirements: 5.1, 5.3, 5.4, 5.5, 5.6_

  - [x] 9.3 实现状态栏任务执行指示
    - 在 Layout Footer 或 Sider 底部显示当前执行状态
    - 轮询 `/api/execution/queue` 检查是否有 running 状态的任务
    - 有任务执行时显示 Spin 动画和任务名称
    - _Requirements: 10.3, 10.4_

- [x] 10. 前端功能页面 — 辅助模块
  - [x] 10.1 实现环境配置页面（`src/pages/EnvConfig.tsx`）
    - 键值对编辑表格（Ant Design Table，editable cells）
    - 敏感值显示为 `****`，编辑时可输入新值
    - 保存按钮调用 `PUT /api/env-config`
    - 导出按钮调用 `GET /api/env-config/export` 下载文件
    - _Requirements: 6.1, 6.2, 6.3, 6.4_

  - [x] 10.2 实现数据库查看器页面（`src/pages/DBViewer.tsx`）
    - 左侧 Tree：Schema → Table 层级浏览
    - 右侧上方：SQL 编辑器（Ant Design Input.TextArea 或集成 CodeMirror）
    - 右侧下方：查询结果 Table
    - 选择表时自动展示列定义
    - 执行查询按钮，结果分页展示
    - _Requirements: 7.1, 7.2, 7.3, 7.4_

  - [x] 10.3 实现 ETL 状态页面（`src/pages/ETLStatus.tsx`）
    - 游标状态 Table：任务编码、最后抓取时间、记录数
    - 最近执行记录 Table：任务名称、状态 Tag、开始时间、执行时长
    - 刷新按钮
    - _Requirements: 8.1, 8.2, 8.3_

  - [x] 10.4 实现日志查看器页面（`src/pages/LogViewer.tsx`）和 LogStream 组件
    - WebSocket 连接 `/ws/logs/{execution_id}`，实时追加日志行
    - 日志过滤输入框：按关键词过滤显示
    - 自动滚动到底部，可手动暂停滚动
    - 历史日志：从 `/api/execution/{id}/logs` 加载
    - _Requirements: 9.1, 9.2, 9.3, 9.4_

  - [x] 10.5 编写日志过滤前端单元测试
    - **Property 19: 日志过滤正确性**
    - 使用 Vitest 测试过滤函数
    - **Validates: Requirements 9.2**

- [x] 11. 门店隔离集成验证
  - [x] 11.1 编写门店隔离属性测试
    - **Property 20: 门店隔离 — 队列和调度数据不跨站泄露**
    - 使用 pytest + hypothesis 生成随机 site_id 对，验证数据隔离
    - **Validates: Requirements 1.3**

  - [x] 11.2 编写任务注册表分组属性测试
    - **Property 4: 任务注册表按业务域正确分组**
    - **Validates: Requirements 2.1**

- [x] 12. 最终检查点 — 确保所有测试通过 ✅
  - 后端 302 passed / 0 failed，前端 33 passed / 0 failed，全部通过。

## 说明

- 标记 `*` 的子任务为可选测试任务，可跳过以加速 MVP
- 每个任务引用了具体的需求编号，确保可追溯
- 检查点用于阶段性验证，确保增量正确
- 属性测试验证通用正确性属性，单元测试覆盖具体示例和边界条件
