309 lines
17 KiB
Markdown
309 lines
17 KiB
Markdown
# A 卡模型运维模块后端架构与数据库设计 V0.2
|
||
|
||
> 状态:设计评审稿;独立库 DDL 已生成但尚未执行,ORM、迁移和接口尚未实现
|
||
> 日期:2026-08-31
|
||
> 依据:当前 `develop` 工程、运维前端 V1.9、现有三条 API 契约及甲方提供的 `model_platform` 18 张表 DDL
|
||
|
||
## 1. 结论
|
||
|
||
采用“**同一工程、两个数据库、平台库只读、运维库自有**”的方式:
|
||
|
||
- 现有 `model_platform` 库保持不变,只读复用用户、角色、工作空间、对象存储、脚本版本和调度数据。
|
||
- 新建独立 `model_operations` 库;模型平台直接写入其中 7 张受控源表,不通过运维平台复制一份数据。
|
||
- 运维模块只读取状态为 `published` 的监控批次,未发布或失败批次对页面不可见。
|
||
- 判级、复核、报告、流程、文档、Prompt 和配置由运维模块独立维护,模型平台不得写入。
|
||
- 已发布监控数据不原地覆盖;勘误时新增修订批次,保留历史版本和审计链。
|
||
- 首期不新增微服务、Redis或消息中间件;复用现有 Nginx、React、FastAPI、对象存储和 Schedule Executor,新库自带独立 Outbox/Inbox。
|
||
|
||
这能满足“模型方直接写业务表”和“我方单独建库”的双重要求,同时避免取得现有平台库不必要的写权限。
|
||
|
||
## 2. 模块边界
|
||
|
||
### 2.1 模型平台负责
|
||
|
||
- 银行、模型实例、模型版本及生命周期数据的写入。
|
||
- 月度监控原始结果、特征指标和分箱分布数据的计算与写入。
|
||
- 写入批次的完整性校验、计数、校验和及发布。
|
||
- 仅获得 `model_operations` 7 张源表的受控写权限,以及发布事件所需的 Outbox 插入权限。
|
||
- 已发布数据发生错误时,以新修订批次更正,不修改旧记录。
|
||
|
||
### 2.2 运维模块负责
|
||
|
||
- 按已发布原始结果和已发布规则版本计算异常等级、监控结果等级和命中原因。
|
||
- 模型团队初审、业务团队终审、超时默认不处理及全程留痕。
|
||
- 监控/诊断报告生成、编辑、发送、汇总和历史修订。
|
||
- 七阶段开发评审流程、材料、知识库、规则、Prompt、报告模板和按银行配置。
|
||
- 工作台、模型大类、细分银行、监控明细和详情等查询 API。
|
||
|
||
### 2.3 明确不做
|
||
|
||
- 运维模块不重复建设账号、角色、权限表;只复用登录模块返回的用户、角色和工作空间。
|
||
- 运维模块不得读取 `model_platform.users.password_hash`,不得写现有平台任一业务表。
|
||
- 模型平台不写判级、处理、报告、流程或配置表。
|
||
- “手工同步”不再复制数据;其含义调整为“校验最新发布批次并重建派生判级”。
|
||
- 报告汇总、知识库和工作台不单独建冗余汇总表,首期由业务表查询生成。
|
||
|
||
## 3. 总体架构
|
||
|
||
现有服务拓扑保持不变。运维能力作为 FastAPI 后端内的新 bounded context 接入,但增加第二套数据库连接:
|
||
|
||
1. 浏览器继续经 Nginx 访问 React SPA 和同源 `/api/v1/*`。
|
||
2. `DATABASE_URL` 继续连接 `model_platform`,只承担现有认证、工作空间、存储和调度能力。
|
||
3. 新增 `OPERATIONS_DATABASE_URL` 连接 `model_operations`,承载全部运维业务数据。
|
||
4. 模型平台模块或调度任务直接写新库的 `ops_*` 源表。
|
||
5. 批次发布后,运维服务计算并保存规则判级快照。
|
||
6. 运维 API 联查新库源数据、判级和处理结果;用户显示名等按需只读平台库。
|
||
7. 报告生成、提醒等动作写新库 `ops_outbox_events`;接入异步阶段时让 Schedule Executor 增加运维库轮询连接。
|
||
8. 报告文件、流程材料继续复用平台库 `storage_objects` 与现有对象存储服务,运维库只保存对象 ID。
|
||
|
||
配套图:
|
||
|
||
- `A卡模型运维模块-系统架构-V0.2.png/.svg`
|
||
- `A卡模型运维模块-数据域关系-V0.2.png/.svg`
|
||
|
||
修改 YAML 后运行 `python3 docs/architecture/render_diagrams.py` 可重新生成两种格式;脚本依赖 `diagrams`、`pyyaml` 和本机 Graphviz。
|
||
|
||
配套建库建表脚本位于 `docs/database/model_operations-V0.1/`,包含 24 张表、1 个视图、四类模型大类种子数据和静态安全校验脚本;原始平台 DDL 已只读归档到 `docs/database/reference/`。
|
||
|
||
## 4. 数据设计约定
|
||
|
||
- 表名前缀统一为 `ops_`,与现有模型平台表隔离。
|
||
- 全部 `ops_*` 对象均位于独立 `model_operations` 库。
|
||
- 主键统一使用 ULID `CHAR(26)`;日期时间统一 `DATETIME(3)`。
|
||
- 月份在库内用 `DATE` 且固定为当月 1 日,API 序列化为 `YYYY-MM`。
|
||
- KS、PSI、IV、CSI、坏账率和降幅在数据库中统一存 **0~1 比率**,使用 `DECIMAL(12,8)`;API 转换为 0~100 的百分数。
|
||
- 沿用当前仓库约定:不创建物理外键,使用 `fk_*` 逻辑索引并在服务层校验完整性。
|
||
- 可修改业务状态表使用 `state_version INTEGER` 做乐观锁。
|
||
- 需要删除的配置或流程数据采用 `is_deleted`、`deleted_at` 软删除;监控历史不允许删除。
|
||
- 所有根业务表包含 `workspace_id`,避免未来多个项目空间数据串用。
|
||
- `workspace_id/user_id/storage_object_id/platform_versions_id` 是对 `model_platform` 的逻辑引用,不建立跨库物理外键。
|
||
|
||
## 5. 表清单与归属
|
||
|
||
### 5.1 模型平台可写源表(7 张)
|
||
|
||
| 表 | 用途 | 核心唯一性 |
|
||
|---|---|---|
|
||
| `ops_banks` | 银行主数据及无极银行标记 | `(workspace_id, bank_code)` |
|
||
| `ops_model_instances` | 银行下的模型实例、通用模型关系、当前版本 | `(workspace_id, model_id)` |
|
||
| `ops_model_versions` | 版本、开发指标和生命周期日期 | `(model_instance_id, version_label)` |
|
||
| `ops_monitor_batches` | 月度写入批次、修订号、发布状态和校验和 | `(workspace_id, source_system, monitor_month, revision_no)` |
|
||
| `ops_monitor_results` | 模型单月原始排序性、KS、PSI和样本量 | `(batch_id, model_instance_id)` |
|
||
| `ops_monitor_feature_metrics` | 单月特征级 IV、CSI及贡献变化 | `(monitor_result_id, feature_code)` |
|
||
| `ops_monitor_distributions` | 评分分箱或特征分箱的基准期/当期分布 | `(monitor_result_id, dimension_type, feature_code, bin_order)` |
|
||
|
||
`ops_model_categories` 为运维模块维护的固定字典,模型平台只有读取权限。首期种子为 `std / bai / big / afd` 四类。
|
||
|
||
### 5.2 运维模块独占业务表(15 张)
|
||
|
||
| 数据域 | 表 |
|
||
|---|---|
|
||
| 字典 | `ops_model_categories` |
|
||
| 判级与处理 | `ops_rule_versions`、`ops_rule_items`、`ops_monitor_evaluations`、`ops_monitor_reviews` |
|
||
| 报告治理 | `ops_reports`、`ops_report_revisions`、`ops_report_template_versions`、`ops_prompt_versions`、`ops_prompt_regression_runs`、`ops_bank_report_configs` |
|
||
| 流程与材料 | `ops_workflows`、`ops_workflow_stages`、`ops_documents` |
|
||
| 使用统计 | `ops_usage_events` |
|
||
|
||
不新增通知任务表;通知和后台报告任务复用新库 `ops_outbox_events`。若后续需要展示逐次送达结果,再增通知投递日志表。
|
||
|
||
### 5.3 运维库技术表(2 张)
|
||
|
||
- `ops_outbox_events`:与运维业务写入同事务提交的后台事件。
|
||
- `ops_consumer_inbox`:异步消费者幂等去重。
|
||
|
||
新库合计 **24 张表 + 1 个当前监控结果视图**。不直接写 `model_platform.outbox_events`,防止运维事件污染现有模型调度事件流。
|
||
|
||
## 6. 核心表字段初稿
|
||
|
||
### 6.1 模型与版本
|
||
|
||
`ops_model_instances`
|
||
|
||
- `model_instance_id`、`workspace_id`、`bank_id`、`category_id`
|
||
- `model_id`、`model_name`、`model_status`
|
||
- `current_version_id`
|
||
- `is_common_model`、`common_source_model_id`、`common_model_name`
|
||
- `source_updated_at`、`created_at`、`updated_at`
|
||
|
||
`ops_model_versions`
|
||
|
||
- `model_version_id`、`model_instance_id`、`version_label`、`version_status`
|
||
- `developer_user_ref`、`developer_display_name`
|
||
- `development_date`、`iteration_start_date`、`last_iteration_date`、`iteration_reason`
|
||
- `escort_start_date`、`escort_end_date`、`online_date`、`offline_date`
|
||
- `development_ks`、`development_psi`、`max_lift`
|
||
- `scoring_logic_storage_object_id`、`source_updated_at`
|
||
|
||
模型实例只保存当前版本指针,所有生命周期和开发指标按版本留存。
|
||
|
||
### 6.2 监控批次和原始结果
|
||
|
||
`ops_monitor_batches`
|
||
|
||
- `batch_id`、`workspace_id`、`source_system`、`source_batch_no`
|
||
- `monitor_month`、`revision_no`
|
||
- `batch_status`:`writing / published / failed / superseded`
|
||
- `expected_model_count`、`written_model_count`、`feature_row_count`、`distribution_row_count`
|
||
- `checksum_sha256`、`generated_at`、`published_at`、`failed_reason`
|
||
|
||
`ops_monitor_results`
|
||
|
||
- `monitor_result_id`、`batch_id`、`model_instance_id`、`model_version_id`、`monitor_month`
|
||
- `ranking_result`:`matched / unmatched / not_applicable`
|
||
- `ks_value`、`psi_value`
|
||
- `sample_count`、`good_count`、`bad_count`
|
||
- `source_result_json`:仅保存尚未结构化且确有追溯价值的源字段
|
||
- `calculated_at`、`created_at`
|
||
|
||
源表不保存 A/B/C、异常等级、处理建议或报告状态,避免两个模块同时改一行。
|
||
|
||
### 6.3 判级快照
|
||
|
||
`ops_monitor_evaluations`
|
||
|
||
- `evaluation_id`、`monitor_result_id`、`rule_version_id`、`rule_item_id`
|
||
- `ks_mom_drop_rate`、`secondary_level2_hits_6m`
|
||
- `abnormal_level`:`normal / level1 / level2 / level3`
|
||
- `monitor_grade`:`A / B / C`
|
||
- `reason_code`、`reason_text_snapshot`、`action_snapshot`
|
||
- `is_current`、`evaluated_at`
|
||
|
||
每次规则发布或源数据修订都新增判级记录;旧快照不覆盖。页面默认读取 `is_current=1` 的记录,历史报告仍绑定生成时的判级快照。
|
||
|
||
### 6.4 两段处理
|
||
|
||
`ops_monitor_reviews`
|
||
|
||
- `review_id`、`monitor_result_id`、`evaluation_id`
|
||
- `review_stage`:`model_initial / business_final`
|
||
- `review_status`:`pending / handled / auto_closed`
|
||
- `decision`:`no_action / tune_or_rebuild`
|
||
- `handling_note`(手工处理必填)
|
||
- `due_at`、`handled_by`、`handled_at`、`auto_closed_at`
|
||
- `state_version`、`created_at`、`updated_at`
|
||
|
||
唯一约束为 `(monitor_result_id, review_stage)`。模型团队阶段完成后才创建或激活业务团队阶段;超时任务通过 Outbox 执行默认“不处理”。
|
||
|
||
### 6.5 报告
|
||
|
||
`ops_reports` 保存报告主状态与绑定快照,`ops_report_revisions` 保存每次正文修改:
|
||
|
||
- 报告绑定 `monitor_result_id + evaluation_id + template_version_id + prompt_version_id`。
|
||
- `ops_report_revisions` 使用 `(report_id, revision_no)` 唯一约束,正文和结构化快照均不可变。
|
||
- 监控报告页默认只查询最新监控月份;报告汇总页查询全部历史。数据库不删除旧月份报告。
|
||
- PDF/Excel 等文件通过 `storage_object_id` 指向现有 `storage_objects`。
|
||
|
||
### 6.6 流程、材料与知识库
|
||
|
||
- `ops_workflows`:银行、模型大类、模型实例、独立开发/复用通用模型、当前阶段、状态和计划日期。
|
||
- `ops_workflow_stages`:1~7 阶段实例、负责人角色、开始/截止/完成时间、确认状态和停滞天数。
|
||
- `ops_documents`:同时支持 `storage_object_id` 和 `external_url`,可关联流程、阶段、模型和版本。
|
||
- 知识库不是新表,而是对 `ops_documents` 按银行、模型、版本、环节和通用模型标记查询。
|
||
|
||
## 7. 发布协议
|
||
|
||
模型平台每次写入必须执行以下协议:
|
||
|
||
1. 创建 `ops_monitor_batches`,状态为 `writing`,携带幂等的 `source_batch_no`。
|
||
2. 幂等写入银行、模型实例和版本;插入本批次结果、特征指标和分布。
|
||
3. 校验模型数、明细行数、必填字段、指标范围和 SHA-256 校验和。
|
||
4. 在一个短事务内将批次改为 `published`;同月份上一修订改为 `superseded`。
|
||
5. 写入 `monitor_batch.published` Outbox 事件,触发运维模块生成判级快照。
|
||
6. 运维查询只允许关联 `published` 批次,并按最高修订号取当前结果。
|
||
|
||
失败批次保留为 `failed` 供排查。任何已发布结果、特征和分布均不得执行 `UPDATE` 或物理删除。
|
||
|
||
## 8. 数据库权限
|
||
|
||
建议生产环境至少分四类账号:
|
||
|
||
| 账号 | 权限 |
|
||
|---|---|
|
||
| `schema_migrator` | 仅部署时使用,可对 `model_operations` 执行 DDL;不改 `model_platform` |
|
||
| `platform_reader` | 对平台库指定字段只读;禁止读取密码散列和权限内部表 |
|
||
| `model_writer` | 读取字典;对新库 7 张源表执行限定 `SELECT/INSERT/UPDATE`,并插入发布事件;无 `DELETE`,不能写运维独占表 |
|
||
| `operations_app` | 读写运维库;只读平台库必要表;服务层禁止修改已发布源结果 |
|
||
|
||
若模型任务与运维 API 暂时运行在同一 FastAPI 进程,代码层仍按平台库/运维库两个 session factory 和 repository 隔离;生产批处理脚本优先使用独立 `MODEL_WRITER_DATABASE_URL`,将权限边界落实到数据库。
|
||
|
||
## 9. API 设计
|
||
|
||
### 9.1 首条真实纵向链路
|
||
|
||
保持前端已写好的路径,不重新发明契约:
|
||
|
||
| 接口 | 主要数据来源 |
|
||
|---|---|
|
||
| `GET /api/v1/operations/models` | 模型实例 + 当前版本 + 最新已发布结果 + 当前判级 + 最近处理 |
|
||
| `GET /api/v1/operations/models/{model_id}` | 单模型完整元数据和最新状态 |
|
||
| `GET /api/v1/operations/models/{model_id}/monitor-results?month=YYYY-MM` | 指定月最新已发布修订 + 当前判级 |
|
||
|
||
所有接口返回现有 `{ data, meta? }` 包装。切换 `VITE_OPERATIONS_API_MODE=api` 前,前端 `operationsApi.ts` 需要统一附加当前 `workspace_id`,用于选择并校验数据空间。
|
||
|
||
### 9.2 后续资源接口
|
||
|
||
- 查询:`/model-categories/overview`、`/banks/overview`、`/monitor-results`、`/monitor-results/{id}/features`、`/monitor-results/{id}/distributions`
|
||
- 处理:`/monitor-results/{id}/reviews/model-initial`、`/reviews/business-final`
|
||
- 报告:`/reports`、`/reports/{id}`、`/reports/{id}/revisions`、`/reports/{id}/send`
|
||
- 流程:`/workflows`、`/workflows/{id}`、`/workflows/{id}/stages/{stage}`、`/documents`
|
||
- 治理:`/rule-versions`、`/prompt-versions`、`/report-template-versions`、`/bank-report-configs`
|
||
|
||
列表统一支持分页、排序和与 V1.9 筛选框一致的查询参数;Excel/PDF 导出走独立端点,不让普通列表接口返回超大数据集。
|
||
|
||
## 10. 登录与角色
|
||
|
||
- 不增加运维 RBAC 表,也不接管现有权限管理模块。
|
||
- API 复用登录 Cookie、`Users`、`Roles` 和当前工作空间,不创建新的角色或权限数据。
|
||
- 新增轻量 `operations_context`:校验用户已登录且可访问所选工作空间;业务角色取与 `/api/v1/auth/me`、前端 `AuthUser.role_code` 相同的登录角色来源,避免前后端角色口径不一致。
|
||
- 角色映射约定:`admin` → 管理员;`developer/model_team` → 模型团队;`business/business_team/biz` → 业务团队。
|
||
- 前端按角色决定页面呈现,后端仍必须按角色校验写操作,不能把前端隐藏按钮当安全边界。
|
||
- 角色代码最终值由登录/权限模块给出,运维模块只维护一处映射常量。
|
||
|
||
## 11. 代码落位建议
|
||
|
||
沿用现有 router / schema / service 分层,每个文件不超过 500 行:
|
||
|
||
```text
|
||
common/src/common/db/models/operations/
|
||
reference.py models.py monitoring.py governance.py
|
||
reviews.py reports.py workflows.py usage.py
|
||
backend/src/backend/api/operations/
|
||
__init__.py _deps.py models.py monitoring.py reviews.py
|
||
reports.py workflows.py governance.py
|
||
backend/src/backend/services/operations/
|
||
queries.py evaluation.py publishing.py reports.py workflows.py
|
||
backend/src/backend/schemas/operations/
|
||
common.py models.py monitoring.py reports.py workflows.py governance.py
|
||
```
|
||
|
||
运维 ORM 使用独立 Alembic 版本线和 `OPERATIONS_DATABASE_URL`,不能让现有 `model_platform` 基线迁移误扫新表。`backend.main` 创建第二个 engine/session factory 并只注册一个 `operations_router`;业务规则不写进路由文件。
|
||
|
||
## 12. 实施顺序
|
||
|
||
### P0:首条真实链路
|
||
|
||
1. DBA 审核并创建 `model_operations`;建模型/版本/批次/结果/特征/分布及规则判级 11 张核心表。
|
||
2. 导入一组 2026-07 样例数据并走完整 `writing → published` 协议。
|
||
3. 实现三条既有 API,前端追加 `workspace_id` 后切到 API 模式。
|
||
4. 验证列表、模型详情、单月监控详情数值一致。
|
||
|
||
### P1:业务闭环
|
||
|
||
实现两段复核、报告及修订、七阶段流程、材料和知识库。
|
||
|
||
### P2:治理与运营
|
||
|
||
实现规则版本、Prompt 回归、模板版本、使用统计、提醒和导出优化。
|
||
|
||
## 13. 开发前需共同确认的 5 项
|
||
|
||
以下均给出推荐默认值;未得到反对意见时可按推荐值实施:
|
||
|
||
1. **模型 ID 唯一性**:推荐 `model_id` 在工作空间内全局唯一,不只在银行内唯一。
|
||
2. **指标单位**:推荐数据库存 0~1 比率,API 输出百分数;禁止两种单位混写。
|
||
3. **批次粒度**:推荐一个来源系统每月一个全量批次,勘误整体升修订号。
|
||
4. **工作空间范围**:推荐全部根表带 `workspace_id` 并逻辑引用 `model_platform.workspaces`,即使首期只有一个 A 卡项目空间。
|
||
5. **模型开发材料**:推荐运维表同时支持对象存储文件和模型平台链接,避免重复上传。
|
||
|
||
这 5 项确认后即可开始 ORM、Alembic 迁移和首条真实接口实现。
|