Merge branch 'hhk' into main

plp
HuangHuiKang 3 weeks ago
commit dbd85b6665

@ -237,4 +237,18 @@ public class DeviceController {
}
@PostMapping("/scheduledStart")
public CommonResult<Boolean> scheduledStart(@RequestParam("id") Long id) {
return success(deviceService.scheduledStart(id));
}
@PostMapping("/scheduledStop")
public CommonResult<Boolean> scheduledStop(@RequestParam("id") Long id) {
return success(deviceService.scheduledStop(id));
}
}

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@Configuration
@EnableScheduling
public class SchedulerConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(20); // 设置线程池大小
scheduler.setThreadNamePrefix("scheduled-task-"); // 线程名前缀
scheduler.setAwaitTerminationSeconds(60); // 等待任务完成的秒数
scheduler.setWaitForTasksToCompleteOnShutdown(true); // 关闭时等待任务完成
scheduler.initialize(); // 初始化
return scheduler;
}
}

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.coretask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class DeviceTask {
private static final Logger logger = LoggerFactory.getLogger(DeviceTask.class);
/**
*
* @param deviceId ID
* @param deviceCode
*/
public void executeDeviceLogic(Long deviceId, String deviceCode) {
try {
// 创建时间格式化对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String currentTime = sdf.format(new Date());
logger.info("执行设备任务设备ID: {}, 设备编码: {}, 时间: {}",
deviceId, deviceCode, currentTime);
// TODO: 这里编写具体的设备执行逻辑
// 比如:读取设备数据、发送指令、处理响应等
} catch (Exception e) {
// 异常信息中也加入时间戳
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String errorTime = sdf.format(new Date());
logger.error("执行设备任务异常设备ID: {}, 异常时间: {}",
deviceId, errorTime, e);
}
}
}

