1.修改定时任务方法,禁用原@Scheduled注解方式

2.记录仪详情分页查询
develop-QA
qinyulong 3 months ago
parent 8d52e46d35
commit 575609d9f3
  1. 2
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/device/pojo/entity/RecorderEntity.java
  2. 26
      blade-service/blade-desk/src/main/java/org/springblade/desk/device/controller/EquipmentController.java
  3. 26
      blade-service/blade-desk/src/main/java/org/springblade/desk/device/controller/FeiBaSetController.java
  4. 26
      blade-service/blade-desk/src/main/java/org/springblade/desk/device/controller/RecorderController.java
  5. 7
      blade-service/blade-desk/src/main/java/org/springblade/desk/device/service/IRecorderService.java
  6. 409
      blade-service/blade-desk/src/main/java/org/springblade/desk/device/service/impl/RecorderServiceImpl.java
  7. 8
      blade-service/blade-desk/src/main/resources/application-dev.yml
  8. 5
      doc/sql/mes/increase-260120.sql

@ -31,7 +31,7 @@ public class RecorderEntity extends BaseEntity {
* 记录仪
*/
@Schema(description = "记录仪")
private BigDecimal recorder;
private String recorder;
/**
* 记录时间
*/

@ -186,18 +186,18 @@ public class EquipmentController extends BladeController {
ExcelUtil.export(response, "设备信息表数据" + DateUtil.time(), "设备信息表数据表", list, EquipmentExcel.class);
}
@PostConstruct // 项目启动后立即执行一次
public void init() {
regularlyUpdated();
}
/**
* 同步IOT设备数据
* TODO 以后需要交由PowerJob服务统一管理
*/
@Scheduled(cron = "0 0 0/23 * * ?") // 每23小时执行一次
public void regularlyUpdated() {
equipmentService.regularlyUpdated();
}
// @PostConstruct // 项目启动后立即执行一次
// public void init() {
// regularlyUpdated();
// }
//
// /**
// * 同步IOT设备数据
// * TODO 以后需要交由PowerJob服务统一管理
// */
// @Scheduled(cron = "0 0 0/23 * * ?") // 每23小时执行一次
// public void regularlyUpdated() {
// equipmentService.regularlyUpdated();
// }
}

@ -149,17 +149,17 @@ public class FeiBaSetController extends BladeController {
ExcelUtil.export(response, "飞靶设置数据" + DateUtil.time(), "飞靶设置数据表", list, FeiBaSetExcel.class);
}
@PostConstruct // 项目启动后立即执行一次
public void init() {
regularlyUpdated();
}
/**
* 同步IOT飞靶数据
* TODO 以后需要交由PowerJob服务统一管理
*/
@Scheduled(cron = "0 0 0/23 * * ?") // 每23小时执行一次
public void regularlyUpdated() {
feiBaSetService.regularlyUpdated();
}
// @PostConstruct // 项目启动后立即执行一次
// public void init() {
// regularlyUpdated();
// }
//
// /**
// * 同步IOT飞靶数据
// * TODO 以后需要交由PowerJob服务统一管理
// */
// @Scheduled(cron = "0 0 0/23 * * ?") // 每23小时执行一次
// public void regularlyUpdated() {
// feiBaSetService.regularlyUpdated();
// }
}

@ -1,9 +1,13 @@
package org.springblade.desk.device.controller;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import jakarta.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import jakarta.validation.Valid;
@ -16,6 +20,7 @@ import org.springblade.core.tool.utils.Func;
import org.springblade.desk.device.pojo.excel.RecorderExcel;
import org.springblade.desk.device.pojo.request.RecorderQuery;
import org.springblade.desk.device.pojo.vo.LoadRecorderVO;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -64,9 +69,12 @@ public class RecorderController extends BladeController {
@GetMapping("/page")
@ApiOperationSupport(order = 3)
@Operation(summary = "分页", description = "传入recorder")
@Parameters({
@Parameter(name = "recorder", description = "记录仪编码", in = ParameterIn.QUERY, schema = @Schema(type = "string")),
})
public R<IPage<RecorderVO>> page(RecorderVO recorder, Query query) {
IPage<RecorderVO> pages = recorderService.selectRecorderPage(Condition.getPage(query), recorder);
return R.data(pages);
IPage<RecorderEntity> pages = recorderService.selectRecorderPage(Condition.getPage(query), recorder);
return R.data(RecorderWrapper.build().pageVO(pages));
}
/**
@ -137,4 +145,18 @@ public class RecorderController extends BladeController {
ExcelUtil.export(response, "记录仪记录数据" + DateUtil.time(), "记录仪记录数据表", list, RecorderExcel.class);
}
// @PostConstruct // 项目启动后立即执行一次
// public void init() {
// regularlyUpdated();
// }
//
// /**
// * 同步记录仪数据
// * TODO 以后需要交由PowerJob服务统一管理
// */
// @Scheduled(cron = "0 0 0/23 * * ?") // 每23小时执行一次
// public void regularlyUpdated() {
// recorderService.regularlyUpdated();
// }
}

