指标模型动态配置方案.md 14 KB

指标模型动态配置方案

最后更新:2026-04-12 | 状态:Phase 1–4 已完成并部署


1. 背景与目标

1.1 原始问题

系统原有两套独立的指标管理体系:

体系 范围 编码格式
MetricCatalog ado_smart_ops_metric_catalog 仅 S4,12 条 S4_CYCLE_L1S4_L2_CYCLE
KpiMaster ado_smart_ops_kpi_master S1~S9,115 条 S4_L1_001S4_L2_001

两套编码互不关联、名称不同步、字段丰富度不一致,导致"运营指标建模"(S0)和"运营指标主数据"页面各管各的。

1.2 目标架构

KpiMaster 作为唯一指标主数据源,废弃 MetricCatalog:

┌──────────────────────────────────────────────────┐
│              KpiMaster(115 条,S1~S9)            │
│   统一编码 S{n}_L{m}_NNN │ 父子关系 │ 公式/规则   │
└──────────┬───────────────────────────┬────────────┘
           │                           │
     ┌─────▼─────┐             ┌───────▼───────┐
     │ LayoutItem │             │ kpi_value 表  │
     │ 租户级展示  │             │ 日值/目标值    │
     │ 配置偏好    │             │ metric_code   │
     └────────────┘             └───────────────┘

1.3 双向联动

  • KpiMaster 页面 → 建模页面:建模页面每次打开从 KpiMaster 实时读取,天然同步
  • 建模页面 → KpiMaster:保存布局时,如果 DisplayName 与 MetricName 不同,自动同步回写 KpiMaster

2. 已完成工作

Phase 1:SQL 数据迁移 ✅

将 LayoutItem、kpi_value 表中的旧 MetricCatalog 编码全部迁移为 KpiMaster 编码。

编码映射表(旧 → 新):

旧编码 新编码 名称 层级变化
S4_CYCLE_L1 S4_L1_001 物料交货周期 L1→L1
S4_SAT_L1 S4_L1_002 物料交货满足率 L1→L1
S4_EFF_L1 S4_L1_003 物料采购人效 L1→L1
S4_TRANSIT_L1 S4_L1_004 采购在途周转 L1→L1
S4_L2_CYCLE S4_L2_001 品类物料交货周期 L2→L2
S4_L2_SAT S4_L2_002 品类物料交货满足率 L2→L2
S4_L2_EFF S4_L2_003 品类物料采购人效 L2→L2
S4_L2_TRANSIT S4_L2_004 品类在途物料周转 L2→L2
S4_L2_RCYCLE S4_L3_001 供应商物料交货周期 L2→L3
S4_L2_RSAT S4_L3_002 供应商物料交货满足率 L2→L3
S4_L2_REFF S4_L3_003 供应商物料采购人效 L2→L3
S4_L2_RTRANSIT S4_L3_004 供应商在途物料周转 L2→L3

同步更新的表:

  • ado_smart_ops_layout_item:MetricCode、RowId、ParentRowId、MetricLevel
  • ado_s9_kpi_value_l1_day:metric_code
  • ado_s9_kpi_value_l2_day:metric_code

Phase 2:后端重构 ✅

改动文件:

文件 变更
AidopKanbanController.S4.cs 5 个端点全部改为查 AdoSmartOpsKpiMasterPutS4OperationLayout 增加双向同步逻辑;API 返回增加 id 字段
Startup.cs 移除 MetricCatalog 的 InitTables 和 Seed 调用
AdoSmartOpsMetricCatalog.cs 已删除
AidopS4MetricModelSeed.cs 已删除

API 端点现状(/api/AidopKanban/):