@ -0,0 +1,142 @@
// TaskSchedulerManager.java
package cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.scheduler;
import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.coretask.DeviceTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
@Component
public class TaskSchedulerManager {
private static final Logger logger = LoggerFactory.getLogger(TaskSchedulerManager.class);
private TaskScheduler taskScheduler; // 移除 static
private DeviceTask deviceTask; // 移除 static
private final Map<Long, ScheduledFuture<?>> taskMap = new ConcurrentHashMap<>();
/**
* TaskScheduler
* Spring TaskScheduler Bean
*/
@Autowired
public TaskSchedulerManager(TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
/**
* DeviceTask
*/
@Autowired
public void setDeviceTask(DeviceTask deviceTask) {
this.deviceTask = deviceTask;
}
/**
* Spring TaskScheduler Bean
*
*/
@PostConstruct
public void init() {
if (taskScheduler == null) {
logger.warn("TaskScheduler not found in context, creating default one");
ThreadPoolTaskScheduler defaultScheduler = new ThreadPoolTaskScheduler();
defaultScheduler.setPoolSize(10);
defaultScheduler.setThreadNamePrefix("device-task-");
defaultScheduler.initialize();
this.taskScheduler = defaultScheduler;
}
}
/**
*
* static
*/
public boolean startDeviceTask(Long deviceId, String deviceCode, String cronExpression) {
try {
// 先停止已存在的任务
stopDeviceTask(deviceId);
// 验证 DeviceTask
if (deviceTask == null) {
logger.error("DeviceTask is not initialized");
return false;
}
// 创建新的定时任务
ScheduledFuture<?> future = taskScheduler.schedule(
() -> {
try {
deviceTask.executeDeviceLogic(deviceId, deviceCode);
} catch (Exception e) {
logger.error("Device task execution failed for deviceId: {}", deviceId, e);
}
},
new CronTrigger(cronExpression)
);
taskMap.put(deviceId, future);
logger.info("启动设备定时任务成功设备ID: {}, cron表达式: {}", deviceId, cronExpression);
return true;
} catch (Exception e) {
logger.error("启动设备定时任务失败设备ID: {}", deviceId, e);
return false;
}
}
/**
*
* static
*/
public boolean stopDeviceTask(Long deviceId) {
try {
ScheduledFuture<?> future = taskMap.get(deviceId);
if (future != null) {
future.cancel(true);
taskMap.remove(deviceId);
logger.info("停止设备定时任务成功设备ID: {}", deviceId);
return true;
} else {
logger.info("设备定时任务不存在设备ID: {}", deviceId);
return false;
}
} catch (Exception e) {
logger.error("停止设备定时任务失败设备ID: {}", deviceId, e);
return false;
}
}
/**
*
*/
public boolean isTaskRunning(Long deviceId) {
ScheduledFuture<?> future = taskMap.get(deviceId);
return future != null && !future.isCancelled() && !future.isDone();
}
/**
*
*/
public int getTaskCount() {
return taskMap.size();
}
/**
*
*/
public void stopAllTasks() {
logger.info("停止所有定时任务,共 {} 个", taskMap.size());
for (Long deviceId : taskMap.keySet()) {
stopDeviceTask(deviceId);
}
}
}

@ -0,0 +1,160 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.utils;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class CronExpressionUtils {
/**
* cron
* @param intervalSeconds
* @return cron
*/
public static String secondsToCron(Double intervalSeconds) {
if (intervalSeconds == null || intervalSeconds <= 0) {
throw new IllegalArgumentException("间隔秒数必须大于0");
}
long seconds = Math.round(intervalSeconds);
// 如果秒数小于60使用秒级定时
if (seconds < 60) {
return String.format("0/%d * * * * ?", seconds);
}
// 如果秒数在60-3599之间使用分钟级定时
else if (seconds < 3600) {
long minutes = seconds / 60;
if (seconds % 60 != 0) {
// 如果不是整分钟,需要特殊处理
return String.format("0 */%d * * * ?", minutes);
}
return String.format("0 0/%d * * * ?", minutes);
}
// 如果秒数在3600-86399之间使用小时级定时
else if (seconds < 86400) {
long hours = seconds / 3600;
if (seconds % 3600 != 0) {
// 如果不是整小时,转换为分钟
long minutes = seconds / 60;
return String.format("0 0/%d * * * ?", minutes);
}
return String.format("0 0 0/%d * * ?", hours);
}
// 如果大于等于1天使用天级定时
else {
long days = seconds / 86400;
if (seconds % 86400 != 0) {
// 如果不是整天,转换为小时
long hours = seconds / 3600;
return String.format("0 0 0/%d * * ?", hours);
}
return String.format("0 0 0 */%d * ?", days);
}
}
/**
* cron0
* @param intervalSeconds
* @return cron
*/
public String secondsToFixedRateCron(Double intervalSeconds) {
if (intervalSeconds == null || intervalSeconds <= 0) {
throw new IllegalArgumentException("间隔秒数必须大于0");
}
long seconds = Math.round(intervalSeconds);
// 计算分钟和剩余秒数
long minutes = seconds / 60;
long remainingSeconds = seconds % 60;
long remainingMinutes;
if (minutes == 0) {
// 小于1分钟只使用秒
return String.format("*/%d * * * * ?", seconds);
} else if (minutes < 60) {
// 小于1小时
if (remainingSeconds == 0) {
return String.format("0 */%d * * * ?", minutes);
} else {
// 有剩余秒数,需要更复杂的表达式
return String.format("%d */%d * * * ?", remainingSeconds, minutes);
}
} else if (minutes < 1440) { // 小于24小时
long hours = minutes / 60;
remainingMinutes = minutes % 60;
if (remainingMinutes == 0 && remainingSeconds == 0) {
return String.format("0 0 */%d * * ?", hours);
} else if (remainingSeconds == 0) {
return String.format("0 %d */%d * * ?", remainingMinutes, hours);
} else {
return String.format("%d %d */%d * * ?", remainingSeconds, remainingMinutes, hours);
}
} else {
// 大于等于1天
long days = minutes / 1440;
long remainingHours = (minutes % 1440) / 60;
remainingMinutes = minutes % 60;
if (remainingHours == 0 && remainingMinutes == 0 && remainingSeconds == 0) {
return String.format("0 0 0 */%d * ?", days);
} else {
// 简化处理,转换为小时
long totalHours = (days * 24) + remainingHours;
if (remainingMinutes == 0 && remainingSeconds == 0) {
return String.format("0 0 */%d * * ?", totalHours);
} else {
return String.format("%d %d */%d * * ?",
remainingSeconds, remainingMinutes, totalHours);
}
}
}
}
/**
* cron
* @param cronExpression cron
* @return
*/
public boolean isValidCron(String cronExpression) {
if (!StringUtils.hasText(cronExpression)) {
return false;
}
try {
new CronTrigger(cronExpression);
return true;
} catch (Exception e) {
return false;
}
}
/**
*
* @param intervalSeconds
* @return
*/
public String getIntervalDescription(Double intervalSeconds) {
if (intervalSeconds == null) {
return "未设置";
}
long seconds = Math.round(intervalSeconds);
if (seconds < 60) {
return seconds + "秒";
} else if (seconds < 3600) {
return (seconds / 60) + "分" + (seconds % 60) + "秒";
} else if (seconds < 86400) {
return (seconds / 3600) + "小时" + ((seconds % 3600) / 60) + "分";
} else {
return (seconds / 86400) + "天" + ((seconds % 86400) / 3600) + "小时";
}
}
}

@ -87,6 +87,7 @@ public class DeviceModelController {
return success(BeanUtils.toBean(pageResult, DeviceModelRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出采集设备模型 Excel")
@PreAuthorize("@ss.hasPermission('iot:device-model:export')")

@ -126,4 +126,9 @@ public interface DeviceService {
Map<Long, Map<String, Object>> createDeviceDataMap(Long deviceId);
List<DevicePointRespVO> devicePointList();
Boolean scheduledStart(Long id);
Boolean scheduledStop(Long id);
}

@ -6,6 +6,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.opc.OpcUtils;
import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.utils.CronExpressionUtils;
import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.scheduler.TaskSchedulerManager;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*;
import cn.iocoder.yudao.module.iot.controller.admin.devicecontactmodel.vo.DeviceContactModelPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.mqttdatarecord.vo.MqttDataRecordPageReqVO;
@ -87,6 +89,8 @@ public class DeviceServiceImpl implements DeviceService {
@Resource
private DeviceAttributeTypeMapper deviceAttributeTypeMapper;
@Resource
private TaskSchedulerManager taskSchedulerManager;
@Override
@Transactional(rollbackFor = Exception.class)
@ -234,7 +238,7 @@ public class DeviceServiceImpl implements DeviceService {
for (DeviceContactModelDO record : records) {
Map<String, Object> data = deviceDataMap.get(record.getId());
if (data != null) {
record.setAddressValue(data.get("addressValue")); // 设置 addressValue
record.setAddressValue(adjustByRatio(data.get("addressValue"), record.getRatio())); // 设置 addressValue
record.setLatestCollectionTime((String) data.get("timestamp")); // 设置 latestCollectionTime
}
}
@ -242,6 +246,18 @@ public class DeviceServiceImpl implements DeviceService {
return deviceModelAttributeDOPageResult;
}
private Object adjustByRatio(Object value, Double ratio) {
if (value == null) return null;
try {
Double result = Double.parseDouble(value.toString());
return (ratio != null && ratio != 0.0) ? result / ratio : result;
} catch (NumberFormatException e) {
return value; // 转换失败返回原值
}
}
@Override
public Map<Long, Map<String, Object>> createDeviceDataMap(Long deviceId) {
// 创建结果Map键为数据记录ID (Long),值为该条记录的详细信息 (Map<String, Object>)
@ -350,6 +366,7 @@ public class DeviceServiceImpl implements DeviceService {
}
@Override
public Long createDeviceAttribute(DeviceAttributeDO deviceAttribute) {
deviceAttributeMapper.insert(deviceAttribute);
@ -666,4 +683,53 @@ public class DeviceServiceImpl implements DeviceService {
return deviceMapper.selectBatchIds(ids);
}
@Override
public Boolean scheduledStart(Long id) {
try {
// 1. 查询设备信息
DeviceDO deviceDO = deviceMapper.selectById(id);
if (deviceDO==null) {
throw new RuntimeException("设备不存在");
}
if (deviceDO.getSampleCycle() == null) {
throw new RuntimeException("设备cron表达式为空");
}
String cronExpression = CronExpressionUtils.secondsToCron(deviceDO.getSampleCycle());
// 2. 启动定时任务
boolean success = taskSchedulerManager.startDeviceTask(
deviceDO.getId(),
deviceDO.getDeviceCode(),
cronExpression
);
if (!success) {
throw new RuntimeException("启动定时任务失败");
}
return true;
} catch (Exception e) {
throw new RuntimeException("连接设备失败: " + e.getMessage(), e);
}
}
@Override
public Boolean scheduledStop(Long id) {
try {
// 1. 停止定时任务
taskSchedulerManager.stopDeviceTask(id);
return true;
} catch (Exception e) {
throw new RuntimeException("断开设备失败: " + e.getMessage(), e);
}
}
}

@ -377,7 +377,7 @@ public class TDengineService {
}
});
} catch (Exception e) {
log.error("查询设备" + id + "的最新数据时发生异常", e);
// log.error("查询设备" + id + "的最新数据时发生异常", e);
// e.printStackTrace();
return Collections.singletonList(createEmptyResult(id));
}

@ -85,6 +85,14 @@ public class CriticalComponentController {
return success(BeanUtils.toBean(pageResult, CriticalComponentRespVO.class));
}
@GetMapping("/list")
@Operation(summary = "获得设备关键件列表")
@PreAuthorize("@ss.hasPermission('mes:critical-component:query')")
public CommonResult<List<CriticalComponentDO>> getCriticalComponentList() {
List<CriticalComponentDO> criticalComponentList= criticalComponentService.getCriticalComponentList();
return success(criticalComponentList);
}
@GetMapping("/export-excel")
@Operation(summary = "导出设备关键件 Excel")
@PreAuthorize("@ss.hasPermission('mes:critical-component:export')")

@ -85,6 +85,8 @@ public class DeviceLedgerController {
return success(BeanUtils.toBean(deviceLedger, DeviceLedgerRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得设备台账分页")
@PreAuthorize("@ss.hasPermission('mes:device-ledger:query')")
@ -94,13 +96,21 @@ public class DeviceLedgerController {
}
@GetMapping("/list")
@Operation(summary = "获得设备台账列表")
@PreAuthorize("@ss.hasPermission('mes:device-ledger:query')")
public CommonResult<List<DeviceLedgerDO>> getDeviceLedgerList() {
List<DeviceLedgerDO> deviceLedgerDOList = deviceLedgerService.getDeviceLedgerList();
return success(deviceLedgerDOList);
}
@GetMapping("/export-excel")
@Operation(summary = "导出设备台账 Excel")
@PreAuthorize("@ss.hasPermission('mes:device-ledger:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportDeviceLedgerExcel(@Valid DeviceLedgerPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<DeviceLedgerDO> list = deviceLedgerService.getDeviceLedgerPage(pageReqVO).getList();
List<DeviceLedgerRespVO> deviceLedgerRespVOS = BeanUtils.toBean(list, DeviceLedgerRespVO.class);

@ -135,11 +135,15 @@ public class DvRepairController {
for (DvRepairRespVO dvSubjectRespVO : dvSubjectRespVOPageResult.getList()) {
if (dvSubjectRespVO.getAcceptedBy() !=null) {
AdminUserRespDTO user = adminUserApi.getUser(Long.valueOf(dvSubjectRespVO.getAcceptedBy()));
dvSubjectRespVO.setAcceptedBy("(" + user.getUsername() + ")" + user.getNickname());
if (user !=null ){
dvSubjectRespVO.setAcceptedBy("(" + user.getUsername() + ")" + user.getNickname());
}
}
if (dvSubjectRespVO.getConfirmBy() !=null) {
AdminUserRespDTO user = adminUserApi.getUser(Long.valueOf(dvSubjectRespVO.getConfirmBy()));
dvSubjectRespVO.setConfirmBy("(" + user.getUsername() + ")" + user.getNickname());
if (user !=null){
dvSubjectRespVO.setConfirmBy("(" + user.getUsername() + ")" + user.getNickname());
}
}
}

@ -103,6 +103,14 @@ public class DvSubjectController {
return success(buildCreatorName(dvSubjectRespVOPageResult));
}
@GetMapping("/list")
@Operation(summary = "获得维保项目列表")
@PreAuthorize("@ss.hasPermission('mes:dv-subject:query')")
public CommonResult<List<DvSubjectRespVO>> getDvSubjectList() {
List<DvSubjectDO> dvSubjectDOList = dvSubjectService.getDvSubjectList();
return success(dvSubjectDOList);
}
@GetMapping("/export-excel")
@Operation(summary = "导出维保项目 Excel")
@PreAuthorize("@ss.hasPermission('mes:dv-subject:export')")
@ -149,7 +157,9 @@ public class DvSubjectController {
private PageResult<DvSubjectRespVO> buildCreatorName(PageResult<DvSubjectRespVO> dvSubjectRespVOPageResult) {
for (DvSubjectRespVO dvSubjectRespVO : dvSubjectRespVOPageResult.getList()) {
AdminUserRespDTO user = adminUserApi.getUser(Long.valueOf(dvSubjectRespVO.getCreator()));
dvSubjectRespVO.setCreatorName("(" + user.getUsername()+ ")" + user.getNickname());
if (user!=null) {
dvSubjectRespVO.setCreatorName("(" + user.getUsername() + ")" + user.getNickname());
}
}
return dvSubjectRespVOPageResult;

@ -15,7 +15,7 @@ import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 维保项目 Response VO")
@Data
@ExcelIgnoreUnannotated
public class DvSubjectRespVO extends BaseDO {
public class DvSubjectRespVO{
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "24679")
// @ExcelProperty("ID")
@ -63,6 +63,9 @@ public class DvSubjectRespVO extends BaseDO {
@ExcelProperty("判定基准")
private String judgmentCriteria;
@Schema(description = "创建人")
private String creator;
@Schema(description = "创建人名字")
@ExcelProperty("创建人名字")
private String creatorName;

@ -6,10 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.EnergyDeviceConsumptionReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.EnergyDevicePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.EnergyDeviceRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.EnergyDeviceSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.energydevice.EnergyDeviceCheckRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.energydevice.EnergyDeviceDO;
import cn.iocoder.yudao.module.mes.service.energydevice.EnergyDeviceService;
@ -24,6 +21,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
@ -108,6 +106,23 @@ public class EnergyDeviceController {
@GetMapping("/record-export-excel")
@Operation(summary = "导出数据记录 Excel")
@PreAuthorize("@ss.hasPermission('mes:energy-device:export')")
@ApiAccessLog(operateType = EXPORT)
public void recordExportExcel(@Valid EnergyDeviceConsumptionReqVO deviceConsumptionReqVO,
HttpServletResponse response) throws IOException {
List<EnergyDeviceRespVO> energyDeviceRespVOS = energyDeviceService.queryDataRecords(deviceConsumptionReqVO);
List<EnergyDeviceRecordRespVO> energyDeviceRecordRespVOS = BeanUtils.toBean(energyDeviceRespVOS, EnergyDeviceRecordRespVO.class);
// 设置响应头
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode("能耗报表.xls", "UTF-8"));
response.setHeader("Content-Encoding", "identity");
// 导出 Excel
ExcelUtils.write(response, "能耗报表.xls", "数据", EnergyDeviceRecordRespVO.class, energyDeviceRecordRespVOS);
}
// ==================== 子表(抄表记录) ====================
@GetMapping("/energy-device-check-record/page")

@ -20,5 +20,7 @@ public class EnergyDeviceConsumptionReqVO {
@Schema(description = "结束时间")
private String endTime;
@Schema(description = "ids导出集合用")
private String ids;
}

@ -61,4 +61,8 @@ public class EnergyDevicePageReqVO extends PageParam {
@Schema(description = "计算规则", example = "车间1")
private String rules;
@Schema(description = "ids导出集合用")
private String ids;
}

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 能耗管理-能耗报表导出 Response VO")
@Data
@ExcelIgnoreUnannotated
public class EnergyDeviceRecordRespVO {
@Schema(description = "名称", example = "芋艿")
@ExcelProperty("名称")
private String name;
@Schema(description = "能耗类型名称", example = "水")
@ExcelProperty("能耗类型名称")
private String deviceTypeName;
@Schema(description = "所属区域名称", example = "车间1")
@ExcelProperty("所属区域名称")
private String orgName;
@Schema(description = "能耗总用量")
@ExcelProperty("能耗总用量")
private String energyConsumption;
@Schema(description = "最新数据时间")
@ExcelProperty("开始时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private String latestDataTime;
@Schema(description = "最早数据时间")
@ExcelProperty("结束时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private String earliestDataTime;
}

@ -1,6 +1,11 @@
package cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
@ -21,14 +26,14 @@ public class EnergyDeviceRespVO {
// @ExcelProperty("ID")
private Long id;
@Schema(description = "名称", example = "芋艿")
@ExcelProperty("名称")
private String name;
@Schema(description = "编码")
@ExcelProperty("编码")
private String code;
@Schema(description = "名称", example = "芋艿")
@ExcelProperty("名称")
private String name;
// @Schema(description = "设备类型", example = "1")
// @ExcelProperty(value = "设备类型", converter = DictConvert.class)
// @DictFormat("mes_energy_device_type") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
@ -54,26 +59,29 @@ public class EnergyDeviceRespVO {
// @ExcelProperty("单位")
private String unitName;
@Schema(description = "能耗类型名称", example = "水")
@ExcelProperty("能耗类型名称")
private String deviceTypeName;
@Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("是否启用")
private Boolean isEnable;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
@ColumnWidth(20)
// @ExcelProperty("创建时间")
// @ColumnWidth(20)
private LocalDateTime createTime;
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("更新时间")
@ColumnWidth(20)
private LocalDateTime updateTime;
@Schema(description = "能耗类型ID", example = "1")
// @ExcelProperty("能耗类型ID")
private Long deviceTypeId;
@Schema(description = "能耗类型名称", example = "水")
@ExcelProperty("能耗类型名称")
private String deviceTypeName;
@Schema(description = "所属区域ID", example = "1")
// @ExcelProperty("所属区域ID")
@ -92,15 +100,25 @@ public class EnergyDeviceRespVO {
@Schema(description = "能耗总用量")
@ExcelProperty("能耗总用量")
// @ExcelProperty("能耗总用量")
private String energyConsumption;
@Schema(description = "子列表点位参数值")
@ExcelProperty("子列表点位参数值")
private Map<String,String> sublistPointList;
@Schema(description = "点位差值列表")
private Map<String, String> operationRulesVOMap;
@Schema(description = "最新数据时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String latestDataTime;
@Schema(description = "最早数据时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String earliestDataTime;
@Schema(description = "点位详情列表")
private List<PointDetailVO> pointDetails;
}

@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PointDetailVO {
/**
*
*/
private String pointName;
/**
* ID
*/
private Long pointId;
/**
* ID
*/
private Long deviceId;
/**
*
*/
private String earliestValue;
/**
*
*/
private String earliestTime;
/**
*
*/
private String latestValue;
/**
*
*/
private String latestTime;
/**
*
*/
private String difference;
/**
*
*/
private String operator;
/**
*
*/
private String unit;
}

@ -135,11 +135,15 @@ public class MoldRepairController {
for (MoldRepairRespVO moldSubjectRespVO : moldSubjectRespVOPageResult.getList()) {
if (moldSubjectRespVO.getAcceptedBy() !=null) {
AdminUserRespDTO user = adminUserApi.getUser(Long.valueOf(moldSubjectRespVO.getAcceptedBy()));
moldSubjectRespVO.setAcceptedBy("(" + user.getUsername() + ")" + user.getNickname());
if (user!=null) {
moldSubjectRespVO.setAcceptedBy("(" + user.getUsername() + ")" + user.getNickname());
}
}
if (moldSubjectRespVO.getConfirmBy() !=null) {
AdminUserRespDTO user = adminUserApi.getUser(Long.valueOf(moldSubjectRespVO.getConfirmBy()));
moldSubjectRespVO.setConfirmBy("(" + user.getUsername() + ")" + user.getNickname());
if (user!=null) {
moldSubjectRespVO.setConfirmBy("(" + user.getUsername() + ")" + user.getNickname());
}
}
}

@ -149,7 +149,9 @@ public class MoldSubjectController {
private PageResult<MoldSubjectRespVO> buildCreatorName(PageResult<MoldSubjectRespVO> moldSubjectRespVOPageResult) {
for (MoldSubjectRespVO moldSubjectRespVO : moldSubjectRespVOPageResult.getList()) {
AdminUserRespDTO user = adminUserApi.getUser(Long.valueOf(moldSubjectRespVO.getCreator()));
moldSubjectRespVO.setCreatorName("(" + user.getUsername()+ ")" + user.getNickname());
if (user!=null) {
moldSubjectRespVO.setCreatorName("(" + user.getUsername() + ")" + user.getNickname());
}
}
return moldSubjectRespVOPageResult;

@ -99,6 +99,14 @@ public class PlanMaintenanceController {
return success(buildPageCreatorName(planMaintenanceRespVOPageResult));
}
@GetMapping("/list")
@Operation(summary = "获得方案维护列表")
@PreAuthorize("@ss.hasPermission('mes:plan-maintenance:query')")
public CommonResult<List<PlanMaintenanceDO>> getPlanMaintenanceList() {
List<PlanMaintenanceDO> planMaintenanceList = planMaintenanceService.getPlanMaintenanceList();
return success(planMaintenanceList);
}
@GetMapping("/export-excel")
@Operation(summary = "导出方案维护 Excel")
@PreAuthorize("@ss.hasPermission('mes:plan-maintenance:export')")

@ -1,11 +1,14 @@
package cn.iocoder.yudao.module.mes.dal.mysql.energydevice;
import java.util.*;
import java.util.stream.Collectors;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.mes.dal.dataobject.deviceledger.DeviceLedgerDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.energydevice.EnergyDeviceDO;
import com.alibaba.excel.util.StringUtils;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.*;
@ -18,7 +21,10 @@ import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.*;
public interface EnergyDeviceMapper extends BaseMapperX<EnergyDeviceDO> {
default PageResult<EnergyDeviceDO> selectPage(EnergyDevicePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<EnergyDeviceDO>()
LambdaQueryWrapperX<EnergyDeviceDO> energyDeviceDOLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
energyDeviceDOLambdaQueryWrapperX
.likeIfPresent(EnergyDeviceDO::getName, reqVO.getName())
.eqIfPresent(EnergyDeviceDO::getCode, reqVO.getCode())
.eqIfPresent(EnergyDeviceDO::getInfo, reqVO.getInfo())
@ -28,7 +34,19 @@ public interface EnergyDeviceMapper extends BaseMapperX<EnergyDeviceDO> {
.likeIfPresent(EnergyDeviceDO::getUnitName, reqVO.getUnitName())
.eqIfPresent(EnergyDeviceDO::getIsEnable, reqVO.getIsEnable())
.betweenIfPresent(EnergyDeviceDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(EnergyDeviceDO::getId));
.orderByDesc(EnergyDeviceDO::getId);
// 单独处理 ids 条件
if (StringUtils.isNotBlank(reqVO.getIds())) {
List<Long> idList = Arrays.stream(reqVO.getIds().split(","))
.map(String::trim)
.map(Long::valueOf)
.collect(Collectors.toList());
energyDeviceDOLambdaQueryWrapperX.in(EnergyDeviceDO::getId, idList);
}
return selectPage(reqVO, energyDeviceDOLambdaQueryWrapperX);
}
}

@ -52,4 +52,5 @@ public interface CriticalComponentService {
*/
PageResult<CriticalComponentDO> getCriticalComponentPage(CriticalComponentPageReqVO pageReqVO);
List<CriticalComponentDO> getCriticalComponentList();
}

@ -156,4 +156,9 @@ public class CriticalComponentServiceImpl implements CriticalComponentService {
return criticalComponentMapper.selectPage(pageReqVO);
}
@Override
public List<CriticalComponentDO> getCriticalComponentList() {
return criticalComponentMapper.selectList();
}
}

@ -51,4 +51,5 @@ public interface DeviceLedgerService {
*/
PageResult<DeviceLedgerDO> getDeviceLedgerPage(DeviceLedgerPageReqVO pageReqVO);
List<DeviceLedgerDO> getDeviceLedgerList();
}

@ -198,7 +198,13 @@ public class DeviceLedgerServiceImpl implements DeviceLedgerService {
@Override
public PageResult<DeviceLedgerDO> getDeviceLedgerPage(DeviceLedgerPageReqVO pageReqVO) {
return deviceLedgerMapper.selectPage(pageReqVO);
PageResult<DeviceLedgerDO> deviceLedgerDOPageResult = deviceLedgerMapper.selectPage(pageReqVO);
return deviceLedgerDOPageResult;
}
@Override
public List<DeviceLedgerDO> getDeviceLedgerList() {
return deviceLedgerMapper.selectList();
}
}

@ -53,4 +53,5 @@ public interface DvSubjectService {
*/
PageResult<DvSubjectDO> getDvSubjectPage(DvSubjectPageReqVO pageReqVO);
List<DvSubjectDO> getDvSubjectList();
}

@ -125,4 +125,9 @@ public class DvSubjectServiceImpl implements DvSubjectService {
return dvSubjectMapper.selectPage(pageReqVO);
}
@Override
public List<DvSubjectDO> getDvSubjectList() {
return dvSubjectMapper.selectList();
}
}

@ -28,6 +28,7 @@ import javax.annotation.Resource;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
@ -253,8 +254,14 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
List<EnergyDeviceRespVO> result = new ArrayList<>();
List<EnergyDeviceDO> energyDeviceDO = energyDeviceMapper.selectList(
Wrappers.<EnergyDeviceDO>lambdaQuery()
.in(StringUtils.isNotBlank(deviceConsumptionReqVO.getIds()),
EnergyDeviceDO::getId,
StringUtils.isNotBlank(deviceConsumptionReqVO.getIds())
? Arrays.asList(deviceConsumptionReqVO.getIds().split(","))
: null)
.like(StringUtils.isNotBlank(deviceConsumptionReqVO.getName()), EnergyDeviceDO::getName, deviceConsumptionReqVO.getName())
.eq(deviceConsumptionReqVO.getOrgId() != null, EnergyDeviceDO::getOrgId, deviceConsumptionReqVO.getOrgId()));
.eq(deviceConsumptionReqVO.getOrgId() != null, EnergyDeviceDO::getOrgId, deviceConsumptionReqVO.getOrgId())
.orderByDesc(EnergyDeviceDO::getCreateTime));
if (energyDeviceDO == null || energyDeviceDO.isEmpty()) {
throw exception(ENERGY_LIST_NOT_EXISTS);
@ -330,13 +337,127 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
respVO.setEnergyConsumption("0.0");
}
// 设置每个点位参数的差值key为点位参数名称
Map<String, String> operationRulesVOMap = buildPointDifferenceMap(calculationResult, deviceDO, rules);
respVO.setOperationRulesVOMap(operationRulesVOMap);
// 设置最新和最晚数据时间
if (StringUtils.isNotBlank(startTime)) {
respVO.setEarliestDataTime(formatDateTime(startTime, "yyyy-MM-dd HH:mm"));
}
if (StringUtils.isNotBlank(endTime)) {
respVO.setLatestDataTime(formatDateTime(endTime, "yyyy-MM-dd HH:mm"));
}
// 构建点位详情列表
List<PointDetailVO> pointDetails = buildPointDetails(calculationResult, deviceDO, rules);
respVO.setPointDetails(pointDetails);
return respVO;
}
/**
*
*/
private String formatDateTime(String time, String pattern) {
if (StringUtils.isBlank(time)) {
return "";
}
try {
// 常见时间格式列表
String[] possiblePatterns = {
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy/MM/dd HH:mm:ss",
"yyyy-MM-dd HH:mm"
};
for (String possiblePattern : possiblePatterns) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(possiblePattern);
Date date = sdf.parse(time);
SimpleDateFormat targetFormat = new SimpleDateFormat(pattern);
return targetFormat.format(date);
} catch (Exception e) {
continue;
}
}
// 如果解析失败,尝试简单截取
if (time.length() >= 16) {
return time.substring(0, 16);
}
} catch (Exception e) {
log.warn("时间格式化失败: {}", time, e);
}
return time;
}
/**
*
*/
/**
* yyyy-MM-dd HH:mm:ss
*/
private String getTimeFromData(Map<String, Object> calculationResult, String dataKey) {
if (calculationResult == null || dataKey == null) {
return "";
}
Map<String, Object> timeData = (Map<String, Object>) calculationResult.get(dataKey);
if (timeData == null) {
return "";
}
Object timestamp = timeData.get("timestamp");
if (timestamp == null) {
return "";
}
String timeStr = timestamp.toString();
// 如果已经是目标格式的长度,直接返回
if (timeStr.length() == 19) { // yyyy-MM-dd HH:mm:ss
return timeStr;
}
// 如果有毫秒部分截取前19位
if (timeStr.length() > 19 && timeStr.charAt(19) == '.') {
return timeStr.substring(0, 19);
}
// 如果是其他格式,尝试解析
try {
// 移除毫秒部分
if (timeStr.contains(".")) {
timeStr = timeStr.substring(0, timeStr.indexOf("."));
}
// 确保格式正确
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat originalSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
try {
// 尝试用带毫秒的格式解析
Date date = originalSdf.parse(timeStr);
return sdf.format(date);
} catch (Exception e) {
// 尝试不带毫秒解析
try {
Date date = sdf.parse(timeStr);
return sdf.format(date);
} catch (Exception e2) {
// 如果不是标准格式,只做简单截取
if (timeStr.length() > 19) {
return timeStr.substring(0, Math.min(19, timeStr.length()));
}
return timeStr;
}
}
} catch (Exception e) {
return timeStr;
}
}
/**
*
@ -357,8 +478,8 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
return null;
}
// 处理编码问题
queryData = fixEncoding(queryData);
// // 处理编码问题
// queryData = fixEncoding(queryData);
// 解析JSON数组
JSONArray jsonArray = JSON.parseArray(queryData);
@ -475,6 +596,10 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
return result;
}
// 提取实际采集时间
String latestTime = extractTimestamp(latestData);
String earliestTime = extractTimestamp(earliestData);
// 使用相同的规则计算两个时间点的总值
Double latestTotal = calculateTotalByRules(latestData, rules);
Double earliestTotal = calculateTotalByRules(earliestData, rules);
@ -484,6 +609,8 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
result.put("latestData", latestData);
result.put("earliestData", earliestData);
result.put("latestTime", latestTime);
result.put("earliestTime", earliestTime);
result.put("latestTotal", latestTotal != null ? latestTotal : 0.0);
result.put("earliestTotal", earliestTotal != null ? earliestTotal : 0.0);
result.put("totalDifference", (latestTotal != null && earliestTotal != null) ?
@ -497,6 +624,22 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
return result;
}
/**
*
*/
private String extractTimestamp(Map<String, Object> timePointData) {
if (timePointData == null) {
return "";
}
Object timestamp = timePointData.get("timestamp");
if (timestamp != null) {
return timestamp.toString();
}
return "";
}
/**
*
*/
@ -754,12 +897,13 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
/**
* Map
*
*
*/
private Map<String, String> buildPointDifferenceMap(Map<String, Object> calculationResult,
EnergyDeviceDO deviceDO,
List<OperationRulesVO> originalRules) {
Map<String, String> result = new HashMap<>();
private List<PointDetailVO> buildPointDetails(Map<String, Object> calculationResult,
EnergyDeviceDO deviceDO,
List<OperationRulesVO> originalRules) {
List<PointDetailVO> result = new ArrayList<>();
if (calculationResult == null || originalRules == null || originalRules.isEmpty()) {
return result;
@ -770,8 +914,8 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
return result;
}
// 获取最新数据用于获取点位名称
Map<String, Object> latestData = (Map<String, Object>) calculationResult.get("latestData");
Map<String, Object> earliestData = (Map<String, Object>) calculationResult.get("earliestData");
// 收集所有点位ID
List<Long> allPointIds = new ArrayList<>();
@ -782,37 +926,300 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
}
}
// 批量从本地数据库获取点位名称
Map<Long, String> pointNameMap = batchGetPointNamesFromLocalDB(allPointIds);
// 批量从本地数据库获取点位信息
Map<Long, DeviceContactModelDO> pointInfoMap = batchGetPointInfoFromLocalDB(allPointIds);
for (Map<String, Object> pointDiff : pointDifferences) {
Long pointId = (Long) pointDiff.get("pointId");
Long deviceId = (Long) pointDiff.get("deviceId");
if (pointId == null) {
continue;
}
// 获取点位名称
String pointName = null;
// 获取点位详细信息
PointDetailVO pointDetail = new PointDetailVO();
pointDetail.setPointId(pointId);
pointDetail.setDeviceId(deviceId);
// 设置运算符
String operator = (String) pointDiff.get("operator");
pointDetail.setOperator(operator != null ? operator : "");
// 1. 尝试从TD数据库获取
pointName = getPointNameFromTDData(latestData, pointId, pointDiff);
// 从TD数据获取点位信息和实际采集时间
setPointDataFromTD(pointDetail, latestData, earliestData, pointId, deviceId, pointDiff);
// 2. 如果TD数据库没有从本地数据库获取
if (StringUtils.isBlank(pointName)) {
pointName = pointNameMap.get(pointId);
// 如果没有从TD数据获取到点位名称,从本地数据库获取
if (StringUtils.isBlank(pointDetail.getPointName())) {
setPointInfoFromLocalDB(pointDetail, pointInfoMap.get(pointId));
}
// 获取差值
// 如果没有单位,尝试从本地数据库获取
if (StringUtils.isBlank(pointDetail.getUnit())) {
setPointUnitFromLocalDB(pointDetail, pointInfoMap.get(pointId));
}
// 设置数值
Double earliestValue = (Double) pointDiff.get("earliestValue");
Double latestValue = (Double) pointDiff.get("latestValue");
Double difference = (Double) pointDiff.get("difference");
String differenceStr = formatDouble(difference != null ? difference : 0.0);
// 以点位名称为key差值为value
if (StringUtils.isNotBlank(pointName)) {
result.put(pointName, differenceStr);
pointDetail.setEarliestValue(formatDouble(earliestValue != null ? earliestValue : 0.0));
pointDetail.setLatestValue(formatDouble(latestValue != null ? latestValue : 0.0));
pointDetail.setDifference(formatDouble(difference != null ? difference : 0.0));
result.add(pointDetail);
}
return result;
}
/**
* TD
*/
private void setPointDataFromTD(PointDetailVO pointDetail,
Map<String, Object> latestData,
Map<String, Object> earliestData,
Long pointId,
Long deviceId,
Map<String, Object> pointDiff) {
// 获取最新数据的时间
if (latestData != null) {
String deviceKey = "device_" + deviceId;
Map<String, Object> deviceData = (Map<String, Object>) latestData.get(deviceKey);
if (deviceData != null) {
Object timestamp = deviceData.get("timestamp");
if (timestamp != null) {
pointDetail.setLatestTime(formatToFullTime(timestamp.toString()));
}
}
}
// 获取最早数据的时间
if (earliestData != null) {
String deviceKey = "device_" + deviceId;
Map<String, Object> deviceData = (Map<String, Object>) earliestData.get(deviceKey);
if (deviceData != null) {
Object timestamp = deviceData.get("timestamp");
if (timestamp != null) {
pointDetail.setEarliestTime(formatToFullTime(timestamp.toString()));
}
}
}
// 从最新数据获取点位名称和单位
if (latestData != null) {
String deviceKey = "device_" + deviceId;
Map<String, Object> deviceData = (Map<String, Object>) latestData.get(deviceKey);
if (deviceData != null) {
Map<String, Object> pointData = getPointData(deviceData, pointId);
if (pointData != null) {
// 获取点位名称
String pointName = (String) pointData.get("attributeName");
if (StringUtils.isNotBlank(pointName)) {
pointDetail.setPointName(pointName);
} else {
pointName = (String) pointData.get("attributeCode");
if (StringUtils.isNotBlank(pointName)) {
pointDetail.setPointName(pointName);
}
}
// 获取单位
String unit = (String) pointData.get("dataUnit");
if (StringUtils.isNotBlank(unit)) {
pointDetail.setUnit(unit);
}
}
}
}
}
/**
* yyyy-MM-dd HH:mm:ss
*/
private String formatToFullTime(String time) {
if (StringUtils.isBlank(time)) {
return "";
}
try {
String timeStr = time.trim();
// 1. 如果已经是 yyyy-MM-dd HH:mm:ss 格式
if (timeStr.length() >= 19 && timeStr.charAt(4) == '-' && timeStr.charAt(10) == ' ') {
if (timeStr.length() == 19) {
return timeStr; // 直接返回
} else {
return timeStr.substring(0, 19); // 截取前19位
}
}
// 2. ISO格式: 2024-01-15T14:30:00.000+08:00
if (timeStr.contains("T")) {
timeStr = timeStr.replace("T", " ");
int dotIndex = timeStr.indexOf(".");
if (dotIndex > 0) {
timeStr = timeStr.substring(0, dotIndex);
} else {
int plusIndex = Math.min(timeStr.indexOf("+"), timeStr.indexOf("-", 11));
if (plusIndex > 0) {
timeStr = timeStr.substring(0, plusIndex);
} else if (timeStr.contains("Z")) {
timeStr = timeStr.substring(0, timeStr.indexOf("Z"));
}
}
return timeStr.length() >= 19 ? timeStr.substring(0, 19) : timeStr;
}
// 3. 尝试解析
String[] patterns = {
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy/MM/dd HH:mm:ss",
"yyyy/MM/dd HH:mm:ss.SSS"
};
for (String pattern : patterns) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
Date date = sdf.parse(timeStr);
SimpleDateFormat targetFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return targetFormat.format(date);
} catch (Exception e) {
continue;
}
}
} catch (Exception e) {
log.warn("时间格式化失败: {}", time, e);
}
return time;
}
/**
*
*/
private String extractHMSFromTimestamp(String timestamp) {
if (StringUtils.isBlank(timestamp)) {
return "";
}
try {
String timeStr = timestamp.trim();
// 处理多种格式
// 1. 标准格式: yyyy-MM-dd HH:mm:ss
if (timeStr.length() >= 19) {
return timeStr.substring(11, 19); // 提取 HH:mm:ss
}
// 2. ISO格式: 2024-01-15T14:30:00.000+08:00
if (timeStr.contains("T")) {
int tIndex = timeStr.indexOf("T");
int dotIndex = timeStr.indexOf(".");
if (dotIndex > tIndex) {
return timeStr.substring(tIndex + 1, dotIndex);
} else {
int plusIndex = timeStr.indexOf("+");
if (plusIndex > tIndex) {
return timeStr.substring(tIndex + 1, plusIndex);
} else if (timeStr.contains("Z") && timeStr.indexOf("Z") > tIndex) {
int zIndex = timeStr.indexOf("Z");
return timeStr.substring(tIndex + 1, zIndex);
}
}
}
// 3. 如果是 HH:mm:ss 格式
if (timeStr.matches("\\d{1,2}:\\d{1,2}:\\d{1,2}")) {
return timeStr;
}
// 4. 尝试解析
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date = sdf.parse(timeStr);
SimpleDateFormat hmsFormat = new SimpleDateFormat("HH:mm:ss");
return hmsFormat.format(date);
} catch (Exception e) {
// 继续尝试其他格式
}
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(timeStr);
SimpleDateFormat hmsFormat = new SimpleDateFormat("HH:mm:ss");
return hmsFormat.format(date);
} catch (Exception e) {
// 解析失败
}
} catch (Exception e) {
log.warn("时间格式解析失败: {}", timestamp, e);
}
return timestamp;
}
/**
*
*/
private void setPointInfoFromLocalDB(PointDetailVO pointDetail, DeviceContactModelDO pointInfo) {
if (pointDetail == null || pointInfo == null) {
return;
}
if (StringUtils.isBlank(pointDetail.getPointName())) {
if (StringUtils.isNotBlank(pointInfo.getAttributeName())) {
pointDetail.setPointName(pointInfo.getAttributeName());
} else if (StringUtils.isNotBlank(pointInfo.getAttributeCode())) {
pointDetail.setPointName(pointInfo.getAttributeCode());
} else {
// 如果还没有点位名称使用点位ID作为key
result.put("点位" + pointId, differenceStr);
pointDetail.setPointName("点位" + pointDetail.getPointId());
}
}
}
/**
*
*/
private void setPointUnitFromLocalDB(PointDetailVO pointDetail, DeviceContactModelDO pointInfo) {
if (pointDetail == null || pointInfo == null) {
return;
}
if (StringUtils.isBlank(pointDetail.getUnit())) {
if (StringUtils.isNotBlank(pointInfo.getDataUnit())) {
pointDetail.setUnit(pointInfo.getDataUnit());
}
}
}
/**
*
*/
private Map<Long, DeviceContactModelDO> batchGetPointInfoFromLocalDB(List<Long> pointIds) {
Map<Long, DeviceContactModelDO> result = new HashMap<>();
if (pointIds == null || pointIds.isEmpty()) {
return result;
}
try {
List<DeviceContactModelDO> deviceContacts = deviceContactModelMapper.selectBatchIds(pointIds);
if (deviceContacts != null && !deviceContacts.isEmpty()) {
for (DeviceContactModelDO deviceContact : deviceContacts) {
if (deviceContact != null && deviceContact.getId() != null) {
result.put(deviceContact.getId(), deviceContact);
}
}
}
} catch (Exception e) {
log.error("批量查询点位信息失败, pointIds: {}", pointIds, e);
}
return result;

@ -56,4 +56,5 @@ public interface PlanMaintenanceService {
List<DvSubjectDO> getSubjectList(Long id);
List<PlanMaintenanceDO> getPlanMaintenanceList();
}

@ -213,4 +213,9 @@ public class PlanMaintenanceServiceImpl implements PlanMaintenanceService {
return dvSubjectDOList;
}
@Override
public List<PlanMaintenanceDO> getPlanMaintenanceList() {
return planMaintenanceMapper.selectList();
}
}

@ -88,7 +88,7 @@ spring:
# Quartz 配置项,对应 QuartzProperties 配置类
spring:
quartz:
auto-startup: true # 本地开发环境,尽量不要开启 Job
auto-startup: false # 本地开发环境,尽量不要开启 Job
scheduler-name: schedulerName # Scheduler 名字。默认为 schedulerName
job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。
wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true

Loading…
Cancel
Save