# 电能质量 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 分钟时间戳步长 - 支持重复补数时跳过已存在数据 - 支持前端按图片规则展示参数和相别 - 在当前仓库约束下把改动范围控制在最小必要集合