端点 数据源 说明
GET s4-metric-catalog KpiMaster 返回 id、metricCode、parentId、formula、calcRule 等富字段
GET s4-operation-layout LayoutItem + KpiMaster 布局行 + KpiMaster 补充默认名/单位/方向
PUT s4-operation-layout LayoutItem + KpiMaster 保存布局 + DisplayName 变更同步回写 KpiMaster
GET s4-home-grid LayoutItem + KpiMaster + kpi_value_l1_day 九宫格卡片
GET s4-detail-kpis LayoutItem + KpiMaster + kpi_value_l2_day 详情页 KPI

Phase 3:前端适配 ✅

改动文件:

文件 变更
kanbanData.ts S4CatalogRow 增加 idparentIddescriptionformulacalcRule 等 7 个 KpiMaster 字段
s0.vue ① 一次获取全量 S4 指标按层级分组;② L2/L3 子集过滤改为 KpiMaster parentId 关系;③ RowId 按实际 metricLevel 区分前缀;④ L3 父级选择器自动显示 L2 选项
operationModelSchema.ts S4 模块从 3 个 L1 扩展为 4 个,名称与 KpiMaster 对齐
s4GraphServerMap.ts 映射数组从 3 项扩展为 4 项
kpiMaster.vue 左侧树增加 S 模块分组层(S1~S9),虚拟模块节点不可拖拽/删除,样式区分

Phase 4:构建部署 ✅

  • Docker 镜像构建已优化(分层缓存),改代码后增量构建 ≤2 分钟
  • 九宫格、详情页、建模页面端到端验证通过

3. 当前数据架构

3.1 KpiMaster 表结构

表名:ado_smart_ops_kpi_master

字段 类型 说明
Id bigint PK 自增主键
MetricCode varchar(50) UK 自动生成,格式 S{n}_L{m}_NNN
ModuleCode varchar(20) S1~S9
MetricLevel int 1=L1, 2=L2, 3=L3
ParentId bigint? 父指标 ID(L1 为 null)
MetricName varchar(200) 指标名称
Description text KPI 描述
Formula text 计算公式
CalcRule text 计算规则(含举例)
DataSource varchar(200) 数据来源模块
StatFrequency varchar(50) 统计频率
Department varchar(200) 责任部门
DopFields varchar(500) DOP 系统字段映射
Unit varchar(50) 单位
Direction varchar(20) 优劣方向
IsHomePage bool 是否可上九宫格首页
SortNo int 同级排序号
IsEnabled bool 启用/禁用
TenantId bigint 租户 ID

数据统计: 115 条(S1~S9 全模块),其中 S4 有 L1×4、L2×4、L3×4 = 12 条。

3.2 S4 指标父子关系

S4_L1_001 物料交货周期(天)        id=13
  └─ S4_L2_001 品类物料交货周期      id=54, parentId=13
       └─ S4_L3_001 供应商物料交货周期  id=99, parentId=54

S4_L1_002 物料交货满足率(%)       id=14
  └─ S4_L2_002 品类物料交货满足率     id=55, parentId=14
       └─ S4_L3_002 供应商物料交货满足率 id=100, parentId=55

S4_L1_003 物料采购人效(颗/人)     id=15
  └─ S4_L2_003 品类物料采购人效      id=56, parentId=15
       └─ S4_L3_003 供应商物料采购人效  id=101, parentId=56

S4_L1_004 采购在途周转(天)        id=16
  └─ S4_L2_004 品类在途物料周转      id=57, parentId=16
       └─ S4_L3_004 供应商在途物料周转  id=102, parentId=57

3.3 LayoutItem 与 KpiMaster 的关系

概念 职责
指标定义 KpiMaster 名称、公式、规则、单位、方向 — 单一事实源
展示配置 LayoutItem 哪些指标上首页、显示名覆盖、排序、面板分区 — 租户级偏好
运行数据 kpi_value_l*_day 日值、目标值 — 引用 KpiMaster 编码

4. 功能页面现状

