Files
CN_Tool/docs/superpowers/specs/2026-04-28-add-data-flicker-batch-design.md

790 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 电能质量 13 张表批量补数设计说明
## 1. 背景
`tools/add-data` 当前只有模块骨架和历史 SQL 文件 [DATA_FLICKER.sql](D:/Work/SourceCode/CN_Tool/tools/add-data/DATA_FLICKER.sql),尚未提供真实业务接口。
本次需求是在 `add-data` 模块中补齐一套“按时间区间批量生成电能质量数据并写入数据库”的能力,满足以下业务目标:
- 支持按时间区间补数,例如一天、一月。
- 支持时间戳步长选择:`1``3``5``10` 分钟。
- 支持单监测点和多监测点,前端可选择监测点 ID默认单监测点。
- 支持批量写入,优先保证大区间补数时的效率。
- 同一主键数据已存在时跳过,不覆盖、不删除。
- 同步提供前端页面交互方案与参数展示规则。
## 2. 范围确认
根据当前 [DATA_FLICKER.sql](D:/Work/SourceCode/CN_Tool/tools/add-data/DATA_FLICKER.sql) 内容,本次补数范围按 13 张表处理,而不是 14 张表:
1. `data_flicker`
2. `data_fluc`
3. `data_harmphasic_i`
4. `data_harmphasic_v`
5. `data_harmpower_p`
6. `data_harmpower_q`
7. `data_harmpower_s`
8. `data_harmrate_i`
9. `data_harmrate_v`
10. `data_i`
11. `data_inharm_i`
12. `data_plt`
13. `data_v`
## 3. 已确认需求
本次设计基于以下已确认结论:
- 写入方式是“按时间戳步长生成整段数据”,不是定时常驻写库。
- 重复补数时采用“跳过已存在数据”策略。
- 前端方案需要和后端接口一起设计。
- 分表时间轴规则已确认:
- `data_flicker` 固定每 `10` 分钟写一组
- `data_fluc` 固定每 `10` 分钟写一组
- `data_plt` 固定每 `2` 小时写一组
- 其余 `10` 张表按前端选择的 `1``3``5``10` 分钟步长写入
- 所有时间点都按自然时间槽对齐,采用整点整分方式生成,不按传入开始时间顺延
- 展示字段需要覆盖:
- `电能质量参数名称`
- `相别`
- `是否合格`
- `最大值`
- `最小值`
- `平均值`
- `95%概率大值`
- `数据展示(保留小数)`
- 数值展示默认保留 `2` 位小数。
- 相别参照用户提供图片中的“相别”列。
## 4. 关键约束
### 4.1 主键约束
13 张表均以 `LINEID + TIMEID + PHASIC_TYPE` 作为主键,重复写入时必须避免主键冲突导致整批失败。
### 4.2 相别字段长度约束
`PHASIC_TYPE` 字段长度为 `varchar(2)`。因此前端展示中的相别值和数据库实际落库值不能简单等同:
- 页面展示可使用 `T``ABC``AB/BC/CA`
- 落库时必须拆分为真实可存储的 `PHASIC_TYPE`
### 4.3 数据量约束
当时间区间为“一月”、步长为“1 分钟”、监测点为“多选”时,补数规模会非常大,不能采用单条插入或一次性全量加载入内存的方式。
### 4.4 现有仓库约束
- 当前仓库是后端工程,没有现成前端代码,本次只输出前端交互方案和接口契约。
- 默认不执行 `mvn` 编译、打包、测试。
- 修改范围应尽量收敛在 `tools/add-data` 模块内。
## 5. 方案选择
本次比较三种实现路径:
### 方案 A同步接口直接写库
特点:
- 控制器接收参数后直接在请求线程中完成全部写库。
- 实现最简单。
问题:
- 大区间、多监测点时容易超时。
- 任务执行中断后难以定位进度。
- 前端体验差,无法明确看到当前执行状态。
结论:
不采用。
### 方案 B异步任务 + JDBC 批量写入
特点:
- 请求只负责创建任务,后台异步执行。
- 任务状态可轮询。
- 批量写入时可按表、按批次提交。
优势:
- 更适合月级、多监测点补数。
- 可以精细控制批次大小。
- 当前数据源连接串已开启 `rewriteBatchedStatements=true`,适合批处理。
结论:
采用该方案。
### 方案 C只生成 SQL 文件,人工执行
特点:
- 后端只生成 SQL 脚本,不直接写库。
问题:
- 需要人工执行,流程割裂。
- 不符合“前端选择后直接补数”的目标。
结论:
不采用。
## 6. 总体设计
### 6.1 设计目标
`tools/add-data` 中新增一套“批量补数任务”能力,支持:
- 前端提交补数参数
- 后端预估本次写入规模
- 后端异步生成数据并按表批量写入
- 前端轮询任务状态
- 前端获取参数展示规则
### 6.2 模块边界
本次能力只落在 `tools/add-data`,不向 `system``user``detection` 扩散业务逻辑。
如需监测点列表,首版不假设存在基础监测点表,也不新增点位管理能力,直接由前端传入选中的 `lineIds`
### 6.3 接口拆分
建议把“任务管理”和“参数模板展示”分成两类接口:
- 补数任务接口:负责预估、创建、状态查询
- 模板展示接口:负责返回图片对应的参数展示规则
## 7. 数据模型与相别规则
### 7.1 展示相别与落库相别分离
前端展示相别来自用户图片中的“相别”列,但落库时需要展开成真正的 `PHASIC_TYPE` 值。
建议在模板中同时维护:
- `phaseDisplay`:前端展示值
- `phaseCodes`:落库使用的真实相别集合
### 7.2 相别展开规则
建议默认规则如下:
- `T` -> `["T"]`
- `ABC` -> `["A", "B", "C"]`
- `AB/BC/CA` -> `["AB", "BC", "CA"]`
这套规则同时用于:
- 决定每个参数需要展开多少条写库数据
- 决定前端参数表如何显示相别列
### 7.3 参数模板职责
参数模板不直接等于数据库表结构,而是承担以下职责:
- 描述前端展示行
- 绑定该参数对应的数据表
- 描述展示相别和落库相别
- 约定是否展示“是否合格”
- 约定最大值、最小值、平均值、95%概率大值的展示逻辑
- 约定保留小数位数,默认 `2`
## 8. 后端接口设计
### 8.1 预估接口
接口:
- `POST /addData/task/preview`
作用:
- 根据 `lineIds`、时间区间、时间戳步长,返回本次补数的预估规模
入参:
- `lineIds`:监测点 ID 列表
- `startTime`:开始时间
- `endTime`:结束时间
- `intervalMinutes``1``3``5``10`
返回内容:
- 时间点数量
- 监测点数量
- 每张表预估写入条数
- 总预估条数
说明:
- 预估逻辑必须按分表时间轴分别计算。
- `intervalMinutes` 只影响 `data_harmphasic_i``data_harmphasic_v``data_harmpower_p``data_harmpower_q``data_harmpower_s``data_harmrate_i``data_harmrate_v``data_i``data_inharm_i``data_v``10` 张表。
- `data_flicker``data_fluc` 固定按 `10` 分钟时间槽计算。
- `data_plt` 固定按 `2` 小时时间槽计算。
### 8.2 创建任务接口
接口:
- `POST /addData/task/create`
作用:
- 创建一个批量补数任务,并立即进入后台执行
入参:
- `lineIds`
- `startTime`
- `endTime`
- `intervalMinutes`
返回内容:
- `taskId`
- 初始状态
说明:
- 前端默认提交单个 `lineId`
- 多监测点时前端直接提交多个 `lineIds`
- 不单独拆分“单点接口”和“多点接口”
- `intervalMinutes` 仅作为 `10` 张基础实时类表的生成步长
- 固定频率表仍按各自时间轴生成,不跟随 `intervalMinutes`
### 8.3 任务状态接口
接口:
- `GET /addData/task/status/{taskId}`
作用:
- 供前端轮询当前任务执行状态
返回内容:
- 任务状态:`WAITING``RUNNING``SUCCESS``FAILED`
- 当前执行表名
- 当前批次信息
- 已写入条数
- 已跳过条数
- 失败条数
- 失败原因
- 开始时间、结束时间
### 8.4 参数模板接口
接口:
- `GET /addData/template/list`
作用:
- 返回前端页面参数规则表所需的静态配置
返回内容:
- 电能质量参数名称
- 相别展示值
- 是否展示“是否合格”
- 最大值规则
- 最小值规则
- 平均值规则
- 95%概率大值规则
- 保留小数位数
## 9. 后端分层设计
### 9.1 Controller 层
建议新增两个控制器:
- `AddDataTaskController`
- 补数任务相关接口
- `AddDataTemplateController`
- 参数模板查询接口
### 9.2 Service 层
建议拆分为:
- `AddDataTaskService`
- 创建任务
- 状态查询
- 预估规模
- `AddDataTemplateService`
- 返回前端参数模板
### 9.3 Component 层
建议新增以下组件:
- `AddDataTaskExecutor`
- 负责异步执行任务
- `AddDataBatchWriter`
- 负责按表批量写入
- `AddDataValueGenerator`
- 负责根据模板生成每条记录的值
- `AddDataTaskStatusHolder`
- 首版用内存保存任务状态
- `AddDataTemplateRegistry`
- 持有参数模板与相别映射
### 9.4 数据访问层
本次不建议为 13 张表分别创建超长 MyBatis XML 批量插入语句。
原因如下:
- 宽表字段极多XML 可维护性差。
- 13 张表结构差异大,重复代码很多。
- 本次更偏工具型批处理写数,不适合以业务实体方式逐表建模。
建议采用:
- `JdbcTemplate`
- `PreparedStatement`
- `batchUpdate`
在写入 SQL 上统一使用:
- `INSERT IGNORE`
以满足“跳过已存在数据”的业务要求。
## 10. 批量写入策略
### 10.1 总体原则
批量写入必须满足以下原则:
- 不先查存在再插入
- 不整段数据一次性全部放入内存
- 按表、按批次逐步提交
- 主键冲突自动跳过
### 10.2 跳过已存在数据
采用 `INSERT IGNORE` 而不是以下策略:
- 不采用“先查后写”,避免额外查询放大数据库压力
- 不采用“覆盖更新”,与已确认需求不符
- 不采用“先删后写”,避免误删
### 10.3 流式生成
建议按如下顺序流式生成:
1. 先遍历时间点
2. 再遍历监测点
3. 再按模板展开相别
4. 最后路由到对应表的批缓存
达到批次阈值后立即落库并清空当前缓存。
### 10.4 分批阈值
建议根据表宽度分层设置批次大小:
- 窄表:`500 ~ 1000` 行/批
- 宽表:`100 ~ 200` 行/批
其中以下表建议视为宽表:
- `data_harmphasic_i`
- `data_harmphasic_v`
- `data_harmpower_p`
- `data_harmpower_q`
- `data_harmpower_s`
- `data_harmrate_i`
- `data_harmrate_v`
- `data_i`
- `data_inharm_i`
- `data_v`
### 10.5 任务统计
任务执行过程中需累计以下统计值:
- `insertedCount`
- `skippedCount`
- `failedCount`
其中:
- `insertedCount` 表示本次真正插入成功的行数
- `skippedCount` 表示因主键重复被忽略的行数
- `failedCount` 表示非主键冲突导致的失败数量
## 11. 数据生成策略
### 11.1 首版原则
首版不引入复杂公式配置,不让前端传计算表达式。数据生成规则由后端内置。
### 11.2 分表时间轴规则
本次补数不采用“全部表共用同一时间轴”的方式,而是拆成三类时间轴:
1. 固定 `10` 分钟时间轴
2. 固定 `2` 小时时间轴
3. 用户步长时间轴
具体规则如下:
- `data_flicker`
- 固定每 `10` 分钟一组
- 按自然时间槽对齐,例如 `10:00``10:10``10:20`
- `data_fluc`
- 固定每 `10` 分钟一组
- 按自然时间槽对齐
- `data_plt`
- 固定每 `2` 小时一组
- 按自然时间槽对齐,例如 `00:00``02:00``04:00`
- 其余 `10` 张表
- 按前端传入的 `intervalMinutes` 生成
- 允许值仅为 `1``3``5``10`
- 同样按自然时间槽对齐
如果前端传入 `startTime=10:07`
- `10` 分钟表应从 `10:10` 开始
- `2` 小时表应从 `12:00` 或下一个符合槽位的时刻开始
- 用户步长表应从不小于 `startTime` 的下一个对应槽位开始
### 11.3 统一生成方式
建议采用“基础状态生成 + 派生字段回填 + 分表时间轴裁切”的方式:
- 先按 `(lineId, timeId)` 生成基础电气状态
- 再从基础状态派生电压、电流、谐波、功率、闪变
- 最后按各表自己的时间轴决定是否落库
统一原则如下:
- 同一时间点的 13 张表数据必须同源,不允许各表独立随机
- 主值字段按基准值和受控扰动生成
- 最大值字段不小于主值
- 最小值字段不大于主值
- `95%概率大值` 对应 `CP95` 字段
- 宽表中的谐波分量和派生列按统一规则回填
- 同一输入参数应尽量生成可复现的结果,不建议使用完全无约束随机数
### 11.4 逐表生成规律
#### 11.4.1 基础源表
`data_v` 作为电压源表,优先生成:
- 先生成 `FREQ`
- 再生成 `RMS`
- 再派生 `RMSAB``RMSBC``RMSCA`
- 再派生 `VU_DEV``VL_DEV``FREQ_DEV`
- 再派生 `V_POS``V_NEG``V_ZERO``V_UNBALANCE`
- 最后生成 `V_1 ~ V_50`
- `V_THD``V_2 ~ V_50``V_1` 计算
`data_i` 作为电流源表,与 `data_v` 保持同源:
- 先生成 `RMS`
- 再生成 `I_POS``I_NEG``I_ZERO``I_UNBALANCE`
- 再生成 `I_1 ~ I_50`
- `I_THD``I_2 ~ I_50``I_1` 计算
对这两张源表:
- `RMS_MAX``RMS_MIN``RMS_CP95`
- `FREQ_MAX``FREQ_MIN``FREQ_CP95`
- `V_THD_MAX``V_THD_MIN``V_THD_CP95`
- `I_THD_MAX``I_THD_MIN``I_THD_CP95`
均必须由主值派生,不单独随机。
#### 11.4.2 谐波幅值表
`data_harmphasic_v`
- 直接复用 `data_v` 中的 `V_1 ~ V_50`
- 各次谐波的 `MAX``MIN``CP95``data_v` 同源派生
`data_harmphasic_i`
- 直接复用 `data_i` 中的 `I_1 ~ I_50`
- 各次谐波的 `MAX``MIN``CP95``data_i` 同源派生
#### 11.4.3 谐波占比表
`data_harmrate_v`
-`data_harmphasic_v` 派生
- 建议按 `V_n / V_1 * 100` 计算 `V_1 ~ V_50` 对应占比值
- `MAX``MIN``CP95` 也按同样比例换算
`data_harmrate_i`
-`data_harmphasic_i` 派生
- 建议按 `I_n / I_1 * 100` 计算 `I_1 ~ I_50` 对应占比值
- `MAX``MIN``CP95` 也按同样比例换算
#### 11.4.4 间谐波电流表
`data_inharm_i`
- 作为电流间谐波表生成
- 各阶值应明显小于对应整数次谐波值
- 建议按“整数次谐波值乘以小比例系数”生成
- `MAX``MIN``CP95` 继续由主值派生
#### 11.4.5 谐波功率表
`data_harmpower_p`
- 从电压谐波和电流谐波联合派生
- `P_1 ~ P_50` 建议按电压谐波、电流谐波和相位关系计算
- `P` 为各次分量汇总
- `PF``DF` 由功率关系派生
`data_harmpower_q`
-`data_harmpower_p` 同源
- `Q_1 ~ Q_50` 按无功功率关系派生
- `Q` 为各次分量汇总
`data_harmpower_s`
-`data_harmpower_p``data_harmpower_q` 同源
- `S_1 ~ S_50` 建议由 `P_n``Q_n` 派生
- `S` 为各次分量汇总
这三张表中的 `MAX``MIN``CP95` 也必须基于对应主值派生。
#### 11.4.6 闪变与波动表
`data_flicker`
- 固定 `10` 分钟时间轴
- 生成 `FLUC``PST``PLT`
- `FLUC` 作为波动主值
- `PST``FLUC` 派生
- `PLT` 作为长时闪变指标生成,但落库节奏仍为 `10` 分钟
`data_fluc`
- 固定 `10` 分钟时间轴
- 生成 `FLUC``FLUCCF`
-`data_flicker` 同源
- `FLUCCF` 作为 `FLUC` 的修正或归一化结果生成
`data_plt`
- 固定 `2` 小时时间轴
- 只写 `PLT`
- `PLT` 来源应与 `data_flicker` 中的 `PLT` 同源
- 但只在 `2` 小时槽位落表
### 11.5 前端展示字段映射
由于表结构中没有独立 `AVG` 字段,前端“平均值”首版建议直接取各参数主值字段。
例如:
- 电压类参数取 `RMS``V_THD``FREQ`
- 电流类参数取 `RMS``I_THD`
- 闪变类参数取 `FLUC``PST``PLT`
- 功率类参数取 `P``Q``S`
前端展示中的:
- `最大值` -> 对应 `*_MAX`
- `最小值` -> 对应 `*_MIN`
- `95%概率大值` -> 对应 `*_CP95`
- `平均值` -> 对应主值字段
### 11.6 质量标识
`QUALITYFLAG` 首版建议统一使用固定有效值,例如 `1`
前端“是否合格”不直接读取数据库 `QUALITYFLAG` 的原始含义,而是走模板展示规则,避免把数据生成逻辑和展示文案硬耦合。
## 12. 前端页面交互方案
### 12.1 页面结构
建议页面分为两个区域:
1. 补数任务区
2. 参数规则展示区
### 12.2 补数任务区
表单项建议如下:
- 监测点选择模式:`单点` / `多点`
- 监测点 ID 选择框
- 开始时间
- 结束时间
- 时间戳步长:`1``3``5``10` 分钟
- 预计写入量按钮
- 开始补数按钮
交互说明:
- 默认单监测点
- 多监测点时切换为多选
- 时间戳步长只影响 `10` 张基础实时类表
- `data_flicker``data_fluc` 固定按 `10` 分钟槽位写入
- `data_plt` 固定按 `2` 小时槽位写入
- 前端先调用预估接口,展示预计写入规模
- 用户确认后再调用创建任务接口
### 12.3 任务状态展示
前端创建任务后轮询状态接口,建议展示:
- 当前状态
- 当前表名
- 已写入数量
- 已跳过数量
- 失败数量
- 失败原因
- 开始时间
- 结束时间
### 12.4 参数规则展示区
参数规则表按用户提供图片组织,至少展示以下列:
- `电能质量参数名称`
- `相别`
- `显示`
- `最大值`
- `最小值`
- `平均值`
- `95%概率大值`
- `是否合格`
- `数据展示(保留小数)`
其中:
- 相别列使用 `phaseDisplay`
- 所有数值默认保留 `2` 位小数
- 该表展示的是模板规则,不是实时数据库统计结果
## 13. 文件结构建议
建议在 `tools/add-data` 下按现有仓库风格扩展:
```text
tools/add-data/src/main/java/com/njcn/gather/tool/adddata/
├── controller
│ ├── AddDataTaskController.java
│ └── AddDataTemplateController.java
├── service
│ ├── AddDataTaskService.java
│ ├── AddDataTemplateService.java
│ └── impl
│ ├── AddDataTaskServiceImpl.java
│ └── AddDataTemplateServiceImpl.java
├── component
│ ├── AddDataTaskExecutor.java
│ ├── AddDataBatchWriter.java
│ ├── AddDataValueGenerator.java
│ ├── AddDataTaskStatusHolder.java
│ └── AddDataTemplateRegistry.java
└── pojo
├── param
└── vo
```
如参数模板需落成资源文件,建议放在:
- `tools/add-data/src/main/resources/template/add-data-parameter-template.json`
## 14. 非目标
本次不包含以下内容:
- 不新增监测点基础信息管理能力
- 不新增监测点字典表
- 不新增数据库迁移框架
- 不在 `system``user` 模块中扩展通用任务中心
- 不实现真实前端页面代码
- 不做覆盖更新或先删后写逻辑
## 15. 风险与取舍
### 15.1 内存态任务状态的风险
首版如果使用内存保存任务状态:
- 服务重启后任务状态会丢失
- 适合当前工具型场景
- 不适合后续演进为长期审计能力
当前取舍:
- 首版接受该限制
- 如果后续需要审计与追踪,再补任务表
### 15.2 模板与真实表字段映射的风险
用户图片中的参数项比“13 张表名”更偏业务视角,实际落库需要一层参数模板到表结构的映射。
当前取舍:
- 首版把模板规则集中维护在 `AddDataTemplateRegistry`
- 不在前端直接拼接数据库字段名
### 15.3 大批量写入的数据库压力
多监测点、长区间、1 分钟步长时数据库压力会很大。
当前取舍:
- 采用预估接口提前提示规模
- 控制批次大小
- 使用异步执行与 `INSERT IGNORE`
## 16. 验证方式
本次设计和后续实现默认不执行 `mvn`,验证方式以静态检查和链路闭合检查为主。
### 16.1 设计验证
确认以下设计点闭合:
- 13 张表范围明确
- 相别展示与落库规则明确
- 重复数据处理策略明确
- 前后端接口契约明确
- 批量写入策略明确
### 16.2 实现后验证
后续实现时重点检查:
- 入参校验是否覆盖时间区间、步长、监测点 ID 列表
- 预估条数计算是否正确
- SQL 是否统一使用 `INSERT IGNORE`
- 批次是否按宽表和窄表区分
- 重复补数时是否正确累加 `skippedCount`
- 参数模板返回是否与图片字段一致
## 17. 结论
本次采用“异步任务 + JDBC 批量写入 + 参数模板展示接口”的实现方案,在 `tools/add-data` 模块内完成电能质量 13 张表的批量补数能力。
该方案兼顾以下目标:
- 支持单监测点和多监测点
- 支持日级、月级区间
- 支持 1/3/5/10 分钟时间戳步长
- 支持重复补数时跳过已存在数据
- 支持前端按图片规则展示参数和相别
- 在当前仓库约束下把改动范围控制在最小必要集合