Compare commits

..

11 Commits

Author SHA1 Message Date
0116ea4ec0 Merge pull request '1.首页总累计运行数据修改数据来源为电表报表' (#5) from waibao into dev
Reviewed-on: #5
2026-04-21 08:46:31 +00:00
1fee8e920a 1.首页总累计运行数据修改数据来源为电表报表 2026-04-21 16:32:28 +08:00
111631a426 重构 2026-04-20 20:42:57 +08:00
7153c00d0c 重构 2026-04-16 23:11:25 +08:00
2b3be8636f 1.单体电池批量新增修改 2026-04-16 19:49:56 +08:00
5eb9b455a5 1.单体电池批量新增修改 2026-04-15 22:47:17 +08:00
4947f085c7 增加业务报表备注功能,可以根据业务设计开发,目前电表报表与收益报表均有备注列可以修改 2026-04-14 16:44:31 +08:00
5460aadf6c 增加业务报表备注功能,可以根据业务设计开发,目前电表报表与收益报表均有备注列可以修改 2026-04-14 16:11:23 +08:00
79a8040446 1.一周充放曲线改为了时间聚合柱状图。
2.PCS最高温度修复bug展示多PCS设备的数据
3.PCS的状态根据状态枚举映射配置的内容显示
4.BMS的总览,增加工作状态、与PCS通讯、与EMS通讯的配置,及关联展示
5.增加批量导入单体电池点位的功能
6.修复计算点可能会出现id与code在一个池子内的问题,再次计算后数据正常
7.计算点增加小数位限制的功能,实时计算与7天历史接口都已经按照配置的小数位进行限制
8.统计报表中的功率曲线改为了按照分钟显示
9.功率曲线出现断点的问题是因为数据计算太密集了导致的,增加了前端连线不断的显示
10.PCS和电池堆的曲线与配置增加了关联设备显示
11.点位映射中的电池温度,增加了多设备
12.收益报表增加升序排列,合并当月所有合计
13.增加业务报表备注功能,可以根据业务设计开发,目前电表报表与收益报表均有备注列可以修改
2026-04-12 15:16:57 +08:00
ed36292c94 Merge pull request 'dev' (#3) from dev into waibao
Reviewed-on: #3
2026-04-09 01:31:05 +00:00
5d5a7137fc Merge pull request 'dev' (#2) from dev into main
Reviewed-on: #2
2026-02-11 01:55:45 +00:00
36 changed files with 2202 additions and 247 deletions

View File

@ -8,21 +8,21 @@ import com.xzzn.common.core.page.TableDataInfo;
import com.xzzn.common.enums.BusinessType;
import com.xzzn.common.utils.file.FileUploadUtils;
import com.xzzn.common.utils.file.MimeTypeUtils;
import com.xzzn.common.utils.poi.ExcelUtil;
import com.xzzn.ems.domain.EmsSiteSetting;
import com.xzzn.ems.domain.vo.DeviceUpdateRequest;
import com.xzzn.ems.domain.vo.DevicesSettingVo;
import com.xzzn.ems.domain.vo.PointDataRequest;
import com.xzzn.ems.domain.vo.PointQueryResponse;
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest;
import com.xzzn.ems.domain.vo.SiteDeviceListVo;
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest;
import com.xzzn.ems.domain.vo.SingleBatteryConfigInitRequest;
import com.xzzn.ems.domain.vo.SingleBatteryConfigInitResultVo;
import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportResultVo;
import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportRowVo;
import com.xzzn.ems.domain.vo.WorkStatusEnumMappingSaveRequest;
import com.xzzn.ems.service.IEmsDeviceSettingService;
import com.xzzn.ems.service.IEmsSiteService;
import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
@ -35,14 +35,17 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
/**
*
* 站点配置
*
* 绔欑偣閰嶇疆
*/
@RestController
@RequestMapping("/ems/siteConfig")
public class EmsSiteConfigController extends BaseController{
public class EmsSiteConfigController extends BaseController {
@Autowired
private IEmsSiteService iEmsSiteService;
@ -50,216 +53,169 @@ public class EmsSiteConfigController extends BaseController{
@Autowired
private IEmsDeviceSettingService iEmsDeviceSettingService;
/**
* 获取站点列表
*/
@GetMapping("/getSiteInfoList")
public TableDataInfo getSiteInfoList(@RequestParam String siteName, @RequestParam String startTime, @RequestParam String endTime)
{
public TableDataInfo getSiteInfoList(@RequestParam String siteName, @RequestParam String startTime, @RequestParam String endTime) {
startPage();
List<EmsSiteSetting> list = iEmsSiteService.getAllSiteInfoList(siteName,startTime,endTime);
List<EmsSiteSetting> list = iEmsSiteService.getAllSiteInfoList(siteName, startTime, endTime);
return getDataTable(list);
}
/**
* 新增站点
*/
@PostMapping("/addSite")
public AjaxResult addSite(@RequestBody EmsSiteSetting emsSiteSetting)
{
public AjaxResult addSite(@RequestBody EmsSiteSetting emsSiteSetting) {
emsSiteSetting.setCreateBy(getUsername());
return toAjax(iEmsSiteService.addSite(emsSiteSetting));
}
/**
* 编辑站点
*/
@PostMapping("/updateSite")
public AjaxResult updateSite(@RequestBody EmsSiteSetting emsSiteSetting)
{
public AjaxResult updateSite(@RequestBody EmsSiteSetting emsSiteSetting) {
emsSiteSetting.setUpdateBy(getUsername());
return toAjax(iEmsSiteService.updateSite(emsSiteSetting));
}
/**
* 获取设备列表-分页
*/
@GetMapping("/getDeviceInfoList")
public TableDataInfo getDeviceInfoList(@RequestParam(value = "siteId", required = false) String siteId,
@RequestParam(value = "deviceCategory", required = false) String deviceCategory)
{
public TableDataInfo getDeviceInfoList(@RequestParam(value = "siteId", required = false) String siteId,
@RequestParam(value = "deviceCategory", required = false) String deviceCategory) {
startPage();
List<SiteDeviceListVo> list = iEmsSiteService.getAllDeviceListNoDisp(siteId, deviceCategory);
return getDataTable(list);
}
/**
* 获取设备详细信息
*/
@GetMapping("/getDeviceDetailInfo")
public AjaxResult getDeviceDetailInfo(@RequestParam Long id)
{
public AjaxResult getDeviceDetailInfo(@RequestParam Long id) {
return success(iEmsDeviceSettingService.getDeviceDetailInfo(id));
}
/**
* 获取设备列表-不分页
*/
@GetMapping("/getDeviceList")
public AjaxResult getDeviceInfoList2(@RequestParam String siteId)
{
public AjaxResult getDeviceInfoList2(@RequestParam String siteId) {
return success(iEmsSiteService.getAllDeviceList(siteId, null));
}
/**
* 获取所有设备类别
*/
@GetMapping("/getDeviceCategory")
public AjaxResult getDeviceCategory()
{
public AjaxResult getDeviceCategory() {
return success(iEmsDeviceSettingService.getDeviceCategory());
}
/**
* 新增设备
*/
@PostMapping("/addDevice")
public AjaxResult addDevice(@RequestBody DevicesSettingVo devicesSetting)
{
public AjaxResult addDevice(@RequestBody DevicesSettingVo devicesSetting) {
int result = iEmsDeviceSettingService.addDevice(devicesSetting);
if (result > 0) {
if (result > 0) {
return AjaxResult.success(result);
} else {
return AjaxResult.error("该设备已存在");
}
return AjaxResult.error("璇ヨ澶囧凡瀛樺湪");
}
/**
* 上传设备图片
*/
@PostMapping("/uploadDeviceImg")
public AjaxResult uploadDeviceImg(@RequestParam("avatarfile") MultipartFile file) throws Exception
{
public AjaxResult uploadDeviceImg(@RequestParam("avatarfile") MultipartFile file) throws Exception {
if (!file.isEmpty()) {
String avatar = FileUploadUtils.upload(RuoYiConfig.getDevicePath(), file, MimeTypeUtils.IMAGE_EXTENSION);
AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", avatar);
return ajax;
}
return error("上传图片异常,请联系管理员");
return error("涓婁紶鍥剧墖寮傚父锛岃鑱旂郴绠$悊鍛?");
}
/**
* 修改Modbus设备配置
*/
@PostMapping("/updateDevice")
public AjaxResult updateDevice(@RequestBody DevicesSettingVo emsDevicesSetting)
{
public AjaxResult updateDevice(@RequestBody DevicesSettingVo emsDevicesSetting) {
int result = iEmsDeviceSettingService.updateDevice(emsDevicesSetting);
if (result > 0) {
if (result > 0) {
return AjaxResult.success(result);
} else if (result == -1) {
return AjaxResult.error("数据不存在");
} else if (result == -2) {
return AjaxResult.error("该设备已存在");
} else if (result == -1) {
return AjaxResult.error("鏁版嵁涓嶅瓨鍦?");
} else if (result == -2) {
return AjaxResult.error("璇ヨ澶囧凡瀛樺湪");
}
return AjaxResult.success(result);
}
/**
* 删除Modbus设备配置
*/
@DeleteMapping("/deleteService/{id}")
public AjaxResult deleteService(@PathVariable Long id)
{
public AjaxResult deleteService(@PathVariable Long id) {
return toAjax(iEmsDeviceSettingService.deleteEmsDevicesSettingById(id));
}
/**
* 单个站点单个设备点位查询-点位清单
*/
@GetMapping("/getDevicePointList")
public TableDataInfo getDevicePointList(@Validated PointDataRequest request)
{
public TableDataInfo getDevicePointList(@Validated PointDataRequest request) {
List<PointQueryResponse> result = iEmsDeviceSettingService.getSingleSiteDevicePoints(request);
return getDataTable2(result);
}
/**
* 获取指定站点下的所有设备类别
*/
@GetMapping("/getSiteAllDeviceCategory")
public AjaxResult getSiteAllDeviceCategory(String siteId)
{
public AjaxResult getSiteAllDeviceCategory(String siteId) {
return success(iEmsDeviceSettingService.getSiteAllDeviceCategory(siteId));
}
/**
* 根据设备类别获取父类的设备id
*/
@GetMapping("/getParentDeviceId")
public AjaxResult getParentDeviceId(@RequestParam String siteId, @RequestParam String deviceCategory)
{
public AjaxResult getParentDeviceId(@RequestParam String siteId, @RequestParam String deviceCategory) {
return success(iEmsSiteService.getParentCategoryDeviceId(siteId, deviceCategory));
}
/**
* 获取指定站点下的指定设备类型的设备
*/
@GetMapping("/getDeviceListBySiteAndCategory")
public AjaxResult getDeviceListBySiteAndCategory(String siteId,String deviceCategory)
{
public AjaxResult getDeviceListBySiteAndCategory(String siteId, String deviceCategory) {
return success(iEmsDeviceSettingService.getDeviceListBySiteAndCategory(siteId, deviceCategory));
}
/**
* 获取单站监控项目点位映射
*/
@GetMapping("/getSingleMonitorProjectPointMapping")
public AjaxResult getSingleMonitorProjectPointMapping(@RequestParam String siteId)
{
public AjaxResult getSingleMonitorProjectPointMapping(@RequestParam String siteId) {
return success(iEmsDeviceSettingService.getSiteMonitorProjectPointMapping(siteId));
}
/**
* 保存单站监控项目点位映射
*/
@PostMapping("/saveSingleMonitorProjectPointMapping")
public AjaxResult saveSingleMonitorProjectPointMapping(@RequestBody SiteMonitorProjectPointMappingSaveRequest request)
{
public AjaxResult saveSingleMonitorProjectPointMapping(@RequestBody SiteMonitorProjectPointMappingSaveRequest request) {
int rows = iEmsDeviceSettingService.saveSiteMonitorProjectPointMapping(request, getUsername());
return AjaxResult.success(rows);
}
/**
* 获取单站监控工作状态枚举映射PCS
*/
@GetMapping("/getSingleMonitorWorkStatusEnumMappings")
public AjaxResult getSingleMonitorWorkStatusEnumMappings(@RequestParam String siteId)
{
public AjaxResult getSingleMonitorWorkStatusEnumMappings(@RequestParam String siteId) {
return success(iEmsDeviceSettingService.getSiteWorkStatusEnumMappings(siteId));
}
/**
* 保存单站监控工作状态枚举映射PCS
*/
@PostMapping("/saveSingleMonitorWorkStatusEnumMappings")
public AjaxResult saveSingleMonitorWorkStatusEnumMappings(@RequestBody WorkStatusEnumMappingSaveRequest request)
{
public AjaxResult saveSingleMonitorWorkStatusEnumMappings(@RequestBody WorkStatusEnumMappingSaveRequest request) {
int rows = iEmsDeviceSettingService.saveSiteWorkStatusEnumMappings(request.getSiteId(), request.getMappings(), getUsername());
return AjaxResult.success(rows);
}
/**
* PCS设备开关机
*/
@GetMapping("/downloadSingleBatteryMonitorImportTemplate")
public void downloadSingleBatteryMonitorImportTemplate(HttpServletResponse response) {
ExcelUtil<SingleBatteryMonitorImportRowVo> util = new ExcelUtil<>(SingleBatteryMonitorImportRowVo.class);
util.importTemplateExcel(response, "单体电池导入模板", "单体电池导入模板");
}
@PostMapping("/importSingleBatteryMonitorMappings")
public AjaxResult importSingleBatteryMonitorMappings(@RequestParam("siteId") String siteId,
@RequestParam("file") MultipartFile file) throws Exception {
ExcelUtil<SingleBatteryMonitorImportRowVo> util = new ExcelUtil<>(SingleBatteryMonitorImportRowVo.class);
List<SingleBatteryMonitorImportRowVo> rowList = util.importExcel(file.getInputStream(), 1);
if (rowList == null) {
return AjaxResult.error("导入失败:未读取到任何数据,请检查 Excel 文件内容");
}
List<SingleBatteryMonitorImportRowVo> validRowList = new ArrayList<>();
for (int i = 0; i < rowList.size(); i++) {
SingleBatteryMonitorImportRowVo row = rowList.get(i);
if (row == null) {
continue;
}
row.setRowNum(i + 3);
validRowList.add(row);
}
if (validRowList.isEmpty()) {
return AjaxResult.error("导入失败:未识别到有效数据,请使用系统导出的最新模板,并保持表头名称不变");
}
SingleBatteryMonitorImportResultVo result = iEmsDeviceSettingService.importSingleBatteryMonitorMappings(siteId, validRowList, getUsername());
return AjaxResult.success(result);
}
@PostMapping("/initializeSingleBatteryMonitorMappings")
public AjaxResult initializeSingleBatteryMonitorMappings(@RequestBody SingleBatteryConfigInitRequest request) {
SingleBatteryConfigInitResultVo result = iEmsDeviceSettingService.initializeSingleBatteryMonitorMappings(request, getUsername());
return AjaxResult.success(result);
}
// @PreAuthorize("@ss.hasPermi('system:device:onAndOff')")
@Log(title = "开关机", businessType = BusinessType.UPDATE)
@Log(title = "寮€鍏虫満", businessType = BusinessType.UPDATE)
@PostMapping("/updateDeviceStatus")
public AjaxResult updateDeviceStatus(@Valid @RequestBody DeviceUpdateRequest request)
{
public AjaxResult updateDeviceStatus(@Valid @RequestBody DeviceUpdateRequest request) {
return success(iEmsDeviceSettingService.updateDeviceStatus(request));
}
}

View File

@ -61,6 +61,15 @@ public class EmsSiteMonitorController extends BaseController{
return success(iSingleSiteService.getSiteMonitorTotalDataVo(siteId));
}
/**
* 获取单站首页总累计运行数据(首页专用)
*/
@GetMapping("/homeRunningData")
public AjaxResult getSingleSiteHomeRunningData(@RequestParam String siteId)
{
return success(iSingleSiteService.getSiteMonitorHomeRunningData(siteId));
}
/**
* 单站监控-设备监控-实时运行头部数据
*/

View File

@ -0,0 +1,32 @@
package com.xzzn.web.controller.system;
import com.xzzn.common.core.controller.BaseController;
import com.xzzn.common.core.domain.AjaxResult;
import com.xzzn.ems.domain.vo.BizRemarkBatchGetRequest;
import com.xzzn.ems.domain.vo.BizRemarkSaveRequest;
import com.xzzn.ems.service.ISysBizRemarkService;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
@RequestMapping("/system/bizRemark")
public class SysBizRemarkController extends BaseController
{
@Autowired
private ISysBizRemarkService sysBizRemarkService;
@PostMapping("/batchGet")
public AjaxResult batchGet(@RequestBody BizRemarkBatchGetRequest request)
{
return success(sysBizRemarkService.getRemarkMap(request.getBizType(), request.getBizKey1(), request.getBizKey2List()));
}
@PostMapping("/save")
public AjaxResult save(@RequestBody BizRemarkSaveRequest request)
{
return toAjax(sysBizRemarkService.saveRemark(request, getUsername()));
}
}

View File

@ -70,7 +70,7 @@ spring:
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
database: 3
# 密码
password: 12345678
# 连接超时时间
@ -92,7 +92,7 @@ spring:
druid:
# 主库数据源
master:
url: jdbc:mysql://172.17.0.13:3306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://172.17.0.13:3306/setri_ems_new?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ems
password: Aa112211!
# 从库数据源
@ -188,7 +188,7 @@ xss:
mqtt:
broker.url: tcp://121.5.164.6:1883
client.id: ems-cloud
client.id: ems-cloud-new
username: dmbroker
password: qwer1234
connection-timeout: 15
@ -212,3 +212,17 @@ weather:
base-url: https://archive-api.open-meteo.com/v1/archive
api-key:
timezone: Asia/Shanghai
influxdb:
enabled: true
url: http://172.17.0.7:8086/
api-token: l_MUXGYFs15utEaLLLgGUUkHYVA84nweimAyeiHNRIg_FWy3ACcdA85LnxDIBKA8bKxbPp2isTkrqHzrhXtZYw==
write-method: POST
read-method: GET
write-path: /api/v2/write
query-path: /query
org: ems
bucket: point_data
database: ems_point_data
retention-policy: autogen
measurement: mqtt_point_data

View File

@ -94,7 +94,7 @@ spring:
druid:
# 主库数据源
master:
url: jdbc:mysql://122.51.194.184:13306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://122.51.194.184:13306/setri_ems_new?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ems
password: 12345678
# 从库数据源

View File

@ -70,6 +70,18 @@
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- InfluxDB 写入成功日志输出 -->
<appender name="sys-influxdb" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-influxdb.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/sys-influxdb.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.xzzn" level="info" />
@ -90,4 +102,9 @@
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
<!-- InfluxDB 写入成功专用日志 -->
<logger name="sys-influxdb" level="info" additivity="false">
<appender-ref ref="sys-influxdb"/>
</logger>
</configuration>

View File

@ -81,6 +81,9 @@ public class EmsPointConfig extends BaseEntity {
@Excel(name = "Modbus分组")
private String modbusGroup;
@Excel(name = "小数位数")
private Integer decimalScale;
public Long getId() {
return id;
}
@ -265,6 +268,14 @@ public class EmsPointConfig extends BaseEntity {
this.modbusGroup = modbusGroup;
}
public Integer getDecimalScale() {
return decimalScale;
}
public void setDecimalScale(Integer decimalScale) {
this.decimalScale = decimalScale;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
@ -291,6 +302,7 @@ public class EmsPointConfig extends BaseEntity {
.append("modbusDataType", getModbusDataType())
.append("modbusReadOrder", getModbusReadOrder())
.append("modbusGroup", getModbusGroup())
.append("decimalScale", getDecimalScale())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())

View File

@ -0,0 +1,68 @@
package com.xzzn.ems.domain;
import com.xzzn.common.core.domain.BaseEntity;
public class SysBizRemark extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long id;
private String bizType;
private String bizKey1;
private String bizKey2;
private String delFlag;
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getBizType()
{
return bizType;
}
public void setBizType(String bizType)
{
this.bizType = bizType;
}
public String getBizKey1()
{
return bizKey1;
}
public void setBizKey1(String bizKey1)
{
this.bizKey1 = bizKey1;
}
public String getBizKey2()
{
return bizKey2;
}
public void setBizKey2(String bizKey2)
{
this.bizKey2 = bizKey2;
}
public String getDelFlag()
{
return delFlag;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
}

View File

@ -53,6 +53,8 @@ public class AmmeterRevenueStatisListVo {
/** 实际收益 */
private BigDecimal actualRevenue = BigDecimal.ZERO;
private String remark;
public String getDataTime() {
return dataTime;
}
@ -172,4 +174,12 @@ public class AmmeterRevenueStatisListVo {
public void setActualRevenue(BigDecimal actualRevenue) {
this.actualRevenue = actualRevenue;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}

View File

@ -46,6 +46,8 @@ public class AmmeterStatisListVo {
/** 效率-有功总/无功总 */
private BigDecimal effect = BigDecimal.ZERO;
private String remark;
public String getDataTime() {
return dataTime;
}
@ -149,4 +151,12 @@ public class AmmeterStatisListVo {
public void setEffect(BigDecimal effect) {
this.effect = effect;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}

View File

@ -0,0 +1,42 @@
package com.xzzn.ems.domain.vo;
import java.util.List;
public class BizRemarkBatchGetRequest
{
private String bizType;
private String bizKey1;
private List<String> bizKey2List;
public String getBizType()
{
return bizType;
}
public void setBizType(String bizType)
{
this.bizType = bizType;
}
public String getBizKey1()
{
return bizKey1;
}
public void setBizKey1(String bizKey1)
{
this.bizKey1 = bizKey1;
}
public List<String> getBizKey2List()
{
return bizKey2List;
}
public void setBizKey2List(List<String> bizKey2List)
{
this.bizKey2List = bizKey2List;
}
}

View File

@ -0,0 +1,52 @@
package com.xzzn.ems.domain.vo;
public class BizRemarkSaveRequest
{
private String bizType;
private String bizKey1;
private String bizKey2;
private String remark;
public String getBizType()
{
return bizType;
}
public void setBizType(String bizType)
{
this.bizType = bizType;
}
public String getBizKey1()
{
return bizKey1;
}
public void setBizKey1(String bizKey1)
{
this.bizKey1 = bizKey1;
}
public String getBizKey2()
{
return bizKey2;
}
public void setBizKey2(String bizKey2)
{
this.bizKey2 = bizKey2;
}
public String getRemark()
{
return remark;
}
public void setRemark(String remark)
{
this.remark = remark;
}
}

View File

@ -0,0 +1,41 @@
package com.xzzn.ems.domain.vo;
public class SingleBatteryConfigInitRequest {
private String siteId;
private String scopeType;
private String scopeDeviceId;
private Integer targetCount;
public String getSiteId() {
return siteId;
}
public void setSiteId(String siteId) {
this.siteId = siteId;
}
public String getScopeType() {
return scopeType;
}
public void setScopeType(String scopeType) {
this.scopeType = scopeType;
}
public String getScopeDeviceId() {
return scopeDeviceId;
}
public void setScopeDeviceId(String scopeDeviceId) {
this.scopeDeviceId = scopeDeviceId;
}
public Integer getTargetCount() {
return targetCount;
}
public void setTargetCount(Integer targetCount) {
this.targetCount = targetCount;
}
}

View File

@ -0,0 +1,122 @@
package com.xzzn.ems.domain.vo;
public class SingleBatteryConfigInitResultVo {
private Boolean committed;
private String siteId;
private String scopeType;
private String scopeDeviceId;
private Integer targetCount;
private Integer existingBatteryCount;
private Integer initializedBatteryCount;
private Integer insertedBatteryCount;
private Integer pointHitCount;
private Integer fixedValueFallbackCount;
private Integer insertedMappingCount;
private Integer updatedMappingCount;
private String message;
public Boolean getCommitted() {
return committed;
}
public void setCommitted(Boolean committed) {
this.committed = committed;
}
public String getSiteId() {
return siteId;
}
public void setSiteId(String siteId) {
this.siteId = siteId;
}
public String getScopeType() {
return scopeType;
}
public void setScopeType(String scopeType) {
this.scopeType = scopeType;
}
public String getScopeDeviceId() {
return scopeDeviceId;
}
public void setScopeDeviceId(String scopeDeviceId) {
this.scopeDeviceId = scopeDeviceId;
}
public Integer getTargetCount() {
return targetCount;
}
public void setTargetCount(Integer targetCount) {
this.targetCount = targetCount;
}
public Integer getExistingBatteryCount() {
return existingBatteryCount;
}
public void setExistingBatteryCount(Integer existingBatteryCount) {
this.existingBatteryCount = existingBatteryCount;
}
public Integer getInitializedBatteryCount() {
return initializedBatteryCount;
}
public void setInitializedBatteryCount(Integer initializedBatteryCount) {
this.initializedBatteryCount = initializedBatteryCount;
}
public Integer getInsertedBatteryCount() {
return insertedBatteryCount;
}
public void setInsertedBatteryCount(Integer insertedBatteryCount) {
this.insertedBatteryCount = insertedBatteryCount;
}
public Integer getPointHitCount() {
return pointHitCount;
}
public void setPointHitCount(Integer pointHitCount) {
this.pointHitCount = pointHitCount;
}
public Integer getFixedValueFallbackCount() {
return fixedValueFallbackCount;
}
public void setFixedValueFallbackCount(Integer fixedValueFallbackCount) {
this.fixedValueFallbackCount = fixedValueFallbackCount;
}
public Integer getInsertedMappingCount() {
return insertedMappingCount;
}
public void setInsertedMappingCount(Integer insertedMappingCount) {
this.insertedMappingCount = insertedMappingCount;
}
public Integer getUpdatedMappingCount() {
return updatedMappingCount;
}
public void setUpdatedMappingCount(Integer updatedMappingCount) {
this.updatedMappingCount = updatedMappingCount;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -0,0 +1,59 @@
package com.xzzn.ems.domain.vo;
public class SingleBatteryMonitorImportFailureVo {
private Integer rowNum;
private String siteId;
private String stackDeviceId;
private String clusterDeviceId;
private String batteryDeviceId;
private String errorMessage;
public Integer getRowNum() {
return rowNum;
}
public void setRowNum(Integer rowNum) {
this.rowNum = rowNum;
}
public String getSiteId() {
return siteId;
}
public void setSiteId(String siteId) {
this.siteId = siteId;
}
public String getStackDeviceId() {
return stackDeviceId;
}
public void setStackDeviceId(String stackDeviceId) {
this.stackDeviceId = stackDeviceId;
}
public String getClusterDeviceId() {
return clusterDeviceId;
}
public void setClusterDeviceId(String clusterDeviceId) {
this.clusterDeviceId = clusterDeviceId;
}
public String getBatteryDeviceId() {
return batteryDeviceId;
}
public void setBatteryDeviceId(String batteryDeviceId) {
this.batteryDeviceId = batteryDeviceId;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}

View File

@ -0,0 +1,98 @@
package com.xzzn.ems.domain.vo;
import java.util.ArrayList;
import java.util.List;
public class SingleBatteryMonitorImportResultVo {
private Boolean committed;
private Integer totalRows;
private Integer successRows;
private Integer failureRows;
private Integer insertedBatteryCount;
private Integer updatedBatteryCount;
private Integer insertedMappingCount;
private Integer updatedMappingCount;
private String message;
private List<SingleBatteryMonitorImportFailureVo> failureDetails = new ArrayList<>();
public Boolean getCommitted() {
return committed;
}
public void setCommitted(Boolean committed) {
this.committed = committed;
}
public Integer getTotalRows() {
return totalRows;
}
public void setTotalRows(Integer totalRows) {
this.totalRows = totalRows;
}
public Integer getSuccessRows() {
return successRows;
}
public void setSuccessRows(Integer successRows) {
this.successRows = successRows;
}
public Integer getFailureRows() {
return failureRows;
}
public void setFailureRows(Integer failureRows) {
this.failureRows = failureRows;
}
public Integer getInsertedBatteryCount() {
return insertedBatteryCount;
}
public void setInsertedBatteryCount(Integer insertedBatteryCount) {
this.insertedBatteryCount = insertedBatteryCount;
}
public Integer getUpdatedBatteryCount() {
return updatedBatteryCount;
}
public void setUpdatedBatteryCount(Integer updatedBatteryCount) {
this.updatedBatteryCount = updatedBatteryCount;
}
public Integer getInsertedMappingCount() {
return insertedMappingCount;
}
public void setInsertedMappingCount(Integer insertedMappingCount) {
this.insertedMappingCount = insertedMappingCount;
}
public Integer getUpdatedMappingCount() {
return updatedMappingCount;
}
public void setUpdatedMappingCount(Integer updatedMappingCount) {
this.updatedMappingCount = updatedMappingCount;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public List<SingleBatteryMonitorImportFailureVo> getFailureDetails() {
return failureDetails;
}
public void setFailureDetails(List<SingleBatteryMonitorImportFailureVo> failureDetails) {
this.failureDetails = failureDetails;
}
}

View File

@ -0,0 +1,115 @@
package com.xzzn.ems.domain.vo;
import com.xzzn.common.annotation.Excel;
public class SingleBatteryMonitorImportRowVo {
private Integer rowNum;
@Excel(name = "站点ID")
private String siteId;
@Excel(name = "电池堆编号")
private String stackDeviceId;
@Excel(name = "电池簇编号")
private String clusterDeviceId;
@Excel(name = "单体编号")
private String batteryDeviceId;
@Excel(name = "单体名称")
private String batteryDeviceName;
@Excel(name = "电压点位ID")
private String voltagePointId;
@Excel(name = "温度点位ID")
private String temperaturePointId;
@Excel(name = "SOC点位ID")
private String socPointId;
@Excel(name = "SOH点位ID")
private String sohPointId;
public Integer getRowNum() {
return rowNum;
}
public void setRowNum(Integer rowNum) {
this.rowNum = rowNum;
}
public String getSiteId() {
return siteId;
}
public void setSiteId(String siteId) {
this.siteId = siteId;
}
public String getStackDeviceId() {
return stackDeviceId;
}
public void setStackDeviceId(String stackDeviceId) {
this.stackDeviceId = stackDeviceId;
}
public String getClusterDeviceId() {
return clusterDeviceId;
}
public void setClusterDeviceId(String clusterDeviceId) {
this.clusterDeviceId = clusterDeviceId;
}
public String getBatteryDeviceId() {
return batteryDeviceId;
}
public void setBatteryDeviceId(String batteryDeviceId) {
this.batteryDeviceId = batteryDeviceId;
}
public String getBatteryDeviceName() {
return batteryDeviceName;
}
public void setBatteryDeviceName(String batteryDeviceName) {
this.batteryDeviceName = batteryDeviceName;
}
public String getVoltagePointId() {
return voltagePointId;
}
public void setVoltagePointId(String voltagePointId) {
this.voltagePointId = voltagePointId;
}
public String getTemperaturePointId() {
return temperaturePointId;
}
public void setTemperaturePointId(String temperaturePointId) {
this.temperaturePointId = temperaturePointId;
}
public String getSocPointId() {
return socPointId;
}
public void setSocPointId(String socPointId) {
this.socPointId = socPointId;
}
public String getSohPointId() {
return sohPointId;
}
public void setSohPointId(String sohPointId) {
this.sohPointId = sohPointId;
}
}

View File

@ -73,6 +73,9 @@ public interface EmsDailyChargeDataMapper
public EmsDailyChargeData selectBySiteIdAndDateTime(@Param("siteId") String siteId,
@Param("dateTime") Date dateTime);
// 查询站点最新一条每日充放电数据
public EmsDailyChargeData selectLatestBySiteId(@Param("siteId") String siteId);
// 按站点+日期(天)更新收入字段
public int updateRevenueBySiteAndDate(@Param("siteId") String siteId,
@Param("dateTime") Date dateTime,

View File

@ -18,4 +18,6 @@ public interface EmsSiteMonitorPointMatchMapper {
int deleteBySiteIdAndDeviceId(@Param("siteId") String siteId, @Param("deviceId") String deviceId);
int insertBatch(@Param("list") List<EmsSiteMonitorPointMatch> list);
int updateEmsSiteMonitorPointMatch(EmsSiteMonitorPointMatch pointMatch);
}

View File

@ -0,0 +1,21 @@
package com.xzzn.ems.mapper;
import com.xzzn.ems.domain.SysBizRemark;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface SysBizRemarkMapper
{
SysBizRemark selectByBizKeys(@Param("bizType") String bizType,
@Param("bizKey1") String bizKey1,
@Param("bizKey2") String bizKey2);
List<SysBizRemark> selectByBizKey2List(@Param("bizType") String bizType,
@Param("bizKey1") String bizKey1,
@Param("bizKey2List") List<String> bizKey2List);
int insertSysBizRemark(SysBizRemark bizRemark);
int updateSysBizRemark(SysBizRemark bizRemark);
}

View File

@ -10,6 +10,10 @@ import com.xzzn.ems.domain.vo.SiteMonitorDataSaveRequest;
import com.xzzn.ems.domain.vo.SiteMonitorProjectDisplayVo;
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest;
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingVo;
import com.xzzn.ems.domain.vo.SingleBatteryConfigInitRequest;
import com.xzzn.ems.domain.vo.SingleBatteryConfigInitResultVo;
import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportResultVo;
import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportRowVo;
import com.xzzn.ems.domain.vo.WorkStatusEnumMappingVo;
import java.util.Date;
@ -56,4 +60,8 @@ public interface IEmsDeviceSettingService
public int saveSiteMonitorProjectData(SiteMonitorDataSaveRequest request, String operName);
public int syncSiteMonitorDataByMqtt(String siteId, String deviceId, String jsonData, Date valueTime);
public SingleBatteryMonitorImportResultVo importSingleBatteryMonitorMappings(String siteId, List<SingleBatteryMonitorImportRowVo> rows, String operName);
public SingleBatteryConfigInitResultVo initializeSingleBatteryMonitorMappings(SingleBatteryConfigInitRequest request, String operName);
}

View File

@ -15,6 +15,8 @@ public interface ISingleSiteService
public SiteMonitorHomeVo getSiteMonitorTotalDataVo(String siteId);
public SiteMonitorHomeVo getSiteMonitorHomeRunningData(String siteId);
public SiteMonitorRunningHeadInfoVo getSiteRunningHeadInfo(String siteId);

View File

@ -0,0 +1,13 @@
package com.xzzn.ems.service;
import com.xzzn.ems.domain.vo.BizRemarkSaveRequest;
import java.util.List;
import java.util.Map;
public interface ISysBizRemarkService
{
Map<String, String> getRemarkMap(String bizType, String bizKey1, List<String> bizKey2List);
int saveRemark(BizRemarkSaveRequest request, String username);
}

View File

@ -28,6 +28,7 @@ import java.util.Map;
public class InfluxPointDataWriter {
private static final Logger log = LoggerFactory.getLogger(InfluxPointDataWriter.class);
private static final Logger influxWriteSuccessLog = LoggerFactory.getLogger("sys-influxdb");
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Value("${influxdb.enabled:true}")
@ -91,6 +92,7 @@ public class InfluxPointDataWriter {
}
try {
StringBuilder body = new StringBuilder();
List<String> pointSummaries = new ArrayList<>();
for (PointWritePayload payload : payloads) {
if (payload == null || payload.getPointValue() == null) {
continue;
@ -103,6 +105,12 @@ public class InfluxPointDataWriter {
.append(" value=").append(payload.getPointValue().toPlainString())
.append(" ").append(time)
.append("\n");
pointSummaries.add(String.format("siteId=%s, deviceId=%s, pointKey=%s, value=%s, time=%s",
safe(payload.getSiteId()),
safe(payload.getDeviceId()),
safe(payload.getPointKey()),
payload.getPointValue(),
new Date(time)));
}
if (body.length() == 0) {
return;
@ -112,6 +120,8 @@ public class InfluxPointDataWriter {
log.warn("写入 InfluxDB 失败v2 写入地址未构建成功,请检查 influxdb.org / influxdb.bucket 配置");
return;
}
long startMillis = System.currentTimeMillis();
int writeCount = countWriteLines(body);
HttpResult result = executeRequest(methodOrDefault(writeMethod, "POST"), writeUrl, body.toString());
if (result.code < 200 || result.code >= 300) {
if (result.code == 404 && isV2WritePath() && isOrgOrBucketMissing(result.body)) {
@ -119,6 +129,7 @@ public class InfluxPointDataWriter {
HttpResult retryResult = executeRequest(methodOrDefault(writeMethod, "POST"), writeUrl, body.toString());
if (retryResult.code >= 200 && retryResult.code < 300) {
log.info("InfluxDB org/bucket 自动创建成功,写入已恢复");
logWriteSuccess(writeCount, writeUrl, startMillis, pointSummaries);
return;
}
log.warn("InfluxDB 重试写入失败HTTP状态码: {}, url: {}, body: {}", retryResult.code, writeUrl, safeLog(retryResult.body));
@ -126,7 +137,9 @@ public class InfluxPointDataWriter {
}
}
log.warn("写入 InfluxDB 失败HTTP状态码: {}, url: {}, body: {}", result.code, writeUrl, safeLog(result.body));
return;
}
logWriteSuccess(writeCount, writeUrl, startMillis, pointSummaries);
} catch (Exception e) {
log.warn("写入 InfluxDB 失败: {}", e.getMessage());
}
@ -173,7 +186,8 @@ public class InfluxPointDataWriter {
startTime.getTime(),
endTime.getTime()
);
values = parseInfluxQlResponse(executeRequestWithResponse(methodOrDefault(readMethod, "GET"), buildQueryUrl(regexQuery)));
String regexQueryUrl = buildQueryUrl(regexQuery);
values = parseInfluxQlResponse(executeRequestWithResponse(methodOrDefault(readMethod, "GET"), regexQueryUrl));
return values;
} catch (Exception e) {
log.warn("查询 InfluxDB 曲线失败: {}", e.getMessage());
@ -204,7 +218,8 @@ public class InfluxPointDataWriter {
try {
String queryUrl = buildQueryUrl(influxQl);
return parseInfluxQlResponse(executeRequestWithResponse(methodOrDefault(readMethod, "GET"), queryUrl));
List<PointValue> values = parseInfluxQlResponse(executeRequestWithResponse(methodOrDefault(readMethod, "GET"), queryUrl));
return values;
} catch (Exception e) {
log.warn("按 pointKey 查询 InfluxDB 曲线失败: {}", e.getMessage());
return Collections.emptyList();
@ -234,24 +249,9 @@ public class InfluxPointDataWriter {
);
try {
String queryUrl = buildQueryUrl(influxQl);
List<PointValue> values = parseInfluxQlResponse(
executeRequestWithResponse(methodOrDefault(readMethod, "GET"), buildQueryUrl(influxQl))
);
if (!values.isEmpty()) {
return values.get(0);
}
String regexQuery = String.format(
"SELECT \"value\" FROM \"%s\" WHERE \"site_id\" = '%s' AND \"point_key\" =~ /(?i)^%s$/ " +
"AND time >= %dms AND time <= %dms ORDER BY time DESC LIMIT 1",
measurement,
escapeTagValue(normalizedSiteId),
escapeRegex(normalizedPointKey),
startTime.getTime(),
endTime.getTime()
);
values = parseInfluxQlResponse(
executeRequestWithResponse(methodOrDefault(readMethod, "GET"), buildQueryUrl(regexQuery))
executeRequestWithResponse(methodOrDefault(readMethod, "GET"), queryUrl)
);
return values.isEmpty() ? null : values.get(0);
} catch (Exception e) {
@ -260,6 +260,21 @@ public class InfluxPointDataWriter {
}
}
private void logWriteSuccess(int writeCount, String writeUrl, long startMillis, List<String> pointSummaries) {
influxWriteSuccessLog.info("InfluxDB写入成功, count: {}, points: {}, url: {}, costMs: {}",
writeCount, pointSummaries, writeUrl, System.currentTimeMillis() - startMillis);
}
private int countWriteLines(StringBuilder body) {
int count = 0;
for (int i = 0; i < body.length(); i++) {
if (body.charAt(i) == '\n') {
count++;
}
}
return count;
}
private String buildWriteUrl() {
if (isV2WritePath()) {
return buildV2WriteUrl();

View File

@ -840,6 +840,12 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
Date queryEndTime = DateUtils.getNowDate();
Date queryStartTime = new Date(queryEndTime.getTime() - CALC_POINT_INFLUX_QUERY_WINDOW_MS);
Map<String, BigDecimal> contextValues = new HashMap<>();
Set<String> reservedCalcContextKeys = calcPointConfigs.stream()
.map(this::resolveCalcContextKey)
.filter(StringUtils::isNotBlank)
.map(String::trim)
.map(String::toUpperCase)
.collect(Collectors.toSet());
Date latestDataTime = null;
for (EmsPointConfig sourcePointConfig : sourcePointConfigs) {
if (sourcePointConfig == null) {
@ -855,7 +861,7 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
if (latestPointValue == null || latestPointValue.getPointValue() == null) {
continue;
}
putSourcePointValueToContext(sourcePointConfig, latestPointValue.getPointValue(), contextValues);
putSourcePointValueToContext(sourcePointConfig, latestPointValue.getPointValue(), contextValues, reservedCalcContextKeys);
if (latestDataTime == null
|| (latestPointValue.getDataTime() != null && latestPointValue.getDataTime().after(latestDataTime))) {
latestDataTime = latestPointValue.getDataTime();
@ -1578,6 +1584,9 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
String pointId = resolveInfluxPointKey(calcPointConfig);
if (StringUtils.isNotBlank(pointId)) {
BigDecimal storedValue = calcValue.asNumber();
if (calcPointConfig.getDecimalScale() != null && calcPointConfig.getDecimalScale() >= 0) {
storedValue = storedValue.setScale(calcPointConfig.getDecimalScale(), RoundingMode.HALF_UP);
}
enqueuePointData(siteId, deviceId, pointId, storedValue, dataUpdateTime);
calcPointIdValueMap.put(pointId, storedValue);
}
@ -1949,7 +1958,10 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
return new ArrayList<>(uniqueByPointId.values());
}
private void putSourcePointValueToContext(EmsPointConfig pointConfig, BigDecimal pointValue, Map<String, BigDecimal> contextValues) {
private void putSourcePointValueToContext(EmsPointConfig pointConfig,
BigDecimal pointValue,
Map<String, BigDecimal> contextValues,
Set<String> reservedCalcContextKeys) {
if (pointConfig == null || pointValue == null || contextValues == null) {
return;
}
@ -1959,7 +1971,13 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
}
String normalizedDataKey = normalizeVariablePart(pointConfig.getDataKey());
if (StringUtils.isNotBlank(normalizedDataKey)) {
contextValues.put(normalizedDataKey, pointValue);
String reservedKey = normalizedDataKey.trim().toUpperCase();
if (CollectionUtils.isEmpty(reservedCalcContextKeys) || !reservedCalcContextKeys.contains(reservedKey)) {
contextValues.put(normalizedDataKey, pointValue);
} else {
log.debug("计算点上下文跳过源点位 dataKey 裸变量避免覆盖计算点siteId/deviceId/pointId={}/{}/{}, dataKey={}",
pointConfig.getSiteId(), pointConfig.getDeviceId(), pointConfig.getPointId(), pointConfig.getDataKey());
}
String devicePrefix = normalizeVariablePart(pointConfig.getDeviceId());
if (StringUtils.isNotBlank(devicePrefix)) {
contextValues.put(devicePrefix + "_" + normalizedDataKey, pointValue);
@ -2798,15 +2816,16 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
private JSONArray parseJsonData(String message) {
try {
JSONObject object = JSONObject.parseObject(message);
return object.getJSONArray("payload");
} catch (JSONException e) {
log.info("mqtt message is not a json object: " + e.getMessage());
try {
return JSONArray.parseArray(message);
} catch (Exception arrayException) {
log.info("mqtt message is not a json array: " + e.getMessage());
Object parsed = JSON.parse(message);
if (parsed instanceof JSONArray) {
return (JSONArray) parsed;
}
if (parsed instanceof JSONObject) {
return ((JSONObject) parsed).getJSONArray("payload");
}
log.info("mqtt message root type is unsupported: {}", parsed == null ? "null" : parsed.getClass().getName());
} catch (JSONException e) {
log.info("mqtt message parse failed: {}", e.getMessage());
}
return null;
}
@ -3936,15 +3955,22 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
log.info("deviceId:" + deviceId);
if (checkJsonDataEmpty(jsonData)) {
log.warn("设备告警Data为空跳过告警处理siteId: {}deviceId: {}", siteId, deviceId);
continue;
}
// 存放mqtt原始每个设备最晚一次数据便于后面点位获取数据
redisCache.setCacheObject(RedisKeyConstants.ORIGINAL_MQTT_DATA_ALARM + siteId + "_" + deviceId, obj);
// 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据
redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA_ALARM + siteId + "_" + deviceId, obj, 1, TimeUnit.MINUTES);
// 处理设备数据,根据不同设备类型进行不同的数据处理
String deviceCategory = processingDeviceAlarmData(siteId, deviceId, jsonData, dataUpdateTime);
if (StringUtils.isEmpty(deviceCategory)) {
if (StringUtils.isNotEmpty(deviceCategory)) {
// 处理告警信息
alarmDataProcess(siteId, deviceId, jsonData, alarmMatchInfo, deviceCategory);
} else {
log.warn("设备告警数据未识别到设备类型跳过告警处理siteId: {}deviceId: {}", siteId, deviceId);
}
}
}
@ -4079,8 +4105,21 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
private void alarmDataProcess(String siteId, String deviceId, String jsonData,
Map<String, EmsAlarmMatchData> alarmInfoData, String category) {
if (StringUtils.isEmpty(category)) {
log.warn("设备类型为空跳过告警处理siteId: {}deviceId: {}", siteId, deviceId);
return;
}
if (alarmInfoData == null || alarmInfoData.isEmpty()) {
log.warn("告警匹配缓存为空跳过告警处理siteId: {}deviceId: {}category: {}", siteId, deviceId, category);
return;
}
Map<String, Object> obj = JSON.parseObject(jsonData, new TypeReference<Map<String, Object>>() {
});
if (obj == null || obj.isEmpty()) {
log.warn("设备告警Data解析结果为空跳过告警处理siteId: {}deviceId: {}category: {}data: {}",
siteId, deviceId, category, jsonData);
return;
}
String redisKey = RedisKeyConstants.LATEST_ALARM_RECORD + "_" + siteId + "_" + deviceId;
// 获取redis里面的当前有效告警遍历添加到已存在告警key里面
@ -4096,12 +4135,8 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
// 结合同步数据,筛选簇需要更新的告警信息
List<String> needUpdateKeys = obj.entrySet().stream()
.filter(entry -> {
Object valueObj = entry.getValue();
if (valueObj == null) {
return false;
}
int value = Integer.parseInt(valueObj.toString());
return value == 0 && currentAlarmKeys.contains(entry.getKey());
Long value = convertToLong(entry.getValue());
return value != null && value == 0L && currentAlarmKeys.contains(entry.getKey());
})
.map(Map.Entry::getKey)
.collect(Collectors.toList());
@ -4111,11 +4146,13 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
if (!needUpdateKeys.isEmpty()) {
List<EmsAlarmRecords> records = iEmsAlarmRecordsService.getAllUnfinishedRecords(needUpdateKeys, siteId, deviceId);
// 转为Map便于快速获取
needUpdateMap = records.stream()
.collect(Collectors.toMap(
EmsAlarmRecords::getAlarmPoint,
record -> record
));
if (CollectionUtils.isNotEmpty(records)) {
needUpdateMap = records.stream()
.collect(Collectors.toMap(
EmsAlarmRecords::getAlarmPoint,
record -> record
));
}
}
@ -4129,20 +4166,28 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
continue;
}
String key = entry.getKey();
Long value = (Long) entry.getValue();
Long value = convertToLong(entry.getValue());
if (value == null) {
log.warn("告警值不是有效数字忽略处理siteId: {}deviceId: {}point: {}value: {}",
siteId, deviceId, key, entry.getValue());
continue;
}
Boolean isCurrentAlarm = currentAlarmKeys.contains(key);
String matchRedisKey = siteId + "_" + category + "_" + key;
// 默认告警值0是正常1是异常
Long alarmData = 1L;
Object cacheObj = alarmInfoData.get(matchRedisKey);
if (cacheObj != null) {
EmsAlarmMatchData matchInfo = JSON.toJavaObject(cacheObj, EmsAlarmMatchData.class);
EmsAlarmMatchData matchInfo = alarmInfoData.get(matchRedisKey);
if (matchInfo != null) {
alarmData = matchInfo.getAlarmData();
}
// 处理告警
if (value.equals(alarmData) && !isCurrentAlarm) {
// 上送值和匹配值相同,新增告警
EmsAlarmMatchData matchInfo = JSON.toJavaObject(cacheObj, EmsAlarmMatchData.class);
if (matchInfo == null) {
log.warn("未找到告警匹配配置忽略新增告警siteId: {}deviceId: {}category: {}point: {}",
siteId, deviceId, category, key);
continue;
}
EmsAlarmRecords emsAlarmRecord = convertAlarmRecord(siteId, deviceId, matchInfo);
saveOrUpdateList.add(emsAlarmRecord);
newAddRecordList.add(emsAlarmRecord);
@ -4201,6 +4246,24 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
}
}
private Long convertToLong(Object value) {
if (value == null) {
return null;
}
if (value instanceof Number) {
return ((Number) value).longValue();
}
String text = String.valueOf(value);
if (StringUtils.isBlank(text)) {
return null;
}
try {
return new BigDecimal(text.trim()).longValue();
} catch (NumberFormatException ex) {
return null;
}
}
private EmsAlarmRecords convertAlarmRecord(String siteId, String deviceId, EmsAlarmMatchData matchInfo) {
EmsAlarmRecords emsAlarmRecords = new EmsAlarmRecords();
emsAlarmRecords.setSiteId(siteId);

View File

@ -11,7 +11,9 @@ import com.xzzn.common.core.modbus.domain.WriteTagConfig;
import com.xzzn.common.core.redis.RedisCache;
import com.xzzn.common.enums.BusinessStatus;
import com.xzzn.common.enums.BusinessType;
import com.xzzn.common.enums.CommunicationStatus;
import com.xzzn.common.enums.DeviceCategory;
import com.xzzn.common.enums.DeviceRunningStatus;
import com.xzzn.common.enums.DeviceType;
import com.xzzn.common.enums.OperatorType;
import com.xzzn.common.enums.PointType;
@ -37,6 +39,11 @@ import com.xzzn.ems.domain.vo.SiteMonitorDataVo;
import com.xzzn.ems.domain.vo.SiteMonitorProjectDisplayVo;
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest;
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingVo;
import com.xzzn.ems.domain.vo.SingleBatteryConfigInitRequest;
import com.xzzn.ems.domain.vo.SingleBatteryConfigInitResultVo;
import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportFailureVo;
import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportResultVo;
import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportRowVo;
import com.xzzn.ems.domain.vo.WorkStatusEnumMappingVo;
import com.xzzn.ems.mapper.EmsBatteryDataMinutesMapper;
import com.xzzn.ems.mapper.EmsDailyChargeDataMapper;
@ -64,6 +71,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -113,8 +121,19 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService
private static final String MATCH_FIELD_PCS_COMMUNICATION_STATUS = "pcsCommunicationStatus";
private static final String MATCH_FIELD_EMS_COMMUNICATION_STATUS = "emsCommunicationStatus";
private static final String SITE_LEVEL_CALC_DEVICE_ID = "SITE_CALC";
private static final String BATTERY_FIELD_VOLTAGE = "SBJK_DTDC__voltage";
private static final String BATTERY_FIELD_TEMPERATURE = "SBJK_DTDC__temperature";
private static final String BATTERY_FIELD_SOC = "SBJK_DTDC__soc";
private static final String BATTERY_FIELD_SOH = "SBJK_DTDC__soh";
private static final String SINGLE_BATTERY_SCOPE_CLUSTER = "cluster";
private static final String SINGLE_BATTERY_SCOPE_STACK = "stack";
private static final String SINGLE_BATTERY_FIELD_VALUE_ZERO = "0";
private static final Set<String> SINGLE_BATTERY_IMPORT_FIELD_CODES = new HashSet<>(Arrays.asList(
BATTERY_FIELD_VOLTAGE, BATTERY_FIELD_TEMPERATURE, BATTERY_FIELD_SOC, BATTERY_FIELD_SOH
));
private static final Set<String> DEVICE_DIMENSION_MENU_CODES = new HashSet<>(Arrays.asList(
"SBJK_EMS", "SBJK_PCS", "SBJK_BMSZL", "SBJK_BMSDCC", "SBJK_DTDC", "SBJK_DB", "SBJK_YL", "SBJK_DH", "SBJK_XF"
"SBJK_EMS", "SBJK_PCS", "SBJK_BMSZL", "SBJK_BMSDCC", "SBJK_DTDC", "SBJK_DB", "SBJK_YL", "SBJK_DH", "SBJK_XF",
"TJBB_PCSQX", "TJBB_DCDQX", "TJBB_DCWD"
));
private static final Set<String> DEVICE_DIMENSION_FIELD_CODES = new HashSet<>(Arrays.asList(
FIELD_CURVE_PCS_ACTIVE_POWER,
@ -200,6 +219,9 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService
map.put("SBJK_YL", DeviceCategory.COOLING.getCode());
map.put("SBJK_DH", DeviceCategory.DH.getCode());
map.put("SBJK_XF", DeviceCategory.XF.getCode());
map.put("TJBB_PCSQX", DeviceCategory.PCS.getCode());
map.put("TJBB_DCDQX", DeviceCategory.STACK.getCode());
map.put("TJBB_DCWD", DeviceCategory.CLUSTER.getCode());
return map;
}
@ -1065,6 +1087,766 @@ public class EmsDeviceSettingServiceImpl implements IEmsDeviceSettingService
}
}
private static class SingleBatteryInitScope {
private final String normalizedSiteId;
private final String scopeType;
private final String scopeDeviceId;
private final List<EmsDevicesSetting> scopeClusters;
private final String appendClusterId;
private final List<EmsDevicesSetting> allSiteDevices;
private SingleBatteryInitScope(String normalizedSiteId,
String scopeType,
String scopeDeviceId,
List<EmsDevicesSetting> scopeClusters,
String appendClusterId,
List<EmsDevicesSetting> allSiteDevices) {
this.normalizedSiteId = normalizedSiteId;
this.scopeType = scopeType;
this.scopeDeviceId = scopeDeviceId;
this.scopeClusters = scopeClusters;
this.appendClusterId = appendClusterId;
this.allSiteDevices = allSiteDevices;
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public SingleBatteryConfigInitResultVo initializeSingleBatteryMonitorMappings(SingleBatteryConfigInitRequest request, String operName) {
SingleBatteryInitScope scope = resolveSingleBatteryInitScope(request);
SingleBatteryConfigInitResultVo result = new SingleBatteryConfigInitResultVo();
result.setCommitted(false);
result.setSiteId(scope.normalizedSiteId);
result.setScopeType(scope.scopeType);
result.setScopeDeviceId(scope.scopeDeviceId);
result.setTargetCount(request.getTargetCount());
result.setExistingBatteryCount(0);
result.setInitializedBatteryCount(0);
result.setInsertedBatteryCount(0);
result.setPointHitCount(0);
result.setFixedValueFallbackCount(0);
result.setInsertedMappingCount(0);
result.setUpdatedMappingCount(0);
Map<String, EmsDevicesSetting> deviceMap = scope.allSiteDevices.stream()
.filter(Objects::nonNull)
.filter(item -> StringUtils.isNotBlank(item.getDeviceId()))
.collect(Collectors.toMap(
item -> StringUtils.trim(item.getDeviceId()),
item -> item,
(left, right) -> right,
LinkedHashMap::new
));
Set<String> scopeClusterIdSet = scope.scopeClusters.stream()
.map(EmsDevicesSetting::getDeviceId)
.filter(StringUtils::isNotBlank)
.map(String::trim)
.collect(Collectors.toCollection(LinkedHashSet::new));
List<EmsDevicesSetting> scopeBatteryList = getScopeBatteryList(scope.allSiteDevices, scopeClusterIdSet);
result.setExistingBatteryCount(scopeBatteryList.size());
Set<String> affectedBatteryIds = scopeBatteryList.stream()
.map(EmsDevicesSetting::getDeviceId)
.filter(StringUtils::isNotBlank)
.map(String::trim)
.collect(Collectors.toSet());
if (SINGLE_BATTERY_SCOPE_CLUSTER.equals(scope.scopeType) && !scopeBatteryList.isEmpty()) {
deleteScopeSingleBatteries(scope.normalizedSiteId, scopeBatteryList, deviceMap);
scopeBatteryList = new ArrayList<>();
}
int targetCount = request.getTargetCount();
if (targetCount > scopeBatteryList.size()) {
int appendCount = targetCount - scopeBatteryList.size();
List<EmsDevicesSetting> newBatteryList = createMissingSingleBatteries(
scope.normalizedSiteId,
scope.appendClusterId,
appendCount,
deviceMap,
operName
);
scopeBatteryList.addAll(newBatteryList);
result.setInsertedBatteryCount(newBatteryList.size());
}
Map<String, List<EmsDevicesSetting>> batteryByCluster = scopeBatteryList.stream()
.filter(Objects::nonNull)
.filter(item -> StringUtils.isNotBlank(item.getParentId()))
.collect(Collectors.groupingBy(
item -> StringUtils.trim(item.getParentId()),
LinkedHashMap::new,
Collectors.toList()
));
Set<String> pointIds = new HashSet<>();
List<SingleBatteryFieldInitTask> taskList = new ArrayList<>();
for (EmsDevicesSetting cluster : scope.scopeClusters) {
if (cluster == null || StringUtils.isBlank(cluster.getDeviceId())) {
continue;
}
String clusterId = StringUtils.trim(cluster.getDeviceId());
List<EmsDevicesSetting> clusterBatteryList = batteryByCluster.getOrDefault(clusterId, Collections.emptyList())
.stream()
.sorted(Comparator.comparing(item -> StringUtils.defaultString(item.getDeviceId())))
.collect(Collectors.toList());
for (int i = 0; i < clusterBatteryList.size(); i++) {
EmsDevicesSetting battery = clusterBatteryList.get(i);
String batteryId = StringUtils.trim(battery.getDeviceId());
String serialNo = formatSingleBatterySerial(i + 1);
taskList.add(buildSingleBatteryFieldInitTask(scope.normalizedSiteId, clusterId, batteryId, BATTERY_FIELD_VOLTAGE, "DY", serialNo));
taskList.add(buildSingleBatteryFieldInitTask(scope.normalizedSiteId, clusterId, batteryId, BATTERY_FIELD_TEMPERATURE, "WD", serialNo));
taskList.add(buildSingleBatteryFieldInitTask(scope.normalizedSiteId, clusterId, batteryId, BATTERY_FIELD_SOC, "SOC", serialNo));
taskList.add(buildSingleBatteryFieldInitTask(scope.normalizedSiteId, clusterId, batteryId, BATTERY_FIELD_SOH, "SOH", serialNo));
pointIds.add(clusterId + "DTDY" + serialNo);
pointIds.add(clusterId + "DTWD" + serialNo);
pointIds.add(clusterId + "DTSOC" + serialNo);
pointIds.add(clusterId + "DTSOH" + serialNo);
}
}
Map<String, EmsPointConfig> pointConfigMap = pointIds.isEmpty()
? Collections.emptyMap()
: emsPointConfigMapper.selectBySiteIdAndPointIds(scope.normalizedSiteId, new ArrayList<>(pointIds))
.stream()
.filter(Objects::nonNull)
.filter(item -> StringUtils.isNotBlank(item.getPointId()))
.collect(Collectors.toMap(
item -> StringUtils.trim(item.getPointId()),
item -> item,
(left, right) -> right,
HashMap::new
));
Map<String, EmsSiteMonitorPointMatch> existingPointMatchMap = emsSiteMonitorPointMatchMapper.selectBySiteId(scope.normalizedSiteId)
.stream()
.filter(Objects::nonNull)
.filter(item -> SINGLE_BATTERY_IMPORT_FIELD_CODES.contains(StringUtils.trim(item.getFieldCode())))
.filter(item -> StringUtils.isNotBlank(item.getDeviceId()))
.collect(Collectors.toMap(
item -> buildMatchKey(StringUtils.trim(item.getFieldCode()), StringUtils.trim(item.getDeviceId())),
item -> item,
(left, right) -> right,
LinkedHashMap::new
));
List<EmsSiteMonitorPointMatch> insertList = new ArrayList<>();
affectedBatteryIds.addAll(scopeBatteryList.stream()
.map(EmsDevicesSetting::getDeviceId)
.filter(StringUtils::isNotBlank)
.map(String::trim)
.collect(Collectors.toSet()));
for (SingleBatteryFieldInitTask task : taskList) {
boolean pointExists = pointConfigMap.containsKey(task.pointId);
if (pointExists) {
result.setPointHitCount(result.getPointHitCount() + 1);
} else {
result.setFixedValueFallbackCount(result.getFixedValueFallbackCount() + 1);
}
upsertSingleBatteryPointMatchForInit(insertList, existingPointMatchMap, result, task, pointExists, operName);
}
if (!insertList.isEmpty()) {
emsSiteMonitorPointMatchMapper.insertBatch(insertList);
}
refreshSingleBatteryImportCache(scope.normalizedSiteId, affectedBatteryIds);
result.setInitializedBatteryCount(scopeBatteryList.size());
result.setCommitted(true);
result.setMessage(String.format(
"初始化完成:已初始化单体 %d 个,新增单体 %d 个,命中点位 %d 条,固定值回填 %d 条",
result.getInitializedBatteryCount(),
result.getInsertedBatteryCount(),
result.getPointHitCount(),
result.getFixedValueFallbackCount()
));
return result;
}
private SingleBatteryInitScope resolveSingleBatteryInitScope(SingleBatteryConfigInitRequest request) {
if (request == null) {
throw new ServiceException("鍒濆鍖栬姹備笉鑳戒负绌?");
}
String normalizedSiteId = StringUtils.trim(request.getSiteId());
String normalizedScopeType = StringUtils.trimToEmpty(request.getScopeType()).toLowerCase();
String normalizedScopeDeviceId = StringUtils.trim(request.getScopeDeviceId());
Integer targetCount = request.getTargetCount();
if (StringUtils.isBlank(normalizedSiteId)) {
throw new ServiceException("绔欑偣ID涓嶈兘涓虹┖");
}
if (StringUtils.isBlank(normalizedScopeType) || StringUtils.isBlank(normalizedScopeDeviceId)) {
throw new ServiceException("鍒濆鍖栬寖鍥村拰璁惧ID涓嶈兘涓虹┖");
}
if (!SINGLE_BATTERY_SCOPE_CLUSTER.equals(normalizedScopeType) && !SINGLE_BATTERY_SCOPE_STACK.equals(normalizedScopeType)) {
throw new ServiceException("鍒濆鍖栬寖鍥诲彧鏀寔 cluster 鎴?stack");
}
if (targetCount == null || targetCount < 1) {
throw new ServiceException("鍗曚綋鏁伴噺蹇呴』澶т簬 0");
}
List<EmsDevicesSetting> allSiteDevices = emsDevicesMapper.selectEmsDevicesSettingList(buildSiteDeviceQuery(normalizedSiteId));
Map<String, EmsDevicesSetting> deviceMap = allSiteDevices.stream()
.filter(Objects::nonNull)
.filter(item -> StringUtils.isNotBlank(item.getDeviceId()))
.collect(Collectors.toMap(
item -> StringUtils.trim(item.getDeviceId()),
item -> item,
(left, right) -> right,
LinkedHashMap::new
));
if (SINGLE_BATTERY_SCOPE_CLUSTER.equals(normalizedScopeType)) {
EmsDevicesSetting cluster = deviceMap.get(normalizedScopeDeviceId);
if (cluster == null || !StringUtils.equals(DeviceCategory.CLUSTER.getCode(), StringUtils.trim(cluster.getDeviceCategory()))) {
throw new ServiceException(String.format("电池簇编号[%s]不存在", normalizedScopeDeviceId));
}
return new SingleBatteryInitScope(
normalizedSiteId,
normalizedScopeType,
normalizedScopeDeviceId,
Collections.singletonList(cluster),
normalizedScopeDeviceId,
allSiteDevices
);
}
EmsDevicesSetting stack = deviceMap.get(normalizedScopeDeviceId);
if (stack == null || !StringUtils.equals(DeviceCategory.STACK.getCode(), StringUtils.trim(stack.getDeviceCategory()))) {
throw new ServiceException(String.format("电池堆编号[%s]不存在", normalizedScopeDeviceId));
}
List<EmsDevicesSetting> scopeClusters = allSiteDevices.stream()
.filter(Objects::nonNull)
.filter(item -> StringUtils.equals(DeviceCategory.CLUSTER.getCode(), StringUtils.trim(item.getDeviceCategory())))
.filter(item -> StringUtils.equals(normalizedScopeDeviceId, StringUtils.trim(item.getParentId())))
.sorted(Comparator.comparing(item -> StringUtils.defaultString(item.getDeviceId())))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(scopeClusters)) {
throw new ServiceException(String.format("电池堆[%s]下未配置电池簇,无法初始化单体电池", normalizedScopeDeviceId));
}
return new SingleBatteryInitScope(
normalizedSiteId,
normalizedScopeType,
normalizedScopeDeviceId,
scopeClusters,
StringUtils.trim(scopeClusters.get(0).getDeviceId()),
allSiteDevices
);
}
private List<EmsDevicesSetting> getScopeBatteryList(List<EmsDevicesSetting> allSiteDevices, Set<String> scopeClusterIdSet) {
if (CollectionUtils.isEmpty(allSiteDevices) || CollectionUtils.isEmpty(scopeClusterIdSet)) {
return new ArrayList<>();
}
return allSiteDevices.stream()
.filter(Objects::nonNull)
.filter(item -> StringUtils.equals(DeviceCategory.BATTERY.getCode(), StringUtils.trim(item.getDeviceCategory())))
.filter(item -> scopeClusterIdSet.contains(StringUtils.trim(item.getParentId())))
.collect(Collectors.toCollection(ArrayList::new));
}
private void deleteScopeSingleBatteries(String siteId,
List<EmsDevicesSetting> batteryList,
Map<String, EmsDevicesSetting> deviceMap) {
if (CollectionUtils.isEmpty(batteryList)) {
return;
}
Long[] ids = batteryList.stream()
.map(EmsDevicesSetting::getId)
.filter(Objects::nonNull)
.toArray(Long[]::new);
for (EmsDevicesSetting battery : batteryList) {
if (battery == null || StringUtils.isBlank(battery.getDeviceId())) {
continue;
}
String batteryId = StringUtils.trim(battery.getDeviceId());
emsSiteMonitorPointMatchMapper.deleteBySiteIdAndDeviceId(siteId, batteryId);
if (deviceMap != null) {
deviceMap.remove(batteryId);
}
}
if (ids.length > 0) {
emsDevicesMapper.deleteEmsDevicesSettingByIds(ids);
}
}
private List<EmsDevicesSetting> createMissingSingleBatteries(String siteId,
String clusterDeviceId,
int appendCount,
Map<String, EmsDevicesSetting> deviceMap,
String operName) {
List<EmsDevicesSetting> result = new ArrayList<>();
if (appendCount < 1) {
return result;
}
Set<String> existingIds = deviceMap.keySet().stream()
.filter(StringUtils::isNotBlank)
.map(String::trim)
.collect(Collectors.toSet());
Set<String> clusterBatteryIds = deviceMap.values().stream()
.filter(Objects::nonNull)
.filter(item -> StringUtils.equals(DeviceCategory.BATTERY.getCode(), StringUtils.trim(item.getDeviceCategory())))
.filter(item -> StringUtils.equals(clusterDeviceId, StringUtils.trim(item.getParentId())))
.map(EmsDevicesSetting::getDeviceId)
.filter(StringUtils::isNotBlank)
.map(String::trim)
.collect(Collectors.toCollection(HashSet::new));
Date now = DateUtils.getNowDate();
int serial = 1;
while (result.size() < appendCount) {
String candidateId = formatSingleBatterySerial(serial);
serial++;
if (existingIds.contains(candidateId) || clusterBatteryIds.contains(candidateId)) {
continue;
}
EmsDevicesSetting insertDevice = new EmsDevicesSetting();
insertDevice.setSiteId(siteId);
insertDevice.setDeviceId(candidateId);
insertDevice.setDeviceName(candidateId);
insertDevice.setDeviceType(DeviceType.TCP.name());
insertDevice.setCommunicationStatus(CommunicationStatus.OK.getCode());
insertDevice.setDeviceCategory(DeviceCategory.BATTERY.getCode());
insertDevice.setDeviceStatus(DeviceRunningStatus.OFFLINE.getCode());
insertDevice.setWorkStatus(WorkStatus.NORMAL.getCode());
insertDevice.setParentId(clusterDeviceId);
insertDevice.setDescription("single battery initialize");
insertDevice.setCreateBy(operName);
insertDevice.setUpdateBy(operName);
insertDevice.setCreatedAt(now);
insertDevice.setUpdatedAt(now);
emsDevicesMapper.insertEmsDevicesSetting(insertDevice);
existingIds.add(candidateId);
clusterBatteryIds.add(candidateId);
deviceMap.put(candidateId, insertDevice);
result.add(insertDevice);
}
return result;
}
private String formatSingleBatterySerial(int serialNo) {
return String.format("%03d", serialNo);
}
private SingleBatteryFieldInitTask buildSingleBatteryFieldInitTask(String siteId,
String clusterDeviceId,
String batteryDeviceId,
String fieldCode,
String bizCode,
String serialNo) {
String pointId = clusterDeviceId + "DT" + bizCode + serialNo;
return new SingleBatteryFieldInitTask(siteId, clusterDeviceId, batteryDeviceId, fieldCode, pointId);
}
private void upsertSingleBatteryPointMatchForInit(List<EmsSiteMonitorPointMatch> insertList,
Map<String, EmsSiteMonitorPointMatch> existingPointMatchMap,
SingleBatteryConfigInitResultVo result,
SingleBatteryFieldInitTask task,
boolean pointExists,
String operName) {
String key = buildMatchKey(task.fieldCode, task.batteryDeviceId);
EmsSiteMonitorPointMatch existing = existingPointMatchMap.get(key);
String dataPoint = pointExists ? task.pointId : "";
String fixedDataPoint = pointExists ? null : SINGLE_BATTERY_FIELD_VALUE_ZERO;
int useFixedDisplay = pointExists ? 0 : USE_FIXED_DISPLAY_YES;
if (existing == null) {
EmsSiteMonitorPointMatch insertItem = new EmsSiteMonitorPointMatch();
insertItem.setSiteId(task.siteId);
insertItem.setFieldCode(task.fieldCode);
insertItem.setDeviceId(task.batteryDeviceId);
insertItem.setDataPoint(dataPoint);
insertItem.setFixedDataPoint(fixedDataPoint);
insertItem.setUseFixedDisplay(useFixedDisplay);
insertItem.setCreateBy(operName);
insertItem.setUpdateBy(operName);
insertList.add(insertItem);
result.setInsertedMappingCount(result.getInsertedMappingCount() + 1);
return;
}
boolean changed = !StringUtils.equals(StringUtils.trimToEmpty(existing.getDataPoint()), dataPoint)
|| !StringUtils.equals(StringUtils.trimToEmpty(existing.getFixedDataPoint()), StringUtils.trimToEmpty(fixedDataPoint))
|| (existing.getUseFixedDisplay() == null ? 0 : existing.getUseFixedDisplay()) != useFixedDisplay;
if (!changed) {
return;
}
existing.setDataPoint(dataPoint);
existing.setFixedDataPoint(fixedDataPoint);
existing.setUseFixedDisplay(useFixedDisplay);
existing.setUpdateBy(operName);
emsSiteMonitorPointMatchMapper.updateEmsSiteMonitorPointMatch(existing);
result.setUpdatedMappingCount(result.getUpdatedMappingCount() + 1);
}
private static class SingleBatteryFieldInitTask {
private final String siteId;
private final String clusterDeviceId;
private final String batteryDeviceId;
private final String fieldCode;
private final String pointId;
private SingleBatteryFieldInitTask(String siteId,
String clusterDeviceId,
String batteryDeviceId,
String fieldCode,
String pointId) {
this.siteId = siteId;
this.clusterDeviceId = clusterDeviceId;
this.batteryDeviceId = batteryDeviceId;
this.fieldCode = fieldCode;
this.pointId = pointId;
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public SingleBatteryMonitorImportResultVo importSingleBatteryMonitorMappings(String siteId, List<SingleBatteryMonitorImportRowVo> rows, String operName) {
SingleBatteryMonitorImportResultVo result = new SingleBatteryMonitorImportResultVo();
String normalizedSiteId = StringUtils.trim(siteId);
List<SingleBatteryMonitorImportRowVo> sourceRows = rows == null ? Collections.emptyList() : rows;
result.setCommitted(false);
result.setTotalRows(sourceRows.size());
result.setInsertedBatteryCount(0);
result.setUpdatedBatteryCount(0);
result.setInsertedMappingCount(0);
result.setUpdatedMappingCount(0);
if (StringUtils.isBlank(normalizedSiteId)) {
result.setFailureRows(sourceRows.size());
result.setSuccessRows(0);
result.setMessage("导入失败站点ID不能为空");
return result;
}
if (CollectionUtils.isEmpty(sourceRows)) {
result.setFailureRows(0);
result.setSuccessRows(0);
result.setCommitted(true);
result.setMessage("导入文件中未读取到任何数据");
return result;
}
List<EmsDevicesSetting> settingList = emsDevicesMapper.selectEmsDevicesSettingList(buildSiteDeviceQuery(normalizedSiteId));
Map<String, EmsDevicesSetting> deviceMap = settingList.stream()
.filter(Objects::nonNull)
.filter(item -> StringUtils.isNotBlank(item.getDeviceId()))
.collect(Collectors.toMap(
item -> StringUtils.trim(item.getDeviceId()),
item -> item,
(left, right) -> right,
LinkedHashMap::new
));
List<SingleBatteryMonitorImportFailureVo> failureList = new ArrayList<>();
Set<String> seenBatteryKeys = new HashSet<>();
Map<String, Integer> seenClusterBatteryRowMap = new HashMap<>();
Map<String, Integer> seenSiteBatteryRowMap = new HashMap<>();
Set<String> pointIds = new HashSet<>();
List<SingleBatteryMonitorImportRowVo> validRows = new ArrayList<>();
for (SingleBatteryMonitorImportRowVo row : sourceRows) {
SingleBatteryMonitorImportRowVo normalizedRow = normalizeSingleBatteryImportRow(row);
String duplicateKey = normalizedRow.getSiteId() + "|" + normalizedRow.getBatteryDeviceId();
String clusterDuplicateKey = normalizedRow.getSiteId() + "|" + normalizedRow.getClusterDeviceId() + "|" + normalizedRow.getBatteryDeviceId();
String rowError = validateSingleBatteryImportRow(normalizedSiteId, normalizedRow, deviceMap, duplicateKey, clusterDuplicateKey,
seenBatteryKeys, seenSiteBatteryRowMap, seenClusterBatteryRowMap);
if (rowError != null) {
failureList.add(buildSingleBatteryImportFailure(normalizedRow, rowError));
continue;
}
collectPointIds(pointIds, normalizedRow);
validRows.add(normalizedRow);
}
if (!pointIds.isEmpty()) {
Map<String, EmsPointConfig> pointConfigMap = emsPointConfigMapper.selectBySiteIdAndPointIds(normalizedSiteId, new ArrayList<>(pointIds))
.stream()
.filter(Objects::nonNull)
.filter(item -> StringUtils.isNotBlank(item.getPointId()))
.collect(Collectors.toMap(
item -> StringUtils.trim(item.getPointId()),
item -> item,
(left, right) -> right
));
for (SingleBatteryMonitorImportRowVo row : validRows) {
String missingPointMessage = validateSingleBatteryImportPoints(row, pointConfigMap);
if (missingPointMessage != null) {
failureList.add(buildSingleBatteryImportFailure(row, missingPointMessage));
}
}
}
if (!failureList.isEmpty()) {
result.setFailureDetails(failureList);
result.setFailureRows(failureList.size());
result.setSuccessRows(Math.max(sourceRows.size() - failureList.size(), 0));
result.setMessage("导入校验未通过,请根据失败明细修改后重试");
return result;
}
Map<String, EmsSiteMonitorPointMatch> existingPointMatchMap = emsSiteMonitorPointMatchMapper.selectBySiteId(normalizedSiteId)
.stream()
.filter(Objects::nonNull)
.filter(item -> SINGLE_BATTERY_IMPORT_FIELD_CODES.contains(StringUtils.trim(item.getFieldCode())))
.filter(item -> StringUtils.isNotBlank(item.getDeviceId()))
.collect(Collectors.toMap(
item -> buildMatchKey(StringUtils.trim(item.getFieldCode()), StringUtils.trim(item.getDeviceId())),
item -> item,
(left, right) -> right,
LinkedHashMap::new
));
List<EmsSiteMonitorPointMatch> insertList = new ArrayList<>();
Set<String> importedBatteryIds = new HashSet<>();
Date now = DateUtils.getNowDate();
for (SingleBatteryMonitorImportRowVo row : validRows) {
String batteryId = row.getBatteryDeviceId();
importedBatteryIds.add(batteryId);
EmsDevicesSetting batterySetting = deviceMap.get(batteryId);
if (batterySetting == null) {
EmsDevicesSetting insertDevice = new EmsDevicesSetting();
insertDevice.setSiteId(normalizedSiteId);
insertDevice.setDeviceId(batteryId);
insertDevice.setDeviceName(StringUtils.defaultIfBlank(row.getBatteryDeviceName(), batteryId));
insertDevice.setDeviceType(DeviceType.TCP.name());
insertDevice.setCommunicationStatus(CommunicationStatus.OK.getCode());
insertDevice.setDeviceCategory(DeviceCategory.BATTERY.getCode());
insertDevice.setDeviceStatus(DeviceRunningStatus.OFFLINE.getCode());
insertDevice.setWorkStatus(WorkStatus.NORMAL.getCode());
insertDevice.setParentId(row.getClusterDeviceId());
insertDevice.setDescription("single battery import");
insertDevice.setCreatedAt(now);
insertDevice.setUpdatedAt(now);
emsDevicesMapper.insertEmsDevicesSetting(insertDevice);
deviceMap.put(batteryId, insertDevice);
result.setInsertedBatteryCount(result.getInsertedBatteryCount() + 1);
batterySetting = insertDevice;
} else {
boolean shouldUpdateBattery = false;
if (!StringUtils.equals(StringUtils.trimToEmpty(batterySetting.getParentId()), row.getClusterDeviceId())) {
batterySetting.setParentId(row.getClusterDeviceId());
shouldUpdateBattery = true;
}
if (StringUtils.isNotBlank(row.getBatteryDeviceName()) &&
!StringUtils.equals(StringUtils.trimToEmpty(batterySetting.getDeviceName()), row.getBatteryDeviceName())) {
batterySetting.setDeviceName(row.getBatteryDeviceName());
shouldUpdateBattery = true;
}
if (shouldUpdateBattery) {
batterySetting.setUpdatedAt(now);
emsDevicesMapper.updateEmsDevicesSetting(batterySetting);
result.setUpdatedBatteryCount(result.getUpdatedBatteryCount() + 1);
}
}
upsertSingleBatteryPointMatch(insertList, existingPointMatchMap, result, normalizedSiteId, batteryId, BATTERY_FIELD_VOLTAGE, row.getVoltagePointId(), operName);
upsertSingleBatteryPointMatch(insertList, existingPointMatchMap, result, normalizedSiteId, batteryId, BATTERY_FIELD_TEMPERATURE, row.getTemperaturePointId(), operName);
upsertSingleBatteryPointMatch(insertList, existingPointMatchMap, result, normalizedSiteId, batteryId, BATTERY_FIELD_SOC, row.getSocPointId(), operName);
upsertSingleBatteryPointMatch(insertList, existingPointMatchMap, result, normalizedSiteId, batteryId, BATTERY_FIELD_SOH, row.getSohPointId(), operName);
}
if (!insertList.isEmpty()) {
emsSiteMonitorPointMatchMapper.insertBatch(insertList);
}
refreshSingleBatteryImportCache(normalizedSiteId, importedBatteryIds);
result.setCommitted(true);
result.setFailureRows(0);
result.setSuccessRows(sourceRows.size());
result.setMessage(String.format("导入完成:成功 %d 行,新增单体 %d 个,更新单体 %d 个,新增映射 %d 条,更新映射 %d 条",
result.getSuccessRows(),
result.getInsertedBatteryCount(),
result.getUpdatedBatteryCount(),
result.getInsertedMappingCount(),
result.getUpdatedMappingCount()));
return result;
}
private EmsDevicesSetting buildSiteDeviceQuery(String siteId) {
EmsDevicesSetting query = new EmsDevicesSetting();
query.setSiteId(siteId);
return query;
}
private SingleBatteryMonitorImportRowVo normalizeSingleBatteryImportRow(SingleBatteryMonitorImportRowVo row) {
SingleBatteryMonitorImportRowVo normalized = row == null ? new SingleBatteryMonitorImportRowVo() : row;
normalized.setSiteId(StringUtils.trimToEmpty(normalized.getSiteId()));
normalized.setStackDeviceId(StringUtils.trimToEmpty(normalized.getStackDeviceId()));
normalized.setClusterDeviceId(StringUtils.trimToEmpty(normalized.getClusterDeviceId()));
normalized.setBatteryDeviceId(StringUtils.trimToEmpty(normalized.getBatteryDeviceId()));
normalized.setBatteryDeviceName(StringUtils.trimToEmpty(normalized.getBatteryDeviceName()));
normalized.setVoltagePointId(StringUtils.trimToEmpty(normalized.getVoltagePointId()));
normalized.setTemperaturePointId(StringUtils.trimToEmpty(normalized.getTemperaturePointId()));
normalized.setSocPointId(StringUtils.trimToEmpty(normalized.getSocPointId()));
normalized.setSohPointId(StringUtils.trimToEmpty(normalized.getSohPointId()));
return normalized;
}
private String validateSingleBatteryImportRow(String siteId,
SingleBatteryMonitorImportRowVo row,
Map<String, EmsDevicesSetting> deviceMap,
String duplicateKey,
String clusterDuplicateKey,
Set<String> seenBatteryKeys,
Map<String, Integer> seenSiteBatteryRowMap,
Map<String, Integer> seenClusterBatteryRowMap) {
if (row == null) {
return "导入行为空,请检查模板内容";
}
if (StringUtils.isBlank(row.getSiteId())) {
return "站点ID不能为空";
}
if (!StringUtils.equals(siteId, row.getSiteId())) {
return String.format("站点ID不一致当前导入站点为[%s]Excel中为[%s]", siteId, row.getSiteId());
}
if (StringUtils.isBlank(row.getStackDeviceId())) {
return "电池堆编号不能为空";
}
if (StringUtils.isBlank(row.getClusterDeviceId())) {
return "电池簇编号不能为空";
}
if (StringUtils.isBlank(row.getBatteryDeviceId())) {
return "单体编号不能为空";
}
if (StringUtils.isAllBlank(row.getVoltagePointId(), row.getTemperaturePointId(), row.getSocPointId(), row.getSohPointId())) {
return "电压/温度/SOC/SOH 点位ID至少需要填写一个";
}
int currentRowNum = safeRowNum(row);
Integer duplicateClusterRowNum = seenClusterBatteryRowMap.putIfAbsent(clusterDuplicateKey, currentRowNum);
if (duplicateClusterRowNum != null) {
return String.format("同一电池簇[%s]下的单体编号[%s]重复,首次出现在第%d行",
row.getClusterDeviceId(), row.getBatteryDeviceId(), duplicateClusterRowNum);
}
if (!seenBatteryKeys.add(duplicateKey)) {
Integer duplicateSiteRowNum = seenSiteBatteryRowMap.get(duplicateKey);
return String.format("单体编号[%s]在当前导入文件中重复,首次出现在第%d行",
row.getBatteryDeviceId(), duplicateSiteRowNum == null ? safeRowNum(row) : duplicateSiteRowNum);
}
seenSiteBatteryRowMap.putIfAbsent(duplicateKey, currentRowNum);
EmsDevicesSetting stackSetting = deviceMap.get(row.getStackDeviceId());
if (stackSetting == null || !StringUtils.equals(DeviceCategory.STACK.getCode(), StringUtils.trimToEmpty(stackSetting.getDeviceCategory()))) {
return String.format("电池堆编号[%s]不存在,请先确认设备已创建", row.getStackDeviceId());
}
EmsDevicesSetting clusterSetting = deviceMap.get(row.getClusterDeviceId());
if (clusterSetting == null || !StringUtils.equals(DeviceCategory.CLUSTER.getCode(), StringUtils.trimToEmpty(clusterSetting.getDeviceCategory()))) {
return String.format("电池簇编号[%s]不存在,请先确认设备已创建", row.getClusterDeviceId());
}
if (!StringUtils.equals(StringUtils.trimToEmpty(clusterSetting.getParentId()), row.getStackDeviceId())) {
return String.format("电池簇[%s]不属于电池堆[%s],请检查堆簇关系", row.getClusterDeviceId(), row.getStackDeviceId());
}
EmsDevicesSetting batterySetting = deviceMap.get(row.getBatteryDeviceId());
if (batterySetting != null && !StringUtils.equals(DeviceCategory.BATTERY.getCode(), StringUtils.trimToEmpty(batterySetting.getDeviceCategory()))) {
return String.format("单体编号[%s]已存在,但对应设备类型不是单体电池", row.getBatteryDeviceId());
}
return null;
}
private void collectPointIds(Set<String> pointIds, SingleBatteryMonitorImportRowVo row) {
if (row == null || pointIds == null) {
return;
}
if (StringUtils.isNotBlank(row.getVoltagePointId())) {
pointIds.add(row.getVoltagePointId());
}
if (StringUtils.isNotBlank(row.getTemperaturePointId())) {
pointIds.add(row.getTemperaturePointId());
}
if (StringUtils.isNotBlank(row.getSocPointId())) {
pointIds.add(row.getSocPointId());
}
if (StringUtils.isNotBlank(row.getSohPointId())) {
pointIds.add(row.getSohPointId());
}
}
private String validateSingleBatteryImportPoints(SingleBatteryMonitorImportRowVo row, Map<String, EmsPointConfig> pointConfigMap) {
if (row == null) {
return "导入行为空,请检查模板内容";
}
if (StringUtils.isNotBlank(row.getVoltagePointId()) && !pointConfigMap.containsKey(row.getVoltagePointId())) {
return String.format("电压点位ID[%s]不存在,请确认当前站点已配置该点位", row.getVoltagePointId());
}
if (StringUtils.isNotBlank(row.getTemperaturePointId()) && !pointConfigMap.containsKey(row.getTemperaturePointId())) {
return String.format("温度点位ID[%s]不存在,请确认当前站点已配置该点位", row.getTemperaturePointId());
}
if (StringUtils.isNotBlank(row.getSocPointId()) && !pointConfigMap.containsKey(row.getSocPointId())) {
return String.format("SOC点位ID[%s]不存在,请确认当前站点已配置该点位", row.getSocPointId());
}
if (StringUtils.isNotBlank(row.getSohPointId()) && !pointConfigMap.containsKey(row.getSohPointId())) {
return String.format("SOH点位ID[%s]不存在,请确认当前站点已配置该点位", row.getSohPointId());
}
return null;
}
private int safeRowNum(SingleBatteryMonitorImportRowVo row) {
if (row == null || row.getRowNum() == null || row.getRowNum() < 1) {
return 0;
}
return row.getRowNum();
}
private SingleBatteryMonitorImportFailureVo buildSingleBatteryImportFailure(SingleBatteryMonitorImportRowVo row, String message) {
SingleBatteryMonitorImportFailureVo failure = new SingleBatteryMonitorImportFailureVo();
if (row != null) {
failure.setRowNum(row.getRowNum());
failure.setSiteId(row.getSiteId());
failure.setStackDeviceId(row.getStackDeviceId());
failure.setClusterDeviceId(row.getClusterDeviceId());
failure.setBatteryDeviceId(row.getBatteryDeviceId());
}
failure.setErrorMessage(message);
return failure;
}
private void upsertSingleBatteryPointMatch(List<EmsSiteMonitorPointMatch> insertList,
Map<String, EmsSiteMonitorPointMatch> existingPointMatchMap,
SingleBatteryMonitorImportResultVo result,
String siteId,
String batteryId,
String fieldCode,
String pointId,
String operName) {
if (StringUtils.isBlank(pointId)) {
return;
}
String key = buildMatchKey(fieldCode, batteryId);
EmsSiteMonitorPointMatch existing = existingPointMatchMap.get(key);
if (existing == null) {
EmsSiteMonitorPointMatch insertItem = new EmsSiteMonitorPointMatch();
insertItem.setSiteId(siteId);
insertItem.setFieldCode(fieldCode);
insertItem.setDeviceId(batteryId);
insertItem.setDataPoint(pointId);
insertItem.setFixedDataPoint(null);
insertItem.setUseFixedDisplay(0);
insertItem.setCreateBy(operName);
insertItem.setUpdateBy(operName);
insertList.add(insertItem);
result.setInsertedMappingCount(result.getInsertedMappingCount() + 1);
return;
}
boolean changed = !StringUtils.equals(StringUtils.trimToEmpty(existing.getDataPoint()), pointId)
|| StringUtils.isNotBlank(existing.getFixedDataPoint())
|| (existing.getUseFixedDisplay() != null && existing.getUseFixedDisplay() != 0);
if (!changed) {
return;
}
existing.setDataPoint(pointId);
existing.setFixedDataPoint(null);
existing.setUseFixedDisplay(0);
existing.setUpdateBy(operName);
emsSiteMonitorPointMatchMapper.updateEmsSiteMonitorPointMatch(existing);
result.setUpdatedMappingCount(result.getUpdatedMappingCount() + 1);
}
private void refreshSingleBatteryImportCache(String siteId, Set<String> batteryIds) {
redisCache.deleteObject(RedisKeyConstants.INIT_DEVICE_INFO);
if (!CollectionUtils.isEmpty(batteryIds)) {
for (String batteryId : batteryIds) {
if (StringUtils.isBlank(batteryId)) {
continue;
}
redisCache.deleteObject(RedisKeyConstants.DEVICE_SETTING + siteId + "_" + batteryId);
}
}
redisCache.deleteObject(buildSiteMonitorPointMatchRedisKey(siteId));
clearSiteMonitorLatestCache(siteId);
projectDisplayCache.remove(siteId);
}
@Override
public List<SiteMonitorProjectDisplayVo> getSiteMonitorProjectDisplay(String siteId) {
String normalizedSiteId = StringUtils.trim(siteId);

View File

@ -306,7 +306,7 @@ public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
}
String siteId = StringUtils.trim(request.getSiteId());
String pointId = StringUtils.trim(request.getPointId());
String deviceId = StringUtils.trimToEmpty(request.getDeviceId());
String deviceId = StringUtils.isBlank(request.getDeviceId()) ? "SITE_CALC" : StringUtils.trim(request.getDeviceId());
EmsPointConfig targetConfig = selectPointConfigByExactPointId(siteId, pointId);
if (targetConfig == null || !"calc".equalsIgnoreCase(StringUtils.defaultString(targetConfig.getPointType()))) {
@ -317,7 +317,8 @@ public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
throw new ServiceException("计算点缺少表达式,无法生成数据");
}
List<InfluxPointDataWriter.PointWritePayload> payloads = buildCalcRecentPayloads(siteId, deviceId, pointId, expression);
Integer decimalScale = targetConfig.getDecimalScale();
List<InfluxPointDataWriter.PointWritePayload> payloads = buildCalcRecentPayloads(siteId, deviceId, pointId, expression, decimalScale);
if (payloads.isEmpty()) {
throw new ServiceException("未生成有效数据,请检查表达式依赖点是否有历史数据");
}
@ -325,7 +326,7 @@ public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
return String.format("已生成点位 %s 最近7天数据共 %d 条", pointId, payloads.size());
}
private List<InfluxPointDataWriter.PointWritePayload> buildCalcRecentPayloads(String siteId, String deviceId, String pointId, String expression) {
private List<InfluxPointDataWriter.PointWritePayload> buildCalcRecentPayloads(String siteId, String deviceId, String pointId, String expression, Integer decimalScale) {
CompiledExpression compiledExpression = new CompiledExpression(expression);
List<LocalDateTime> evaluateTimes = buildEvaluateTimes(expression);
if (evaluateTimes.isEmpty()) {
@ -348,7 +349,9 @@ public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
BigDecimal value;
try {
prepareAutoPeriodDiffContextValues(expression, contextValues, evaluateTime, periodDiffStateMap);
value = compiledExpression.evaluate(contextValues).asNumber().setScale(4, RoundingMode.HALF_UP);
value = compiledExpression.evaluate(contextValues).asNumber();
int scale = (decimalScale != null && decimalScale >= 0) ? decimalScale : 4;
value = value.setScale(scale, RoundingMode.HALF_UP);
} catch (IllegalArgumentException ex) {
continue;
}
@ -1758,6 +1761,7 @@ public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
pointConfig.setModbusDataType(getString(valueList, headerIndex, "modbus_data_type"));
pointConfig.setModbusReadOrder(getInteger(valueList, headerIndex, "modbus_read_order", lineNo));
pointConfig.setModbusGroup(getString(valueList, headerIndex, "modbus_group"));
pointConfig.setDecimalScale(getInteger(valueList, headerIndex, "decimal_scale", lineNo));
pointConfig.setRemark(getString(valueList, headerIndex, "remark"));
normalizeAndValidatePointConfig(pointConfig);
applyCollectDefaultsForInsert(pointConfig);
@ -1921,6 +1925,9 @@ public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
case "modbusgroup":
case "modbus分组":
return "modbus_group";
case "decimalscale":
case "小数位数":
return "decimal_scale";
case "remark":
case "备注":
return "remark";

View File

@ -28,6 +28,7 @@ import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
import com.xzzn.ems.mapper.EmsEnergyPriceConfigMapper;
import com.xzzn.ems.mapper.EmsPcsDataMapper;
import com.xzzn.ems.service.IEmsStatsReportService;
import com.xzzn.ems.service.ISysBizRemarkService;
import java.math.BigDecimal;
import java.math.RoundingMode;
@ -99,6 +100,40 @@ public class EmsStatsReportServiceImpl implements IEmsStatsReportService
private EmsDailyEnergyDataMapper emsDailyEnergyDataMapper;
@Autowired
private EmsEnergyPriceConfigMapper emsEnergyPriceConfigMapper;
@Autowired
private ISysBizRemarkService sysBizRemarkService;
private static final String STATS_REPORT_BIZ_TYPE = "stats_report";
private static final String AMMETER_REPORT_KEY = "DBBB";
private static final String REVENUE_REPORT_KEY = "SYBB";
private String buildRemarkBizKey(String siteId, String dataTime) {
String safeSiteId = siteId == null ? "" : siteId.trim();
String safeDate = dataTime == null ? "" : dataTime.trim();
return safeSiteId + "_" + safeDate;
}
private void fillAmmeterRemarks(String siteId, String reportKey, List<AmmeterStatisListVo> rows) {
if (CollectionUtils.isEmpty(rows)) {
return;
}
List<String> bizKeys = rows.stream()
.map(row -> buildRemarkBizKey(siteId, row.getDataTime()))
.collect(Collectors.toList());
Map<String, String> remarkMap = sysBizRemarkService.getRemarkMap(STATS_REPORT_BIZ_TYPE, reportKey, bizKeys);
rows.forEach(row -> row.setRemark(remarkMap.getOrDefault(buildRemarkBizKey(siteId, row.getDataTime()), "")));
}
private void fillRevenueRemarks(String siteId, String reportKey, List<AmmeterRevenueStatisListVo> rows) {
if (CollectionUtils.isEmpty(rows)) {
return;
}
List<String> bizKeys = rows.stream()
.map(row -> buildRemarkBizKey(siteId, row.getDataTime()))
.collect(Collectors.toList());
Map<String, String> remarkMap = sysBizRemarkService.getRemarkMap(STATS_REPORT_BIZ_TYPE, reportKey, bizKeys);
rows.forEach(row -> row.setRemark(remarkMap.getOrDefault(buildRemarkBizKey(siteId, row.getDataTime()), "")));
}
// 电量指标
@Override
@ -374,6 +409,7 @@ public class EmsStatsReportServiceImpl implements IEmsStatsReportService
}
dataList.add(totalVo);*/
fillAmmeterRemarks(requestVo.getSiteId(), AMMETER_REPORT_KEY, dataList);
return dataList;
}
@ -447,6 +483,7 @@ public class EmsStatsReportServiceImpl implements IEmsStatsReportService
weatherMissingCount,
allPriceZeroCount);
fillRevenueRemarks(requestVo.getSiteId(), REVENUE_REPORT_KEY, resultList);
return resultList;
}
@ -707,6 +744,8 @@ public class EmsStatsReportServiceImpl implements IEmsStatsReportService
cell3.setCellValue("放电量");
Cell cell4 = row1.createCell(11);
cell4.setCellValue("效率");
Cell cell5Top = row1.createCell(12);
cell5Top.setCellValue("备注");
// 合并充电量列
CellRangeAddress mergeRegion1 = new CellRangeAddress(0, 0, 1, 5);
@ -742,6 +781,8 @@ public class EmsStatsReportServiceImpl implements IEmsStatsReportService
cell15.setCellValue("");
Cell cell16 = row2.createCell(11);
cell16.setCellValue("");
Cell cell17 = row2.createCell(12);
cell17.setCellValue("备注");
// 设置背景颜色
CellStyle headerStyle = workbook.createCellStyle();
@ -821,6 +862,8 @@ public class EmsStatsReportServiceImpl implements IEmsStatsReportService
dataCell11.setCellValue(ammeterStatisVo.getReActiveTotalKwh().doubleValue());
Cell dataCell12 = dataRow.createCell(11);
dataCell12.setCellValue(ammeterStatisVo.getEffect().doubleValue());
Cell dataCell13 = dataRow.createCell(12);
dataCell13.setCellValue(ammeterStatisVo.getRemark() == null ? "" : ammeterStatisVo.getRemark());
// 根据行号设置背景色
if (i % 2 == 0) {
@ -868,7 +911,9 @@ public class EmsStatsReportServiceImpl implements IEmsStatsReportService
Cell cell3 = row1.createCell(8);
cell3.setCellValue("放电价格");
Cell cell4 = row1.createCell(13);
cell4.setCellValue("");
cell4.setCellValue("实际收益");
Cell cell5Top = row1.createCell(14);
cell5Top.setCellValue("备注");
// 合并充电量列
CellRangeAddress mergeRegion1 = new CellRangeAddress(0, 0, 3, 7);
@ -908,6 +953,8 @@ public class EmsStatsReportServiceImpl implements IEmsStatsReportService
cell17.setCellValue("");
Cell cell18 = row2.createCell(13);
cell18.setCellValue("实际收益");
Cell cell19 = row2.createCell(14);
cell19.setCellValue("备注");
// 设置背景颜色
CellStyle headerStyle = workbook.createCellStyle();
@ -1002,6 +1049,8 @@ public class EmsStatsReportServiceImpl implements IEmsStatsReportService
dataCell13.setCellValue(ammeterRevenueStatisVo.getReActiveTotalPrice().doubleValue());
Cell dataCell14 = dataRow.createCell(13);
dataCell14.setCellValue(ammeterRevenueStatisVo.getActualRevenue().doubleValue());
Cell dataCell15 = dataRow.createCell(14);
dataCell15.setCellValue(ammeterRevenueStatisVo.getRemark() == null ? "" : ammeterRevenueStatisVo.getRemark());
// 根据行号设置背景色
if (i % 2 == 0) {
@ -1059,6 +1108,8 @@ public class EmsStatsReportServiceImpl implements IEmsStatsReportService
lastRowCell13.setCellValue(reActiveTotalPrice.doubleValue());
Cell lastRowCell14 = lastRow.createCell(13);
lastRowCell14.setCellValue(actualRevenue.doubleValue());
Cell lastRowCell15 = lastRow.createCell(14);
lastRowCell15.setCellValue("");
Iterator<Cell> lastRowCellIterator = lastRow.cellIterator();
while (lastRowCellIterator.hasNext()) {
int i = lastRowCellIterator.next().getColumnIndex();

View File

@ -97,6 +97,7 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
"deviceId", "clusterDeviceId", "dataTimestamp"
));
private static final String MENU_SBJK_DTDC = "SBJK_DTDC";
private static final String MENU_SBJK_BMSDCC = "SBJK_BMSDCC";
private static final String CLUSTER_DATA_TEP = "温度";
@ -270,6 +271,63 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
return siteMonitorHomeVo;
}
@Override
public SiteMonitorHomeVo getSiteMonitorHomeRunningData(String siteId) {
SiteMonitorHomeVo siteMonitorHomeVo = new SiteMonitorHomeVo();
if (StringUtils.isEmpty(siteId)) {
return siteMonitorHomeVo;
}
EmsDailyChargeData latestChargeData = emsDailyChargeDataMapper.selectLatestBySiteId(siteId);
BigDecimal totalChargedCap = latestChargeData == null || latestChargeData.getTotalChargeData() == null
? BigDecimal.ZERO
: latestChargeData.getTotalChargeData();
BigDecimal totalDischargedCap = latestChargeData == null || latestChargeData.getTotalDischargeData() == null
? BigDecimal.ZERO
: latestChargeData.getTotalDischargeData();
siteMonitorHomeVo.setTotalChargedCap(totalChargedCap);
siteMonitorHomeVo.setTotalDischargedCap(totalDischargedCap);
Date todayDate = DateUtils.toDate(LocalDate.now());
Date yesterdayDate = DateUtils.toDate(LocalDate.now().minusDays(1));
EmsDailyChargeData todayChargeData = emsDailyChargeDataMapper.selectBySiteIdAndDateTime(siteId, todayDate);
EmsDailyChargeData yesterdayChargeData = emsDailyChargeDataMapper.selectBySiteIdAndDateTime(siteId, yesterdayDate);
siteMonitorHomeVo.setDayChargedCap(todayChargeData == null || todayChargeData.getChargeData() == null
? BigDecimal.ZERO
: todayChargeData.getChargeData());
siteMonitorHomeVo.setDayDisChargedCap(todayChargeData == null || todayChargeData.getDischargeData() == null
? BigDecimal.ZERO
: todayChargeData.getDischargeData());
siteMonitorHomeVo.setYesterdayChargedCap(yesterdayChargeData == null || yesterdayChargeData.getChargeData() == null
? BigDecimal.ZERO
: yesterdayChargeData.getChargeData());
siteMonitorHomeVo.setYesterdayDisChargedCap(yesterdayChargeData == null || yesterdayChargeData.getDischargeData() == null
? BigDecimal.ZERO
: yesterdayChargeData.getDischargeData());
siteMonitorHomeVo.setTotalRevenue(latestChargeData == null || latestChargeData.getTotalRevenue() == null
? BigDecimal.ZERO
: latestChargeData.getTotalRevenue());
siteMonitorHomeVo.setDayRevenue(todayChargeData == null || todayChargeData.getDayRevenue() == null
? BigDecimal.ZERO
: todayChargeData.getDayRevenue());
siteMonitorHomeVo.setYesterdayRevenue(yesterdayChargeData == null || yesterdayChargeData.getDayRevenue() == null
? BigDecimal.ZERO
: yesterdayChargeData.getDayRevenue());
List<SiteMonitorHomeAlarmVo> alarmList = emsAlarmRecordsMapper.getAlarmRecordsBySiteId(siteId);
siteMonitorHomeVo.setSiteMonitorHomeAlarmVo(alarmList);
LocalDate sevenDaysAgo = LocalDate.now().minusDays(6);
Date startDate = DateUtils.toDate(sevenDaysAgo);
Date endDate = new Date();
List<SiteMonitorDataVo> siteMonitorDataVoList = emsDailyChargeDataMapper.getSingleSiteChargeData(siteId, startDate, endDate);
siteMonitorHomeVo.setSiteMonitorDataVo(siteMonitorDataVoList);
siteMonitorHomeVo.setEnergyStorageAvailElec(totalDischargedCap.subtract(totalChargedCap));
return siteMonitorHomeVo;
}
// 获取单站监控实时运行头部数据
@Override
public SiteMonitorRunningHeadInfoVo getSiteRunningHeadInfo(String siteId) {
@ -1092,11 +1150,17 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
log.warn("getClusterDataInfoList early return, siteId is empty");
return batteryDataStatsListVo;
}
List<SiteMonitorProjectDisplayVo> projectDisplayList = iEmsDeviceSettingService.getSiteMonitorProjectDisplay(siteId);
Map<String, Map<String, SiteMonitorProjectDisplayVo>> batteryDisplayMap =
buildBatteryDisplayMap(iEmsDeviceSettingService.getSiteMonitorProjectDisplay(siteId));
buildBatteryDisplayMap(projectDisplayList);
Map<String, Map<String, SiteMonitorProjectDisplayVo>> clusterDisplayMap =
buildClusterDisplayMap(projectDisplayList);
if (batteryDisplayMap.isEmpty()) {
log.warn("getClusterDataInfoList no battery display data from projectDisplay, siteId={}", siteId);
}
if (clusterDisplayMap.isEmpty()) {
log.warn("getClusterDataInfoList no cluster display data from projectDisplay, siteId={}", siteId);
}
log.info("getClusterDataInfoList battery list source projectDisplay, siteId={}, batteryCount={}",
siteId, batteryDisplayMap.size());
@ -1136,6 +1200,9 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
fillBatteryValueFromProjectDisplay(batteryDataStatsVo, batteryDisplayMap.get(batteryDeviceId));
batteryDataStatsVo.setDeviceId(batteryDeviceId);
batteryDataStatsVo.setClusterDeviceId(currentClusterDeviceId);
// 单体电池未配置 SOC/SOH 点位时,回退使用所属电池簇的 SOC/SOH 点位和值。
applyClusterSocSohFallback(batteryDataStatsVo,
clusterDisplayMap.get(currentClusterDeviceId), currentClusterDeviceId);
batteryDataStatsListVo.add(batteryDataStatsVo);
log.info("getClusterDataInfoList battery from projectDisplay list, siteId={}, batteryDeviceId={}, clusterDeviceId={}, voltage={}, temperature={}, soc={}, soh={}, voltagePointId={}, temperaturePointId={}, socPointId={}, sohPointId={}, dataTimestamp={}",
siteId, batteryDeviceId, currentClusterDeviceId, batteryDataStatsVo.getVoltage(),
@ -1149,7 +1216,7 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
log.info("getClusterDataInfoList fallback to realtime cache, siteId={}, stackDeviceId={}, clusterDeviceId={}, batteryId={}",
siteId, targetStackDeviceId, targetClusterDeviceId, targetBatteryId);
appendBatteryFromRealtimeCache(siteId, targetStackDeviceId, targetClusterDeviceId, targetBatteryId,
batteryDisplayMap, batteryDataStatsListVo);
batteryDisplayMap, clusterDisplayMap, batteryDataStatsListVo);
}
log.info("getClusterDataInfoList end, siteId={}, stackDeviceId={}, clusterDeviceId={}, batteryId={}, total={}",
@ -1162,6 +1229,7 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
String targetClusterDeviceId,
String targetBatteryId,
Map<String, Map<String, SiteMonitorProjectDisplayVo>> batteryDisplayMap,
Map<String, Map<String, SiteMonitorProjectDisplayVo>> clusterDisplayMap,
List<BatteryDataStatsListVo> targetList) {
if (StringUtils.isBlank(siteId) || targetList == null) {
return;
@ -1204,6 +1272,9 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
batteryDataStatsVo.setClusterDeviceId(clusterId);
}
fillBatteryPointIdFromProjectDisplay(batteryDataStatsVo, batteryDisplayMap == null ? null : batteryDisplayMap.get(currentBatteryId));
// Redis 实时缓存中的单体数据如果没有单独配置 SOC/SOH 点位,同样回退到电池簇配置。
applyClusterSocSohFallback(batteryDataStatsVo,
clusterDisplayMap == null ? null : clusterDisplayMap.get(clusterId), clusterId);
targetList.add(batteryDataStatsVo);
}
}
@ -1283,6 +1354,31 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
return result;
}
private Map<String, Map<String, SiteMonitorProjectDisplayVo>> buildClusterDisplayMap(List<SiteMonitorProjectDisplayVo> displayList) {
Map<String, Map<String, SiteMonitorProjectDisplayVo>> result = new HashMap<>();
if (CollectionUtils.isEmpty(displayList)) {
return result;
}
for (SiteMonitorProjectDisplayVo item : displayList) {
if (item == null) {
continue;
}
String menuCode = StringUtils.trimToEmpty(item.getMenuCode());
String deviceId = StringUtils.trimToEmpty(item.getDeviceId());
String fieldCode = StringUtils.trimToEmpty(item.getFieldCode());
if (!MENU_SBJK_BMSDCC.equals(menuCode) || StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(fieldCode)) {
continue;
}
Map<String, SiteMonitorProjectDisplayVo> fieldMap = result.computeIfAbsent(deviceId, key -> new HashMap<>());
fieldMap.put(fieldCode, item);
String normalizedFieldCode = normalizeBatteryFieldCode(fieldCode);
if (StringUtils.isNotBlank(normalizedFieldCode)) {
fieldMap.put(normalizedFieldCode, item);
}
}
return result;
}
private String normalizeBatteryFieldCode(String fieldCode) {
String normalized = StringUtils.trimToEmpty(fieldCode);
if (StringUtils.isBlank(normalized)) {
@ -1348,6 +1444,48 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
target.setSohPointId(sohVo == null ? null : StringUtils.trimToNull(sohVo.getDataPoint()));
}
private void applyClusterSocSohFallback(BatteryDataStatsListVo target,
Map<String, SiteMonitorProjectDisplayVo> clusterFieldMap,
String clusterDeviceId) {
if (target == null || clusterFieldMap == null || clusterFieldMap.isEmpty()) {
return;
}
boolean useClusterSoc = StringUtils.isBlank(target.getSocPointId());
boolean useClusterSoh = StringUtils.isBlank(target.getSohPointId());
if (!useClusterSoc && !useClusterSoh) {
return;
}
SiteMonitorProjectDisplayVo clusterSocVo = clusterFieldMap.get("currentsoc");
SiteMonitorProjectDisplayVo clusterSohVo = clusterFieldMap.get("soh");
Date latest = target.getDataTimestamp();
// 单体没有配置 SOC 点位时,使用所属电池簇的 currentSoc 作为兜底。
if (useClusterSoc && clusterSocVo != null) {
target.setSocPointId(StringUtils.trimToNull(clusterSocVo.getDataPoint()));
if (StringUtils.isNotBlank(clusterSocVo.getFieldValue())) {
target.setSoc(StringUtils.getBigDecimal(clusterSocVo.getFieldValue()));
}
latest = pickLaterTime(latest, clusterSocVo.getValueTime());
}
// 单体没有配置 SOH 点位时,使用所属电池簇的 soh 作为兜底。
if (useClusterSoh && clusterSohVo != null) {
target.setSohPointId(StringUtils.trimToNull(clusterSohVo.getDataPoint()));
if (StringUtils.isNotBlank(clusterSohVo.getFieldValue())) {
target.setSoh(StringUtils.getBigDecimal(clusterSohVo.getFieldValue()));
}
latest = pickLaterTime(latest, clusterSohVo.getValueTime());
}
if (latest != null) {
target.setDataTimestamp(latest);
}
log.info("applyClusterSocSohFallback, clusterDeviceId={}, batteryDeviceId={}, useClusterSoc={}, useClusterSoh={}, socPointId={}, sohPointId={}, soc={}, soh={}",
clusterDeviceId, target.getDeviceId(), useClusterSoc, useClusterSoh,
target.getSocPointId(), target.getSohPointId(), target.getSoc(), target.getSoh());
}
private Date pickLaterTime(Date left, Date right) {
if (left == null) {
return right;

View File

@ -0,0 +1,69 @@
package com.xzzn.ems.service.impl;
import com.xzzn.common.utils.DateUtils;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.SysBizRemark;
import com.xzzn.ems.domain.vo.BizRemarkSaveRequest;
import com.xzzn.ems.mapper.SysBizRemarkMapper;
import com.xzzn.ems.service.ISysBizRemarkService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Service
public class SysBizRemarkServiceImpl implements ISysBizRemarkService
{
@Autowired
private SysBizRemarkMapper sysBizRemarkMapper;
@Override
public Map<String, String> getRemarkMap(String bizType, String bizKey1, List<String> bizKey2List)
{
if (StringUtils.isAnyBlank(bizType, bizKey1) || bizKey2List == null || bizKey2List.isEmpty())
{
return Collections.emptyMap();
}
List<SysBizRemark> remarkList = sysBizRemarkMapper.selectByBizKey2List(bizType, bizKey1, bizKey2List);
Map<String, String> result = new LinkedHashMap<>();
for (SysBizRemark item : remarkList)
{
result.put(item.getBizKey2(), StringUtils.nvl(item.getRemark(), ""));
}
return result;
}
@Override
public int saveRemark(BizRemarkSaveRequest request, String username)
{
if (request == null || StringUtils.isAnyBlank(request.getBizType(), request.getBizKey1(), request.getBizKey2()))
{
return 0;
}
SysBizRemark existed = sysBizRemarkMapper.selectByBizKeys(request.getBizType(), request.getBizKey1(), request.getBizKey2());
if (existed == null)
{
SysBizRemark bizRemark = new SysBizRemark();
bizRemark.setBizType(request.getBizType());
bizRemark.setBizKey1(request.getBizKey1());
bizRemark.setBizKey2(request.getBizKey2());
bizRemark.setRemark(StringUtils.nvl(request.getRemark(), ""));
bizRemark.setCreateBy(username);
bizRemark.setUpdateBy(username);
bizRemark.setCreateTime(DateUtils.getNowDate());
bizRemark.setUpdateTime(DateUtils.getNowDate());
bizRemark.setDelFlag("0");
return sysBizRemarkMapper.insertSysBizRemark(bizRemark);
}
existed.setRemark(StringUtils.nvl(request.getRemark(), ""));
existed.setUpdateBy(username);
existed.setUpdateTime(DateUtils.getNowDate());
existed.setDelFlag("0");
return sysBizRemarkMapper.updateSysBizRemark(existed);
}
}

View File

@ -125,7 +125,10 @@
t.alarm_content as alarmContent,
t.ticket_no as ticketNo,
t.id
from ems_alarm_records t INNER JOIN ems_devices_setting t2 on t.site_id = t2.site_id and t.device_id = t2.device_id
from ems_alarm_records t
INNER JOIN ems_devices_setting t2
on t.site_id COLLATE utf8mb4_general_ci = t2.site_id
and t.device_id COLLATE utf8mb4_general_ci = t2.device_id
where t.site_id = #{siteId}
and t.status != 1
and t.alarm_level in ('C','D')
@ -156,7 +159,9 @@
t.ticket_no as ticketNo,
t.id
from ems_alarm_records t
LEFT JOIN ems_devices_setting t2 on t.site_id = t2.site_id and t.device_id = t2.device_id
LEFT JOIN ems_devices_setting t2
on t.site_id = t2.site_id
and t.device_id = t2.device_id
where t.site_id = #{siteId}
<if test="deviceType != null and deviceType != ''">
AND t.device_type = #{deviceType}
@ -265,4 +270,4 @@
and device_id = #{deviceId}
and alarm_point is not null
</select>
</mapper>
</mapper>

View File

@ -53,6 +53,13 @@
limit 1
</select>
<select id="selectLatestBySiteId" resultMap="EmsDailyChargeDataResult">
<include refid="selectEmsDailyChargeDataVo"/>
where site_id = #{siteId}
order by date_time desc, id desc
limit 1
</select>
<insert id="insertEmsDailyChargeData" parameterType="EmsDailyChargeData" useGeneratedKeys="true" keyProperty="id">
insert into ems_daily_charge_data
<trim prefix="(" suffix=")" suffixOverrides=",">

View File

@ -288,65 +288,80 @@
<select id="getRevenueDataBySiteId" resultType="com.xzzn.ems.domain.vo.AmmeterRevenueStatisListVo">
select
DATE_FORMAT(t.data_date, '%Y-%m-%d') as dataTime,
COALESCE(c.is_workday, CASE WHEN WEEKDAY(t.data_date) &lt; 5 THEN 1 ELSE 0 END) as isWorkday,
CASE
WHEN COALESCE(c.is_workday, CASE WHEN WEEKDAY(t.data_date) &lt; 5 THEN 1 ELSE 0 END) = 1 THEN '工作日'
ELSE '节假日'
END as dayType,
COALESCE(NULLIF(TRIM(w.weather_desc), ''), '--') as weatherDesc,
ROUND(SUM(IFNULL(t.peak_charge_diff, 0) * IFNULL(pc.peak, 0)), 3) as activePeakPrice,
ROUND(SUM(IFNULL(t.peak_discharge_diff, 0) * IFNULL(pc.peak, 0)), 3) as reActivePeakPrice,
ROUND(SUM(IFNULL(t.high_charge_diff, 0) * IFNULL(pc.high, 0)), 3) as activeHighPrice,
ROUND(SUM(IFNULL(t.high_discharge_diff, 0) * IFNULL(pc.high, 0)), 3) as reActiveHighPrice,
ROUND(SUM(IFNULL(t.flat_charge_diff, 0) * IFNULL(pc.flat, 0)), 3) as activeFlatPrice,
ROUND(SUM(IFNULL(t.flat_discharge_diff, 0) * IFNULL(pc.flat, 0)), 3) as reActiveFlatPrice,
ROUND(SUM(IFNULL(t.valley_charge_diff, 0) * IFNULL(pc.valley, 0)), 3) as activeValleyPrice,
ROUND(SUM(IFNULL(t.valley_discharge_diff, 0) * IFNULL(pc.valley, 0)), 3) as reActiveValleyPrice,
ROUND(
SUM(
(IFNULL(t.peak_discharge_diff, 0) - IFNULL(t.peak_charge_diff, 0)) * IFNULL(pc.peak, 0)
+ (IFNULL(t.high_discharge_diff, 0) - IFNULL(t.high_charge_diff, 0)) * IFNULL(pc.high, 0)
+ (IFNULL(t.flat_discharge_diff, 0) - IFNULL(t.flat_charge_diff, 0)) * IFNULL(pc.flat, 0)
+ (IFNULL(t.valley_discharge_diff, 0) - IFNULL(t.valley_charge_diff, 0)) * IFNULL(pc.valley, 0)
revenue.dataTime as dataTime,
revenue.isWorkday as isWorkday,
case
when revenue.isWorkday = 1 then '工作日'
else '节假日'
end as dayType,
revenue.weatherDesc as weatherDesc,
revenue.activePeakPrice as activePeakPrice,
revenue.reActivePeakPrice as reActivePeakPrice,
revenue.activeHighPrice as activeHighPrice,
revenue.reActiveHighPrice as reActiveHighPrice,
revenue.activeFlatPrice as activeFlatPrice,
revenue.reActiveFlatPrice as reActiveFlatPrice,
revenue.activeValleyPrice as activeValleyPrice,
revenue.reActiveValleyPrice as reActiveValleyPrice,
revenue.actualRevenue as actualRevenue
from (
select
DATE_FORMAT(t.data_date, '%Y-%m-%d') as dataTime,
COALESCE(c.is_workday, CASE WHEN WEEKDAY(t.data_date) &lt; 5 THEN 1 ELSE 0 END) as isWorkday,
COALESCE(NULLIF(TRIM(w.weather_desc), ''), '--') as weatherDesc,
ROUND(SUM(IFNULL(t.peak_charge_diff, 0) * IFNULL(pc.peak, 0)), 3) as activePeakPrice,
ROUND(SUM(IFNULL(t.peak_discharge_diff, 0) * IFNULL(pc.peak, 0)), 3) as reActivePeakPrice,
ROUND(SUM(IFNULL(t.high_charge_diff, 0) * IFNULL(pc.high, 0)), 3) as activeHighPrice,
ROUND(SUM(IFNULL(t.high_discharge_diff, 0) * IFNULL(pc.high, 0)), 3) as reActiveHighPrice,
ROUND(SUM(IFNULL(t.flat_charge_diff, 0) * IFNULL(pc.flat, 0)), 3) as activeFlatPrice,
ROUND(SUM(IFNULL(t.flat_discharge_diff, 0) * IFNULL(pc.flat, 0)), 3) as reActiveFlatPrice,
ROUND(SUM(IFNULL(t.valley_charge_diff, 0) * IFNULL(pc.valley, 0)), 3) as activeValleyPrice,
ROUND(SUM(IFNULL(t.valley_discharge_diff, 0) * IFNULL(pc.valley, 0)), 3) as reActiveValleyPrice,
ROUND(
SUM(
(IFNULL(t.peak_discharge_diff, 0) - IFNULL(t.peak_charge_diff, 0)) * IFNULL(pc.peak, 0)
+ (IFNULL(t.high_discharge_diff, 0) - IFNULL(t.high_charge_diff, 0)) * IFNULL(pc.high, 0)
+ (IFNULL(t.flat_discharge_diff, 0) - IFNULL(t.flat_charge_diff, 0)) * IFNULL(pc.flat, 0)
+ (IFNULL(t.valley_discharge_diff, 0) - IFNULL(t.valley_charge_diff, 0)) * IFNULL(pc.valley, 0)
),
3
) as actualRevenue
from ems_daily_energy_data t
left join ems_calendar_day c on c.calendar_date = t.data_date
left join ems_site_weather_day w on w.site_id = t.site_id and w.calendar_date = t.data_date
left join ems_energy_price_config pc on pc.id = COALESCE(
(
select p.id
from ems_energy_price_config p
where p.site_id = t.site_id
and STR_TO_DATE(CONCAT(p.year, '-', LPAD(p.month, 2, '0'), '-01'), '%Y-%m-%d') &lt;= DATE_FORMAT(t.data_date, '%Y-%m-01')
order by STR_TO_DATE(CONCAT(p.year, '-', LPAD(p.month, 2, '0'), '-01'), '%Y-%m-%d') desc
limit 1
),
3
) as actualRevenue
from ems_daily_energy_data t
left join ems_calendar_day c on c.calendar_date = t.data_date
left join ems_site_weather_day w on w.site_id = t.site_id and w.calendar_date = t.data_date
left join ems_energy_price_config pc on pc.id = COALESCE(
(
select p.id
from ems_energy_price_config p
where p.site_id = t.site_id
and STR_TO_DATE(CONCAT(p.year, '-', LPAD(p.month, 2, '0'), '-01'), '%Y-%m-%d') &lt;= DATE_FORMAT(t.data_date, '%Y-%m-01')
order by STR_TO_DATE(CONCAT(p.year, '-', LPAD(p.month, 2, '0'), '-01'), '%Y-%m-%d') desc
limit 1
),
(
select p2.id
from ems_energy_price_config p2
where p2.site_id = t.site_id
order by STR_TO_DATE(CONCAT(p2.year, '-', LPAD(p2.month, 2, '0'), '-01'), '%Y-%m-%d') asc
limit 1
(
select p2.id
from ems_energy_price_config p2
where p2.site_id = t.site_id
order by STR_TO_DATE(CONCAT(p2.year, '-', LPAD(p2.month, 2, '0'), '-01'), '%Y-%m-%d') asc
limit 1
)
)
)
where 1=1
and t.data_hour is not null
<if test="siteId != null">
and t.site_id = #{siteId}
</if>
<if test="startTime != null">
and t.data_date &gt;= #{startTime}
</if>
<if test="endTime != null">
and t.data_date &lt;= #{endTime}
</if>
group by t.data_date,
COALESCE(c.is_workday, CASE WHEN WEEKDAY(t.data_date) &lt; 5 THEN 1 ELSE 0 END),
COALESCE(NULLIF(TRIM(w.weather_desc), ''), '--')
order by t.data_date desc
where 1=1
and t.data_hour is not null
<if test="siteId != null">
and t.site_id = #{siteId}
</if>
<if test="startTime != null">
and t.data_date &gt;= #{startTime}
</if>
<if test="endTime != null">
and t.data_date &lt;= #{endTime}
</if>
group by t.data_date,
COALESCE(c.is_workday, CASE WHEN WEEKDAY(t.data_date) &lt; 5 THEN 1 ELSE 0 END),
COALESCE(NULLIF(TRIM(w.weather_desc), ''), '--')
) revenue
order by revenue.dataTime asc
</select>
</mapper>

View File

@ -28,6 +28,7 @@
<result property="modbusDataType" column="modbus_data_type"/>
<result property="modbusReadOrder" column="modbus_read_order"/>
<result property="modbusGroup" column="modbus_group"/>
<result property="decimalScale" column="decimal_scale"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
@ -39,7 +40,7 @@
select id, point_id, site_id, device_category, device_id, point_name, data_key, point_desc, register_address,
data_unit, data_a, data_k, data_b, data_bit, is_alarm, point_type, calc_expression,
collect_enabled, collect_source, modbus_register_type, modbus_data_type, modbus_read_order, modbus_group,
create_by, create_time, update_by, update_time, remark
decimal_scale, create_by, create_time, update_by, update_time, remark
from ems_point_config
</sql>
@ -92,6 +93,7 @@
<if test="modbusDataType != null">modbus_data_type,</if>
<if test="modbusReadOrder != null">modbus_read_order,</if>
<if test="modbusGroup != null">modbus_group,</if>
<if test="decimalScale != null">decimal_scale,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
@ -121,6 +123,7 @@
<if test="modbusDataType != null">#{modbusDataType},</if>
<if test="modbusReadOrder != null">#{modbusReadOrder},</if>
<if test="modbusGroup != null">#{modbusGroup},</if>
<if test="decimalScale != null">#{decimalScale},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
@ -134,7 +137,7 @@
point_id, site_id, device_category, device_id, point_name, data_key, point_desc, register_address,
data_unit, data_a, data_k, data_b, data_bit, is_alarm, point_type, calc_expression,
collect_enabled, collect_source, modbus_register_type, modbus_data_type, modbus_read_order, modbus_group,
create_by, create_time, update_by, update_time, remark
decimal_scale, create_by, create_time, update_by, update_time, remark
)
values
<foreach collection="list" item="item" separator=",">
@ -161,6 +164,7 @@
#{item.modbusDataType},
#{item.modbusReadOrder},
#{item.modbusGroup},
#{item.decimalScale},
#{item.createBy},
now(),
#{item.updateBy},
@ -195,6 +199,7 @@
<if test="modbusDataType != null">modbus_data_type = #{modbusDataType},</if>
<if test="modbusReadOrder != null">modbus_read_order = #{modbusReadOrder},</if>
<if test="modbusGroup != null">modbus_group = #{modbusGroup},</if>
<if test="decimalScale != null">decimal_scale = #{decimalScale},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remark != null">remark = #{remark},</if>
@ -226,6 +231,7 @@
modbus_data_type = #{modbusDataType},
modbus_read_order = #{modbusReadOrder},
modbus_group = #{modbusGroup},
decimal_scale = #{decimalScale},
update_by = #{updateBy},
update_time = now(),
remark = #{remark}
@ -259,13 +265,13 @@
point_id, site_id, device_category, device_id, point_name, data_key, point_desc, register_address,
data_unit, data_a, data_k, data_b, data_bit, is_alarm, point_type, calc_expression,
collect_enabled, collect_source, modbus_register_type, modbus_data_type, modbus_read_order, modbus_group,
create_by, create_time, update_by, update_time, remark
decimal_scale, create_by, create_time, update_by, update_time, remark
)
select
concat('PT_', replace(uuid(), '-', '')), #{targetSiteId}, device_category, device_id, point_name, data_key, point_desc, register_address,
data_unit, data_a, data_k, data_b, data_bit, is_alarm, point_type, calc_expression,
collect_enabled, collect_source, modbus_register_type, modbus_data_type, modbus_read_order, modbus_group,
#{operName}, now(), #{operName}, now(), remark
decimal_scale, #{operName}, now(), #{operName}, now(), remark
from ems_point_config
where site_id = #{templateSiteId}
</insert>

View File

@ -54,4 +54,14 @@
</foreach>
</insert>
<update id="updateEmsSiteMonitorPointMatch" parameterType="com.xzzn.ems.domain.EmsSiteMonitorPointMatch">
update ems_site_monitor_point_match
set data_point = #{dataPoint},
fixed_data_point = #{fixedDataPoint},
use_fixed_display = #{useFixedDisplay},
update_by = #{updateBy},
update_time = now()
where id = #{id}
</update>
</mapper>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xzzn.ems.mapper.SysBizRemarkMapper">
<resultMap id="SysBizRemarkResult" type="com.xzzn.ems.domain.SysBizRemark">
<result property="id" column="id"/>
<result property="bizType" column="biz_type"/>
<result property="bizKey1" column="biz_key1"/>
<result property="bizKey2" column="biz_key2"/>
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="delFlag" column="del_flag"/>
</resultMap>
<sql id="selectSysBizRemarkColumns">
select id, biz_type, biz_key1, biz_key2, remark, create_by, create_time, update_by, update_time, del_flag
from sys_biz_remark
</sql>
<select id="selectByBizKeys" resultMap="SysBizRemarkResult">
<include refid="selectSysBizRemarkColumns"/>
where biz_type = #{bizType}
and biz_key1 = #{bizKey1}
and biz_key2 = #{bizKey2}
and del_flag = '0'
limit 1
</select>
<select id="selectByBizKey2List" resultMap="SysBizRemarkResult">
<include refid="selectSysBizRemarkColumns"/>
where biz_type = #{bizType}
and biz_key1 = #{bizKey1}
and del_flag = '0'
and biz_key2 in
<foreach collection="bizKey2List" item="bizKey2" open="(" separator="," close=")">
#{bizKey2}
</foreach>
</select>
<insert id="insertSysBizRemark" parameterType="com.xzzn.ems.domain.SysBizRemark" useGeneratedKeys="true" keyProperty="id">
insert into sys_biz_remark
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="bizType != null">biz_type,</if>
<if test="bizKey1 != null">biz_key1,</if>
<if test="bizKey2 != null">biz_key2,</if>
<if test="remark != null">remark,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="delFlag != null">del_flag,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="bizType != null">#{bizType},</if>
<if test="bizKey1 != null">#{bizKey1},</if>
<if test="bizKey2 != null">#{bizKey2},</if>
<if test="remark != null">#{remark},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="delFlag != null">#{delFlag},</if>
</trim>
</insert>
<update id="updateSysBizRemark" parameterType="com.xzzn.ems.domain.SysBizRemark">
update sys_biz_remark
<trim prefix="set" suffixOverrides=",">
<if test="remark != null">remark = #{remark},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="delFlag != null">del_flag = #{delFlag},</if>
</trim>
where id = #{id}
</update>
</mapper>