- .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>
12 KiB
需求文档 — NS4.1 + P16:Admin-Web 管理后台增强
简介
本 Spec 合并两个独立需求:NS4.1(租户管理员页面重构 + 项目级注册体系)和 P16(调度任务最小运行间隔机制)。两者均为 admin-web 管理后台的功能增强,改动文件无重叠,合并执行以减少上下文切换。
合并理由
- 两者都是 admin-web 后台功能迭代,共享同一前端项目(
apps/admin-web/)和后端项目(apps/backend/) - NS4.1 改动集中在租户管理员页面(
TenantAdmins/)+ 新建注册体系路由,P16 改动集中在调度任务页面(ScheduleTab.tsx)+ 调度器逻辑 - 改动文件完全不重叠,合并不增加冲突风险
- 共享收尾流程(DDL 合并、BD 手册、文档同步)
依赖
- NS4(租户管理后台基础设施)—
tenant-admin-webspec 已完成 - P3(用户认证体系)—
03-miniapp-auth-systemspec 已完成
来源文档(权威参考)
实施过程中如遇细节不明确,应优先查阅以下 PRD 原文:
docs/prd/Neo_Specs/NS4.1-tenant-admin-redesign.md— NS4.1 PRD 主文档(数据模型 DDL、页面布局、接口设计、迁移步骤、边界条件)docs/prd/specs/P16-task-min-run-interval.md— P16 PRD 主文档(调度器逻辑、API 扩展、前端变更、边界条件)
不在本 spec 范围
- P15(AI 监控后台 + 测试重建 + 回填)— 单独 Spec
- 多连接器完整实现(仅预留
biz.connectors表结构) dwd.dim_site物理迁移(保留在dwdschema)- 租户管理员自助注册
- 简写ID 自动生成
- ETL 任务注册机制修改(
TaskMeta/TaskRegistry) - 批量 seed SQL 设定初始间隔值
术语表
- admin-web:系统管理后台(
apps/admin-web/),面向系统管理员 - tenant-admin:租户管理后台(
apps/tenant-admin/),面向租户管理员 - biz.connectors:连接器注册表,记录接入的上游 SaaS 系统
- biz.tenants:租户注册表,连接器下的租户
- biz.sites:店铺注册表,合并原
auth.site_code_mapping - biz.site_code_history:简写ID 变更历史表
- site_code:店铺简写ID,6 位字符(3+3 格式,如
LLQ001) - min_run_interval:调度任务最小运行间隔,任务开始执行后的最小等待时间
- scheduled_tasks:调度任务表(
public.scheduled_tasks),存储 ETL 调度配置 - scheduler.py:调度器核心逻辑,每 30 秒轮询到期任务
需求
模块 A:NS4.1 — 租户管理员页面重构 + 项目级注册体系
需求 A1:项目级注册体系 — 连接器/租户/店铺三级表
用户故事: 作为系统管理员,我希望建立「连接器 → 租户 → 店铺」三级注册体系,以便统一管理上游 SaaS 系统、租户和店铺的关系,并为简写ID 提供归属。
验收标准
- THE Backend SHALL 在
bizschema 新建connectors表(id, connector_key UNIQUE, display_name, is_active, created_at),初始数据插入('feiqiu', '飞球') - THE Backend SHALL 在
bizschema 新建tenants表(id, connector_id FK, tenant_id BIGINT, tenant_name, is_active, created_at, updated_at),UNIQUE(connector_id, tenant_id) - THE Backend SHALL 在
bizschema 新建sites表(id, tenant_id FK, site_id BIGINT UNIQUE, site_name, site_code VARCHAR(6) UNIQUE, site_label, is_active, created_at, updated_at) - THE Backend SHALL 在
bizschema 新建site_code_history表(id, site_id BIGINT, site_code VARCHAR(6) UNIQUE, is_current BOOLEAN, created_at, retired_at) - THE Backend SHALL 将
auth.site_code_mapping中真实数据(tenant_id IS NOT NULL)迁移到biz.sites,并为已有site_code创建site_code_history记录(is_current=true) - THE Backend SHALL 在迁移完成并验证后,将
auth.site_code_mapping重命名为auth._archived_site_code_mapping
需求 A1b:数据迁移后初始同步
用户故事: 作为系统管理员,我希望迁移完成后立即运行一次 ETL 同步,补充 biz.sites 中缺失的店铺(dwd.dim_site 中有但 auth.site_code_mapping 中没有的),确保注册体系数据完整。
验收标准
- THE Backend SHALL 在数据迁移(A1)完成后、代码切换(A6)之前,执行一次店铺同步(复用 A5 的同步逻辑),将
dwd.dim_site(scd2_is_current=1)中存在但biz.sites中不存在的店铺补充插入 - THE Backend SHALL 在初始同步完成后输出同步结果(新增数/更新数),供验证使用
需求 A2:租户/店铺管理 API
用户故事: 作为系统管理员,我希望通过 API 查询租户列表和店铺列表,以便在创建管理员时选择所属租户和管辖门店。
验收标准
- THE Backend SHALL 实现
GET /api/admin/tenants端点,返回所有活跃租户(含连接器名称) - THE Backend SHALL 实现
GET /api/admin/tenants/{tenant_id}/sites端点,返回指定租户下所有活跃店铺(含当前 site_code) - THE Backend SHALL 实现
DELETE /api/admin/tenant-admins/{id}端点,软删除管理员(is_active=false) - THE Backend SHALL 实现
PUT /api/admin/sites/{site_id}/site-code端点,设置/修改店铺简写ID - THE Backend SHALL 实现
GET /api/admin/sites/{site_id}/site-code-history端点,查看简写ID 变更历史 - THE Backend SHALL 修改
POST /api/admin/tenant-admins端点,创建时tenant_id从biz.tenants选择,managed_site_ids从biz.sites选择 - THE Backend SHALL 修改
GET /api/admin/tenant-admins端点,默认只返回is_active=true,增加include_inactive参数 - THE Backend SHALL 修改
PATCH /api/admin/tenant-admins/{id}端点,支持修改username(需校验全局唯一性,冲突返回 409)
需求 A3:简写ID 管理逻辑
用户故事: 作为系统管理员,我希望在管理后台设置和修改店铺简写ID,并保留变更历史,以便保护已提交但未审核的用户申请。
验收标准
- WHEN 修改简写ID 时,THE Backend SHALL 在事务内执行:旧 code 标记
is_current=false+retired_at=NOW(),新 code 插入site_code_history(is_current=true),更新biz.sites.site_code - THE Backend SHALL 校验新 code 格式(6 位,3+3 模式,统一大写存储)和全局唯一性(含
biz.sites.site_code+biz.site_code_history.site_code) - WHEN 旧 code 有未审核申请引用(
auth.user_applications WHERE site_code = :old_code AND status = 'pending')时,THE Backend SHALL 保留历史记录不删除 - WHEN 旧 code 无任何申请引用时,THE Backend SHALL 从
biz.site_code_history中删除该条记录
需求 A4:租户管理员页面重构(admin-web)
用户故事: 作为系统管理员,我希望在 admin-web 中通过改进的界面管理租户管理员,支持 2 步创建流程、软删除和简写ID 管理。
验收标准
- THE admin-web SHALL 重构租户管理员列表页,新增「删除」操作按钮(二次确认 → 软删除),默认只显示活跃记录,可选「显示已禁用」开关
- THE admin-web SHALL 实现 2 步创建流程:第 1 步选择租户(下拉
biz.tenants)+ 输入账号信息 + 选择管辖门店(biz.sites);第 2 步可选设置简写ID - THE admin-web SHALL 在编辑弹窗中增加「管理简写ID」区域,展示该租户下所有店铺及其当前 code,支持修改;编辑时所属租户(
tenant_id)为只读不可修改;用户名(username)可修改(需校验唯一性) - THE admin-web SHALL 新增简写ID 管理弹窗,展示变更历史,支持修改操作
- THE admin-web SHALL 新增
src/api/registry.ts封装租户/店铺列表 API 调用
需求 A5:ETL 店铺信息增量同步
用户故事: 作为系统管理员,我希望 ETL 完成后能自动同步店铺信息到业务库,以便 biz.sites 中的店铺名称和标签保持最新。
验收标准
- THE Backend SHALL 实现店铺同步逻辑:通过 FDW 读取 ETL 库
dwd.dim_site(scd2_is_current=1),对比biz.sites,新增店铺 INSERT(site_code留空),名称/标签变更 UPDATE - THE Backend SHALL 不删除已有店铺记录(即使上游标记为关闭)
- THE Backend SHALL 支持手动触发同步(管理后台按钮或 API 端点)
- THE Backend SHALL 支持定时触发同步(随 ETL 日常调度,DWD 层完成后通过内部 API 触发)
需求 A6:后端代码切换 — site_code 查询源
用户故事: 作为后端开发者,我希望所有读取 auth.site_code_mapping 的代码切换到 biz.sites + biz.site_code_history,以便完成数据迁移。
验收标准
- THE Backend SHALL 将所有读取
auth.site_code_mapping的查询切换到biz.sites - THE 小程序端 SHALL 将用户申请时的
site_code查询从auth.site_code_mapping切换到biz.sites+biz.site_code_history - THE Backend SHALL 确保切换后所有现有功能(用户申请、关联建议匹配等)正常工作
模块 B:P16 — 调度任务最小运行间隔机制
需求 B1:scheduled_tasks 表扩展
用户故事: 作为管理员,我希望为每个调度任务设置最小运行间隔,使任务即使调度到期也不会在间隔内重复执行。
验收标准
- THE Backend SHALL 在
scheduled_tasks表新增min_run_interval_value(INTEGER DEFAULT 0)、min_run_interval_unit(VARCHAR(20) DEFAULT 'minutes')、last_success_at(TIMESTAMPTZ NULL)3 个字段 - THE Backend SHALL 确保
min_run_interval_value = 0表示无限制,与现有行为完全一致(向后兼容)
需求 B2:调度器核心逻辑 — 并发检查 + 间隔检查
用户故事: 作为管理员,我希望调度器在轮询时自动检查最小间隔和并发状态,避免任务重复执行或并发执行。
验收标准
- THE scheduler SHALL 在
check_and_enqueue()中新增并发检查:若last_status = 'running',跳过本次入队,日志记录skipped_concurrent - THE scheduler SHALL 在
check_and_enqueue()中新增间隔检查:若min_run_interval_value > 0且now() - last_run_at < min_interval_seconds,跳过本次执行并推进next_run_at,日志记录skipped_interval - WHEN
last_run_at IS NULL(从未执行)时,THE scheduler SHALL 跳过间隔检查,正常执行 - THE scheduler SHALL 在任务成功完成时同时更新
last_success_at = NOW(),失败时不更新last_success_at - THE scheduler SHALL 实现
_convert_interval_to_seconds(value, unit)辅助函数,支持minutes/hours/days单位
需求 B3:API 扩展 — 创建/更新/手动执行
用户故事: 作为管理员,我希望通过 API 配置最小运行间隔,并在必要时强制执行任务。
验收标准
- THE Backend SHALL 在
POST /api/schedules和PUT /api/schedules/{id}端点的请求体中新增min_run_interval_value(int, default=0)和min_run_interval_unit(str, default='minutes') - THE Backend SHALL 在
GET /api/schedules响应中新增min_run_interval_value、min_run_interval_unit、last_success_at字段 - THE Backend SHALL 在
POST /api/schedules/{id}/run端点新增force: bool = False查询参数 - WHEN
force=true时,THE Backend SHALL 绕过最小间隔和并发检查,直接入队执行 - WHEN
force=false且间隔未到时,THE Backend SHALL 返回 409 Conflict,提示"最小运行间隔未到,距下次可执行还有 X 分钟" - WHEN
force=false且任务正在运行时,THE Backend SHALL 返回 409 Conflict,提示"任务正在执行中"
需求 B4:Admin Web 前端 — ScheduleTab 扩展
用户故事: 作为管理员,我希望在调度任务管理界面中看到和配置最小运行间隔。
验收标准
- THE admin-web SHALL 在创建/编辑调度任务表单中新增「最小运行间隔」行:
InputNumber(数值)+Select(单位:分钟/小时/天),数值为 0 时显示提示"无限制" - THE admin-web SHALL 在任务列表表格中新增「最小间隔」列(显示如"10 天"、"无限制")和「上次成功」列(相对时间)
- THE admin-web SHALL 在手动执行确认框中新增「强制执行(忽略最小间隔)」勾选项,默认不勾选
- WHEN 勾选强制执行时,THE admin-web SHALL 调用
POST /api/schedules/{id}/run?force=true