4.1 运营指标主数据(/aidop/kanban/kpiMaster

  • 左侧树:三层结构 = S 模块分组层(S1~S9)→ L1 → L2 → L3
  • 右侧详情:编辑 KpiMaster 全部字段(名称、公式、规则、数据来源等)
  • 操作:新增/删除/拖拽排序/启用禁用
  • 数据:直连 AdoSmartOpsKpiMasterController

4.2 运营指标建模(/aidop/kanban/s0

  • 图谱:ECharts 力导向图,显示 S1~S7 全模块 L1/L2 关系
  • S4 面板
    • 点击 S4 模块 → 右侧 L1 勾选表(最多 8 个上首页)
    • 点击 S4 L1 节点 → 右侧显示该 L1 的 L2/L3 子指标(按 KpiMaster parentId 过滤)
  • 双向同步:保存布局时 DisplayName 变更自动回写 KpiMaster
  • 数据:S4 部分读 AidopKanbanController S4 端点(底层查 KpiMaster);其他模块为本地图谱草稿

4.3 S4 采购看板(九宫格 + 详情)

  • 首页九宫格GET s4-home-grid,展示 L1 布局 × kpi_value_l1_day
  • 详情页GET s4-detail-kpis,展示 L2/L3 布局 × kpi_value_l2_day
  • 指标元信息(名称、单位、方向)全部从 KpiMaster 实时读取

5. Docker 构建优化

Dockerfile 已优化为分层缓存策略:

Dockerfile 优化方式 改代码后耗时
Dockerfile.api 先拷 *.csproj + slndotnet restore → 再拷源码 → dotnet publish ~38s
Dockerfile.web 先拷 package*.jsonnpm install → 再拷源码 → npm run build ~1m46s

只有修改 package.json(加依赖)或 .csproj(加 NuGet 包)时才会触发 install/restore 重跑。


6. Demo 数据租户隔离方案

状态:待执行

6.1 背景与需求

需要方便快速地导入用于演示的数据来展示系统功能,但不影响实际业务数据。要求:

  • 正式账号看正式数据,Demo 账号看 Demo 数据,可同时在线
  • 前端完全无感知,无需切换模式
  • Demo 数据可一键刷新

6.2 当前问题:AiDOP 未接入 Admin.NET 多租户体系

Admin.NET 框架已有完整的行级多租户隔离(ITenantIdFilter + SqlSugar 全局过滤),但 AiDOP 插件完全绕开了这套体系:

问题 现状
实体未实现 ITenantIdFilter KpiMaster/LayoutItem/HomeModule 有 TenantId 字段但无自动过滤
Controller 硬编码 tenantId 所有 API 用 [FromQuery] long tenantId = 1 默认参数
TenantId 值不一致 AiDOP 用 1,Admin.NET 默认租户是 1300000000001
前端硬传 tenantId kanbanData.ts 所有请求带 tenantId=1

6.3 架构方案

登录入口
├─ 正式账号 → Token.TenantId = 1300000000001
│   └─ 后端自动识别 → WHERE TenantId=1300000000001 → 正式数据
└─ Demo 账号 → Token.TenantId = 1300000000888
    └─ 后端自动识别 → WHERE TenantId=1300000000888 → Demo 数据

同一数据库,按 TenantId 行级隔离
系统管理(菜单/角色/权限)共享,业务数据隔离

6.4 执行步骤

Phase 1:后端 — AiDOP 接入租户体系

  • 1a 新建 AidopTenantHelper.cs:从 HttpContext.User.Claims 取 TenantId,未登录 fallback 1300000000001
  • 1b 三个实体加 ITenantIdFilter 接口:AdoSmartOpsKpiMasterAdoSmartOpsLayoutItemAdoSmartOpsHomeModule
  • 1c 所有 Controller 移除 [FromQuery] long tenantId = 1,改用 Helper 自动获取
  • 1d AidopKpiMasterSeed.cs 默认值从 1 改为 SqlSugarConst.DefaultTenantId

Phase 2:数据迁移 — TenantId 从 1 改为 1300000000001

UPDATE ado_smart_ops_kpi_master  SET TenantId=1300000000001  WHERE TenantId=1;
UPDATE ado_smart_ops_layout_item SET TenantId=1300000000001  WHERE TenantId=1;
UPDATE ado_smart_ops_home_module SET TenantId=1300000000001  WHERE TenantId=1;
UPDATE ado_s9_kpi_value_l1_day   SET tenant_id=1300000000001 WHERE tenant_id=1;
UPDATE ado_s9_kpi_value_l2_day   SET tenant_id=1300000000001 WHERE tenant_id=1;

Phase 3:前端 — 去掉硬编码 tenantId

  • kanbanData.ts:所有 API 函数移除 tenantId 参数
  • s0.vueloadS4Panel/saveS4LayoutClick 去掉 tenantId 实参
  • kpiMasterApi.ts:同上

Phase 4:创建 Demo 租户 + 账号

SQL 脚本 scripts/setup-demo-tenant.sql

  1. SysTenant 插入 Demo 租户(Id=1300000000888,TenantType=Id)
  2. SysTenantMenu 授权全部菜单
  3. SysUser 创建 DemoAdmin 账号
  4. SysRole + SysUserRole + SysRoleMenu 配权限

Phase 5:Demo 数据智能生成

Python 脚本 scripts/gen_demo_data.py,读取 KpiMaster 定义自动生成全套自洽数据:

输入:

  • 连接数据库读取 115 条 KpiMaster 定义(层级、父子关系、direction、unit)
  • 可选:CSV 覆盖部分 L1 的 target/actual

生成逻辑:

  1. 根据 unit/direction 确定合理的 target 范围(%类 90~99.5、天类 2~10、人效类 500~1000)
  2. 生成 30 天日值(带趋势 + 随机波动)
  3. L1 → L2 → L3 自动拆分,加权平均自洽(误差 <1%)
  4. 制造红/黄/绿分布(约 60%/25%/15%)

自洽规则:

  • higher_is_better:绿 actual >= target;黄 actual >= target×0.95;红 < target×0.95
  • lower_is_better:绿 actual <= target;黄 actual <= target×1.1;红 > target×1.1
  • 末位 L2 做差值补偿确保加权平均 ≈ L1

输出数据量:

行数 说明
kpi_master 115 复制全套指标定义
layout_item ~12 S4 看板布局
home_module 1 S4 模块配置
kpi_value_l1_day ~960 32 L1 × 30天
kpi_value_l2_day ~1800 60 L2 × 30天
kpi_value_l3_day ~690 23 L3 × 30天

使用方式:

python3 scripts/gen_demo_data.py --db-host=... --demo-tenant-id=1300000000888
mysql -h... -u... -p... aidopdev < scripts/import-demo-data.sql

Phase 6:构建部署 + 验证

  • Docker 构建(利用缓存 ≤3 分钟)
  • 正式账号登录 → 正式数据不受影响
  • DemoAdmin 登录 → 完整 Demo 数据
  • 两个浏览器同时在线互不干扰

6.5 风险与注意

项目 说明
旧表无 TenantId ado_order/ado_work_order/ado_plan 无 TenantId 列,本期不改,后续按需加列
kpi_value 原生 SQL S4 控制器中 raw SQL 已有 tenant_id=@t,需确认 @t 取自 Helper
系统管理不动 菜单/用户/角色的租户隔离由 Admin.NET 框架自动处理

7. 遗留与后续

项目 状态 说明
S4_ONTIME 编码 待确认 存在于 kpi_value_l1_day 但无对应 KpiMaster 行,可能是历史残留
其他模块(S1~S3, S5~S9)看板 未接入 目前不走 LayoutItem/KpiMaster 看板流程,仅 S4 完成了全链路
MetricCatalog 旧表 可清理 实体和种子已删除,数据库表 ado_smart_ops_metric_catalog 可手动 DROP
建模页面非 S4 模块 本地草稿 S1~S3、S5~S7 的图谱编辑保存在浏览器 localStorage,未接入服务端