From 90219a3dafb8893ccde4554cc0207573216267a2 Mon Sep 17 00:00:00 2001 From: yexb <553699424@qq.com> Date: Fri, 15 May 2026 16:37:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=88=91=E5=8F=AB=E5=9C=A3=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- entrance/pom.xml | 5 + event/event-list/API_DEBUG.md | 317 ++++++++++++++++ pom.xml | 1 + steady/pom.xml | 24 ++ steady/steady-DataView/API_DEBUG.md | 239 ++++++++++++ steady/steady-DataView/pom.xml | 51 +++ .../component/SteadyInfluxQueryComponent.java | 183 +++++++++ .../component/SteadyTrendFieldResolver.java | 261 +++++++++++++ .../SteadyTrendIndicatorCatalog.java | 112 ++++++ .../config/SteadyInfluxDbProperties.java | 28 ++ .../config/SteadySecondTimeSerializer.java | 26 ++ .../controller/SteadyDataViewController.java | 72 ++++ .../SteadyDataViewIndicatorController.java | 43 +++ .../SteadyDataViewLedgerController.java | 44 +++ .../SteadyDataViewTrendController.java | 64 ++++ .../mapper/SteadyDataViewLedgerMapper.java | 14 + .../datavie/mapper/SteadyDataViewMapper.java | 25 ++ .../mapping/SteadyDataViewLedgerMapper.xml | 48 +++ .../mapper/mapping/SteadyDataViewMapper.xml | 50 +++ .../pojo/bo/SteadyDataViewLedgerRowBO.java | 28 ++ .../bo/SteadyTrendIndicatorDefinitionBO.java | 42 +++ .../pojo/bo/SteadyTrendResolvedFieldBO.java | 36 ++ .../pojo/bo/SteadyTrendSeriesFieldBO.java | 24 ++ .../pojo/param/SteadyDataViewDetailParam.java | 25 ++ .../pojo/param/SteadyDataViewQueryParam.java | 49 +++ .../pojo/param/SteadyTrendQueryParam.java | 43 +++ .../vo/SteadyDataViewIndicatorNodeVO.java | 51 +++ .../pojo/vo/SteadyDataViewLedgerNodeVO.java | 36 ++ .../pojo/vo/SteadyDataViewTemplateVO.java | 26 ++ .../datavie/pojo/vo/SteadyDataViewVO.java | 45 +++ .../datavie/pojo/vo/SteadyTrendPointVO.java | 28 ++ .../datavie/pojo/vo/SteadyTrendQueryVO.java | 30 ++ .../datavie/pojo/vo/SteadyTrendSeriesVO.java | 38 ++ .../pojo/vo/SteadyTrendSummaryItemVO.java | 27 ++ .../datavie/pojo/vo/SteadyTrendSummaryVO.java | 20 + .../SteadyDataViewIndicatorService.java | 13 + .../service/SteadyDataViewLedgerService.java | 13 + .../service/SteadyDataViewService.java | 21 ++ .../service/SteadyDataViewTrendService.java | 17 + .../SteadyDataViewIndicatorServiceImpl.java | 60 +++ .../impl/SteadyDataViewLedgerServiceImpl.java | 70 ++++ .../impl/SteadyDataViewServiceImpl.java | 352 ++++++++++++++++++ .../impl/SteadyDataViewTrendServiceImpl.java | 210 +++++++++++ .../SteadyInfluxQueryComponentTest.java | 51 +++ .../SteadyTrendFieldResolverTest.java | 99 +++++ ...teadyDataViewIndicatorServiceImplTest.java | 27 ++ .../SteadyDataViewTrendServiceImplTest.java | 123 ++++++ .../dictionary/pojo/enums/DictDataEnum.java | 10 +- .../system/pojo/constant/DictConst.java | 20 + .../service/impl/SysRegResServiceImpl.java | 3 +- .../wave/component/WaveVectorComponent.java | 3 +- .../component/WaveVectorComponentTest.java | 73 ++++ 52 files changed, 3315 insertions(+), 5 deletions(-) create mode 100644 event/event-list/API_DEBUG.md create mode 100644 steady/pom.xml create mode 100644 steady/steady-DataView/API_DEBUG.md create mode 100644 steady/steady-DataView/pom.xml create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyInfluxQueryComponent.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyTrendFieldResolver.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyTrendIndicatorCatalog.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/config/SteadyInfluxDbProperties.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/config/SteadySecondTimeSerializer.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewController.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewIndicatorController.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewLedgerController.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewTrendController.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/SteadyDataViewLedgerMapper.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/SteadyDataViewMapper.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/mapping/SteadyDataViewLedgerMapper.xml create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/mapping/SteadyDataViewMapper.xml create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyDataViewLedgerRowBO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendIndicatorDefinitionBO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendResolvedFieldBO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendSeriesFieldBO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyDataViewDetailParam.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyDataViewQueryParam.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyTrendQueryParam.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewIndicatorNodeVO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewLedgerNodeVO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewTemplateVO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewVO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendPointVO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendQueryVO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSeriesVO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSummaryItemVO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSummaryVO.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewIndicatorService.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewLedgerService.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewService.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewTrendService.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewIndicatorServiceImpl.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewLedgerServiceImpl.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewServiceImpl.java create mode 100644 steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewTrendServiceImpl.java create mode 100644 steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/component/SteadyInfluxQueryComponentTest.java create mode 100644 steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/component/SteadyTrendFieldResolverTest.java create mode 100644 steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewIndicatorServiceImplTest.java create mode 100644 steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewTrendServiceImplTest.java create mode 100644 tools/wave-tool/src/test/java/com/njcn/gather/tool/wave/component/WaveVectorComponentTest.java diff --git a/entrance/pom.xml b/entrance/pom.xml index eabe987..16f8932 100644 --- a/entrance/pom.xml +++ b/entrance/pom.xml @@ -63,6 +63,11 @@ event-list 1.0.0 + + com.njcn.gather + steady-DataView + 1.0.0 + diff --git a/event/event-list/API_DEBUG.md b/event/event-list/API_DEBUG.md new file mode 100644 index 0000000..263bd1a --- /dev/null +++ b/event/event-list/API_DEBUG.md @@ -0,0 +1,317 @@ +# event-list API 调试文档 + +## 1. 基础信息 + +- 模块:`event/event-list` +- 控制器:`EventListController` +- 接口前缀:`/event/list` +- 本地默认地址:`http://localhost:18192` +- Content-Type:`application/json` +- 认证:除 `/admin/login`、`/admin/getPublicKey`、Swagger 资源外,请求需要携带登录后的 `Authorization` 头。 + +```text +Authorization: Bearer +``` + +说明: + +- `event-list` 当前提供暂态事件分页查询、详情查询和导出能力。 +- 事件数据来自 `r_mp_event_detail`。 +- 工程、项目、设备、监测点名称由 `tools/add-ledger` 内部服务按监测点 ID 批量补齐。 +- 本文示例中的业务数据为调试示例,实际值以数据库为准。 + +## 2. 通用响应 + +分页和详情接口统一返回 `HttpResult` 包装结果。外层字段由公共组件序列化,常见结构如下: + +```json +{ + "code": "000000", + "message": "成功", + "data": {} +} +``` + +调试时重点查看: + +- `code`:业务响应码。 +- `message`:业务响应消息。 +- `data`:接口返回数据。 + +导出接口直接返回 Excel 文件流,不返回 `HttpResult` JSON。 + +## 3. 分页查询暂态事件列表 + +### 3.1 接口信息 + +- 路径:`POST /event/list/transient/page` +- 完整地址:`http://localhost:18192/event/list/transient/page` +- 控制器方法:`EventListController#pageTransientEvents` +- 服务方法:`EventListService#pageTransientEvents` +- 返回:`HttpResult>` +- 默认排序:`start_time DESC, event_id DESC` + +### 3.2 请求示例 + +```json +{ + "pageNum": 1, + "pageSize": 10, + "startTimeStart": "2026-05-01 00:00:00", + "startTimeEnd": "2026-05-09 23:59:59", + "eventType": "VOLTAGE_SAG", + "phase": "A", + "eventDescribe": "暂降", + "durationMin": 0.02, + "durationMax": 10, + "featureAmplitudeMin": 10, + "featureAmplitudeMax": 90, + "fileFlag": 1, + "dealFlag": 0, + "lineIds": [ + "line-001" + ], + "engineeringName": "示例工程", + "projectName": "示例项目", + "equipmentName": "示例设备", + "lineName": "示例测点" +} +``` + +分页字段继承公共 `BaseParam`,调试时按项目现有分页参数约定传入。示例使用 `pageNum`、`pageSize`。 + +### 3.3 请求字段 + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `pageNum` | Number | 否 | 页码,来自公共分页参数 | +| `pageSize` | Number | 否 | 每页条数,来自公共分页参数 | +| `startTimeStart` | String | 否 | 发生时刻开始,格式 `yyyy-MM-dd HH:mm:ss` | +| `startTimeEnd` | String | 否 | 发生时刻结束,格式 `yyyy-MM-dd HH:mm:ss` | +| `eventType` | String | 否 | 事件类型,对应 `r_mp_event_detail.event_type` | +| `phase` | String | 否 | 相别,对应 `r_mp_event_detail.phase` | +| `eventDescribe` | String | 否 | 事件描述关键字,按 `LIKE` 查询 | +| `durationMin` | Number | 否 | 持续时间下限,单位秒 | +| `durationMax` | Number | 否 | 持续时间上限,单位秒 | +| `featureAmplitudeMin` | Number | 否 | 暂降/暂升幅值下限 | +| `featureAmplitudeMax` | Number | 否 | 暂降/暂升幅值上限 | +| `fileFlag` | Number | 否 | 波形文件状态:`0` 未招,`1` 已招 | +| `dealFlag` | Number | 否 | 处理状态:`0` 未处理,`1` 已处理,`2` 已处理无结果,`3` 计算失败 | +| `lineIds` | Array | 否 | 监测点 ID 列表,最多 1000 个 | +| `engineeringName` | String | 否 | 工程名称关键字,通过 `add-ledger` 反查监测点 | +| `projectName` | String | 否 | 项目名称关键字,通过 `add-ledger` 反查监测点 | +| `equipmentName` | String | 否 | 设备名称关键字,通过 `add-ledger` 反查监测点 | +| `lineName` | String | 否 | 监测点名称关键字,通过 `add-ledger` 反查监测点 | + +时间字段支持以下输入格式: + +- `yyyy-MM-dd HH:mm:ss` +- `yyyy-MM-dd HH:mm:ss.SSS` +- `yyyy-MM-dd'T'HH:mm:ss` +- `yyyy-MM-dd'T'HH:mm:ss.SSS` + +如果不传 `startTimeStart`,后端默认取当前月 1 日 `00:00:00`。如果不传 `startTimeEnd`,后端默认取当前时间。 + +### 3.4 响应示例 + +```json +{ + "code": "000000", + "message": "成功", + "data": { + "records": [ + { + "eventId": "event-001", + "measurementPointId": "line-001", + "eventType": "VOLTAGE_SAG", + "equipmentName": "示例设备", + "engineeringName": "示例工程", + "projectName": "示例项目", + "startTime": "2026-05-09 10:20:30", + "lineName": "示例测点", + "eventDescribe": "电压暂降", + "sagsource": "上游", + "phase": "A", + "duration": 0.12, + "featureAmplitude": 65.5, + "wavePath": "D:/wave/event-001.cfg", + "fileFlag": 1, + "dealFlag": 0, + "createTime": "2026-05-09 10:21:00" + } + ], + "total": 1, + "size": 10, + "current": 1, + "pages": 1 + } +} +``` + +`Page` 对象可能包含 MyBatis-Plus 的其他分页字段,调试时主要关注 `records`、`total`、`size`、`current`、`pages`。 + +### 3.5 curl 示例 + +```powershell +curl.exe -X POST "http://localhost:18192/event/list/transient/page" ` + -H "Content-Type: application/json" ` + -H "Authorization: Bearer " ` + -d "{\"pageNum\":1,\"pageSize\":10,\"startTimeStart\":\"2026-05-01 00:00:00\",\"startTimeEnd\":\"2026-05-09 23:59:59\",\"eventDescribe\":\"暂降\"}" +``` + +## 4. 查询暂态事件详情 + +### 4.1 接口信息 + +- 路径:`GET /event/list/transient/{eventId}` +- 完整地址:`http://localhost:18192/event/list/transient/{eventId}` +- 控制器方法:`EventListController#getTransientEventDetail` +- 服务方法:`EventListService#getTransientEventDetail` +- 返回:`HttpResult` + +### 4.2 请求参数 + +| 参数 | 位置 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | --- | +| `eventId` | Path | String | 是 | 暂态事件 ID,对应 `r_mp_event_detail.event_id` | + +### 4.3 响应示例 + +```json +{ + "code": "000000", + "message": "成功", + "data": { + "eventId": "event-001", + "measurementPointId": "line-001", + "eventType": "VOLTAGE_SAG", + "equipmentName": "示例设备", + "engineeringName": "示例工程", + "projectName": "示例项目", + "startTime": "2026-05-09 10:20:30", + "lineName": "示例测点", + "eventDescribe": "电压暂降", + "sagsource": "上游", + "phase": "A", + "duration": 0.12, + "featureAmplitude": 65.5, + "wavePath": "D:/wave/event-001.cfg", + "fileFlag": 1, + "dealFlag": 0, + "createTime": "2026-05-09 10:21:00" + } +} +``` + +### 4.4 curl 示例 + +```powershell +curl.exe -X GET "http://localhost:18192/event/list/transient/event-001" ` + -H "Authorization: Bearer " +``` + +## 5. 导出暂态事件列表 + +### 5.1 接口信息 + +- 路径:`POST /event/list/transient/export` +- 完整地址:`http://localhost:18192/event/list/transient/export` +- 控制器方法:`EventListController#exportTransientEvents` +- 服务方法:`EventListService#exportTransientEvents` +- 返回:Excel 文件流 +- 文件名:`暂态事件列表.xlsx` +- Sheet 名称:`暂态事件列表` + +导出复用分页查询的筛选条件,但不使用分页结果。当前同步导出最多允许 5000 条,超过时返回业务错误。 + +### 5.2 请求示例 + +```json +{ + "startTimeStart": "2026-05-01 00:00:00", + "startTimeEnd": "2026-05-09 23:59:59", + "eventDescribe": "暂降", + "fileFlag": 1 +} +``` + +### 5.3 导出字段 + +| Excel 列名 | 响应字段 | 说明 | +| --- | --- | --- | +| 设备名称 | `equipmentName` | 由 `add-ledger` 按监测点补齐 | +| 工程名称 | `engineeringName` | 由 `add-ledger` 按监测点补齐 | +| 项目名称 | `projectName` | 由 `add-ledger` 按监测点补齐 | +| 发生时刻 | `startTime` | 格式 `yyyy-MM-dd HH:mm:ss` | +| 监测点名称 | `lineName` | 由 `add-ledger` 按监测点补齐 | +| 事件描述 | `eventDescribe` | 为空时使用 `eventType` | +| 事件发生位置 | `sagsource` | 为空时返回 `-` | +| 相别 | `phase` | 为空时返回 `-` | +| 持续时间(s) | `duration` | 单位秒 | +| 暂降/暂升幅值(%) | `featureAmplitude` | 原值导出 | + +### 5.4 curl 示例 + +```powershell +curl.exe -X POST "http://localhost:18192/event/list/transient/export" ` + -H "Content-Type: application/json" ` + -H "Authorization: Bearer " ` + -d "{\"startTimeStart\":\"2026-05-01 00:00:00\",\"startTimeEnd\":\"2026-05-09 23:59:59\",\"eventDescribe\":\"暂降\"}" ` + -o "D:/temp/暂态事件列表.xlsx" +``` + +## 6. 返回字段说明 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `eventId` | String | 事件 ID | +| `measurementPointId` | String | 监测点 ID | +| `eventType` | String | 事件类型 | +| `equipmentName` | String | 设备名称,台账缺失时为 `-` | +| `engineeringName` | String | 工程名称,台账缺失时为 `-` | +| `projectName` | String | 项目名称,台账缺失时为 `-` | +| `startTime` | String | 发生时刻,格式 `yyyy-MM-dd HH:mm:ss` | +| `lineName` | String | 监测点名称,台账缺失时为 `-` | +| `eventDescribe` | String | 事件描述,空值时返回 `eventType` | +| `sagsource` | String | 事件发生位置,空值时为 `-` | +| `phase` | String | 相别,空值时为 `-` | +| `duration` | Number | 持续时间,单位秒 | +| `featureAmplitude` | Number | 暂降/暂升幅值 | +| `wavePath` | String | 波形文件路径 | +| `fileFlag` | Number | 波形文件状态:`0` 未招,`1` 已招 | +| `dealFlag` | Number | 处理状态:`0` 未处理,`1` 已处理,`2` 已处理无结果,`3` 计算失败 | +| `createTime` | String | 创建时间,格式 `yyyy-MM-dd HH:mm:ss` | + +## 7. 常见错误场景 + +| 场景 | 后端提示 | +| --- | --- | +| 详情接口 `eventId` 为空 | `事件 ID 不能为空` | +| 详情数据不存在 | `暂态事件不存在` | +| 开始时间大于结束时间 | `开始时间不能大于结束时间` | +| 时间格式无法解析 | `时间格式不正确,仅支持 yyyy-MM-dd HH:mm:ss` | +| 持续时间下限大于上限 | `持续时间下限不能大于上限` | +| 幅值下限大于上限 | `幅值下限不能大于上限` | +| `fileFlag` 不为 `0` 或 `1` | `波形文件状态只能是 0 或 1` | +| `dealFlag` 不在 `0-3` 范围内 | `处理状态只能是 0、1、2、3` | +| `lineIds` 超过 1000 个 | `监测点 ID 查询数量不能超过 1000 个` | +| 台账关键字匹配监测点超过 1000 个 | `台账检索匹配监测点过多,请缩小查询条件` | +| 导出超过 5000 条 | `导出数据超过 5000 条,请缩小查询条件` | + +未携带有效 `Authorization` 时,全局认证过滤器会返回 token 解析相关错误。 + +## 8. 调试注意事项 + +- 先登录获取 `accessToken`,再调试 `event-list` 接口。 +- 分页查询不传时间范围时,后端默认查当前月 1 日到当前时间。 +- 台账关键字筛选会先通过 `add-ledger` 查询匹配监测点,再查询事件表。 +- 如果台账关键字命中范围过大,需要缩小工程、项目、设备或测点关键字。 +- 导出接口建议始终传入明确时间范围,避免超过 5000 条限制。 +- 如查询性能不稳定,可先检查是否已按需执行 `event/event-list/src/main/resources/sql/event-list/event-list-index.sql` 中的建议索引。 + +## 9. 当前限制 + +- 本文档只补充 API 调试说明,未改动业务代码。 +- 当前未执行 `mvn` 编译、打包、测试或真实接口联调。 +- 响应外层 `HttpResult` 字段以公共组件实际序列化结果为准。 +- 分页参数字段以公共 `BaseParam` 实际定义和前端现有调用约定为准。 diff --git a/pom.xml b/pom.xml index 18b8fde..85cf6bb 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ detection tools event + steady diff --git a/steady/pom.xml b/steady/pom.xml new file mode 100644 index 0000000..11d39b7 --- /dev/null +++ b/steady/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + com.njcn.gather + CN_Tool + 1.0.0 + + + steady + pom + + + steady-DataView + + + + 8 + 8 + UTF-8 + + diff --git a/steady/steady-DataView/API_DEBUG.md b/steady/steady-DataView/API_DEBUG.md new file mode 100644 index 0000000..13a59b9 --- /dev/null +++ b/steady/steady-DataView/API_DEBUG.md @@ -0,0 +1,239 @@ +# steady-DataView API 调试文档 + +## 1. 基础信息 + +- 模块:`steady/steady-DataView` +- 控制器:`SteadyDataViewController` +- 接口前缀:`/steady/data-view` +- 本地默认地址:`http://localhost:18192` +- Content-Type:`application/json` +- 认证:除登录和 Swagger 资源外,请求需要携带登录后的 `Authorization` 头。 + +## 2. 分页查询稳态数据 + +- 路径:`POST /steady/data-view/page` +- 返回:`HttpResult>` +- 默认表:`data_v` +- 默认时间范围:当前月 1 日 `00:00:00` 到当前时间 +- 默认排序:`TIMEID DESC, LINEID ASC, PHASIC_TYPE ASC` + +请求示例: + +```json +{ + "pageNum": 1, + "pageSize": 10, + "tableName": "data_v", + "timeStart": "2026-05-01 00:00:00", + "timeEnd": "2026-05-09 23:59:59", + "phasicType": "A", + "qualityFlag": 1, + "lineIds": [ + "line-001" + ], + "engineeringName": "示例工程", + "projectName": "示例项目", + "equipmentName": "示例设备", + "lineName": "示例测点" +} +``` + +`tableName` 只允许 `tools/add-data` 已注册的 13 张 `data_*` 表;台账关键字会先通过 `add-ledger` 转换为监测点 ID,再查询稳态数据表。 + +## 3. 查询稳态数据详情 + +- 路径:`POST /steady/data-view/detail` +- 返回:`HttpResult` + +请求示例: + +```json +{ + "tableName": "data_v", + "lineId": "line-001", + "timeId": "2026-05-09 10:20:30", + "phasicType": "A" +} +``` + +详情使用 `LINEID + TIMEID + PHASIC_TYPE` 定位单条数据。 + +## 4. 查询稳态数据模板 + +- 路径:`GET /steady/data-view/templates` +- 返回:`HttpResult>` + +模板来自 `tools/add-data` 的前端展示模板,返回参数名称、表名、相别和当前表可展示值字段。 + +## 5. 查询趋势台账树 + +- 路径:`GET /steady/data-view/ledger-tree` +- 返回:`HttpResult>` +- 查询参数:`keyword`,可选,按台账节点名称搜索并保留父级路径。 + +节点字段: + +| 字段 | 说明 | +| --- | --- | +| `id` | 台账节点 ID | +| `parentId` | 父节点 ID | +| `name` | 节点名称 | +| `level` | 层级:0 工程,1 项目,2 设备,3 监测点 | +| `deviceCount` | 当前节点下有效设备数 | +| `lineCount` | 当前节点下有效监测点数 | +| `selectable` | 是否可直接选择 | +| `children` | 子节点 | + +## 6. 查询趋势指标树 + +- 路径:`GET /steady/data-view/indicator-tree` +- 返回:`HttpResult>` + +当前指标目录覆盖: + +- 电压趋势:`V_RMS`、`V_LINE_RMS` +- 电流趋势:`I_RMS` +- 频率趋势:`FREQ` +- 谐波趋势:`V_THD`、`I_THD`、`V_HARMONIC`、`I_HARMONIC`、`V_HARMONIC_RATE`、`I_HARMONIC_RATE`、`I_INTER_HARMONIC`、`P_HARMONIC_POWER`、`Q_HARMONIC_POWER`、`S_HARMONIC_POWER` +- 闪变趋势:`FLUC`、`PST`、`PLT` + +叶子节点会返回 `tableName`、`phaseCodes`、`seriesFields`、`supportStats`、`harmonicOrderStart`、`harmonicOrderEnd`、`unit`,前端按这些字段驱动相别、统计类型和谐波次数选择。 + +## 7. 查询趋势数据 + +- 路径:`POST /steady/data-view/trend/query` +- 返回:`HttpResult` + +请求示例: + +```json +{ + "lineIds": ["line-001"], + "indicatorCodes": ["V_RMS"], + "statTypes": ["AVG", "MAX", "MIN", "CP95"], + "phases": ["A", "B", "C"], + "timeStart": "2026-05-01 00:00:00", + "timeEnd": "2026-05-01 23:59:59", + "bucket": "10m", + "qualityFlag": 1 +} +``` + +返回示例: + +```json +{ + "sampled": true, + "bucket": "10m", + "sourcePointCount": 144, + "displayPointCount": 144, + "loadableDays": ["2026-05-01"], + "series": [ + { + "seriesKey": "line-001|V_RMS|A|AVG|RMS", + "lineId": "line-001", + "lineName": "进线一", + "indicatorCode": "V_RMS", + "indicatorName": "相电压有效值", + "seriesName": "相电压有效值", + "phase": "A", + "statType": "AVG", + "unit": "V", + "points": [ + { + "time": "2026-05-01 00:00:00", + "value": 220.1 + } + ] + } + ] +} +``` + +谐波请求必须指定 `harmonicOrders`,最多 6 个: + +```json +{ + "lineIds": ["line-001"], + "indicatorCodes": ["V_HARMONIC"], + "statTypes": ["MAX"], + "phases": ["A"], + "harmonicOrders": [3, 5, 7], + "timeStart": "2026-05-01 00:00:00", + "timeEnd": "2026-05-01 23:59:59", + "bucket": "10m", + "qualityFlag": 1 +} +``` + +## 8. 按天查询趋势数据 + +- 路径:`POST /steady/data-view/trend/day` +- 返回:`HttpResult` + +请求体与 `/trend/query` 一致。前端切换日期或加载某一天数据时,将 `timeStart`、`timeEnd` 控制在当天范围即可。 + +## 9. 查询趋势统计摘要 + +- 路径:`POST /steady/data-view/trend/summary` +- 返回:`HttpResult` + +请求体与 `/trend/query` 一致。后端按当前查询范围返回每条曲线的 `max`、`avg`、`min`、`cp95`。 + +## 10. InfluxDB 配置 + +配置项前缀:`steady.influxdb`。 + +```yaml +steady: + influxdb: + url: http://192.168.1.103:18086 + database: pqsbase + username: admin + password: ${STEADY_INFLUXDB_PASSWORD:} + ssl: false + connect-timeout-ms: 5000 + read-timeout-ms: 30000 +``` + +接口按 InfluxDB 1.x InfluxQL `/query` 方式访问。代码不会提交明文密码;本地密码请通过环境变量或本地覆盖配置提供。 + +## 11. 返回字段说明 + +| 字段 | 说明 | +| --- | --- | +| `tableName` | 数据表名 | +| `lineId` | 监测点 ID | +| `timeId` | 数据时间 | +| `phasicType` | 相别 | +| `qualityFlag` | 质量标识 | +| `equipmentName` | 设备名称,台账缺失时为 `-` | +| `engineeringName` | 工程名称,台账缺失时为 `-` | +| `projectName` | 项目名称,台账缺失时为 `-` | +| `lineName` | 监测点名称,台账缺失时为 `-` | +| `values` | 动态指标字段,字段名与目标 `data_*` 表保持一致 | + +## 12. 常见错误场景 + +| 场景 | 后端提示 | +| --- | --- | +| 表名不在 add-data 注册表范围内 | `稳态数据表不支持:xxx` | +| 开始时间大于结束时间 | `开始时间不能大于结束时间` | +| 时间格式无法解析 | `时间格式不正确,仅支持 yyyy-MM-dd HH:mm:ss` | +| 相别不为 `A/B/C/T` | `相别只能是 A、B、C、T` | +| 质量标识不为 `0/1` | `质量标识只能是 0 或 1` | +| `lineIds` 超过 1000 个 | `监测点 ID 查询数量不能超过 1000 个` | +| 台账关键字匹配监测点超过 1000 个 | `台账检索匹配监测点过多,请缩小查询条件` | +| 趋势监测点为空 | `监测点 ID 不能为空` | +| 趋势指标为空 | `指标不能为空` | +| 多监测点同时多指标查询 | `多监测点查询时只能选择 1 个指标` | +| 趋势曲线超过 24 条 | `趋势曲线数量不能超过 24 条,请缩小监测点、指标、相别或统计类型范围` | +| 谐波指标未传次数 | `谐波次数不能为空` | +| 谐波次数超过 6 个 | `谐波次数最多选择 6 个` | +| InfluxDB 未配置地址 | `InfluxDB 地址未配置` | + +## 13. 当前限制 + +- 当前仅提供分页、详情和模板查询,未提供动态 Excel 导出。 +- 趋势接口已提供后端结构和 InfluxQL 查询封装,未做真实 InfluxDB 联调。 +- `sourcePointCount` 当前与实际返回点数一致,未额外发 InfluxDB `count` 查询。 diff --git a/steady/steady-DataView/pom.xml b/steady/steady-DataView/pom.xml new file mode 100644 index 0000000..2d661e8 --- /dev/null +++ b/steady/steady-DataView/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + com.njcn.gather + steady + 1.0.0 + + + steady-DataView + + + + com.njcn + njcn-common + 0.0.1 + + + + com.njcn + mybatis-plus + 0.0.1 + + + + com.njcn + spingboot2.3.12 + 2.3.12 + + + + com.njcn.gather + add-ledger + 1.0.0 + + + + com.njcn.gather + add-data + 1.0.0 + + + + org.springframework.boot + spring-boot-starter-test + test + + + diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyInfluxQueryComponent.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyInfluxQueryComponent.java new file mode 100644 index 0000000..726ba6a --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyInfluxQueryComponent.java @@ -0,0 +1,183 @@ +package com.njcn.gather.steady.datavie.component; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.gather.steady.datavie.config.SteadyInfluxDbProperties; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendPointVO; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.net.HttpURLConnection; +import java.net.URLEncoder; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +/** + * 稳态趋势 InfluxDB 查询组件。 + */ +@Component +@RequiredArgsConstructor +public class SteadyInfluxQueryComponent { + + private static final DateTimeFormatter INFLUX_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); + private static final DateTimeFormatter OUTPUT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final SteadyInfluxDbProperties properties; + + public List queryTrendPoints(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, + LocalDateTime endTime, String bucket, Integer qualityFlag) { + validateConfig(); + String query = buildTrendQuery(field, startTime, endTime, bucket, qualityFlag); + String body = executeQuery(query); + return parseTrendPoints(body); + } + + public String buildTrendQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime, + String bucket, Integer qualityFlag) { + StringBuilder sql = new StringBuilder(); + if (bucket == null || bucket.trim().isEmpty()) { + sql.append("SELECT \"").append(field.getField()).append("\" AS \"value\""); + } else { + sql.append("SELECT mean(\"").append(field.getField()).append("\") AS \"value\""); + } + sql.append(" FROM \"").append(field.getMeasurement()).append("\""); + sql.append(" WHERE time >= '").append(INFLUX_TIME_FORMATTER.format(startTime)).append("'"); + sql.append(" AND time <= '").append(INFLUX_TIME_FORMATTER.format(endTime)).append("'"); + sql.append(" AND \"LINEID\" = '").append(escapeTagValue(field.getLineId())).append("'"); + sql.append(" AND \"PHASIC_TYPE\" = '").append(escapeTagValue(field.getPhase())).append("'"); + if (qualityFlag != null) { + sql.append(" AND \"QUALITYFLAG\" = '").append(qualityFlag).append("'"); + } + if (bucket != null && !bucket.trim().isEmpty()) { + sql.append(" GROUP BY time(").append(bucket.trim()).append(") fill(none)"); + } else { + sql.append(" ORDER BY time ASC"); + } + return sql.toString(); + } + + private String executeQuery(String query) { + HttpURLConnection connection = null; + try { + URL url = new URL(buildQueryUrl(query)); + connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(properties.getConnectTimeoutMs()); + connection.setReadTimeout(properties.getReadTimeoutMs()); + int status = connection.getResponseCode(); + InputStream stream = status >= 200 && status < 300 ? connection.getInputStream() : connection.getErrorStream(); + String body = readBody(stream); + if (status < 200 || status >= 300) { + throw fail("InfluxDB 查询失败:" + body); + } + return body; + } catch (IOException ex) { + throw fail("InfluxDB 查询异常:" + ex.getMessage()); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } + + private String buildQueryUrl(String query) throws IOException { + StringBuilder url = new StringBuilder(trimRightSlash(properties.getUrl())).append("/query?"); + url.append("db=").append(encode(properties.getDatabase())); + if (properties.getUsername() != null && !properties.getUsername().trim().isEmpty()) { + url.append("&u=").append(encode(properties.getUsername().trim())); + } + if (properties.getPassword() != null && !properties.getPassword().trim().isEmpty()) { + url.append("&p=").append(encode(properties.getPassword())); + } + url.append("&q=").append(encode(query)); + return url.toString(); + } + + private List parseTrendPoints(String body) { + try { + JsonNode root = OBJECT_MAPPER.readTree(body); + JsonNode series = root.path("results").path(0).path("series").path(0); + JsonNode values = series.path("values"); + if (!values.isArray()) { + return new ArrayList(); + } + List result = new ArrayList(); + for (JsonNode value : values) { + if (value.size() < 2 || value.get(1).isNull()) { + continue; + } + String time = formatInfluxTime(value.get(0).asText()); + BigDecimal pointValue = value.get(1).decimalValue(); + result.add(new SteadyTrendPointVO(time, pointValue)); + } + return result; + } catch (IOException ex) { + throw fail("InfluxDB 返回结果解析失败:" + ex.getMessage()); + } + } + + private String formatInfluxTime(String value) { + try { + return OUTPUT_TIME_FORMATTER.format(OffsetDateTime.parse(value).withOffsetSameInstant(ZoneOffset.UTC).toLocalDateTime()); + } catch (RuntimeException ex) { + return value; + } + } + + private void validateConfig() { + if (properties.getUrl() == null || properties.getUrl().trim().isEmpty()) { + throw fail("InfluxDB 地址未配置"); + } + if (properties.getDatabase() == null || properties.getDatabase().trim().isEmpty()) { + throw fail("InfluxDB database 未配置"); + } + } + + private String readBody(InputStream stream) throws IOException { + if (stream == null) { + return ""; + } + BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); + StringBuilder body = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + body.append(line); + } + return body.toString(); + } + + private String escapeTagValue(String value) { + return value == null ? "" : value.replace("\\", "\\\\").replace("'", "\\'"); + } + + private String trimRightSlash(String value) { + String text = value.trim(); + while (text.endsWith("/")) { + text = text.substring(0, text.length() - 1); + } + return text; + } + + private String encode(String value) throws IOException { + return URLEncoder.encode(value, StandardCharsets.UTF_8.name()); + } + + private BusinessException fail(String message) { + return new BusinessException(CommonResponseEnum.FAIL, message); + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyTrendFieldResolver.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyTrendFieldResolver.java new file mode 100644 index 0000000..2fc1078 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyTrendFieldResolver.java @@ -0,0 +1,261 @@ +package com.njcn.gather.steady.datavie.component; + +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendSeriesFieldBO; +import com.njcn.gather.steady.datavie.pojo.param.SteadyTrendQueryParam; +import com.njcn.gather.tool.adddata.component.AddDataTableRegistry; +import com.njcn.gather.tool.adddata.pojo.bo.AddDataTableDefinition; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 稳态趋势字段白名单解析器。 + */ +@Component +@RequiredArgsConstructor +public class SteadyTrendFieldResolver { + + private static final int MAX_LINE_COUNT = 8; + private static final int MAX_INDICATOR_COUNT = 8; + private static final int MAX_SERIES_COUNT = 24; + private static final int MAX_HARMONIC_ORDER_COUNT = 6; + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + private final SteadyTrendIndicatorCatalog indicatorCatalog; + private final AddDataTableRegistry addDataTableRegistry; + + public List resolveFields(SteadyTrendQueryParam param) { + validateBasicParam(param); + List lineIds = normalizeTextList(param.getLineIds()); + List indicatorCodes = normalizeTextList(param.getIndicatorCodes()); + List requestPhases = normalizeUpperList(param.getPhases()); + List statTypes = normalizeUpperList(param.getStatTypes()); + if (statTypes.isEmpty()) { + statTypes.add("AVG"); + } + List result = new ArrayList(); + for (String lineId : lineIds) { + for (String indicatorCode : indicatorCodes) { + SteadyTrendIndicatorDefinitionBO indicator = requireIndicator(indicatorCode); + List phases = resolvePhases(indicator, requestPhases); + for (String phase : phases) { + for (String statType : statTypes) { + validateStatType(indicator, statType); + result.addAll(resolveIndicatorFields(lineId, indicator, phase, statType, param.getHarmonicOrders())); + } + } + } + } + if (result.size() > MAX_SERIES_COUNT) { + throw fail("趋势曲线数量不能超过 24 条,请缩小监测点、指标、相别或统计类型范围"); + } + return result; + } + + public LocalDateTime parseRequiredTime(String time, String emptyMessage) { + String text = trimToNull(time); + if (text == null) { + throw fail(emptyMessage); + } + try { + return LocalDateTime.parse(text, TIME_FORMATTER); + } catch (DateTimeParseException ex) { + throw fail("时间格式不正确,仅支持 yyyy-MM-dd HH:mm:ss"); + } + } + + private List resolveIndicatorFields(String lineId, SteadyTrendIndicatorDefinitionBO indicator, + String phase, String statType, List harmonicOrders) { + if (Boolean.TRUE.equals(indicator.getHarmonic())) { + return resolveHarmonicFields(lineId, indicator, phase, statType, harmonicOrders); + } + List result = new ArrayList(); + for (SteadyTrendSeriesFieldBO seriesField : indicator.getSeriesFields()) { + String field = resolveStatField(seriesField.getField(), statType); + validateColumn(indicator.getTableName(), field); + result.add(buildResolvedField(lineId, indicator, phase, statType, field, seriesField.getName())); + } + return result; + } + + private List resolveHarmonicFields(String lineId, SteadyTrendIndicatorDefinitionBO indicator, + String phase, String statType, List harmonicOrders) { + List orders = normalizeOrders(harmonicOrders); + if (orders.isEmpty()) { + throw fail("谐波次数不能为空"); + } + if (orders.size() > MAX_HARMONIC_ORDER_COUNT) { + throw fail("谐波次数最多选择 6 个"); + } + List result = new ArrayList(); + for (Integer order : orders) { + if (order < indicator.getHarmonicOrderStart() || order > indicator.getHarmonicOrderEnd()) { + throw fail("谐波次数只能在 " + indicator.getHarmonicOrderStart() + " 到 " + indicator.getHarmonicOrderEnd() + " 之间"); + } + String baseField = indicator.getHarmonicFieldPrefix() + "_" + order; + String field = resolveStatField(baseField, statType); + validateColumn(indicator.getTableName(), field); + result.add(buildResolvedField(lineId, indicator, phase, statType, field, order + "次" + indicator.getName())); + } + return result; + } + + private SteadyTrendResolvedFieldBO buildResolvedField(String lineId, SteadyTrendIndicatorDefinitionBO indicator, + String phase, String statType, String field, String seriesName) { + SteadyTrendResolvedFieldBO resolved = new SteadyTrendResolvedFieldBO(); + resolved.setMeasurement(indicator.getTableName()); + resolved.setField(field); + resolved.setLineId(lineId); + resolved.setIndicatorCode(indicator.getIndicatorCode()); + resolved.setIndicatorName(indicator.getName()); + resolved.setSeriesName(seriesName); + resolved.setPhase(phase); + resolved.setStatType(statType); + resolved.setUnit(indicator.getUnit()); + resolved.setSeriesKey(lineId + "|" + indicator.getIndicatorCode() + "|" + phase + "|" + statType + "|" + field); + return resolved; + } + + private void validateBasicParam(SteadyTrendQueryParam param) { + if (param == null) { + throw fail("趋势查询参数不能为空"); + } + List lineIds = normalizeTextList(param.getLineIds()); + List indicatorCodes = normalizeTextList(param.getIndicatorCodes()); + if (lineIds.isEmpty()) { + throw fail("监测点 ID 不能为空"); + } + if (indicatorCodes.isEmpty()) { + throw fail("指标不能为空"); + } + if (lineIds.size() > MAX_LINE_COUNT) { + throw fail("监测点数量不能超过 8 个"); + } + if (indicatorCodes.size() > MAX_INDICATOR_COUNT) { + throw fail("指标数量不能超过 8 个"); + } + if (lineIds.size() > 1 && indicatorCodes.size() > 1) { + throw fail("多监测点查询时只能选择 1 个指标"); + } + LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空"); + LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空"); + if (startTime.isAfter(endTime)) { + throw fail("开始时间不能大于结束时间"); + } + if (param.getQualityFlag() != null && param.getQualityFlag() != 0 && param.getQualityFlag() != 1) { + throw fail("质量标识只能是 0 或 1"); + } + } + + private SteadyTrendIndicatorDefinitionBO requireIndicator(String indicatorCode) { + SteadyTrendIndicatorDefinitionBO indicator = indicatorCatalog.getIndicator(indicatorCode); + if (indicator == null) { + throw fail("稳态趋势指标不支持:" + indicatorCode); + } + return indicator; + } + + private List resolvePhases(SteadyTrendIndicatorDefinitionBO indicator, List requestPhases) { + if (requestPhases.isEmpty()) { + return new ArrayList(indicator.getPhaseCodes()); + } + List result = new ArrayList(); + for (String phase : requestPhases) { + if (!"A".equals(phase) && !"B".equals(phase) && !"C".equals(phase) && !"T".equals(phase)) { + throw fail("相别只能是 A、B、C、T"); + } + if (indicator.getPhaseCodes().contains(phase) && !result.contains(phase)) { + result.add(phase); + } + } + if (result.isEmpty()) { + throw fail("指标 " + indicator.getIndicatorCode() + " 不支持当前相别"); + } + return result; + } + + private void validateStatType(SteadyTrendIndicatorDefinitionBO indicator, String statType) { + if (!"AVG".equals(statType) && !"MAX".equals(statType) && !"MIN".equals(statType) && !"CP95".equals(statType)) { + throw fail("统计类型只能是 AVG、MAX、MIN、CP95"); + } + if (!indicator.getSupportStats().contains(statType)) { + throw fail("指标 " + indicator.getIndicatorCode() + " 不支持统计类型:" + statType); + } + } + + private String resolveStatField(String baseField, String statType) { + if ("AVG".equals(statType)) { + return baseField; + } + return baseField + "_" + statType; + } + + private void validateColumn(String tableName, String field) { + AddDataTableDefinition definition; + try { + definition = addDataTableRegistry.getDefinition(tableName); + } catch (IllegalArgumentException ex) { + throw fail("稳态数据表不支持:" + tableName); + } + if (!definition.getColumns().contains(field)) { + throw fail("稳态趋势字段不支持:" + tableName + "." + field); + } + } + + private List normalizeTextList(List values) { + if (values == null || values.isEmpty()) { + return new ArrayList(); + } + List result = new ArrayList(); + for (String value : values) { + String text = trimToNull(value); + if (text != null && !result.contains(text)) { + result.add(text); + } + } + return result; + } + + private List normalizeUpperList(List values) { + List result = normalizeTextList(values); + for (int i = 0; i < result.size(); i++) { + result.set(i, result.get(i).toUpperCase()); + } + return result; + } + + private List normalizeOrders(List orders) { + if (orders == null || orders.isEmpty()) { + return Collections.emptyList(); + } + List result = new ArrayList(); + for (Integer order : orders) { + if (order != null && !result.contains(order)) { + result.add(order); + } + } + return result; + } + + private String trimToNull(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed; + } + + private BusinessException fail(String message) { + return new BusinessException(CommonResponseEnum.FAIL, message); + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyTrendIndicatorCatalog.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyTrendIndicatorCatalog.java new file mode 100644 index 0000000..68aade7 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/component/SteadyTrendIndicatorCatalog.java @@ -0,0 +1,112 @@ +package com.njcn.gather.steady.datavie.component; + +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendSeriesFieldBO; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 稳态趋势指标目录。 + */ +@Component +public class SteadyTrendIndicatorCatalog { + + private static final List FULL_STATS = Collections.unmodifiableList(Arrays.asList("AVG", "MAX", "MIN", "CP95")); + private static final List AVG_ONLY = Collections.unmodifiableList(Collections.singletonList("AVG")); + private static final List ABC_PHASES = Collections.unmodifiableList(Arrays.asList("A", "B", "C")); + private static final List T_PHASE = Collections.unmodifiableList(Collections.singletonList("T")); + + private final List indicators; + private final Map indicatorMap; + + public SteadyTrendIndicatorCatalog() { + List result = new ArrayList(); + result.add(indicator("V_RMS", "相电压有效值", "VOLTAGE", "电压趋势", "data_v", ABC_PHASES, + fields(field("RMS", "相电压有效值")), FULL_STATS, "V")); + result.add(indicator("V_LINE_RMS", "线电压有效值", "VOLTAGE", "电压趋势", "data_v", T_PHASE, + fields(field("RMSAB", "AB线电压"), field("RMSBC", "BC线电压"), field("RMSCA", "CA线电压")), + FULL_STATS, "V")); + result.add(indicator("FREQ", "频率", "FREQUENCY", "频率趋势", "data_v", T_PHASE, + fields(field("FREQ", "频率")), FULL_STATS, "Hz")); + result.add(indicator("V_THD", "电压总谐波畸变率", "HARMONIC", "谐波趋势", "data_v", ABC_PHASES, + fields(field("V_THD", "电压总谐波畸变率")), FULL_STATS, "%")); + result.add(indicator("I_RMS", "电流有效值", "CURRENT", "电流趋势", "data_i", ABC_PHASES, + fields(field("RMS", "电流有效值")), FULL_STATS, "A")); + result.add(indicator("I_THD", "电流总谐波畸变率", "HARMONIC", "谐波趋势", "data_i", ABC_PHASES, + fields(field("I_THD", "电流总谐波畸变率")), FULL_STATS, "%")); + result.add(harmonic("V_HARMONIC", "电压谐波幅值", "data_harmphasic_v", "V", "V")); + result.add(harmonic("I_HARMONIC", "电流谐波幅值", "data_harmphasic_i", "I", "A")); + result.add(harmonic("V_HARMONIC_RATE", "电压谐波含有率", "data_harmrate_v", "V", "%")); + result.add(harmonic("I_HARMONIC_RATE", "电流谐波含有率", "data_harmrate_i", "I", "%")); + result.add(harmonic("I_INTER_HARMONIC", "间谐波电流", "data_inharm_i", "I", "A")); + result.add(harmonicPower("P_HARMONIC_POWER", "有功谐波功率", "data_harmpower_p", "P", "kW")); + result.add(harmonicPower("Q_HARMONIC_POWER", "无功谐波功率", "data_harmpower_q", "Q", "kvar")); + result.add(harmonicPower("S_HARMONIC_POWER", "视在谐波功率", "data_harmpower_s", "S", "kVA")); + result.add(indicator("FLUC", "电压波动", "FLICKER", "闪变趋势", "data_fluc", T_PHASE, + fields(field("FLUC", "电压波动")), AVG_ONLY, "%")); + result.add(indicator("PST", "短时闪变", "FLICKER", "闪变趋势", "data_flicker", T_PHASE, + fields(field("PST", "短时闪变")), AVG_ONLY, "")); + result.add(indicator("PLT", "长时闪变", "FLICKER", "闪变趋势", "data_plt", T_PHASE, + fields(field("PLT", "长时闪变")), AVG_ONLY, "")); + indicators = Collections.unmodifiableList(result); + + Map map = new LinkedHashMap(); + for (SteadyTrendIndicatorDefinitionBO indicator : indicators) { + map.put(indicator.getIndicatorCode(), indicator); + } + indicatorMap = Collections.unmodifiableMap(map); + } + + public List listIndicators() { + return indicators; + } + + public SteadyTrendIndicatorDefinitionBO getIndicator(String indicatorCode) { + return indicatorMap.get(indicatorCode); + } + + private SteadyTrendIndicatorDefinitionBO harmonic(String code, String name, String tableName, String prefix, String unit) { + SteadyTrendIndicatorDefinitionBO indicator = indicator(code, name, "HARMONIC", "谐波趋势", tableName, ABC_PHASES, + new ArrayList(), FULL_STATS, unit); + indicator.setHarmonic(true); + indicator.setHarmonicFieldPrefix(prefix); + indicator.setHarmonicOrderStart(2); + indicator.setHarmonicOrderEnd(50); + return indicator; + } + + private SteadyTrendIndicatorDefinitionBO harmonicPower(String code, String name, String tableName, String prefix, String unit) { + return harmonic(code, name, tableName, prefix, unit); + } + + private SteadyTrendIndicatorDefinitionBO indicator(String code, String name, String groupCode, String groupName, + String tableName, List phaseCodes, + List seriesFields, + List supportStats, String unit) { + SteadyTrendIndicatorDefinitionBO indicator = new SteadyTrendIndicatorDefinitionBO(); + indicator.setIndicatorCode(code); + indicator.setName(name); + indicator.setGroupCode(groupCode); + indicator.setGroupName(groupName); + indicator.setTableName(tableName); + indicator.setPhaseCodes(new ArrayList(phaseCodes)); + indicator.setSeriesFields(new ArrayList(seriesFields)); + indicator.setSupportStats(new ArrayList(supportStats)); + indicator.setUnit(unit); + return indicator; + } + + private List fields(SteadyTrendSeriesFieldBO... fields) { + return new ArrayList(Arrays.asList(fields)); + } + + private SteadyTrendSeriesFieldBO field(String field, String name) { + return new SteadyTrendSeriesFieldBO(field, name); + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/config/SteadyInfluxDbProperties.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/config/SteadyInfluxDbProperties.java new file mode 100644 index 0000000..5db0cdf --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/config/SteadyInfluxDbProperties.java @@ -0,0 +1,28 @@ +package com.njcn.gather.steady.datavie.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 稳态趋势 InfluxDB 配置。 + */ +@Data +@Component +@ConfigurationProperties(prefix = "steady.influxdb") +public class SteadyInfluxDbProperties { + + private String url; + + private String database; + + private String username; + + private String password; + + private Boolean ssl = false; + + private Integer connectTimeoutMs = 5000; + + private Integer readTimeoutMs = 30000; +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/config/SteadySecondTimeSerializer.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/config/SteadySecondTimeSerializer.java new file mode 100644 index 0000000..80d7527 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/config/SteadySecondTimeSerializer.java @@ -0,0 +1,26 @@ +package com.njcn.gather.steady.datavie.config; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * 稳态数据时间字段按秒输出,避免接口响应携带毫秒。 + */ +public class SteadySecondTimeSerializer extends JsonSerializer { + + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + @Override + public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value == null) { + gen.writeNull(); + return; + } + gen.writeString(FORMATTER.format(value)); + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewController.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewController.java new file mode 100644 index 0000000..143d12b --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewController.java @@ -0,0 +1,72 @@ +package com.njcn.gather.steady.datavie.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.LogUtil; +import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewDetailParam; +import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewQueryParam; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewTemplateVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewVO; +import com.njcn.gather.steady.datavie.service.SteadyDataViewService; +import com.njcn.web.controller.BaseController; +import com.njcn.web.utils.HttpResultUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 稳态数据查看接口。 + */ +@Validated +@Slf4j +@Api(tags = "稳态数据查看") +@RestController +@RequestMapping("/steady/data-view") +@RequiredArgsConstructor +public class SteadyDataViewController extends BaseController { + + /** 稳态数据查看服务。 */ + private final SteadyDataViewService steadyDataViewService; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @ApiOperation("分页查询稳态数据") + @PostMapping("/page") + public HttpResult> pageSteadyData(@RequestBody SteadyDataViewQueryParam param) { + String methodDescribe = getMethodDescribe("pageSteadyData"); + LogUtil.njcnDebug(log, "{},开始分页查询稳态数据,param={}", methodDescribe, param); + Page result = steadyDataViewService.pageSteadyData(param); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @ApiOperation("查询稳态数据详情") + @PostMapping("/detail") + public HttpResult getSteadyDataDetail(@RequestBody SteadyDataViewDetailParam param) { + String methodDescribe = getMethodDescribe("getSteadyDataDetail"); + LogUtil.njcnDebug(log, "{},开始查询稳态数据详情,param={}", methodDescribe, param); + SteadyDataViewVO result = steadyDataViewService.getSteadyDataDetail(param); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @ApiOperation("查询稳态数据模板") + @GetMapping("/templates") + public HttpResult> listTemplates() { + String methodDescribe = getMethodDescribe("listTemplates"); + LogUtil.njcnDebug(log, "{},开始查询稳态数据模板", methodDescribe); + List result = steadyDataViewService.listTemplates(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewIndicatorController.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewIndicatorController.java new file mode 100644 index 0000000..b8a842c --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewIndicatorController.java @@ -0,0 +1,43 @@ +package com.njcn.gather.steady.datavie.controller; + +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.LogUtil; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewIndicatorNodeVO; +import com.njcn.gather.steady.datavie.service.SteadyDataViewIndicatorService; +import com.njcn.web.controller.BaseController; +import com.njcn.web.utils.HttpResultUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 稳态趋势指标接口。 + */ +@Slf4j +@Api(tags = "稳态趋势指标") +@RestController +@RequestMapping("/steady/data-view") +@RequiredArgsConstructor +public class SteadyDataViewIndicatorController extends BaseController { + + private final SteadyDataViewIndicatorService indicatorService; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @ApiOperation("查询稳态趋势指标树") + @GetMapping("/indicator-tree") + public HttpResult> listIndicatorTree() { + String methodDescribe = getMethodDescribe("listIndicatorTree"); + LogUtil.njcnDebug(log, "{},开始查询稳态趋势指标树", methodDescribe); + List result = indicatorService.listIndicatorTree(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewLedgerController.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewLedgerController.java new file mode 100644 index 0000000..6b6e3af --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewLedgerController.java @@ -0,0 +1,44 @@ +package com.njcn.gather.steady.datavie.controller; + +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.LogUtil; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewLedgerNodeVO; +import com.njcn.gather.steady.datavie.service.SteadyDataViewLedgerService; +import com.njcn.web.controller.BaseController; +import com.njcn.web.utils.HttpResultUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 稳态趋势台账树接口。 + */ +@Slf4j +@Api(tags = "稳态趋势台账树") +@RestController +@RequestMapping("/steady/data-view") +@RequiredArgsConstructor +public class SteadyDataViewLedgerController extends BaseController { + + private final SteadyDataViewLedgerService ledgerService; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @ApiOperation("查询稳态趋势台账树") + @GetMapping("/ledger-tree") + public HttpResult> listLedgerTree(@RequestParam(value = "keyword", required = false) String keyword) { + String methodDescribe = getMethodDescribe("listLedgerTree"); + LogUtil.njcnDebug(log, "{},开始查询稳态趋势台账树,keyword={}", methodDescribe, keyword); + List result = ledgerService.listLedgerTree(keyword); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewTrendController.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewTrendController.java new file mode 100644 index 0000000..87a9e5b --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/controller/SteadyDataViewTrendController.java @@ -0,0 +1,64 @@ +package com.njcn.gather.steady.datavie.controller; + +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.LogUtil; +import com.njcn.gather.steady.datavie.pojo.param.SteadyTrendQueryParam; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendQueryVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendSummaryVO; +import com.njcn.gather.steady.datavie.service.SteadyDataViewTrendService; +import com.njcn.web.controller.BaseController; +import com.njcn.web.utils.HttpResultUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 稳态趋势查询接口。 + */ +@Slf4j +@Api(tags = "稳态趋势查询") +@RestController +@RequestMapping("/steady/data-view/trend") +@RequiredArgsConstructor +public class SteadyDataViewTrendController extends BaseController { + + private final SteadyDataViewTrendService trendService; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @ApiOperation("查询稳态趋势") + @PostMapping("/query") + public HttpResult queryTrend(@RequestBody SteadyTrendQueryParam param) { + String methodDescribe = getMethodDescribe("queryTrend"); + LogUtil.njcnDebug(log, "{},开始查询稳态趋势,param={}", methodDescribe, param); + SteadyTrendQueryVO result = trendService.queryTrend(param); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @ApiOperation("按天查询稳态趋势") + @PostMapping("/day") + public HttpResult queryTrendDay(@RequestBody SteadyTrendQueryParam param) { + String methodDescribe = getMethodDescribe("queryTrendDay"); + LogUtil.njcnDebug(log, "{},开始按天查询稳态趋势,param={}", methodDescribe, param); + SteadyTrendQueryVO result = trendService.queryTrendDay(param); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @ApiOperation("查询稳态趋势统计摘要") + @PostMapping("/summary") + public HttpResult summarizeTrend(@RequestBody SteadyTrendQueryParam param) { + String methodDescribe = getMethodDescribe("summarizeTrend"); + LogUtil.njcnDebug(log, "{},开始查询稳态趋势统计摘要,param={}", methodDescribe, param); + SteadyTrendSummaryVO result = trendService.summarizeTrend(param); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/SteadyDataViewLedgerMapper.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/SteadyDataViewLedgerMapper.java new file mode 100644 index 0000000..dae839d --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/SteadyDataViewLedgerMapper.java @@ -0,0 +1,14 @@ +package com.njcn.gather.steady.datavie.mapper; + +import com.njcn.gather.steady.datavie.pojo.bo.SteadyDataViewLedgerRowBO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 稳态趋势台账树 Mapper。 + */ +public interface SteadyDataViewLedgerMapper { + + List selectLedgerTree(@Param("keyword") String keyword); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/SteadyDataViewMapper.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/SteadyDataViewMapper.java new file mode 100644 index 0000000..9791876 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/SteadyDataViewMapper.java @@ -0,0 +1,25 @@ +package com.njcn.gather.steady.datavie.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewQueryParam; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 稳态数据查看 Mapper。 + */ +public interface SteadyDataViewMapper { + + Page> selectSteadyPage(Page> page, + @Param("tableName") String tableName, + @Param("columns") List columns, + @Param("param") SteadyDataViewQueryParam param); + + Map selectSteadyDetail(@Param("tableName") String tableName, + @Param("columns") List columns, + @Param("lineId") String lineId, + @Param("timeId") String timeId, + @Param("phasicType") String phasicType); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/mapping/SteadyDataViewLedgerMapper.xml b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/mapping/SteadyDataViewLedgerMapper.xml new file mode 100644 index 0000000..158bc15 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/mapping/SteadyDataViewLedgerMapper.xml @@ -0,0 +1,48 @@ + + + + + + diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/mapping/SteadyDataViewMapper.xml b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/mapping/SteadyDataViewMapper.xml new file mode 100644 index 0000000..b346fc1 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/mapper/mapping/SteadyDataViewMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + AND TIMEID >= #{param.timeStart} + + + AND TIMEID <= #{param.timeEnd} + + + AND PHASIC_TYPE = #{param.phasicType} + + + AND QUALITYFLAG = #{param.qualityFlag} + + + AND LINEID IN + + #{lineId} + + + + + + + + + diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyDataViewLedgerRowBO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyDataViewLedgerRowBO.java new file mode 100644 index 0000000..056676a --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyDataViewLedgerRowBO.java @@ -0,0 +1,28 @@ +package com.njcn.gather.steady.datavie.pojo.bo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 稳态趋势台账树查询行。 + */ +@Data +public class SteadyDataViewLedgerRowBO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String id; + + private String parentId; + + private String name; + + private Integer level; + + private Integer sort; + + private Integer deviceCount; + + private Integer lineCount; +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendIndicatorDefinitionBO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendIndicatorDefinitionBO.java new file mode 100644 index 0000000..e52e5ba --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendIndicatorDefinitionBO.java @@ -0,0 +1,42 @@ +package com.njcn.gather.steady.datavie.pojo.bo; + +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 稳态趋势指标定义。 + */ +@Data +public class SteadyTrendIndicatorDefinitionBO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String indicatorCode; + + private String name; + + private String groupCode; + + private String groupName; + + private String tableName; + + private List phaseCodes = new ArrayList(); + + private List seriesFields = new ArrayList(); + + private List supportStats = new ArrayList(); + + private Boolean harmonic = false; + + private String harmonicFieldPrefix; + + private Integer harmonicOrderStart; + + private Integer harmonicOrderEnd; + + private String unit; +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendResolvedFieldBO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendResolvedFieldBO.java new file mode 100644 index 0000000..5682693 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendResolvedFieldBO.java @@ -0,0 +1,36 @@ +package com.njcn.gather.steady.datavie.pojo.bo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 稳态趋势查询字段解析结果。 + */ +@Data +public class SteadyTrendResolvedFieldBO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String measurement; + + private String field; + + private String lineId; + + private String lineName; + + private String indicatorCode; + + private String indicatorName; + + private String seriesName; + + private String phase; + + private String statType; + + private String unit; + + private String seriesKey; +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendSeriesFieldBO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendSeriesFieldBO.java new file mode 100644 index 0000000..350dd19 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/bo/SteadyTrendSeriesFieldBO.java @@ -0,0 +1,24 @@ +package com.njcn.gather.steady.datavie.pojo.bo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 稳态趋势指标曲线字段。 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SteadyTrendSeriesFieldBO implements Serializable { + + private static final long serialVersionUID = 1L; + + /** InfluxDB field 基础名。 */ + private String field; + + /** 曲线展示名称。 */ + private String name; +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyDataViewDetailParam.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyDataViewDetailParam.java new file mode 100644 index 0000000..2071dcf --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyDataViewDetailParam.java @@ -0,0 +1,25 @@ +package com.njcn.gather.steady.datavie.pojo.param; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 稳态数据详情查询参数。 + */ +@Data +@ApiModel("稳态数据详情查询参数") +public class SteadyDataViewDetailParam { + + @ApiModelProperty("表名,对应 add-data 模板表名") + private String tableName; + + @ApiModelProperty("监测点 ID") + private String lineId; + + @ApiModelProperty("时间,格式 yyyy-MM-dd HH:mm:ss") + private String timeId; + + @ApiModelProperty("相别:A/B/C/T") + private String phasicType; +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyDataViewQueryParam.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyDataViewQueryParam.java new file mode 100644 index 0000000..65b767d --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyDataViewQueryParam.java @@ -0,0 +1,49 @@ +package com.njcn.gather.steady.datavie.pojo.param; + +import com.njcn.web.pojo.param.BaseParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 稳态数据查看查询参数。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ApiModel("稳态数据查看查询参数") +public class SteadyDataViewQueryParam extends BaseParam { + + @ApiModelProperty("表名,对应 add-data 模板表名") + private String tableName; + + @ApiModelProperty("时间开始,格式 yyyy-MM-dd HH:mm:ss") + private String timeStart; + + @ApiModelProperty("时间结束,格式 yyyy-MM-dd HH:mm:ss") + private String timeEnd; + + @ApiModelProperty("相别:A/B/C/T") + private String phasicType; + + @ApiModelProperty("质量标识") + private Integer qualityFlag; + + @ApiModelProperty("监测点 ID 列表") + private List lineIds = new ArrayList(); + + @ApiModelProperty("工程名称关键字") + private String engineeringName; + + @ApiModelProperty("项目名称关键字") + private String projectName; + + @ApiModelProperty("设备名称关键字") + private String equipmentName; + + @ApiModelProperty("监测点名称关键字") + private String lineName; +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyTrendQueryParam.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyTrendQueryParam.java new file mode 100644 index 0000000..431a211 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/param/SteadyTrendQueryParam.java @@ -0,0 +1,43 @@ +package com.njcn.gather.steady.datavie.pojo.param; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 稳态趋势查询参数。 + */ +@Data +@ApiModel("稳态趋势查询参数") +public class SteadyTrendQueryParam { + + @ApiModelProperty("监测点 ID 列表") + private List lineIds = new ArrayList(); + + @ApiModelProperty("指标编码列表") + private List indicatorCodes = new ArrayList(); + + @ApiModelProperty("统计类型:AVG/MAX/MIN/CP95") + private List statTypes = new ArrayList(); + + @ApiModelProperty("相别:A/B/C/T") + private List phases = new ArrayList(); + + @ApiModelProperty("开始时间,格式 yyyy-MM-dd HH:mm:ss") + private String timeStart; + + @ApiModelProperty("结束时间,格式 yyyy-MM-dd HH:mm:ss") + private String timeEnd; + + @ApiModelProperty("分桶粒度,如 1m、5m、10m、30m、1h") + private String bucket; + + @ApiModelProperty("质量标识") + private Integer qualityFlag; + + @ApiModelProperty("谐波次数,谐波指标必填,最多 6 个") + private List harmonicOrders = new ArrayList(); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewIndicatorNodeVO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewIndicatorNodeVO.java new file mode 100644 index 0000000..396bcc5 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewIndicatorNodeVO.java @@ -0,0 +1,51 @@ +package com.njcn.gather.steady.datavie.pojo.vo; + +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendSeriesFieldBO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 稳态趋势指标树节点。 + */ +@Data +@ApiModel("稳态趋势指标树节点") +public class SteadyDataViewIndicatorNodeVO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String id; + + private String parentId; + + private String name; + + private String groupCode; + + private String indicatorCode; + + private String tableName; + + private List phaseCodes = new ArrayList(); + + private List seriesFields = new ArrayList(); + + private List supportStats = new ArrayList(); + + private Boolean harmonic = false; + + private Integer harmonicOrderStart; + + private Integer harmonicOrderEnd; + + private String unit; + + @ApiModelProperty("是否可选") + private Boolean selectable = false; + + private List children = new ArrayList(); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewLedgerNodeVO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewLedgerNodeVO.java new file mode 100644 index 0000000..79677df --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewLedgerNodeVO.java @@ -0,0 +1,36 @@ +package com.njcn.gather.steady.datavie.pojo.vo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 稳态趋势台账树节点。 + */ +@Data +@ApiModel("稳态趋势台账树节点") +public class SteadyDataViewLedgerNodeVO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String id; + + private String parentId; + + private String name; + + private Integer level; + + private Integer sort; + + private Integer deviceCount; + + private Integer lineCount; + + private Boolean selectable; + + private List children = new ArrayList(); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewTemplateVO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewTemplateVO.java new file mode 100644 index 0000000..a2239df --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewTemplateVO.java @@ -0,0 +1,26 @@ +package com.njcn.gather.steady.datavie.pojo.vo; + +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 稳态数据查看模板。 + */ +@Data +public class SteadyDataViewTemplateVO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String parameterName; + + private String tableName; + + private String phaseDisplay; + + private List phaseCodes = new ArrayList(); + + private List valueColumns = new ArrayList(); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewVO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewVO.java new file mode 100644 index 0000000..3d69797 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyDataViewVO.java @@ -0,0 +1,45 @@ +package com.njcn.gather.steady.datavie.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.njcn.gather.steady.datavie.config.SteadySecondTimeSerializer; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 稳态数据查看展示对象。 + */ +@Data +public class SteadyDataViewVO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String tableName; + + private String lineId; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonSerialize(using = SteadySecondTimeSerializer.class) + private LocalDateTime timeId; + + private String phasicType; + + private Integer qualityFlag; + + private String equipmentName; + + private String engineeringName; + + private String projectName; + + private String lineName; + + private Map values = new LinkedHashMap(); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendPointVO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendPointVO.java new file mode 100644 index 0000000..fc35d8b --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendPointVO.java @@ -0,0 +1,28 @@ +package com.njcn.gather.steady.datavie.pojo.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 稳态趋势点位。 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel("稳态趋势点位") +public class SteadyTrendPointVO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("时间,格式 yyyy-MM-dd HH:mm:ss") + private String time; + + @ApiModelProperty("点位值") + private BigDecimal value; +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendQueryVO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendQueryVO.java new file mode 100644 index 0000000..48366df --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendQueryVO.java @@ -0,0 +1,30 @@ +package com.njcn.gather.steady.datavie.pojo.vo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 稳态趋势查询结果。 + */ +@Data +@ApiModel("稳态趋势查询结果") +public class SteadyTrendQueryVO implements Serializable { + + private static final long serialVersionUID = 1L; + + private Boolean sampled; + + private String bucket; + + private Integer sourcePointCount; + + private Integer displayPointCount; + + private List loadableDays = new ArrayList(); + + private List series = new ArrayList(); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSeriesVO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSeriesVO.java new file mode 100644 index 0000000..3d45dde --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSeriesVO.java @@ -0,0 +1,38 @@ +package com.njcn.gather.steady.datavie.pojo.vo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 稳态趋势曲线。 + */ +@Data +@ApiModel("稳态趋势曲线") +public class SteadyTrendSeriesVO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String seriesKey; + + private String lineId; + + private String lineName; + + private String indicatorCode; + + private String indicatorName; + + private String seriesName; + + private String phase; + + private String statType; + + private String unit; + + private List points = new ArrayList(); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSummaryItemVO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSummaryItemVO.java new file mode 100644 index 0000000..5e3bbd3 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSummaryItemVO.java @@ -0,0 +1,27 @@ +package com.njcn.gather.steady.datavie.pojo.vo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 稳态趋势统计项。 + */ +@Data +@ApiModel("稳态趋势统计项") +public class SteadyTrendSummaryItemVO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String seriesKey; + + private BigDecimal max; + + private BigDecimal avg; + + private BigDecimal min; + + private BigDecimal cp95; +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSummaryVO.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSummaryVO.java new file mode 100644 index 0000000..2b03c8a --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/pojo/vo/SteadyTrendSummaryVO.java @@ -0,0 +1,20 @@ +package com.njcn.gather.steady.datavie.pojo.vo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 稳态趋势统计结果。 + */ +@Data +@ApiModel("稳态趋势统计结果") +public class SteadyTrendSummaryVO implements Serializable { + + private static final long serialVersionUID = 1L; + + private List items = new ArrayList(); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewIndicatorService.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewIndicatorService.java new file mode 100644 index 0000000..e59f997 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewIndicatorService.java @@ -0,0 +1,13 @@ +package com.njcn.gather.steady.datavie.service; + +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewIndicatorNodeVO; + +import java.util.List; + +/** + * 稳态趋势指标服务。 + */ +public interface SteadyDataViewIndicatorService { + + List listIndicatorTree(); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewLedgerService.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewLedgerService.java new file mode 100644 index 0000000..36b72ff --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewLedgerService.java @@ -0,0 +1,13 @@ +package com.njcn.gather.steady.datavie.service; + +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewLedgerNodeVO; + +import java.util.List; + +/** + * 稳态趋势台账树服务。 + */ +public interface SteadyDataViewLedgerService { + + List listLedgerTree(String keyword); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewService.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewService.java new file mode 100644 index 0000000..56d9e02 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewService.java @@ -0,0 +1,21 @@ +package com.njcn.gather.steady.datavie.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewDetailParam; +import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewQueryParam; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewTemplateVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewVO; + +import java.util.List; + +/** + * 稳态数据查看服务。 + */ +public interface SteadyDataViewService { + + Page pageSteadyData(SteadyDataViewQueryParam param); + + SteadyDataViewVO getSteadyDataDetail(SteadyDataViewDetailParam param); + + List listTemplates(); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewTrendService.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewTrendService.java new file mode 100644 index 0000000..94b4c3a --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/SteadyDataViewTrendService.java @@ -0,0 +1,17 @@ +package com.njcn.gather.steady.datavie.service; + +import com.njcn.gather.steady.datavie.pojo.param.SteadyTrendQueryParam; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendQueryVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendSummaryVO; + +/** + * 稳态趋势查询服务。 + */ +public interface SteadyDataViewTrendService { + + SteadyTrendQueryVO queryTrend(SteadyTrendQueryParam param); + + SteadyTrendQueryVO queryTrendDay(SteadyTrendQueryParam param); + + SteadyTrendSummaryVO summarizeTrend(SteadyTrendQueryParam param); +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewIndicatorServiceImpl.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewIndicatorServiceImpl.java new file mode 100644 index 0000000..6ac58b3 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewIndicatorServiceImpl.java @@ -0,0 +1,60 @@ +package com.njcn.gather.steady.datavie.service.impl; + +import com.njcn.gather.steady.datavie.component.SteadyTrendIndicatorCatalog; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewIndicatorNodeVO; +import com.njcn.gather.steady.datavie.service.SteadyDataViewIndicatorService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 稳态趋势指标服务实现。 + */ +@Service +@RequiredArgsConstructor +public class SteadyDataViewIndicatorServiceImpl implements SteadyDataViewIndicatorService { + + private final SteadyTrendIndicatorCatalog indicatorCatalog; + + @Override + public List listIndicatorTree() { + Map groupMap = new LinkedHashMap(); + for (SteadyTrendIndicatorDefinitionBO indicator : indicatorCatalog.listIndicators()) { + SteadyDataViewIndicatorNodeVO groupNode = groupMap.get(indicator.getGroupCode()); + if (groupNode == null) { + groupNode = new SteadyDataViewIndicatorNodeVO(); + groupNode.setId(indicator.getGroupCode()); + groupNode.setName(indicator.getGroupName()); + groupNode.setGroupCode(indicator.getGroupCode()); + groupNode.setSelectable(false); + groupMap.put(indicator.getGroupCode(), groupNode); + } + groupNode.getChildren().add(buildIndicatorNode(indicator, groupNode.getId())); + } + return new ArrayList(groupMap.values()); + } + + private SteadyDataViewIndicatorNodeVO buildIndicatorNode(SteadyTrendIndicatorDefinitionBO indicator, String parentId) { + SteadyDataViewIndicatorNodeVO node = new SteadyDataViewIndicatorNodeVO(); + node.setId(indicator.getIndicatorCode()); + node.setParentId(parentId); + node.setName(indicator.getName()); + node.setGroupCode(indicator.getGroupCode()); + node.setIndicatorCode(indicator.getIndicatorCode()); + node.setTableName(indicator.getTableName()); + node.setPhaseCodes(new ArrayList(indicator.getPhaseCodes())); + node.setSeriesFields(new ArrayList(indicator.getSeriesFields())); + node.setSupportStats(new ArrayList(indicator.getSupportStats())); + node.setHarmonic(indicator.getHarmonic()); + node.setHarmonicOrderStart(indicator.getHarmonicOrderStart()); + node.setHarmonicOrderEnd(indicator.getHarmonicOrderEnd()); + node.setUnit(indicator.getUnit()); + node.setSelectable(true); + return node; + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewLedgerServiceImpl.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewLedgerServiceImpl.java new file mode 100644 index 0000000..ad2f447 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewLedgerServiceImpl.java @@ -0,0 +1,70 @@ +package com.njcn.gather.steady.datavie.service.impl; + +import com.njcn.gather.steady.datavie.mapper.SteadyDataViewLedgerMapper; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyDataViewLedgerRowBO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewLedgerNodeVO; +import com.njcn.gather.steady.datavie.service.SteadyDataViewLedgerService; +import com.njcn.gather.tool.addledger.pojo.constant.AddLedgerConst; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 稳态趋势台账树服务实现。 + */ +@Service +@RequiredArgsConstructor +public class SteadyDataViewLedgerServiceImpl implements SteadyDataViewLedgerService { + + private final SteadyDataViewLedgerMapper ledgerMapper; + + @Override + public List listLedgerTree(String keyword) { + List rows = ledgerMapper.selectLedgerTree(trimToNull(keyword)); + Map nodeMap = new LinkedHashMap(); + for (SteadyDataViewLedgerRowBO row : rows) { + SteadyDataViewLedgerNodeVO node = new SteadyDataViewLedgerNodeVO(); + node.setId(row.getId()); + node.setParentId(row.getParentId()); + node.setName(row.getName()); + node.setLevel(row.getLevel()); + node.setSort(row.getSort()); + node.setDeviceCount(row.getDeviceCount() == null ? 0 : row.getDeviceCount()); + node.setLineCount(row.getLineCount() == null ? 0 : row.getLineCount()); + node.setSelectable(AddLedgerConst.LEVEL_EQUIPMENT == row.getLevel() || AddLedgerConst.LEVEL_LINE == row.getLevel()); + nodeMap.put(node.getId(), node); + } + List roots = new ArrayList(); + for (SteadyDataViewLedgerNodeVO node : nodeMap.values()) { + SteadyDataViewLedgerNodeVO parent = nodeMap.get(node.getParentId()); + if (parent == null || AddLedgerConst.ROOT_PARENT_ID.equals(node.getParentId())) { + roots.add(node); + } else { + parent.getChildren().add(node); + } + } + sortTree(roots); + return roots; + } + + private void sortTree(List nodes) { + nodes.sort(Comparator.comparing(SteadyDataViewLedgerNodeVO::getSort, Comparator.nullsLast(Integer::compareTo)) + .thenComparing(SteadyDataViewLedgerNodeVO::getName, Comparator.nullsLast(String::compareTo))); + for (SteadyDataViewLedgerNodeVO node : nodes) { + sortTree(node.getChildren()); + } + } + + private String trimToNull(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed; + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewServiceImpl.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewServiceImpl.java new file mode 100644 index 0000000..b9bc5b3 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewServiceImpl.java @@ -0,0 +1,352 @@ +package com.njcn.gather.steady.datavie.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.gather.steady.datavie.mapper.SteadyDataViewMapper; +import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewDetailParam; +import com.njcn.gather.steady.datavie.pojo.param.SteadyDataViewQueryParam; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewTemplateVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewVO; +import com.njcn.gather.steady.datavie.service.SteadyDataViewService; +import com.njcn.gather.tool.adddata.component.AddDataTableRegistry; +import com.njcn.gather.tool.adddata.component.AddDataTemplateRegistry; +import com.njcn.gather.tool.adddata.pojo.bo.AddDataTableDefinition; +import com.njcn.gather.tool.adddata.pojo.vo.AddDataTemplateVO; +import com.njcn.gather.tool.addledger.pojo.param.AddLedgerLinePathQueryParam; +import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO; +import com.njcn.gather.tool.addledger.service.AddLedgerService; +import com.njcn.web.factory.PageFactory; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 稳态数据查看服务实现。 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class SteadyDataViewServiceImpl implements SteadyDataViewService { + + private static final int LEDGER_LINE_QUERY_LIMIT = 1000; + private static final int STEADY_LINE_ID_QUERY_LIMIT = 1000; + private static final String DEFAULT_TABLE_NAME = "data_v"; + private static final String EMPTY_TEXT = "-"; + private static final DateTimeFormatter OUTPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter[] INPUT_FORMATTERS = new DateTimeFormatter[]{ + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"), + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS") + }; + + private final SteadyDataViewMapper steadyDataViewMapper; + private final AddLedgerService addLedgerService; + private final AddDataTableRegistry addDataTableRegistry; + private final AddDataTemplateRegistry addDataTemplateRegistry; + + @Override + public Page pageSteadyData(SteadyDataViewQueryParam param) { + SteadyDataViewQueryParam queryParam = normalizeQueryParam(param); + AddDataTableDefinition definition = resolveTableDefinition(queryParam.getTableName()); + if (!resolveLineFilter(queryParam)) { + return emptyPage(queryParam); + } + Page> steadyPage = steadyDataViewMapper.selectSteadyPage( + new Page>(PageFactory.getPageNum(queryParam), PageFactory.getPageSize(queryParam)), + definition.getTableName(), definition.getColumns(), queryParam); + List records = buildSteadyDataList(definition.getTableName(), definition.getColumns(), steadyPage.getRecords()); + Page resultPage = new Page(steadyPage.getCurrent(), steadyPage.getSize(), steadyPage.getTotal()); + resultPage.setRecords(records); + return resultPage; + } + + @Override + public SteadyDataViewVO getSteadyDataDetail(SteadyDataViewDetailParam param) { + if (param == null) { + throw new BusinessException(CommonResponseEnum.FAIL, "详情查询参数不能为空"); + } + String tableName = normalizeTableName(param.getTableName()); + String lineId = trimToNull(param.getLineId()); + String timeId = normalizeRequiredTime(param.getTimeId(), "时间不能为空"); + String phasicType = normalizePhasicType(param.getPhasicType()); + if (lineId == null) { + throw new BusinessException(CommonResponseEnum.FAIL, "监测点 ID 不能为空"); + } + if (phasicType == null) { + throw new BusinessException(CommonResponseEnum.FAIL, "相别不能为空"); + } + AddDataTableDefinition definition = resolveTableDefinition(tableName); + Map row = steadyDataViewMapper.selectSteadyDetail(definition.getTableName(), definition.getColumns(), + lineId, timeId, phasicType); + if (row == null || row.isEmpty()) { + throw new BusinessException(CommonResponseEnum.FAIL, "稳态数据不存在"); + } + List> rows = new ArrayList>(); + rows.add(row); + return buildSteadyDataList(definition.getTableName(), definition.getColumns(), rows).get(0); + } + + @Override + public List listTemplates() { + List result = new ArrayList(); + for (AddDataTemplateVO template : addDataTemplateRegistry.getTemplates()) { + AddDataTableDefinition definition = resolveTableDefinition(template.getTableName()); + SteadyDataViewTemplateVO vo = new SteadyDataViewTemplateVO(); + vo.setParameterName(template.getParameterName()); + vo.setTableName(template.getTableName()); + vo.setPhaseDisplay(template.getPhaseDisplay()); + vo.setPhaseCodes(template.getPhaseCodes()); + vo.setValueColumns(resolveValueColumns(definition.getColumns())); + result.add(vo); + } + return result; + } + + private List buildSteadyDataList(String tableName, List columns, List> rows) { + if (rows == null || rows.isEmpty()) { + return Collections.emptyList(); + } + List lineIds = new ArrayList(); + for (Map row : rows) { + String lineId = trimToNull(toStringValue(row.get("LINEID"))); + if (lineId != null && !lineIds.contains(lineId)) { + lineIds.add(lineId); + } + } + Map linePathMap = addLedgerService.listLinePathByLineIds(lineIds); + List result = new ArrayList(); + for (Map row : rows) { + String lineId = trimToNull(toStringValue(row.get("LINEID"))); + AddLedgerLinePathVO linePath = linePathMap.get(lineId); + result.add(buildSteadyDataVO(tableName, columns, row, linePath)); + } + return result; + } + + private SteadyDataViewVO buildSteadyDataVO(String tableName, List columns, Map row, AddLedgerLinePathVO linePath) { + SteadyDataViewVO vo = new SteadyDataViewVO(); + vo.setTableName(tableName); + vo.setLineId(toStringValue(row.get("LINEID"))); + vo.setTimeId(toLocalDateTime(row.get("TIMEID"))); + vo.setPhasicType(toStringValue(row.get("PHASIC_TYPE"))); + vo.setQualityFlag(toInteger(row.get("QUALITYFLAG"))); + vo.setEquipmentName(linePath == null ? EMPTY_TEXT : defaultText(linePath.getEquipmentName())); + vo.setEngineeringName(linePath == null ? EMPTY_TEXT : defaultText(linePath.getEngineeringName())); + vo.setProjectName(linePath == null ? EMPTY_TEXT : defaultText(linePath.getProjectName())); + vo.setLineName(linePath == null ? EMPTY_TEXT : defaultText(linePath.getLineName())); + Map values = new LinkedHashMap(); + for (String column : columns) { + if (!isInfrastructureColumn(column)) { + values.put(column, row.get(column)); + } + } + vo.setValues(values); + return vo; + } + + private SteadyDataViewQueryParam normalizeQueryParam(SteadyDataViewQueryParam param) { + SteadyDataViewQueryParam queryParam = param == null ? new SteadyDataViewQueryParam() : param; + queryParam.setTableName(normalizeTableName(queryParam.getTableName())); + LocalDateTime startTime = parseDateTime(queryParam.getTimeStart()); + LocalDateTime endTime = parseDateTime(queryParam.getTimeEnd()); + if (startTime == null) { + LocalDateTime now = LocalDateTime.now(); + startTime = LocalDateTime.of(now.getYear(), now.getMonth(), 1, 0, 0, 0); + } + if (endTime == null) { + endTime = LocalDateTime.now(); + } + if (startTime.isAfter(endTime)) { + throw new BusinessException(CommonResponseEnum.FAIL, "开始时间不能大于结束时间"); + } + queryParam.setTimeStart(OUTPUT_FORMATTER.format(startTime)); + queryParam.setTimeEnd(OUTPUT_FORMATTER.format(endTime)); + queryParam.setPhasicType(normalizePhasicType(queryParam.getPhasicType())); + validateQualityFlag(queryParam.getQualityFlag()); + queryParam.setEngineeringName(trimToNull(queryParam.getEngineeringName())); + queryParam.setProjectName(trimToNull(queryParam.getProjectName())); + queryParam.setEquipmentName(trimToNull(queryParam.getEquipmentName())); + queryParam.setLineName(trimToNull(queryParam.getLineName())); + List lineIds = normalizeIds(queryParam.getLineIds()); + if (lineIds.size() > STEADY_LINE_ID_QUERY_LIMIT) { + throw new BusinessException(CommonResponseEnum.FAIL, "监测点 ID 查询数量不能超过 1000 个"); + } + queryParam.setLineIds(lineIds); + return queryParam; + } + + private boolean resolveLineFilter(SteadyDataViewQueryParam queryParam) { + if (!hasLedgerKeyword(queryParam)) { + return true; + } + AddLedgerLinePathQueryParam linePathQueryParam = new AddLedgerLinePathQueryParam(); + linePathQueryParam.setEngineeringName(queryParam.getEngineeringName()); + linePathQueryParam.setProjectName(queryParam.getProjectName()); + linePathQueryParam.setEquipmentName(queryParam.getEquipmentName()); + linePathQueryParam.setLineName(queryParam.getLineName()); + linePathQueryParam.setLimit(LEDGER_LINE_QUERY_LIMIT + 1); + List ledgerLineIds = addLedgerService.listLineIdsByPathQuery(linePathQueryParam); + if (ledgerLineIds.size() > LEDGER_LINE_QUERY_LIMIT) { + throw new BusinessException(CommonResponseEnum.FAIL, "台账检索匹配监测点过多,请缩小查询条件"); + } + if (ledgerLineIds.isEmpty()) { + return false; + } + List explicitLineIds = normalizeIds(queryParam.getLineIds()); + if (explicitLineIds.isEmpty()) { + queryParam.setLineIds(ledgerLineIds); + return true; + } + List intersectLineIds = new ArrayList(); + for (String lineId : explicitLineIds) { + if (ledgerLineIds.contains(lineId)) { + intersectLineIds.add(lineId); + } + } + queryParam.setLineIds(intersectLineIds); + return !intersectLineIds.isEmpty(); + } + + private AddDataTableDefinition resolveTableDefinition(String tableName) { + try { + return addDataTableRegistry.getDefinition(tableName); + } catch (IllegalArgumentException ex) { + throw new BusinessException(CommonResponseEnum.FAIL, "稳态数据表不支持:" + tableName); + } + } + + private String normalizeTableName(String tableName) { + String normalizedTableName = trimToNull(tableName); + return normalizedTableName == null ? DEFAULT_TABLE_NAME : normalizedTableName; + } + + private String normalizeRequiredTime(String value, String emptyMessage) { + LocalDateTime dateTime = parseDateTime(value); + if (dateTime == null) { + throw new BusinessException(CommonResponseEnum.FAIL, emptyMessage); + } + return OUTPUT_FORMATTER.format(dateTime); + } + + private LocalDateTime parseDateTime(String value) { + String text = trimToNull(value); + if (text == null) { + return null; + } + for (DateTimeFormatter formatter : INPUT_FORMATTERS) { + try { + return LocalDateTime.parse(text, formatter); + } catch (DateTimeParseException ignored) { + // 尝试下一个前端可能传入的时间格式。 + } + } + throw new BusinessException(CommonResponseEnum.FAIL, "时间格式不正确,仅支持 yyyy-MM-dd HH:mm:ss"); + } + + private String normalizePhasicType(String phasicType) { + String text = trimToNull(phasicType); + if (text == null) { + return null; + } + String normalized = text.toUpperCase(); + if (!"A".equals(normalized) && !"B".equals(normalized) && !"C".equals(normalized) && !"T".equals(normalized)) { + throw new BusinessException(CommonResponseEnum.FAIL, "相别只能是 A、B、C、T"); + } + return normalized; + } + + private void validateQualityFlag(Integer qualityFlag) { + if (qualityFlag != null && qualityFlag != 0 && qualityFlag != 1) { + throw new BusinessException(CommonResponseEnum.FAIL, "质量标识只能是 0 或 1"); + } + } + + private List normalizeIds(List ids) { + if (ids == null || ids.isEmpty()) { + return Collections.emptyList(); + } + List normalizedIds = new ArrayList(); + for (String id : ids) { + String normalizedId = trimToNull(id); + if (normalizedId != null && !normalizedIds.contains(normalizedId)) { + normalizedIds.add(normalizedId); + } + } + return normalizedIds; + } + + private boolean hasLedgerKeyword(SteadyDataViewQueryParam queryParam) { + return trimToNull(queryParam.getEngineeringName()) != null + || trimToNull(queryParam.getProjectName()) != null + || trimToNull(queryParam.getEquipmentName()) != null + || trimToNull(queryParam.getLineName()) != null; + } + + private Page emptyPage(SteadyDataViewQueryParam queryParam) { + Page page = new Page(PageFactory.getPageNum(queryParam), PageFactory.getPageSize(queryParam), 0); + page.setRecords(Collections.emptyList()); + return page; + } + + private List resolveValueColumns(List columns) { + List result = new ArrayList(); + for (String column : columns) { + if (!isInfrastructureColumn(column)) { + result.add(column); + } + } + return result; + } + + private boolean isInfrastructureColumn(String column) { + return "TIMEID".equals(column) || "LINEID".equals(column) || "PHASIC_TYPE".equals(column) || "QUALITYFLAG".equals(column); + } + + private LocalDateTime toLocalDateTime(Object value) { + if (value instanceof LocalDateTime) { + return (LocalDateTime) value; + } + if (value instanceof Timestamp) { + return ((Timestamp) value).toLocalDateTime(); + } + String text = trimToNull(toStringValue(value)); + return text == null ? null : parseDateTime(text); + } + + private Integer toInteger(Object value) { + if (value instanceof Number) { + return ((Number) value).intValue(); + } + String text = trimToNull(toStringValue(value)); + return text == null ? null : Integer.valueOf(text); + } + + private String toStringValue(Object value) { + return value == null ? null : String.valueOf(value); + } + + private String defaultText(String value) { + String text = trimToNull(value); + return text == null ? EMPTY_TEXT : text; + } + + private String trimToNull(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed; + } +} diff --git a/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewTrendServiceImpl.java b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewTrendServiceImpl.java new file mode 100644 index 0000000..fbb8038 --- /dev/null +++ b/steady/steady-DataView/src/main/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewTrendServiceImpl.java @@ -0,0 +1,210 @@ +package com.njcn.gather.steady.datavie.service.impl; + +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.gather.steady.datavie.component.SteadyInfluxQueryComponent; +import com.njcn.gather.steady.datavie.component.SteadyTrendFieldResolver; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO; +import com.njcn.gather.steady.datavie.pojo.param.SteadyTrendQueryParam; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendPointVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendQueryVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendSeriesVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendSummaryItemVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendSummaryVO; +import com.njcn.gather.steady.datavie.service.SteadyDataViewTrendService; +import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO; +import com.njcn.gather.tool.addledger.service.AddLedgerService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * 稳态趋势查询服务实现。 + */ +@Service +@RequiredArgsConstructor +public class SteadyDataViewTrendServiceImpl implements SteadyDataViewTrendService { + + private static final String EMPTY_TEXT = "-"; + + private final SteadyTrendFieldResolver fieldResolver; + private final SteadyInfluxQueryComponent influxQueryComponent; + private final AddLedgerService addLedgerService; + + @Override + public SteadyTrendQueryVO queryTrend(SteadyTrendQueryParam param) { + return queryTrendInternal(param, true); + } + + @Override + public SteadyTrendQueryVO queryTrendDay(SteadyTrendQueryParam param) { + return queryTrendInternal(param, true); + } + + @Override + public SteadyTrendSummaryVO summarizeTrend(SteadyTrendQueryParam param) { + SteadyTrendQueryParam summaryParam = copyParam(param); + summaryParam.setBucket(null); + SteadyTrendQueryVO trend = queryTrendInternal(summaryParam, false); + SteadyTrendSummaryVO result = new SteadyTrendSummaryVO(); + for (SteadyTrendSeriesVO series : trend.getSeries()) { + result.getItems().add(buildSummaryItem(series)); + } + return result; + } + + private SteadyTrendQueryVO queryTrendInternal(SteadyTrendQueryParam param, boolean autoBucket) { + LocalDateTime startTime = fieldResolver.parseRequiredTime(param == null ? null : param.getTimeStart(), "开始时间不能为空"); + LocalDateTime endTime = fieldResolver.parseRequiredTime(param == null ? null : param.getTimeEnd(), "结束时间不能为空"); + List fields = fieldResolver.resolveFields(param); + enrichLineNames(fields); + String bucket = autoBucket ? resolveBucket(param.getBucket(), startTime, endTime) : null; + + SteadyTrendQueryVO result = new SteadyTrendQueryVO(); + result.setBucket(bucket); + result.setSampled(bucket != null); + result.setLoadableDays(resolveLoadableDays(startTime, endTime)); + int displayPointCount = 0; + for (SteadyTrendResolvedFieldBO field : fields) { + List points = influxQueryComponent.queryTrendPoints(field, startTime, endTime, bucket, param.getQualityFlag()); + displayPointCount += points.size(); + result.getSeries().add(buildSeries(field, points)); + } + /* + * 当前 Influx 查询按曲线独立执行,未额外发 count 查询;sourcePointCount 保持与实际返回点数一致。 + * 后续如需要精确原始点数,可单独增加 count(field) 查询。 + */ + result.setDisplayPointCount(displayPointCount); + result.setSourcePointCount(displayPointCount); + return result; + } + + private SteadyTrendSeriesVO buildSeries(SteadyTrendResolvedFieldBO field, List points) { + SteadyTrendSeriesVO series = new SteadyTrendSeriesVO(); + series.setSeriesKey(field.getSeriesKey()); + series.setLineId(field.getLineId()); + series.setLineName(field.getLineName()); + series.setIndicatorCode(field.getIndicatorCode()); + series.setIndicatorName(field.getIndicatorName()); + series.setSeriesName(field.getSeriesName()); + series.setPhase(field.getPhase()); + series.setStatType(field.getStatType()); + series.setUnit(field.getUnit()); + series.setPoints(points); + return series; + } + + private void enrichLineNames(List fields) { + List lineIds = new ArrayList(); + for (SteadyTrendResolvedFieldBO field : fields) { + if (!lineIds.contains(field.getLineId())) { + lineIds.add(field.getLineId()); + } + } + Map linePathMap = addLedgerService.listLinePathByLineIds(lineIds); + for (SteadyTrendResolvedFieldBO field : fields) { + AddLedgerLinePathVO linePath = linePathMap.get(field.getLineId()); + field.setLineName(linePath == null || trimToNull(linePath.getLineName()) == null ? EMPTY_TEXT : linePath.getLineName()); + } + } + + private SteadyTrendSummaryItemVO buildSummaryItem(SteadyTrendSeriesVO series) { + SteadyTrendSummaryItemVO item = new SteadyTrendSummaryItemVO(); + item.setSeriesKey(series.getSeriesKey()); + List values = new ArrayList(); + for (SteadyTrendPointVO point : series.getPoints()) { + if (point.getValue() != null) { + values.add(point.getValue()); + } + } + if (values.isEmpty()) { + return item; + } + values.sort(Comparator.naturalOrder()); + BigDecimal sum = BigDecimal.ZERO; + for (BigDecimal value : values) { + sum = sum.add(value); + } + item.setMin(values.get(0)); + item.setMax(values.get(values.size() - 1)); + item.setAvg(sum.divide(new BigDecimal(values.size()), 6, RoundingMode.HALF_UP)); + int cp95Index = Math.max(0, (int) Math.ceil(values.size() * 0.95D) - 1); + item.setCp95(values.get(cp95Index)); + return item; + } + + private String resolveBucket(String requestBucket, LocalDateTime startTime, LocalDateTime endTime) { + String bucket = trimToNull(requestBucket); + if (bucket != null) { + if (!bucket.matches("^[1-9][0-9]*(m|h|d)$")) { + throw fail("分桶粒度仅支持 m、h、d,例如 10m、1h"); + } + return bucket; + } + long hours = ChronoUnit.HOURS.between(startTime, endTime); + if (hours < 6) { + return "1m"; + } + if (hours <= 24) { + return "10m"; + } + if (hours <= 24 * 7) { + return "30m"; + } + return "1h"; + } + + private List resolveLoadableDays(LocalDateTime startTime, LocalDateTime endTime) { + List result = new ArrayList(); + LocalDate date = startTime.toLocalDate(); + LocalDate endDate = endTime.toLocalDate(); + while (!date.isAfter(endDate)) { + result.add(date.toString()); + date = date.plusDays(1); + } + return result; + } + + private SteadyTrendQueryParam copyParam(SteadyTrendQueryParam source) { + SteadyTrendQueryParam target = new SteadyTrendQueryParam(); + if (source == null) { + return target; + } + target.setLineIds(copyList(source.getLineIds())); + target.setIndicatorCodes(copyList(source.getIndicatorCodes())); + target.setStatTypes(copyList(source.getStatTypes())); + target.setPhases(copyList(source.getPhases())); + target.setTimeStart(source.getTimeStart()); + target.setTimeEnd(source.getTimeEnd()); + target.setBucket(source.getBucket()); + target.setQualityFlag(source.getQualityFlag()); + target.setHarmonicOrders(source.getHarmonicOrders() == null ? Collections.emptyList() : new ArrayList(source.getHarmonicOrders())); + return target; + } + + private List copyList(List source) { + return source == null ? new ArrayList() : new ArrayList(source); + } + + private String trimToNull(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed; + } + + private BusinessException fail(String message) { + return new BusinessException(CommonResponseEnum.FAIL, message); + } +} diff --git a/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/component/SteadyInfluxQueryComponentTest.java b/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/component/SteadyInfluxQueryComponentTest.java new file mode 100644 index 0000000..b02603e --- /dev/null +++ b/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/component/SteadyInfluxQueryComponentTest.java @@ -0,0 +1,51 @@ +package com.njcn.gather.steady.datavie.component; + +import com.njcn.gather.steady.datavie.config.SteadyInfluxDbProperties; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +/** + * InfluxQL 查询语句生成测试。 + */ +class SteadyInfluxQueryComponentTest { + + @Test + void shouldBuildBucketedTrendQueryWithRequiredTags() { + SteadyInfluxQueryComponent component = new SteadyInfluxQueryComponent(new SteadyInfluxDbProperties()); + SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO(); + field.setMeasurement("data_v"); + field.setField("RMS_CP95"); + field.setLineId("line-001"); + field.setPhase("A"); + + String query = component.buildTrendQuery(field, + LocalDateTime.of(2026, 5, 1, 0, 0, 0), + LocalDateTime.of(2026, 5, 1, 1, 0, 0), + "10m", + 1); + + Assertions.assertEquals("SELECT mean(\"RMS_CP95\") AS \"value\" FROM \"data_v\" WHERE time >= '2026-05-01T00:00:00Z' AND time <= '2026-05-01T01:00:00Z' AND \"LINEID\" = 'line-001' AND \"PHASIC_TYPE\" = 'A' AND \"QUALITYFLAG\" = '1' GROUP BY time(10m) fill(none)", query); + } + + @Test + void shouldEscapeTagValuesInTrendQuery() { + SteadyInfluxQueryComponent component = new SteadyInfluxQueryComponent(new SteadyInfluxDbProperties()); + SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO(); + field.setMeasurement("data_v"); + field.setField("RMS"); + field.setLineId("line'001"); + field.setPhase("A"); + + String query = component.buildTrendQuery(field, + LocalDateTime.of(2026, 5, 1, 0, 0, 0), + LocalDateTime.of(2026, 5, 1, 1, 0, 0), + null, + null); + + Assertions.assertTrue(query.contains("\"LINEID\" = 'line\\'001'")); + Assertions.assertFalse(query.contains("GROUP BY time")); + } +} diff --git a/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/component/SteadyTrendFieldResolverTest.java b/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/component/SteadyTrendFieldResolverTest.java new file mode 100644 index 0000000..da743a5 --- /dev/null +++ b/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/component/SteadyTrendFieldResolverTest.java @@ -0,0 +1,99 @@ +package com.njcn.gather.steady.datavie.component; + +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO; +import com.njcn.gather.steady.datavie.pojo.param.SteadyTrendQueryParam; +import com.njcn.gather.tool.adddata.component.AddDataTableRegistry; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +/** + * 稳态趋势字段解析测试。 + */ +class SteadyTrendFieldResolverTest { + + private SteadyTrendFieldResolver resolver; + + @BeforeEach + void setUp() throws Exception { + AddDataTableRegistry tableRegistry = new AddDataTableRegistry(); + tableRegistry.afterPropertiesSet(); + resolver = new SteadyTrendFieldResolver(new SteadyTrendIndicatorCatalog(), tableRegistry); + } + + @Test + void shouldResolveVoltageRmsAverageAndCp95Fields() { + SteadyTrendQueryParam param = new SteadyTrendQueryParam(); + param.setLineIds(Arrays.asList("line-001")); + param.setIndicatorCodes(Arrays.asList("V_RMS")); + param.setPhases(Arrays.asList("A")); + param.setStatTypes(Arrays.asList("AVG", "CP95")); + param.setTimeStart("2026-05-01 00:00:00"); + param.setTimeEnd("2026-05-01 01:00:00"); + + List fields = resolver.resolveFields(param); + + Assertions.assertEquals(2, fields.size()); + Assertions.assertEquals("data_v", fields.get(0).getMeasurement()); + Assertions.assertEquals("RMS", fields.get(0).getField()); + Assertions.assertEquals("AVG", fields.get(0).getStatType()); + Assertions.assertEquals("RMS_CP95", fields.get(1).getField()); + Assertions.assertEquals("V", fields.get(1).getUnit()); + } + + @Test + void shouldExpandLineVoltageTotalPhaseToThreeSeries() { + SteadyTrendQueryParam param = new SteadyTrendQueryParam(); + param.setLineIds(Arrays.asList("line-001")); + param.setIndicatorCodes(Arrays.asList("V_LINE_RMS")); + param.setPhases(Arrays.asList("T")); + param.setStatTypes(Arrays.asList("AVG")); + param.setTimeStart("2026-05-01 00:00:00"); + param.setTimeEnd("2026-05-01 01:00:00"); + + List fields = resolver.resolveFields(param); + + Assertions.assertEquals(3, fields.size()); + Assertions.assertEquals("RMSAB", fields.get(0).getField()); + Assertions.assertEquals("RMSBC", fields.get(1).getField()); + Assertions.assertEquals("RMSCA", fields.get(2).getField()); + Assertions.assertEquals("T", fields.get(0).getPhase()); + } + + @Test + void shouldRejectHarmonicTrendWithoutOrders() { + SteadyTrendQueryParam param = new SteadyTrendQueryParam(); + param.setLineIds(Arrays.asList("line-001")); + param.setIndicatorCodes(Arrays.asList("V_HARMONIC")); + param.setPhases(Arrays.asList("A")); + param.setStatTypes(Arrays.asList("AVG")); + param.setTimeStart("2026-05-01 00:00:00"); + param.setTimeEnd("2026-05-01 01:00:00"); + + BusinessException exception = Assertions.assertThrows(BusinessException.class, () -> resolver.resolveFields(param)); + + Assertions.assertTrue(exception.getMessage().contains("谐波次数不能为空")); + } + + @Test + void shouldResolveSelectedHarmonicOrdersOnly() { + SteadyTrendQueryParam param = new SteadyTrendQueryParam(); + param.setLineIds(Arrays.asList("line-001")); + param.setIndicatorCodes(Arrays.asList("V_HARMONIC")); + param.setPhases(Arrays.asList("A")); + param.setStatTypes(Arrays.asList("MAX")); + param.setHarmonicOrders(Arrays.asList(3, 5)); + param.setTimeStart("2026-05-01 00:00:00"); + param.setTimeEnd("2026-05-01 01:00:00"); + + List fields = resolver.resolveFields(param); + + Assertions.assertEquals(2, fields.size()); + Assertions.assertEquals("V_3_MAX", fields.get(0).getField()); + Assertions.assertEquals("V_5_MAX", fields.get(1).getField()); + } +} diff --git a/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewIndicatorServiceImplTest.java b/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewIndicatorServiceImplTest.java new file mode 100644 index 0000000..6f95883 --- /dev/null +++ b/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewIndicatorServiceImplTest.java @@ -0,0 +1,27 @@ +package com.njcn.gather.steady.datavie.service.impl; + +import com.njcn.gather.steady.datavie.component.SteadyTrendIndicatorCatalog; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyDataViewIndicatorNodeVO; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +/** + * 稳态趋势指标服务测试。 + */ +class SteadyDataViewIndicatorServiceImplTest { + + @Test + void shouldGroupIndicatorsByCategory() { + SteadyDataViewIndicatorServiceImpl service = new SteadyDataViewIndicatorServiceImpl(new SteadyTrendIndicatorCatalog()); + + List tree = service.listIndicatorTree(); + + Assertions.assertEquals(5, tree.size()); + Assertions.assertEquals("VOLTAGE", tree.get(0).getGroupCode()); + Assertions.assertTrue(tree.get(0).getChildren().size() >= 2); + Assertions.assertEquals("V_RMS", tree.get(0).getChildren().get(0).getIndicatorCode()); + Assertions.assertTrue(tree.get(0).getChildren().get(0).getSelectable()); + } +} diff --git a/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewTrendServiceImplTest.java b/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewTrendServiceImplTest.java new file mode 100644 index 0000000..91438bb --- /dev/null +++ b/steady/steady-DataView/src/test/java/com/njcn/gather/steady/datavie/service/impl/SteadyDataViewTrendServiceImplTest.java @@ -0,0 +1,123 @@ +package com.njcn.gather.steady.datavie.service.impl; + +import com.njcn.gather.steady.datavie.component.SteadyInfluxQueryComponent; +import com.njcn.gather.steady.datavie.component.SteadyTrendFieldResolver; +import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO; +import com.njcn.gather.steady.datavie.pojo.param.SteadyTrendQueryParam; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendPointVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendQueryVO; +import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendSummaryVO; +import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO; +import com.njcn.gather.tool.addledger.service.AddLedgerService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Collections; + +/** + * 稳态趋势服务编排测试。 + */ +class SteadyDataViewTrendServiceImplTest { + + @Test + void shouldBuildTrendQueryWithDefaultBucketAndLineName() { + SteadyTrendFieldResolver fieldResolver = Mockito.mock(SteadyTrendFieldResolver.class); + SteadyInfluxQueryComponent influxQueryComponent = Mockito.mock(SteadyInfluxQueryComponent.class); + AddLedgerService addLedgerService = Mockito.mock(AddLedgerService.class); + + SteadyTrendQueryParam param = new SteadyTrendQueryParam(); + param.setLineIds(Arrays.asList("line-001")); + param.setIndicatorCodes(Arrays.asList("V_RMS")); + param.setPhases(Arrays.asList("A")); + param.setStatTypes(Arrays.asList("AVG")); + param.setTimeStart("2026-05-01 00:00:00"); + param.setTimeEnd("2026-05-01 05:59:59"); + + SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO(); + field.setMeasurement("data_v"); + field.setField("RMS"); + field.setLineId("line-001"); + field.setIndicatorCode("V_RMS"); + field.setIndicatorName("相电压有效值"); + field.setSeriesName("相电压有效值"); + field.setPhase("A"); + field.setStatType("AVG"); + field.setUnit("V"); + field.setSeriesKey("line-001|V_RMS|A|AVG|RMS"); + + Mockito.when(fieldResolver.parseRequiredTime("2026-05-01 00:00:00", "开始时间不能为空")) + .thenReturn(LocalDateTime.of(2026, 5, 1, 0, 0, 0)); + Mockito.when(fieldResolver.parseRequiredTime("2026-05-01 05:59:59", "结束时间不能为空")) + .thenReturn(LocalDateTime.of(2026, 5, 1, 5, 59, 59)); + Mockito.when(fieldResolver.resolveFields(Mockito.any())).thenReturn(Collections.singletonList(field)); + Mockito.when(addLedgerService.listLinePathByLineIds(Collections.singletonList("line-001"))) + .thenReturn(Collections.singletonMap("line-001", buildLinePath("进线一"))); + Mockito.when(influxQueryComponent.queryTrendPoints(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.eq("1m"), Mockito.isNull())) + .thenReturn(Collections.singletonList(new SteadyTrendPointVO("2026-05-01 00:00:00", new BigDecimal("1.2")))); + + SteadyDataViewTrendServiceImpl service = new SteadyDataViewTrendServiceImpl(fieldResolver, influxQueryComponent, addLedgerService); + SteadyTrendQueryVO result = service.queryTrend(param); + + Assertions.assertEquals("1m", result.getBucket()); + Assertions.assertTrue(result.getSampled()); + Assertions.assertEquals("进线一", result.getSeries().get(0).getLineName()); + } + + @Test + void shouldCalculateTrendSummaryFromSeriesPoints() { + SteadyTrendFieldResolver fieldResolver = Mockito.mock(SteadyTrendFieldResolver.class); + SteadyInfluxQueryComponent influxQueryComponent = Mockito.mock(SteadyInfluxQueryComponent.class); + AddLedgerService addLedgerService = Mockito.mock(AddLedgerService.class); + + SteadyTrendQueryParam param = new SteadyTrendQueryParam(); + param.setLineIds(Arrays.asList("line-001")); + param.setIndicatorCodes(Arrays.asList("V_RMS")); + param.setPhases(Arrays.asList("A")); + param.setStatTypes(Arrays.asList("AVG")); + param.setTimeStart("2026-05-01 00:00:00"); + param.setTimeEnd("2026-05-01 05:59:59"); + + SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO(); + field.setMeasurement("data_v"); + field.setField("RMS"); + field.setLineId("line-001"); + field.setIndicatorCode("V_RMS"); + field.setIndicatorName("相电压有效值"); + field.setSeriesName("相电压有效值"); + field.setPhase("A"); + field.setStatType("AVG"); + field.setUnit("V"); + field.setSeriesKey("line-001|V_RMS|A|AVG|RMS"); + + Mockito.when(fieldResolver.parseRequiredTime(Mockito.anyString(), Mockito.anyString())) + .thenReturn(LocalDateTime.of(2026, 5, 1, 0, 0, 0)) + .thenReturn(LocalDateTime.of(2026, 5, 1, 5, 59, 59)); + Mockito.when(fieldResolver.resolveFields(param)).thenReturn(Collections.singletonList(field)); + Mockito.when(addLedgerService.listLinePathByLineIds(Collections.singletonList("line-001"))) + .thenReturn(Collections.emptyMap()); + Mockito.when(influxQueryComponent.queryTrendPoints(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.isNull(), Mockito.isNull())) + .thenReturn(Arrays.asList( + new SteadyTrendPointVO("2026-05-01 00:00:00", new BigDecimal("1")), + new SteadyTrendPointVO("2026-05-01 01:00:00", new BigDecimal("3")), + new SteadyTrendPointVO("2026-05-01 02:00:00", new BigDecimal("2")) + )); + + SteadyDataViewTrendServiceImpl service = new SteadyDataViewTrendServiceImpl(fieldResolver, influxQueryComponent, addLedgerService); + SteadyTrendSummaryVO summary = service.summarizeTrend(param); + + Assertions.assertEquals(new BigDecimal("3"), summary.getItems().get(0).getMax()); + Assertions.assertEquals(new BigDecimal("1"), summary.getItems().get(0).getMin()); + Assertions.assertEquals(new BigDecimal("2.000000"), summary.getItems().get(0).getAvg()); + Assertions.assertEquals(new BigDecimal("3"), summary.getItems().get(0).getCp95()); + } + + private AddLedgerLinePathVO buildLinePath(String lineName) { + AddLedgerLinePathVO linePathVO = new AddLedgerLinePathVO(); + linePathVO.setLineName(lineName); + return linePathVO; + } +} diff --git a/system/src/main/java/com/njcn/gather/system/dictionary/pojo/enums/DictDataEnum.java b/system/src/main/java/com/njcn/gather/system/dictionary/pojo/enums/DictDataEnum.java index 363c3ee..6098e4c 100644 --- a/system/src/main/java/com/njcn/gather/system/dictionary/pojo/enums/DictDataEnum.java +++ b/system/src/main/java/com/njcn/gather/system/dictionary/pojo/enums/DictDataEnum.java @@ -3,6 +3,10 @@ package com.njcn.gather.system.dictionary.pojo.enums; import lombok.Getter; import org.apache.commons.lang3.StringUtils; +import static com.njcn.gather.system.pojo.constant.DictConst.REG_RES_CONTRAST_CODE; +import static com.njcn.gather.system.pojo.constant.DictConst.REG_RES_DIGITAL_CODE; +import static com.njcn.gather.system.pojo.constant.DictConst.REG_RES_SIMULATE_CODE; + /** * @author caozehui * @data 2024-12-12 @@ -14,9 +18,9 @@ public enum DictDataEnum { * Key cleanup point: only keep registration-related platform capability * types that are still referenced by the retained activation flow. */ - DIGITAL("数字式", "Digital"), - SIMULATE("模拟式", "Simulate"), - CONTRAST("比对式", "Contrast"); + DIGITAL("数字式", REG_RES_DIGITAL_CODE), + SIMULATE("模拟式", REG_RES_SIMULATE_CODE), + CONTRAST("比对式", REG_RES_CONTRAST_CODE); private final String name; private final String code; diff --git a/system/src/main/java/com/njcn/gather/system/pojo/constant/DictConst.java b/system/src/main/java/com/njcn/gather/system/pojo/constant/DictConst.java index b44c542..eab1f95 100644 --- a/system/src/main/java/com/njcn/gather/system/pojo/constant/DictConst.java +++ b/system/src/main/java/com/njcn/gather/system/pojo/constant/DictConst.java @@ -18,4 +18,24 @@ public interface DictConst { * 顶层父类的pid */ String FATHER_ID = "0"; + + /** + * 注册资源字典数据编码:数字式 + */ + String REG_RES_DIGITAL_CODE = "Digital"; + + /** + * 注册资源字典数据编码:模拟式 + */ + String REG_RES_SIMULATE_CODE = "Simulate"; + + /** + * 注册资源字典数据编码:比对式 + */ + String REG_RES_CONTRAST_CODE = "Contrast"; + + /** + * 注册资源字典数据 ID:比对式 + */ + String REG_RES_CONTRAST_ID = "7cd65363a6bf675ae408f28a281b77d4"; } diff --git a/system/src/main/java/com/njcn/gather/system/reg/service/impl/SysRegResServiceImpl.java b/system/src/main/java/com/njcn/gather/system/reg/service/impl/SysRegResServiceImpl.java index 66f1608..2c24b90 100644 --- a/system/src/main/java/com/njcn/gather/system/reg/service/impl/SysRegResServiceImpl.java +++ b/system/src/main/java/com/njcn/gather/system/reg/service/impl/SysRegResServiceImpl.java @@ -12,6 +12,7 @@ import com.njcn.gather.system.cfg.service.ISysTestConfigService; import com.njcn.gather.system.dictionary.pojo.enums.DictDataEnum; import com.njcn.gather.system.dictionary.pojo.po.DictData; import com.njcn.gather.system.dictionary.service.IDictDataService; +import com.njcn.gather.system.pojo.constant.DictConst; import com.njcn.gather.system.pojo.enums.SystemResponseEnum; import com.njcn.gather.system.reg.mapper.SysRegResMapper; import com.njcn.gather.system.reg.pojo.dto.RegInfoData; @@ -214,7 +215,7 @@ public class SysRegResServiceImpl extends ServiceImpl titleIndex && StrUtil.startWithIgnoreCase(waveDataDTO.getWaveTitle().get(titleIndex), "U")) { - return waveDataDTO.getPt() == null ? 1F : waveDataDTO.getPt().floatValue() ; + // 电压向量按 kV 展示,需与普通波形查看保持一致,避免将 V 级数值直接标记为 kV。 + return waveDataDTO.getPt() == null ? 0.001F : waveDataDTO.getPt().floatValue() / 1000F; } return waveDataDTO.getCt() == null ? 1F : waveDataDTO.getCt().floatValue(); } diff --git a/tools/wave-tool/src/test/java/com/njcn/gather/tool/wave/component/WaveVectorComponentTest.java b/tools/wave-tool/src/test/java/com/njcn/gather/tool/wave/component/WaveVectorComponentTest.java new file mode 100644 index 0000000..2953cf9 --- /dev/null +++ b/tools/wave-tool/src/test/java/com/njcn/gather/tool/wave/component/WaveVectorComponentTest.java @@ -0,0 +1,73 @@ +package com.njcn.gather.tool.wave.component; + +import com.njcn.gather.tool.wave.pojo.dto.ComtradeCfgDTO; +import com.njcn.gather.tool.wave.pojo.dto.WaveDataDTO; +import com.njcn.gather.tool.wave.pojo.dto.WaveVectorGroupDTO; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 波形向量计算组件测试。 + */ +class WaveVectorComponentTest { + + @Test + void shouldScaleVoltageVectorValuesToKv() { + WaveVectorComponent component = new WaveVectorComponent(); + WaveDataDTO waveDataDTO = buildThreePhaseWaveData("U", 8153.209D, 1D); + + List groups = component.calculateVectors(waveDataDTO); + + Assertions.assertEquals("kV", groups.get(0).getUnit()); + Assertions.assertEquals(5.7652F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getTotalRms()); + Assertions.assertEquals(8.1532F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalAmplitude()); + Assertions.assertEquals(5.7652F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalRms()); + Assertions.assertEquals(5.7652F, groups.get(0).getVectorSeries().get(0).getPositiveSequence().getRms()); + } + + @Test + void shouldKeepCurrentVectorValuesInAmpere() { + WaveVectorComponent component = new WaveVectorComponent(); + WaveDataDTO waveDataDTO = buildThreePhaseWaveData("I", 35D, 1D); + + List groups = component.calculateVectors(waveDataDTO); + + Assertions.assertEquals("A", groups.get(0).getUnit()); + Assertions.assertEquals(24.7487F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getTotalRms()); + Assertions.assertEquals(35F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalAmplitude()); + Assertions.assertEquals(24.7487F, groups.get(0).getVectorSeries().get(0).getPhaseVectors().get(0).getFundamentalRms()); + } + + private WaveDataDTO buildThreePhaseWaveData(String titlePrefix, double amplitude, double ratio) { + int samplePerCycle = 32; + WaveDataDTO waveDataDTO = new WaveDataDTO(); + ComtradeCfgDTO cfgDTO = new ComtradeCfgDTO(); + cfgDTO.setFinalSampleRate(samplePerCycle); + waveDataDTO.setComtradeCfgDTO(cfgDTO); + waveDataDTO.setIPhasic(3); + waveDataDTO.setPt(ratio); + waveDataDTO.setCt(ratio); + waveDataDTO.setWaveTitle(Arrays.asList("x", titlePrefix + "A", titlePrefix + "B", titlePrefix + "C")); + waveDataDTO.setChannelNames(Arrays.asList("x", titlePrefix)); + waveDataDTO.setListWaveData(buildRows(samplePerCycle, amplitude)); + return waveDataDTO; + } + + private List> buildRows(int samplePerCycle, double amplitude) { + List> rows = new ArrayList<>(); + for (int i = 0; i < samplePerCycle; i++) { + double angle = 2D * Math.PI * i / samplePerCycle; + List row = new ArrayList<>(); + row.add((float) i); + row.add((float) (amplitude * Math.sin(angle))); + row.add((float) (amplitude * Math.sin(angle - 2D * Math.PI / 3D))); + row.add((float) (amplitude * Math.sin(angle + 2D * Math.PI / 3D))); + rows.add(row); + } + return rows; + } +}