- .kiro/specs/ → docs/specs/(41 个历史需求 spec 迁移,移除 .config.kiro) - CLAUDE.md 三层拆分:根文件精简 + apps/backend/CLAUDE.md + .claude/commands/ - 新增 /spec-close、/pre-change 两个工作流命令 - DDL 基线刷新(从测试库重新导出 11 个文件,dws 35→38 表,biz 18→21 表) - BD_Manual → BD_manual 命名统一(48 个文件) - 修复 3 处文档与数据库不一致(auth.users.status 默认值、scheduled_tasks 字段、RLS 视图数) - 新增 BD_manual_public_rbac_tables.md(public schema 8 张 RBAC/工作流表) - 合并 biz.trigger_jobs 文档(10→12 字段,归档独立文档) - docs/database/README.md 索引更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
135 lines
5.8 KiB
Markdown
135 lines
5.8 KiB
Markdown
# BD 手册:scheduled_tasks 新增字段(P16 最小运行间隔)
|
||
|
||
## 概述
|
||
|
||
P16 为 `public.scheduled_tasks` 表新增 3 个字段,支持调度任务最小运行间隔机制。调度器轮询时检查并发状态和间隔约束,避免任务重复执行。
|
||
|
||
所有变更位于 `zqyy_app` / `test_zqyy_app` 数据库,`public` Schema。
|
||
|
||
## 变更原因
|
||
|
||
- P16 调度任务最小运行间隔需求:管理员可为每个任务配置最小间隔,任务即使调度到期也不会在间隔内重复执行
|
||
- 新增 `last_success_at` 字段区分"最后执行时间"和"最后成功时间",支持更精确的间隔计算
|
||
|
||
## 变更说明
|
||
|
||
| 库 | Schema | 表 | 变更类型 | 说明 |
|
||
|----|--------|---|---------|------|
|
||
| zqyy_app | public | scheduled_tasks | 新增字段 ×4 | min_run_interval_value, min_run_interval_unit, last_success_at, min_run_intervals |
|
||
|
||
---
|
||
|
||
## 新增字段明细
|
||
|
||
| 列名 | 类型 | 约束 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| min_run_interval_value | INTEGER | NOT NULL | 0 | 最小间隔数值(0=无限制,与现有行为完全一致) |
|
||
| min_run_interval_unit | VARCHAR(20) | NOT NULL | 'minutes' | 间隔单位:`minutes` / `hours` / `days` |
|
||
| last_success_at | TIMESTAMPTZ | — | NULL | 最后一次成功执行的时间 |
|
||
| min_run_intervals | JSONB | NOT NULL | '{}' | 每任务子代码级别的最小运行间隔配置(key=task_code, value=interval_seconds) |
|
||
|
||
### DDL
|
||
|
||
```sql
|
||
ALTER TABLE scheduled_tasks ADD COLUMN min_run_interval_value INTEGER NOT NULL DEFAULT 0;
|
||
ALTER TABLE scheduled_tasks ADD COLUMN min_run_interval_unit VARCHAR(20) NOT NULL DEFAULT 'minutes';
|
||
ALTER TABLE scheduled_tasks ADD COLUMN last_success_at TIMESTAMPTZ;
|
||
|
||
COMMENT ON COLUMN scheduled_tasks.min_run_interval_value IS '最小间隔数值(0=无限制)';
|
||
COMMENT ON COLUMN scheduled_tasks.min_run_interval_unit IS '间隔单位:minutes/hours/days';
|
||
COMMENT ON COLUMN scheduled_tasks.last_success_at IS '最后一次成功执行的时间';
|
||
```
|
||
|
||
---
|
||
|
||
## 业务逻辑
|
||
|
||
调度器(`scheduler.py`)轮询时对每个到期任务执行以下检查:
|
||
|
||
1. **并发检查**:`last_status = 'running'` → 跳过本次入队,日志记录 `skipped_concurrent`
|
||
2. **间隔检查**:`min_run_interval_value > 0` 且 `now() - last_run_at < min_interval_seconds` → 跳过本次执行,推进 `next_run_at`,日志记录 `skipped_interval`
|
||
3. **首次执行**:`last_run_at IS NULL`(从未执行)→ 跳过间隔检查,正常执行
|
||
4. **强制执行**:`force=true` 参数绕过所有检查(并发 + 间隔),直接入队
|
||
|
||
### 间隔转换
|
||
|
||
```python
|
||
def _convert_interval_to_seconds(value: int, unit: str) -> int:
|
||
multipliers = {"minutes": 60, "hours": 3600, "days": 86400}
|
||
return value * multipliers.get(unit, 60)
|
||
```
|
||
|
||
### last_success_at 更新规则
|
||
|
||
- 任务成功完成时:`last_status='completed'`, `last_success_at=NOW()`
|
||
- 任务失败时:`last_status='failed'`,`last_success_at` 不变
|
||
|
||
---
|
||
|
||
## 兼容性
|
||
|
||
| 组件 | 影响 |
|
||
|------|------|
|
||
| 后端 API | `POST /api/schedules` 和 `PUT /api/schedules/{id}` 请求体新增 `min_run_interval_value`、`min_run_interval_unit`。`GET /api/schedules` 响应新增 3 个字段。`POST /api/schedules/{id}/run` 新增 `force` 查询参数 |
|
||
| 前端 ScheduleTab | 创建/编辑表单新增「最小运行间隔」配置行。列表新增「最小间隔」和「上次成功」列。手动执行确认框新增「强制执行」Checkbox |
|
||
| ETL | 无影响。ETL 任务调度由 `scheduled_tasks` 表驱动,新增字段默认值 0 表示无限制,向后兼容 |
|
||
| 小程序 | 无影响 |
|
||
| 调度器 | `scheduler.py` 的 `check_and_enqueue()` 新增并发检查和间隔检查逻辑 |
|
||
|
||
## 回滚策略
|
||
|
||
```sql
|
||
BEGIN;
|
||
ALTER TABLE scheduled_tasks DROP COLUMN IF EXISTS min_run_interval_value;
|
||
ALTER TABLE scheduled_tasks DROP COLUMN IF EXISTS min_run_interval_unit;
|
||
ALTER TABLE scheduled_tasks DROP COLUMN IF EXISTS last_success_at;
|
||
COMMIT;
|
||
```
|
||
|
||
注意:
|
||
- 回滚后所有任务恢复为无间隔限制的行为
|
||
- 回滚前需确认后端代码已移除对这 3 个字段的引用
|
||
- `last_success_at` 数据丢失不可恢复
|
||
|
||
## 验证 SQL
|
||
|
||
```sql
|
||
-- 1. 验证新增字段存在且类型正确
|
||
SELECT column_name, data_type, column_default
|
||
FROM information_schema.columns
|
||
WHERE table_name = 'scheduled_tasks'
|
||
AND column_name IN ('min_run_interval_value', 'min_run_interval_unit', 'last_success_at');
|
||
-- 预期:3 行
|
||
-- min_run_interval_value | integer | 0
|
||
-- min_run_interval_unit | character varying | 'minutes'::character varying
|
||
-- last_success_at | timestamp with time zone | NULL
|
||
|
||
-- 2. 验证现有任务的新字段默认值
|
||
SELECT id, name, min_run_interval_value, min_run_interval_unit, last_success_at
|
||
FROM scheduled_tasks
|
||
LIMIT 5;
|
||
-- 预期:min_run_interval_value=0, min_run_interval_unit='minutes', last_success_at=NULL(除非已手动配置)
|
||
|
||
-- 3. 验证已配置间隔的任务数量
|
||
SELECT COUNT(*) FROM scheduled_tasks WHERE min_run_interval_value > 0;
|
||
-- 预期:≥ 0(初始状态为 0,配置后递增)
|
||
|
||
-- 4. 验证字段注释
|
||
SELECT col_description(
|
||
(SELECT oid FROM pg_class WHERE relname = 'scheduled_tasks'),
|
||
(SELECT attnum FROM pg_attribute WHERE attrelid = 'scheduled_tasks'::regclass AND attname = 'min_run_interval_value')
|
||
);
|
||
-- 预期:'最小间隔数值(0=无限制)'
|
||
```
|
||
|
||
## 关联文件
|
||
|
||
- DDL 基线(public):`docs/database/ddl/zqyy_app__public.sql`
|
||
- 迁移脚本:`db/zqyy_app/migrations/2026-03-22__p16_min_run_interval.sql`
|
||
- 调度器逻辑:`apps/backend/app/services/scheduler.py`
|
||
- 后端路由:`apps/backend/app/routers/schedules.py`
|
||
- 后端 Schema:`apps/backend/app/schemas/schedules.py`
|
||
- 前端组件:`apps/admin-web/src/components/ScheduleTab.tsx`
|
||
- Spec:`.kiro/specs/admin-web-enhancement/`
|
||
- PRD:`docs/prd/specs/P16-task-min-run-interval.md`
|