@ -25,7 +25,7 @@ public interface IRecorderService extends BaseService<RecorderEntity> {
* @param recorder 查询参数
* @return IPage<RecorderVO>
*/
IPage<RecorderVO> selectRecorderPage(IPage<RecorderVO> page, RecorderVO recorder);
IPage<RecorderEntity> selectRecorderPage(IPage<RecorderEntity> page, RecorderVO recorder);
/**
@ -43,4 +43,9 @@ public interface IRecorderService extends BaseService<RecorderEntity> {
* @return
*/
LoadRecorderVO loadCalRecorder(RecorderQuery recorderQuery);
/**
* 同步记录仪数据
*/
void regularlyUpdated();
}

@ -2,9 +2,14 @@ package org.springblade.desk.device.service.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import jakarta.annotation.Resource;
import org.springblade.desk.device.mapper.EquipmentMapper;
import org.springblade.desk.device.pojo.entity.EquipmentEntity;
import org.springblade.desk.device.pojo.entity.RecorderCompareEntity;
import org.springblade.desk.device.pojo.entity.RecorderEntity;
import org.springblade.desk.device.pojo.excel.RecorderExcel;
@ -15,12 +20,20 @@ import org.springblade.desk.device.pojo.vo.RecorderVO;
import org.springblade.desk.device.mapper.RecorderMapper;
import org.springblade.desk.device.service.IRecorderCompareService;
import org.springblade.desk.device.service.IRecorderService;
import org.springblade.desk.jobtransfer.pojo.entity.PostHandleEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.core.mp.base.BaseServiceImpl;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@ -30,15 +43,40 @@ import java.util.stream.Collectors;
* @author qyl
* @since 2026-01-06
*/
@Slf4j
@Service
public class RecorderServiceImpl extends BaseServiceImpl<RecorderMapper, RecorderEntity> implements IRecorderService {
@Autowired
private IRecorderCompareService iRecorderCompareService;
@Resource
private EquipmentMapper equipmentMapper;
@Value("${request.iotNew.url}")
private String iotNewUrl;
@Value("${request.iot.orgId}")
private String orgId;
@Value("${request.iot.systemId}")
private String systemId;
// 使用JDK 17内置的HttpClient(线程安全,可复用)
private final HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.version(HttpClient.Version.HTTP_2)
.build();
@Override
public IPage<RecorderVO> selectRecorderPage(IPage<RecorderVO> page, RecorderVO recorder) {
return page.setRecords(baseMapper.selectRecorderPage(page, recorder));
public IPage<RecorderEntity> selectRecorderPage(IPage<RecorderEntity> page, RecorderVO recorder) {
// 1. 创建条件构造器
LambdaQueryWrapper<RecorderEntity> wrapper = Wrappers.<RecorderEntity>lambdaQuery();
// 2. 动态添加条件(核心!)
// 根据实体对象 entity 中字段是否为空,来动态拼接WHERE条件
wrapper.eq(Objects.nonNull(recorder.getRecorder()), RecorderEntity::getRecorder, recorder.getRecorder());
return baseMapper.selectPage(page, wrapper);
}
@ -137,4 +175,371 @@ public class RecorderServiceImpl extends BaseServiceImpl<RecorderMapper, Recorde
return loadRecorderVO;
}
/**
* 定期更新记录仪数据的方法
* 该方法会查询所有非产线设备并同步其记录仪数据到系统数据库
* 同步时间范围根据设备最后记录时间动态计算确保数据连续性
*/
@Override
public void regularlyUpdated() {
// 查询全部非产线设备
List<EquipmentEntity> equipmentEntities = getNonProductionEquipment();
if (equipmentEntities.isEmpty()) {
log.error("同步记录仪数据失败,当前系统无非产线设备");
return;
}
// 批量处理设备数据,使用并行流提高处理效率(根据设备数量决定是否启用)
equipmentEntities.stream()
.filter(equipment -> processEquipmentData(equipment))
.collect(Collectors.toList());
}
/**
* 获取非产线设备列表
*/
private List<EquipmentEntity> getNonProductionEquipment() {
LambdaQueryWrapper<EquipmentEntity> equiWrapper = new LambdaQueryWrapper<>();
equiWrapper.ne(EquipmentEntity::getCategorys, "产线设备");
return equipmentMapper.selectList(equiWrapper);
}
/**
* 处理单个设备的数据同步
*
* @return 处理成功返回true失败返回false
*/
private boolean processEquipmentData(EquipmentEntity equipmentEntity) {
try {
// 获取设备对应的记录仪信息
RecorderEntity recorderEntity = getRecorderEntity(equipmentEntity.getDeviceCode());
if (recorderEntity == null) {
log.warn("设备 {} 无对应的记录仪信息,跳过处理", equipmentEntity.getDeviceCode());
return false;
}
// 检查记录时间有效性(跳过未来时间的异常记录)
if (isFutureRecord(recorderEntity.getRecordDate())) {
log.debug("记录仪 {} 记录时间为未来时间,跳过处理", equipmentEntity.getDeviceCode());
return false;
}
// 计算时间范围
TimeRange timeRange = calculateTimeRange(recorderEntity);
if (timeRange == null) {
return false;
}
// 调用同步接口获取记录仪数据
JSONArray dataArray = doPost(equipmentEntity.getDeviceCode(),
timeRange.getStartTime(), timeRange.getStopTime());
// 处理同步结果
return processSyncResult(dataArray, equipmentEntity, recorderEntity);
} catch (Exception e) {
log.error("处理记录仪 {} 数据时发生异常", equipmentEntity.getDeviceCode(), e);
return false;
}
}
/**
* 获取记录仪实体
*/
private RecorderEntity getRecorderEntity(String deviceCode) {
return this.getOne(new LambdaQueryWrapper<RecorderEntity>()
.eq(RecorderEntity::getRecorder, deviceCode));
}
/**
* 检查记录时间是否为未来时间
*/
private boolean isFutureRecord(Date recordDate) {
return recordDate.getTime() > System.currentTimeMillis();
}
/**
* 计算数据同步的时间范围
*/
private TimeRange calculateTimeRange(RecorderEntity recorderEntity) {
// 计算基准时间点:当前时间前推8分钟(用于时间比较)
long baselineTime = OffsetDateTime.now().minusMinutes(8).toInstant().toEpochMilli();
Instant recordInstant = recorderEntity.getRecordDate().toInstant();
// 设置默认查询时间范围:当前时间前推6分钟到前推5分钟
String startTime = OffsetDateTime.now().minusMinutes(6)
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
String stopTime = OffsetDateTime.now().minusMinutes(5)
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
// 判断记录时间是否在基准时间之前或相等
if (recordInstant.isBefore(Instant.ofEpochMilli(baselineTime)) ||
recordInstant.equals(Instant.ofEpochMilli(baselineTime))) {
// 设置基于记录时间的开始时间
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
startTime = recordInstant.atZone(ZoneId.systemDefault()).format(formatter);
// 如果记录时间超过当前时间10分钟,设置基于记录时间的结束时间
if (recordInstant.plusSeconds(60 * 10).isBefore(Instant.ofEpochMilli(baselineTime))) {
stopTime = recordInstant.plusSeconds(120).atZone(ZoneId.systemDefault()).format(formatter);
}
}
return new TimeRange(startTime, stopTime);
}
/**
* 处理同步返回的数据
*/
private boolean processSyncResult(JSONArray dataArray, EquipmentEntity equipment,
RecorderEntity existingRecorder) {
if (dataArray != null && !dataArray.isEmpty()) {
return processSuccessfulSync(dataArray, equipment);
} else {
return createFallbackRecord(equipment, existingRecorder);
}
}
/**
* 处理有数据的同步结果
*/
private boolean processSuccessfulSync(JSONArray dataArray, EquipmentEntity equipment) {
// 获取最新的一条记录
JSONObject lastRecord = dataArray.getJSONObject(dataArray.size() - 1);
String deviceId = lastRecord.getString("deivceId");
String value = lastRecord.getString("value");
Date recordTime = lastRecord.getDate("timestamp");
// 检查记录是否已存在(去重判断)
if (isDuplicateRecord(equipment.getDeviceCode(), recordTime)) {
// log.debug("设备 {} 记录已存在,跳过保存", equipment.getDeviceCode());
return true;
}
// 创建新记录并保存
return saveNewRecord(deviceId, value, recordTime);
}
/**
* 检查重复记录
*/
private boolean isDuplicateRecord(String deviceCode, Date recordTime) {
return this.exists(new LambdaQueryWrapper<RecorderEntity>()
.eq(RecorderEntity::getRecorder, deviceCode)
.eq(RecorderEntity::getRecordDate, recordTime));
}
/**
* 保存新记录
*/
private boolean saveNewRecord(String deviceId, String value, Date recordTime) {
try {
RecorderEntity recorder = new RecorderEntity();
recorder.setRecorder(deviceId);
recorder.setRecordDate(recordTime);
recorder.setMemo(value);
return this.save(recorder);
} catch (Exception e) {
log.error("保存记录仪 {} 记录失败", deviceId, e);
return false;
}
}
/**
* 创建无数据时的回退记录
*/
private boolean createFallbackRecord(EquipmentEntity equipment, RecorderEntity existingRecorder) {
try {
RecorderEntity recorder = new RecorderEntity();
recorder.setRecorder(equipment.getDeviceCode());
recorder.setMemo("");
// 设置记录时间:有历史记录则基于最后记录时间,否则基于当前时间
Date newRecordDate = calculateFallbackDate(existingRecorder);
recorder.setRecordDate(newRecordDate);
return this.save(recorder);
} catch (Exception e) {
log.error("创建回退记录失败,记录仪:{}", equipment.getDeviceCode(), e);
return false;
}
}
/**
* 计算回退记录的日期
*/
private Date calculateFallbackDate(RecorderEntity existingRecorder) {
LocalDateTime baseTime;
if (existingRecorder != null && existingRecorder.getId() != null) {
// 有历史记录:在最后记录时间基础上增加2分钟
baseTime = existingRecorder.getRecordDate().toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
.plusMinutes(2);
} else {
// 无历史记录:当前时间前推5分钟
baseTime = LocalDateTime.now().minusMinutes(5);
}
return Date.from(baseTime.atZone(ZoneId.systemDefault()).toInstant());
}
/**
* 时间范围封装类
*/
private static class TimeRange {
private final String startTime;
private final String stopTime;
public TimeRange(String startTime, String stopTime) {
this.startTime = startTime;
this.stopTime = stopTime;
}
public String getStartTime() {
return startTime;
}
public String getStopTime() {
return stopTime;
}
}
/*public void regularlyUpdated() {
//查询全部非产线设备
LambdaQueryWrapper<EquipmentEntity> equiWrapper = new LambdaQueryWrapper<>();
equiWrapper.ne(EquipmentEntity::getCategorys,"产线设备");
List<EquipmentEntity> equipmentEntities = equipmentMapper.selectList(equiWrapper);
if(null == equipmentEntities || equipmentEntities.size() < 1){
log.error("同步记录仪数据失败,当前系统无非产线设备");
return;
}
String startTime;
String stopTime;
for (EquipmentEntity equipmentEntity : equipmentEntities) {
// 获取当前时间并减去8分钟,然后转换为时间戳
long time = OffsetDateTime.now().minusMinutes(8).toInstant().toEpochMilli();
// 获取当前时间并减去6分钟,然后格式化为字符串
startTime = OffsetDateTime.now().minusMinutes(6)
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); // 使用ISO格式,包含时区信息
// 获取当前时间并减去5分钟,然后格式化为字符串
stopTime = OffsetDateTime.now().minusMinutes(5)
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
RecorderEntity recorderEntity = this.getOne(new LambdaQueryWrapper<RecorderEntity>().eq(RecorderEntity::getRecorder, equipmentEntity.getDeviceCode()));
if (recorderEntity == null) return;
Date recordDate = recorderEntity.getRecordDate();
if (recordDate.getTime() > new Date().getTime()) continue;
if (recorderEntity != null && recorderEntity.getId() != null) {
if (recordDate != null) {
Instant recordInstant = recordDate.toInstant();
if (recordInstant.isBefore(Instant.ofEpochMilli(time)) || recordInstant.equals(Instant.ofEpochMilli(time))) {
// 格式化开始时间
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 根据需求调整格式
startTime = recordInstant.atZone(ZoneId.systemDefault()).format(formatter);
// 判断是否超过当前时间10分钟
if (recordInstant.plusSeconds(60 * 10).isBefore(Instant.ofEpochMilli(time))) {
// 在 recordDate 基础上加2分钟作为结束时间
stopTime = recordInstant.plusSeconds(120).atZone(ZoneId.systemDefault()).format(formatter);
}
}
}
}
//同步接口调用
JSONArray array = doPost(equipmentEntity.getDeviceCode(), startTime, stopTime);
//处理返回数据
RecorderEntity recorder = null;
if (array != null && array.size() > 0) {
JSONObject obj = null;
String deivceId = "", value = "";
Date recTime = null;
obj = array.getJSONObject(array.size() - 1);
deivceId = obj.getString("deivceId");
value = obj.getString("value");
recTime = obj.getDate("timestamp");
RecorderEntity getOneRecorder = this.getOne(new LambdaQueryWrapper<RecorderEntity>().eq(RecorderEntity::getRecorder, equipmentEntity.getDeviceCode())
.eq(RecorderEntity::getRecordDate,recTime));
if (getOneRecorder != null && getOneRecorder.getId() != null) continue;
//去重 增加唯一标识
recorder = new RecorderEntity();
recorder.setRecorder(deivceId);
recorder.setRecordDate(recTime);
recorder.setMemo(value);
this.save(recorder);
} else {
recorder = new RecorderEntity();
recorder.setRecorder(equipmentEntity.getDeviceCode());
if (recorderEntity != null && recorderEntity.getId() != null) {
// 将 Date 转换为 LocalDateTime 进行操作
LocalDateTime newRecordDate = recordDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
.plusMinutes(2); // 增加2分钟
recorder.setRecordDate(Date.from(newRecordDate.atZone(ZoneId.systemDefault()).toInstant()));
} else {
// 当前时间减去5分钟
LocalDateTime newRecordDate = LocalDateTime.now().minusMinutes(5);
recorder.setRecordDate(Date.from(newRecordDate.atZone(ZoneId.systemDefault()).toInstant()));
}
recorder.setMemo("");
this.save(recorder);
}
}
}*/
/**
* 从IOT同步数据
*
* @param deviceCode
* @param startTime
* @param stopTime
* @return
*/
private JSONArray doPost(String deviceCode, String startTime, String stopTime) {
log.debug("开始同步记录仪数据");
JSONArray retuArray = null;
// 构建请求体JSON
JSONObject requestBody = buildRequestBody(deviceCode, startTime, stopTime);
// 创建HTTP请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(iotNewUrl + "/deviceForZhgd/deviceDataHistory"))
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody.toJSONString()))
.timeout(Duration.ofSeconds(60))
.build();
try {
// 发送同步请求
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// 处理响应
String responseBody = response.body();
JSONObject result = JSONObject.parseObject(responseBody);
log.debug("同步记录仪数据: {}", result.toJSONString());
if (result != null && result.getInteger("code").equals(0)) {
retuArray = result.getJSONArray("result");
}
} catch (Exception e) {
log.error("同步记录仪数据接口调用失败: {}", e.getMessage(), e);
}
return retuArray;
}
/**
* 构建请求体
*/
private JSONObject buildRequestBody(String deviceCode, String startTime, String stopTime) {
JSONObject requestBody = new JSONObject();
requestBody.put("orgId", orgId);
requestBody.put("systemId", systemId);
requestBody.put("startTime", startTime);
requestBody.put("deviceId", deviceCode);
requestBody.put("endTime", stopTime);
requestBody.put("category", "");
log.debug("同步记录仪数据请求参数: {}", requestBody.toJSONString());
return requestBody;
}
}

@ -45,17 +45,17 @@ request:
orgId: 16
#业务系统id
systemId: 344123
url: http://192.168.169.32:80720
url: "http://192.168.169.32:80720"
iotNew:
url: http://192.168.169.23:8072
url: "http://192.168.169.23:8072"
#量具使用记录
measuringTool:
url: "192.168.191.11:8888"
url: "http://192.168.191.11:8888"
#计量记录
lims:
url: "http://192.168.169.50:30000"
#飞拔信息接口请求地址
equ:
url: http://192.168.169.69
url: "http://192.168.169.69"
logging:
config: classpath:logback.xml

@ -0,0 +1,5 @@
ALTER TABLE MES_RECORDER
MODIFY (RECORDER NVARCHAR2(255));
ALTER TABLE MES_RECORDER
MODIFY (MEMO CLOB);
Loading…
Cancel
Save