20260102:1、新增数据修复页面,支持自动修复与手动修复(B与(A+C)/2的差值超过30%);

2、设备管理-数据调试页面解析数据弹框支持拖动;
3、支持新建产品指令,在产品任意设备下都可新建产品指令,新建之后所属有设备都能看到该产品指令
This commit is contained in:
George 2026-02-02 23:01:17 +08:00
parent dd610bc303
commit ec1c5d7c11
29 changed files with 2574 additions and 606 deletions

View File

@ -291,6 +291,12 @@ public interface FastBeeConstant {
*/ */
String POWER_ON_TRIGGER_KEY_PREFIX = "power_on:trigger:"; String POWER_ON_TRIGGER_KEY_PREFIX = "power_on:trigger:";
/**
* 数据修复配置缓存key
*/
String DATA_REPAIR_CONFIG_KEY = "data:repair:config";
} }
interface PROTOCOL { interface PROTOCOL {

View File

@ -13,7 +13,7 @@ import lombok.Getter;
public enum ThingsModelType { public enum ThingsModelType {
PROP(1, "PROPERTY", "属性", "properties"), PROP(1, "PROPERTY", "属性", "properties"),
SERVICE(2, "FUNCTION", "服务", "functions"), SERVICE(2, "FUNCTION", "服务", "functions"), // 前端页面:功能
EVENT(3, "EVENT", "事件", "events"), EVENT(3, "EVENT", "事件", "events"),
; ;

View File

@ -14,8 +14,7 @@ import java.util.Random;
* *
* @author ruoyi * @author ruoyi
*/ */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
{
public static String YYYY = "yyyy"; public static String YYYY = "yyyy";
public static String YYYY_MM = "yyyy-MM"; public static String YYYY_MM = "yyyy-MM";
@ -42,8 +41,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
* *
* @return Date() 当前日期 * @return Date() 当前日期
*/ */
public static Date getNowDate() public static Date getNowDate() {
{
return new Date(); return new Date();
} }
@ -52,44 +50,44 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
* *
* @return String * @return String
*/ */
public static String getDate() public static String getDate() {
{
return dateTimeNow(YYYY_MM_DD); return dateTimeNow(YYYY_MM_DD);
} }
public static final String getTime() /**
{ * 获取昨天的当前时间
*/
public static String getYesterdayDateTime() {
return parseDateToStr(YYYY_MM_DD_HH_MM_SS, new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000));
}
/**
* 获取当前时间
*/
public static final String getTime() {
return dateTimeNow(YYYY_MM_DD_HH_MM_SS); return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
} }
public static final String dateTimeNow() public static final String dateTimeNow() {
{
return dateTimeNow(YYYYMMDDHHMMSS); return dateTimeNow(YYYYMMDDHHMMSS);
} }
public static final String dateTimeNow(final String format) public static final String dateTimeNow(final String format) {
{
return parseDateToStr(format, new Date()); return parseDateToStr(format, new Date());
} }
public static final String dateTime(final Date date) public static final String dateTime(final Date date) {
{
return parseDateToStr(YYYY_MM_DD, date); return parseDateToStr(YYYY_MM_DD, date);
} }
public static final String parseDateToStr(final String format, final Date date) public static final String parseDateToStr(final String format, final Date date) {
{
return new SimpleDateFormat(format).format(date); return new SimpleDateFormat(format).format(date);
} }
public static final Date dateTime(final String format, final String ts) public static final Date dateTime(final String format, final String ts) {
{ try {
try
{
return new SimpleDateFormat(format).parse(ts); return new SimpleDateFormat(format).parse(ts);
} } catch (ParseException e) {
catch (ParseException e)
{
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@ -97,8 +95,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/** /**
* 日期路径 即年/月/日 如2018/08/08 * 日期路径 即年/月/日 如2018/08/08
*/ */
public static final String datePath() public static final String datePath() {
{
Date now = new Date(); Date now = new Date();
return DateFormatUtils.format(now, "yyyy/MM/dd"); return DateFormatUtils.format(now, "yyyy/MM/dd");
} }
@ -106,8 +103,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/** /**
* 日期路径 即年/月/日 如20180808 * 日期路径 即年/月/日 如20180808
*/ */
public static final String dateTime() public static final String dateTime() {
{
Date now = new Date(); Date now = new Date();
return DateFormatUtils.format(now, "yyyyMMdd"); return DateFormatUtils.format(now, "yyyyMMdd");
} }
@ -116,26 +112,20 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/** /**
* 日期路径 即年/月/日 如20180808 * 日期路径 即年/月/日 如20180808
*/ */
public static final String dateTimeYY(Date date) public static final String dateTimeYY(Date date) {
{
return DateFormatUtils.format(date, YY_MM_DD_HH_MM_SS); return DateFormatUtils.format(date, YY_MM_DD_HH_MM_SS);
} }
/** /**
* 日期型字符串转化为日期 格式 * 日期型字符串转化为日期 格式
*/ */
public static Date parseDate(Object str) public static Date parseDate(Object str) {
{ if (str == null) {
if (str == null)
{
return null; return null;
} }
try try {
{
return parseDate(str.toString(), parsePatterns); return parseDate(str.toString(), parsePatterns);
} } catch (ParseException e) {
catch (ParseException e)
{
return null; return null;
} }
} }
@ -143,8 +133,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/** /**
* 获取服务器启动时间 * 获取服务器启动时间
*/ */
public static Date getServerStartDate() public static Date getServerStartDate() {
{
long time = ManagementFactory.getRuntimeMXBean().getStartTime(); long time = ManagementFactory.getRuntimeMXBean().getStartTime();
return new Date(time); return new Date(time);
} }
@ -152,24 +141,21 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/** /**
* 计算相差天数 * 计算相差天数
*/ */
public static int differentDaysByMillisecond(Date date1, Date date2) public static int differentDaysByMillisecond(Date date1, Date date2) {
{
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
} }
/** /**
* 计算相差秒数 * 计算相差秒数
*/ */
public static int differentSeconds(Date date1, Date date2) public static int differentSeconds(Date date1, Date date2) {
{
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000))); return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000)));
} }
/** /**
* 计算两个时间差 * 计算两个时间差
*/ */
public static String getDatePoor(Date endDate, Date nowDate) public static String getDatePoor(Date endDate, Date nowDate) {
{
long nd = 1000 * 24 * 60 * 60; long nd = 1000 * 24 * 60 * 60;
long nh = 1000 * 60 * 60; long nh = 1000 * 60 * 60;
long nm = 1000 * 60; long nm = 1000 * 60;
@ -190,8 +176,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/** /**
* 增加 LocalDateTime ==> Date * 增加 LocalDateTime ==> Date
*/ */
public static Date toDate(LocalDateTime temporalAccessor) public static Date toDate(LocalDateTime temporalAccessor) {
{
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant()); return Date.from(zdt.toInstant());
} }
@ -199,19 +184,18 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/** /**
* 增加 LocalDate ==> Date * 增加 LocalDate ==> Date
*/ */
public static Date toDate(LocalDate temporalAccessor) public static Date toDate(LocalDate temporalAccessor) {
{
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant()); return Date.from(zdt.toInstant());
} }
public static long getTimestamp(){ public static long getTimestamp() {
return System.currentTimeMillis(); return System.currentTimeMillis();
} }
public static long getTimestampSeconds(){ public static long getTimestampSeconds() {
return System.currentTimeMillis()/1000; return System.currentTimeMillis() / 1000;
} }
public static String generateRandomHex(int length) { public static String generateRandomHex(int length) {
@ -239,6 +223,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/** /**
* 字符串去除毫秒 * 字符串去除毫秒
*
* @param time 时间字符串 * @param time 时间字符串
* @return java.lang.String * @return java.lang.String
*/ */
@ -249,6 +234,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
/** /**
* 日期去除毫秒 * 日期去除毫秒
*
* @param time 时间 * @param time 时间
* @return java.util.Date * @return java.util.Date
*/ */

View File

@ -0,0 +1,132 @@
package com.fastbee.controller.deviceLog;
/**
* @description:
* @author Sumuxi
* @date 2026/2/1
*/
import com.fastbee.common.annotation.Log;
import com.fastbee.common.core.controller.BaseController;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.page.TableDataInfo;
import com.fastbee.common.enums.BusinessType;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.iot.domain.DeviceLog;
import com.fastbee.iot.model.param.DataRepairQueryParam;
import com.fastbee.iot.service.IDataRepairService;
import com.fastbee.iot.service.IDeviceLogService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
/**
* @description:
*
* @author Sumuxi
* @date 2026/2/1
*/
@Api(tags = "设备日志模块")
@RestController
@RequestMapping("/iot/dataRepair")
public class DataRepairController extends BaseController {
@Resource
private IDataRepairService dataRepairService;
@Resource
private IDeviceLogService deviceLogService;
@ApiOperation("批量查询设备日志(用于数据校准)")
@PreAuthorize("@ss.hasAnyPermi('iot:device:list,dataCenter:repair:list')")
@GetMapping("/dataRepairList")
public TableDataInfo dataRepairList(DataRepairQueryParam queryParam) {
if (StringUtils.isNotEmpty(queryParam.getSerialNumber())) {
queryParam.setSerialNumber(queryParam.getSerialNumber().toUpperCase());
}
List<DeviceLog> dataRepairList = dataRepairService.getDataRepairList(queryParam);
return getDataTable(dataRepairList);
}
@ApiOperation("删除指定时间点的所有数据")
@PreAuthorize("@ss.hasAnyPermi('iot:device:edit,dataCenter:repair:list')")
@Log(title = "删除时间点数据", businessType = BusinessType.DELETE)
@DeleteMapping("/deleteByTime")
public AjaxResult deleteDataRepairByTime(@RequestParam String serialNumber, @RequestParam String createTime) {
if (StringUtils.isNotEmpty(serialNumber)) {
serialNumber = serialNumber.toUpperCase();
}
int deletedCount = dataRepairService.deleteDataRepairByTime(serialNumber, createTime);
return AjaxResult.success("成功删除 " + deletedCount + " 条数据");
}
/**
* 批量修改设备日志(数据修复)
*/
@ApiOperation("批量修改设备日志")
@PreAuthorize("@ss.hasAnyPermi('iot:device:edit,dataCenter:repair:list')")
@Log(title = "设备日志批量修复", businessType = BusinessType.UPDATE)
@PostMapping("/batchUpdate")
public AjaxResult batchUpdate(@RequestBody List<DeviceLog> deviceLogList) {
int successCount = 0;
for (DeviceLog deviceLog : deviceLogList) {
try {
int result = deviceLogService.updateDeviceLog(deviceLog);
if (result > 0) {
successCount++;
}
} catch (Exception e) {
// 单条更新失败不影响其他记录
}
}
return AjaxResult.success("成功更新 " + successCount + " 条数据");
}
// 自动校准配置
/**
* 获取自动校准配置列表
*/
@ApiOperation("获取自动校准配置列表")
@PreAuthorize("@ss.hasAnyPermi('iot:device:list,dataCenter:repair:list')")
@GetMapping("/autoCalibration/config")
public AjaxResult getDataRepairConfig() {
List<IDataRepairService.DataRepairConfig> configs = dataRepairService.getAllDataRepairConfigs();
return AjaxResult.success(configs);
}
/**
* 添加数据修复配置
*/
@ApiOperation("添加自动校准配置")
@PreAuthorize("@ss.hasAnyPermi('iot:device:edit,dataCenter:repair:list')")
@Log(title = "自动校准配置", businessType = BusinessType.INSERT)
@PostMapping("/autoCalibration/config")
public AjaxResult addAutoCalibrationConfig(@RequestBody Map<String, Object> params) {
Long deviceId = Long.valueOf(params.get("deviceId").toString());
String serialNumber = params.get("serialNumber").toString();
String deviceName = params.get("deviceName").toString();
String identifier = params.get("identifier").toString();
String modelName = params.get("modelName").toString();
dataRepairService.addDataRepairConfig(deviceId, serialNumber, deviceName, identifier, modelName);
return AjaxResult.success();
}
/**
* 删除数据修复配置
*/
@ApiOperation("删除自动校准配置")
@PreAuthorize("@ss.hasAnyPermi('iot:device:edit,dataCenter:repair:list')")
@Log(title = "数据修复配置", businessType = BusinessType.DELETE)
@DeleteMapping("/autoCalibration/config")
public AjaxResult removeAutoCalibrationConfig(@RequestParam String serialNumber, @RequestParam String identifier) {
dataRepairService.removeDataRepairConfig(serialNumber, identifier);
return AjaxResult.success();
}
}

View File

@ -46,7 +46,7 @@ public class JsonProtocolService implements IProtocol {
public DeviceReport decode(DeviceData deviceData, String clientId) { public DeviceReport decode(DeviceData deviceData, String clientId) {
try { try {
DeviceReport reportMessage = new DeviceReport(); DeviceReport reportMessage = new DeviceReport();
// bytep[] 转String // byte[] 转String
String data = new String(deviceData.getData(), StandardCharsets.UTF_8); String data = new String(deviceData.getData(), StandardCharsets.UTF_8);
List<ThingsModelSimpleItem> values = JSON.parseArray(data, ThingsModelSimpleItem.class); List<ThingsModelSimpleItem> values = JSON.parseArray(data, ThingsModelSimpleItem.class);
//上报数据时间 //上报数据时间

View File

@ -41,7 +41,7 @@ public class JsonPakProtocolService implements IProtocol {
public DeviceReport decode(DeviceData deviceData, String clientId) { public DeviceReport decode(DeviceData deviceData, String clientId) {
try { try {
DeviceReport reportMessage = new DeviceReport(); DeviceReport reportMessage = new DeviceReport();
// bytep[] 转String // byte[] 转String
String data = new String(deviceData.getData(), StandardCharsets.UTF_8); String data = new String(deviceData.getData(), StandardCharsets.UTF_8);
JSONObject params = JSONObject.parseObject(data); JSONObject params = JSONObject.parseObject(data);
List<ThingsModelSimpleItem> result = new ArrayList<>(); List<ThingsModelSimpleItem> result = new ArrayList<>();

View File

@ -38,7 +38,7 @@ public class JsonChenYiProtocolService implements IProtocol {
public DeviceReport decode(DeviceData deviceData, String clientId) { public DeviceReport decode(DeviceData deviceData, String clientId) {
try { try {
DeviceReport reportMessage = new DeviceReport(); DeviceReport reportMessage = new DeviceReport();
// bytep[] 转String // byte[] 转String
String data = new String(deviceData.getData(), StandardCharsets.UTF_8); String data = new String(deviceData.getData(), StandardCharsets.UTF_8);
Map<String, Object> values = JSON.parseObject(data, Map.class); Map<String, Object> values = JSON.parseObject(data, Map.class);
JSONArray array = (JSONArray) values.get("data"); JSONArray array = (JSONArray) values.get("data");

View File

@ -182,14 +182,22 @@ public class ThingsModel extends BaseEntity {
@Excel(name = "是否只读", prompt = "0=否,1=是") @Excel(name = "是否只读", prompt = "0=否,1=是")
private Integer isReadonly; private Integer isReadonly;
/**
* 模型顺序
*/
private Integer modelOrder; private Integer modelOrder;
/**
* Modbus配置
*/
private ModbusConfig modbusConfig; private ModbusConfig modbusConfig;
@Deprecated @Deprecated
private String language; private String language;
/**
* 物模型ID列表
*/
private List<String> modelIdList; private List<String> modelIdList;
} }

View File

@ -46,6 +46,9 @@ public interface DeviceLogMapper {
*/ */
public List<DeviceLog> selectDeviceLogList(DeviceLog deviceLog); public List<DeviceLog> selectDeviceLogList(DeviceLog deviceLog);
List<DeviceLog> getDataRepairList(DeviceLog deviceLog);
/** /**
* 查询设备监测数据 * 查询设备监测数据
* *

View File

@ -75,6 +75,8 @@ public interface TDDeviceLogMapper {
*/ */
List<DeviceLog> selectDeviceLogList(@Param("database") String database, @Param("device") DeviceLog deviceLog); List<DeviceLog> selectDeviceLogList(@Param("database") String database, @Param("device") DeviceLog deviceLog);
List<DeviceLog> getDataRepairList(String dbName, DeviceLog deviceLog);
/*** /***
* 根据设备ID删除设备日志 * 根据设备ID删除设备日志
*/ */

View File

@ -0,0 +1,59 @@
package com.fastbee.iot.model.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @description: 数据修复查询DTO
* @author Sumuxi
* @date 2026/2/1
*/
@Data
@ApiModel(value = "DataRepairQueryParam", description = "数据修复查询参数")
public class DataRepairQueryParam {
/**
* 设备编号
*/
@ApiModelProperty("设备编号")
private String serialNumber;
/**
* 标识符
*/
@ApiModelProperty("标识符")
private String identity;
/**
* 产品ID
*/
@ApiModelProperty("产品ID")
private Long productId;
/**
* 开始时间
*/
@ApiModelProperty("开始时间")
private String beginTime;
/**
* 结束时间
*/
@ApiModelProperty("结束时间")
private String endTime;
/**
* 页码
*/
@ApiModelProperty("页码")
private Integer pageNum;
/**
* 每页大小
*/
@ApiModelProperty("每页大小")
private Integer pageSize;
}

View File

@ -0,0 +1,127 @@
package com.fastbee.iot.service;
import com.fastbee.iot.domain.DeviceLog;
import com.fastbee.iot.model.param.DataRepairQueryParam;
import java.util.List;
/**
* @description: 数据修复服务接口
* @author Sumuxi
* @date 2026/2/1
*/
public interface IDataRepairService {
/**
* 获取数据修复列表
*
* @param queryParam 查询参数
* @return 设备日志列表
*/
List<DeviceLog> getDataRepairList(DataRepairQueryParam queryParam);
/**
* 添加数据修复配置
*
* @param deviceId 设备ID
* @param serialNumber 设备编号
* @param deviceName 设备名称
* @param identifier 物模型标识符
* @param modelName 物模型名称
*/
void addDataRepairConfig(Long deviceId, String serialNumber, String deviceName, String identifier, String modelName);
/**
* 删除数据修复配置
*
* @param serialNumber 设备编号
* @param identifier 物模型标识符
*/
void removeDataRepairConfig(String serialNumber, String identifier);
/**
* 获取所有数据修复配置
*
* @return 配置列表
*/
List<DataRepairConfig> getAllDataRepairConfigs();
/**
* 检查是否需要数据修复
*
* @param serialNumber 设备编号
* @param identifier 物模型标识符
* @return 是否需要修复
*/
boolean needDataRepair(String serialNumber, String identifier);
/**
* 执行数据修复
*
* @param serialNumber 设备编号
* @param isShadow 是否为影子数据
* @param currentLogs 当前上报的日志
*/
void executeDataRepair(String serialNumber, boolean isShadow, List<DeviceLog> currentLogs);
/**
* 根据时间删除数据修复记录
*
* @param serialNumber 设备编号
* @param createTime 创建时间
* @return 删除数量
*/
int deleteDataRepairByTime(String serialNumber, String createTime);
/**
* 数据修复配置实体
*/
class DataRepairConfig {
private Long deviceId;
private String serialNumber;
private String deviceName;
private String identifier;
private String modelName;
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public String getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getModelName() {
return modelName;
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
}
}

View File

@ -75,8 +75,7 @@ public class DataCenterServiceImpl implements DataCenterService {
deviceLog.setSerialNumber(deviceHistoryParam.getSerialNumber()); deviceLog.setSerialNumber(deviceHistoryParam.getSerialNumber());
deviceLog.setBeginTime(deviceHistoryParam.getBeginTime()); deviceLog.setBeginTime(deviceHistoryParam.getBeginTime());
deviceLog.setEndTime(deviceHistoryParam.getEndTime()); deviceLog.setEndTime(deviceHistoryParam.getEndTime());
List<HistoryModel> historyModelList1 = deviceLogService.listHistory(deviceLog); historyModelList.addAll(deviceLogService.listHistory(deviceLog));
historyModelList.addAll(historyModelList1);
} }
List<String> functionIdentifierList = identifierVOList.stream().filter(t -> 2 == t.getType()).map(DeviceHistoryParam.IdentifierVO::getIdentifier).collect(Collectors.toList()); List<String> functionIdentifierList = identifierVOList.stream().filter(t -> 2 == t.getType()).map(DeviceHistoryParam.IdentifierVO::getIdentifier).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(functionIdentifierList)) { if (CollectionUtils.isNotEmpty(functionIdentifierList)) {

View File

@ -0,0 +1,349 @@
package com.fastbee.iot.service.impl;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.iot.domain.DeviceLog;
import com.fastbee.iot.model.DeviceHistoryParam;
import com.fastbee.iot.model.HistoryModel;
import com.fastbee.iot.model.param.DataRepairQueryParam;
import com.fastbee.iot.model.dto.ThingsModelDTO;
import com.fastbee.iot.service.IDataRepairService;
import com.fastbee.iot.service.IDeviceLogService;
import com.fastbee.iot.service.IThingsModelService;
import com.fastbee.iot.tdengine.service.ILogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @description: 数据修复服务实现
* @author Sumuxi
* @date 2026/2/1
*/
@Service
@Slf4j
public class DataRepairServiceImpl implements IDataRepairService {
private static final double REPAIR_THRESHOLD = 0.3; // 30%阈值
@Resource
private IDeviceLogService deviceLogService;
@Resource
private IThingsModelService thingsModelService;
@Resource
private ILogService logService;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Override
public List<DeviceLog> getDataRepairList(DataRepairQueryParam queryParam) {
// 使用productId查询物模型
List<ThingsModelDTO> list = thingsModelService.getCacheAndHandleArrayByProductId(queryParam.getProductId(),
queryParam.getSerialNumber());
List<DeviceHistoryParam.IdentifierVO> identifierVOList = new ArrayList<>();
for (ThingsModelDTO thingsModelDTO : list) {
DeviceHistoryParam.IdentifierVO identifierVO = new DeviceHistoryParam.IdentifierVO();
identifierVO.setIdentifier(thingsModelDTO.getIdentifier());
identifierVO.setType(thingsModelDTO.getType());
identifierVOList.add(identifierVO);
}
// 获取所有标识符的时间-去重
Set<Date> dateTreeSet = new TreeSet<>();
List<String> identifierList = identifierVOList.stream()
.map(DeviceHistoryParam.IdentifierVO::getIdentifier)
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(identifierList)) {
DeviceLog deviceLogParam = new DeviceLog();
deviceLogParam.setIdentityList(identifierList);
deviceLogParam.setSerialNumber(queryParam.getSerialNumber());
deviceLogParam.setBeginTime(queryParam.getBeginTime());
deviceLogParam.setEndTime(queryParam.getEndTime());
List<HistoryModel> historyModels = logService.listHistory(deviceLogParam);
dateTreeSet.addAll(historyModels.stream().map(HistoryModel::getTime).collect(Collectors.toList()));
}
// 将DTO转换为DeviceLog对象
DeviceLog deviceLog = new DeviceLog();
deviceLog.setSerialNumber(queryParam.getSerialNumber());
deviceLog.setIdentity(queryParam.getIdentity());
deviceLog.setBeginTime(queryParam.getBeginTime());
deviceLog.setEndTime(queryParam.getEndTime());
Map<Date, DeviceLog> createTimeMap = logService.getDataRepairList(deviceLog).stream()
.collect(Collectors.toMap(DeviceLog::getCreateTime, Function.identity(), (v1, v2) -> v1));
List<DeviceLog> dataRepairList = new ArrayList<>();
for (Date date : dateTreeSet) {
if (createTimeMap.containsKey(date) && Objects.equals(date, createTimeMap.get(date).getCreateTime())) {
dataRepairList.add(createTimeMap.get(date));
} else {
DeviceLog deviceLogTemp = new DeviceLog();
deviceLogTemp.setSerialNumber(queryParam.getSerialNumber());
deviceLogTemp.setCreateTime(date);
deviceLogTemp.setIdentity(queryParam.getIdentity());
dataRepairList.add(deviceLogTemp);
}
}
return dataRepairList;
}
@Override
public int deleteDataRepairByTime(String serialNumber, String createTime) {
try {
// 解析时间字符串
Date date = DateUtils.parseDate(createTime);
if (date == null) {
log.warn("时间格式错误: createTime={}", createTime);
return 0;
}
// 查询该时间点附近的所有数据
DeviceLog queryLog = new DeviceLog();
queryLog.setSerialNumber(serialNumber);
queryLog.setCreateTime(date);
List<DeviceLog> logsNearTime = logService.getDataRepairList(queryLog);
if (CollectionUtils.isEmpty(logsNearTime)) {
log.info("该时间点无数据: serialNumber={}, createTime={}", serialNumber, createTime);
return 0;
}
// 批量删除
Long[] logIds = logsNearTime.stream()
.map(DeviceLog::getLogId)
.filter(Objects::nonNull)
.toArray(Long[]::new);
if (logIds.length > 0) {
int deletedCount = deviceLogService.deleteDeviceLogByLogIds(logIds);
log.info("删除时间点数据成功: serialNumber={}, createTime={}, 删除记录数={}", serialNumber, createTime, deletedCount);
return deletedCount;
}
return 0;
} catch (Exception e) {
log.error("删除时间点数据失败: serialNumber={}, createTime={}", serialNumber, createTime, e);
return 0;
}
}
@Override
public void addDataRepairConfig(Long deviceId, String serialNumber, String deviceName, String identifier,
String modelName) {
IDataRepairService.DataRepairConfig config = new IDataRepairService.DataRepairConfig();
config.setDeviceId(deviceId);
config.setSerialNumber(serialNumber);
config.setDeviceName(deviceName);
config.setIdentifier(identifier);
config.setModelName(modelName);
// 大Key代表整个业务,hashKey为"设备:物模型",每个配置独立存储
String hashKey = buildDeviceIdentifierKey(serialNumber, identifier);
redisTemplate.opsForHash().put(FastBeeConstant.REDIS.DATA_REPAIR_CONFIG_KEY, hashKey,
JSON.toJSONString(config));
log.info("添加数据修复配置: serialNumber={}, identifier={}", serialNumber, identifier);
}
@Override
public void removeDataRepairConfig(String serialNumber, String identifier) {
// 直接删除对应的hashKey
String hashKey = buildDeviceIdentifierKey(serialNumber, identifier);
Long deleted = redisTemplate.opsForHash().delete(FastBeeConstant.REDIS.DATA_REPAIR_CONFIG_KEY, hashKey);
if (deleted != null && deleted > 0) {
log.info("删除数据修复配置: serialNumber={}, identifier={}", serialNumber, identifier);
} else {
log.warn("数据修复配置不存在: serialNumber={}, identifier={}", serialNumber, identifier);
}
}
@Override
public List<IDataRepairService.DataRepairConfig> getAllDataRepairConfigs() {
// 获取所有hashKey,批量查询所有配置
Set<Object> hashKeys = redisTemplate.opsForHash().keys(FastBeeConstant.REDIS.DATA_REPAIR_CONFIG_KEY);
List<IDataRepairService.DataRepairConfig> configs = new ArrayList<>();
if (hashKeys != null && !hashKeys.isEmpty()) {
// 批量获取所有配置值
List<Object> values = redisTemplate.opsForHash().multiGet(FastBeeConstant.REDIS.DATA_REPAIR_CONFIG_KEY,
new ArrayList<>(hashKeys));
if (values != null) {
for (Object value : values) {
if (value != null) {
try {
IDataRepairService.DataRepairConfig config = JSON.parseObject(value.toString(),
IDataRepairService.DataRepairConfig.class);
configs.add(config);
} catch (Exception e) {
log.warn("解析数据修复配置失败: value={}", value, e);
}
}
}
}
}
return configs.stream()
.sorted(Comparator.comparing(IDataRepairService.DataRepairConfig::getSerialNumber))
.collect(Collectors.toList());
}
@Override
public boolean needDataRepair(String serialNumber, String identifier) {
// 直接通过hashKey判断配置是否存在
String hashKey = buildDeviceIdentifierKey(serialNumber, identifier);
return redisTemplate.opsForHash().hasKey(FastBeeConstant.REDIS.DATA_REPAIR_CONFIG_KEY, hashKey);
}
@Override
@Async("threadPoolTaskExecutor")
public void executeDataRepair(String serialNumber, boolean isShadow, List<DeviceLog> currentLogs) {
if (CollectionUtils.isEmpty(currentLogs) || isShadow) {
log.debug("无数据或为影子模式,不执行数据修复: serialNumber={}, isShadow={}", serialNumber, isShadow);
return;
}
// 方案2优化:一次性批量获取该设备的所有配置,避免重复查询Redis
Set<String> deviceConfigIdentifiers = getDeviceConfigIdentifiers(serialNumber);
if (deviceConfigIdentifiers.isEmpty()) {
log.debug("设备无数据修复配置: serialNumber={}", serialNumber);
return;
}
// 查询最近的数据(按时间倒序)
DeviceLog queryParam = new DeviceLog();
queryParam.setSerialNumber(serialNumber);
queryParam.setIdentityList(new ArrayList<>(deviceConfigIdentifiers));
queryParam.setBeginTime(DateUtils.getYesterdayDateTime());
queryParam.setEndTime(DateUtils.getTime());
List<DeviceLog> recentLogs = logService.getDataRepairList(queryParam);
if (recentLogs == null || recentLogs.size() < 3) {
log.debug("数据为空或数据不足3条,不执行修复: serialNumber={}", serialNumber);
return;
}
Map<String, List<DeviceLog>> identifierLogs = recentLogs.stream().collect(Collectors.groupingBy(DeviceLog::getIdentity));
for (Map.Entry<String, List<DeviceLog>> entry : identifierLogs.entrySet()) {
String identifier = entry.getKey();
List<DeviceLog> value = entry.getValue();
try {
if (value == null || value.size() < 3) {
log.debug("数据不足3条,跳过修复: serialNumber={}, identifier={}", serialNumber, identifier);
return;
}
// 取最近3条数据(已经是按时间倒序)
List<DeviceLog> lastThree = value.stream()
.limit(3)
.collect(Collectors.toList());
// 按时间正序排列:A(最早), B(中间), C(最新)
// 因为查询结果是倒序的,所以需要反转
DeviceLog logC = lastThree.get(0); // 最新的
DeviceLog logB = lastThree.get(1); // 中间的
DeviceLog logA = lastThree.get(2); // 最早的
// 解析数值
double valueA = parseDouble(logA.getLogValue());
double valueB = parseDouble(logB.getLogValue());
double valueC = parseDouble(logC.getLogValue());
if (Double.isNaN(valueA) || Double.isNaN(valueB) || Double.isNaN(valueC)) {
log.debug("数据值无效,跳过修复: serialNumber={}, identifier={}", serialNumber, identifier);
return;
}
// 计算平均值
double avg = (valueA + valueC) / 2.0;
// 检查是否需要修复:B与(A+C)/2的差值超过30%
double diff = Math.abs(valueB - avg);
double threshold = Math.abs(avg) * REPAIR_THRESHOLD;
if (diff > threshold) {
// 需要修复,更新中间的数据点B
String newValue = String.format("%.3f", avg);
log.info("检测到异常数据,自动修复: serialNumber={}, identifier={}, 原值={}, 修复值={}", serialNumber, identifier, logB.getLogValue(), newValue);
DeviceLog updateLog = new DeviceLog();
updateLog.setLogId(logB.getLogId());
updateLog.setLogValue(newValue);
int result = deviceLogService.updateDeviceLog(updateLog);
if (result > 0) {
log.info("数据修复成功: serialNumber={}, identifier={}, logId={}, 修复值={}", serialNumber,
identifier, logB.getLogId(), newValue);
} else {
log.warn("数据修复失败: serialNumber={}, identifier={}, logId={}", serialNumber, identifier,
logB.getLogId());
} } else {
log.debug("数据正常,无需修复: serialNumber={}, identifier={}, diff={}, threshold={}", serialNumber, identifier, diff, threshold);
}
} catch (Exception e) {
log.error("数据修复执行异常: serialNumber={}, identifier={}", serialNumber, identifier, e);
}
}
}
/**
* 解析字符串为double值
*/
private double parseDouble(String value) {
if (value == null || value.trim().isEmpty()) {
return Double.NaN;
}
try {
return Double.parseDouble(value.trim());
} catch (NumberFormatException e) {
return Double.NaN;
}
}
/**
* 构建设备物模型的hashKey
* 格式:serialNumber:identifier
*/
private String buildDeviceIdentifierKey(String serialNumber, String identifier) {
return serialNumber + ":" + identifier;
}
/**
* 获取设备的所有配置标识符(用于executeDataRepair优化)
* 一次性批量获取该设备的所有配置,避免重复查询Redis
*/
private Set<String> getDeviceConfigIdentifiers(String serialNumber) {
Set<String> identifiers = new HashSet<>();
try {
// 获取所有hashKey
Set<Object> hashKeys = redisTemplate.opsForHash().keys(FastBeeConstant.REDIS.DATA_REPAIR_CONFIG_KEY);
if (hashKeys != null && !hashKeys.isEmpty()) {
String prefix = serialNumber + ":";
for (Object hashKeyObj : hashKeys) {
String hashKey = hashKeyObj.toString();
// 过滤出该设备的配置
if (hashKey.startsWith(prefix)) {
// 提取identifier(去掉设备前缀)
String identifier = hashKey.substring(prefix.length());
identifiers.add(identifier);
}
}
}
} catch (Exception e) {
log.warn("获取设备配置标识符失败: serialNumber={}", serialNumber, e);
}
return identifiers;
}
}

View File

@ -2,19 +2,17 @@ package com.fastbee.iot.service.impl;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.fastbee.common.utils.DateUtils; import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.common.utils.StringUtils; import com.fastbee.common.utils.StringUtils;
import com.fastbee.iot.domain.Device; import com.fastbee.iot.domain.Device;
import com.fastbee.iot.domain.DeviceLog; import com.fastbee.iot.domain.DeviceLog;
import com.fastbee.iot.domain.ThingsModel; import com.fastbee.iot.domain.ThingsModel;
import com.fastbee.iot.model.DeviceReport; import com.fastbee.iot.model.*;
import com.fastbee.iot.model.HistoryModel;
import com.fastbee.iot.service.IDeviceService; import com.fastbee.iot.service.IDeviceService;
import com.fastbee.iot.service.IThingsModelService; import com.fastbee.iot.service.IThingsModelService;
import com.fastbee.iot.model.ThingsModelLogCountVO;
import com.fastbee.iot.model.param.DataCenterParam; import com.fastbee.iot.model.param.DataCenterParam;
import com.fastbee.iot.tdengine.service.ILogService; import com.fastbee.iot.tdengine.service.ILogService;
import com.fastbee.iot.mapper.DeviceLogMapper; import com.fastbee.iot.mapper.DeviceLogMapper;
import com.fastbee.iot.model.MonitorModel;
import com.fastbee.iot.service.IDeviceLogService; import com.fastbee.iot.service.IDeviceLogService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -88,7 +86,17 @@ public class DeviceLogServiceImpl implements IDeviceLogService {
*/ */
@Override @Override
public int insertDeviceLog(DeviceLog deviceLog) { public int insertDeviceLog(DeviceLog deviceLog) {
deviceLog.setCreateTime(DateUtils.getNowDate()); if (deviceLog.getCreateTime() == null) {
deviceLog.setCreateTime(DateUtils.getNowDate());
} else {
log.info("dateRepairInsertDeviceLog:{}", deviceLog.getSerialNumber());
Long userId = SecurityUtils.getUserId();
String username = SecurityUtils.getUsername();
deviceLog.setUserId(userId);
deviceLog.setUserName(username);
deviceLog.setTenantId(userId);
deviceLog.setTenantName(username);
}
return logService.saveDeviceLog(deviceLog); return logService.saveDeviceLog(deviceLog);
} }

View File

@ -127,7 +127,8 @@ public class DeviceServiceImpl implements IDeviceService {
private IThingsModelService thingsModelService; private IThingsModelService thingsModelService;
@Resource @Resource
private ApplicationContext applicationContext; @Lazy
private IDataRepairService dataRepairService;
@Resource @Resource
private EventLogMapper eventLogMapper; private EventLogMapper eventLogMapper;
@ -713,6 +714,9 @@ public class DeviceServiceImpl implements IDeviceService {
} }
/* ★★★★★★★★★★★★★★★★★★★★★★ 存储数据 - 结束 ★★★★★★★★★★★★★★★★★★★★★★ */ /* ★★★★★★★★★★★★★★★★★★★★★★ 存储数据 - 结束 ★★★★★★★★★★★★★★★★★★★★★★ */
// 数据修复:在存储数据完成后,异步执行数据修复
dataRepairService.executeDataRepair(serialNumber, isShadow, deviceLogList);
return list; return list;
} }

View File

@ -782,22 +782,4 @@ public class ThingsModelServiceImpl implements IThingsModelService {
return thingsModel; return thingsModel;
} }
/**
* 判断是否为纯计算物模型
* 如果公式包含{},则认为是纯计算物模型(依赖其他物模型计算值)
*
* @param formula 计算公式
* @return 0=设备上报(默认),1=纯计算,2=其他(预留)
*/
private Integer determineIsCalculated(String formula) {
if (StringUtils.isEmpty(formula)) {
return 0; // 默认设备上报
}
// 如果公式包含{},则认为是纯计算物模型
if (formula.contains("{") && formula.contains("}")) {
return 1; // 纯计算
}
return 0; // 设备上报
}
} }

View File

@ -52,6 +52,8 @@ public interface ILogService {
**/ **/
List<DeviceLog> selectDeviceLogList(DeviceLog deviceLog); List<DeviceLog> selectDeviceLogList(DeviceLog deviceLog);
List<DeviceLog> getDataRepairList(DeviceLog deviceLog);
/** /**
* 查询监测数据列表 * 查询监测数据列表
**/ **/

View File

@ -19,6 +19,7 @@ import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -99,6 +100,11 @@ public class MySqlLogServiceImpl implements ILogService {
return deviceLogMapper.selectDeviceLogList(deviceLog); return deviceLogMapper.selectDeviceLogList(deviceLog);
} }
@Override
public List<DeviceLog> getDataRepairList(DeviceLog deviceLog) {
return deviceLogMapper.getDataRepairList(deviceLog);
}
/*** /***
* 监测数据列表 * 监测数据列表
* @return * @return

View File

@ -144,6 +144,11 @@ public class TdengineLogServiceImpl implements ILogService {
return tDDeviceLogMapper.selectDeviceLogList(dbName, deviceLog); return tDDeviceLogMapper.selectDeviceLogList(dbName, deviceLog);
} }
@Override
public List<DeviceLog> getDataRepairList(DeviceLog deviceLog) {
return tDDeviceLogMapper.getDataRepairList(dbName, deviceLog);
}
/*** /***
* 监测数据列表 * 监测数据列表
* @return * @return

View File

@ -64,6 +64,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
order by create_time desc order by create_time desc
</select> </select>
<select id="getDataRepairList" resultType="com.fastbee.iot.domain.DeviceLog">
<include refid="selectDeviceLogVo"/>
<where>
<if test="serialNumber != null and serialNumber !=''"> and serial_number = #{serialNumber}</if>
<if test="identity != null and identity != ''"> and identity = #{identity}</if>
<if test="identityList != null and identityList != ''">
and identity in
<foreach collection="identityList" item="identity" open="(" separator="," close=")">
#{identity}
</foreach>
</if>
<if test="beginTime != null and beginTime != '' and endTime != null and endTime != ''"> and create_time between #{beginTime} and #{endTime}</if>
<if test="createTime != null"> and create_time = #{createTime}</if>
</where>
order by create_time desc
</select>
<select id="selectDeviceLogByLogId" parameterType="Long" resultMap="DeviceLogResult"> <select id="selectDeviceLogByLogId" parameterType="Long" resultMap="DeviceLogResult">
<include refid="selectDeviceLogVo"/> <include refid="selectDeviceLogVo"/>
where log_id = #{logId} where log_id = #{logId}

View File

@ -128,6 +128,21 @@
order by ts desc order by ts desc
</select> </select>
<select id="getDataRepairList" resultType="com.fastbee.iot.domain.DeviceLog">
select * from ${database}.device_log
<where>
<if test="device.serialNumber != null and device.serialNumber !=''"> and serial_number = #{device.serialNumber}</if>
<if test="device.identity != null and device.identity != ''"> and identity = #{device.identity}</if>
<if test="device.identityList != null and device.identityList.size > 0">
and identity in
<foreach collection="device.identityList" item="identity" open="(" separator="," close=")">
#{identity}
</foreach>
</if>
<if test="device.beginTime != null and device.beginTime != '' and device.endTime != null and device.endTime != ''"> and ts between #{device.beginTime} and #{device.endTime}</if>
</where>
</select>
<select id="selectHistoryList" parameterType="com.fastbee.iot.domain.DeviceLog" resultMap="HistoryResult"> <select id="selectHistoryList" parameterType="com.fastbee.iot.domain.DeviceLog" resultMap="HistoryResult">
select log_value, select log_value,
ts, ts,

View File

@ -3201,6 +3201,10 @@ VALUES(3345, '历史记录', 3343, 1, 'history', 'dataCenter/history', NULL, 1,
INSERT INTO fastbee.sys_menu INSERT INTO fastbee.sys_menu
(menu_id, menu_name, parent_id, order_num, `path`, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) (menu_id, menu_name, parent_id, order_num, `path`, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES(3346, '历史记录查询', 3345, 1, '', NULL, NULL, 1, 0, 'F', '0', '0', 'dataCenter:history:query', '#', 'admin', '2024-05-24 14:46:27', 'admin', '2024-05-24 14:47:26', ''); VALUES(3346, '历史记录查询', 3345, 1, '', NULL, NULL, 1, 0, 'F', '0', '0', 'dataCenter:history:query', '#', 'admin', '2024-05-24 14:46:27', 'admin', '2024-05-24 14:47:26', '');
INSERT INTO fastbee.sys_menu
(menu_id, menu_name, parent_id, order_num, `path`, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES(3372, '数据校准', 3343, 3, 'dataRepair', 'dataCenter/dataRepair', NULL, 1, 0, 'C', '0', '0', 'dataCenter:repair:list', 'data-analysis', 'admin', '2024-05-24 14:44:58', 'admin', '2024-05-24 14:46:07', '');
INSERT INTO fastbee.sys_menu INSERT INTO fastbee.sys_menu
(menu_id, menu_name, parent_id, order_num, `path`, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) (menu_id, menu_name, parent_id, order_num, `path`, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES(3347, '模板组态', 3159, 1, 'center/temp', 'scada/center/tempScada', NULL, 1, 0, 'C', '0', '0', 'scada:center:list', 'template', 'admin', '2024-03-01 11:38:48', 'admin', '2024-06-01 17:18:09', '组态中心菜单'); VALUES(3347, '模板组态', 3159, 1, 'center/temp', 'scada/center/tempScada', NULL, 1, 0, 'C', '0', '0', 'scada:center:list', 'template', 'admin', '2024-03-01 11:38:48', 'admin', '2024-06-01 17:18:09', '组态中心菜单');

947
vue/package-lock.json generated

File diff suppressed because it is too large Load Diff

132
vue/src/api/data/repair.js Normal file
View File

@ -0,0 +1,132 @@
import request from '@/utils/request';
/**
* 获取设备日志列表(用于数据修复)
* @param {Object} params 查询参数
* @returns Promise
*/
export function getRepairDataList(params) {
return request({
url: '/iot/dataRepair/dataRepairList',
method: 'get',
params: params
});
}
/**
* 新增设备日志
* @param {Object} data 日志数据
* @returns Promise
*/
export function addDeviceLog(data) {
return request({
url: '/iot/deviceLog',
method: 'post',
data: data
});
}
/**
* 修改设备日志
* @param {Object} data 日志数据
* @returns Promise
*/
export function updateDeviceLog(data) {
return request({
url: '/iot/deviceLog',
method: 'put',
data: data
});
}
/**
* 删除设备日志
* @param {Number|String} logId 日志ID
* @returns Promise
*/
export function deleteDeviceLog(logId) {
return request({
url: `/iot/deviceLog/${logId}`,
method: 'delete'
});
}
/**
* 删除指定时间点的所有设备日志
* @param {String} serialNumber 设备序列号
* @param {String} createTime 时间点
* @returns Promise
*/
export function deleteDeviceLogsByCreateTime(serialNumber, createTime) {
return request({
url: '/iot/dataRepair/deleteByTime',
method: 'delete',
params: {
serialNumber: serialNumber,
createTime: createTime
}
});
}
/**
* 批量删除设备日志
* @param {Array} logIds 日志ID数组
* @returns Promise
*/
export function batchDeleteDeviceLog(logIds) {
return request({
url: `/iot/deviceLog/${logIds.join(',')}`,
method: 'delete'
});
}
/**
* 批量更新设备日志(数据修复)
* @param {Array} dataList 更新数据列表 [{logId, logValue}, ...]
* @returns Promise
*/
export function batchUpdateDeviceLog(dataList) {
return request({
url: '/iot/deviceLog/batchUpdate',
method: 'post',
data: dataList
});
}
/**
* 获取数据修复配置列表
* @returns Promise
*/
export function getAutoCalibrationConfig() {
return request({
url: '/iot/dataRepair/autoCalibration/config',
method: 'get'
});
}
/**
* 添加数据修复配置
* @param {Object} data 配置数据
* @returns Promise
*/
export function addAutoCalibrationConfig(data) {
return request({
url: '/iot/dataRepair/autoCalibration/config',
method: 'post',
data: data
});
}
/**
* 删除数据修复配置
* @param {Object} data 配置数据 {deviceId, identifier}
* @returns Promise
*/
export function removeAutoCalibrationConfig(data) {
return request({
url: '/iot/dataRepair/autoCalibration/config',
method: 'delete',
params: data
});
}

View File

@ -0,0 +1 @@
<svg t="1727404233481" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="42851" width="64" height="64"><path d="M152.832 881.408V150.272h-51.2v782.336h836.096v-51.2H152.832z" fill="#606266"></path><path d="M216.32 698.624a25.6 25.6 0 0 0 25.6-25.6c0-33.536 8.192-134.144 51.2-154.88 32.256-15.36 81.152 16.64 116.48 46.336 117.248 98.56 209.152 134.144 289.024 112.128 85.248-23.552 151.808-111.616 209.408-277.504a25.6 25.6 0 0 0-48.384-16.896c-51.2 146.432-108.032 226.56-174.848 244.992-60.928 16.896-140.544-16.384-242.432-102.4-68.096-57.088-125.696-75.008-171.264-53.504-80.896 38.4-81.408 185.344-81.152 201.984a25.6 25.6 0 0 0 26.368 25.344z" fill="#409EFF"></path><path d="M768 256a32 32 0 0 1 32 32v96a32 32 0 0 1-64 0v-64h-32a32 32 0 0 1 0-64h64z m0 192a32 32 0 0 1 32 32v96a32 32 0 0 1-64 0v-64h-32a32 32 0 0 1 0-64h64z" fill="#67C23A"></path><circle cx="832" cy="320" r="24" fill="#E6A23C" opacity="0.8"></circle><circle cx="832" cy="480" r="24" fill="#E6A23C" opacity="0.8"></circle></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -104,6 +104,13 @@ export const constantRoutes = [
component: () => import('@/views/oauthLogin/sso'), component: () => import('@/views/oauthLogin/sso'),
hidden: true, hidden: true,
}, },
// 数据修复工具
{
path: '/dataCenter/dataRepair',
component: () => import('@/views/dataCenter/dataRepair'),
hidden: true,
meta: { title: '数据校准' },
},
//以下组态特有 //以下组态特有
// { // {
// path: '/scada/topo/fullscreen', // path: '/scada/topo/fullscreen',

File diff suppressed because it is too large Load Diff

View File

@ -377,7 +377,7 @@
<el-button type="primary" size="mini" @click="delQuick">{{ $t('device.instruction-parsing.830424-38') }}</el-button> <el-button type="primary" size="mini" @click="delQuick">{{ $t('device.instruction-parsing.830424-38') }}</el-button>
</div> </div>
</el-dialog> </el-dialog>
<el-dialog :title="$t('device.instruction-parsing.830424-39')" width="40%" :visible.sync="moreDialog"> <el-dialog :title="$t('device.instruction-parsing.830424-39')" width="40%" :visible.sync="moreDialog" v-dialogDrag>
<div class="dialog-content beautify-scroll-def"> <div class="dialog-content beautify-scroll-def">
<el-table :data="analysisList" height="400" border style="width: 100%"> <el-table :data="analysisList" height="400" border style="width: 100%">
<el-table-column type="index" :label="$t('device.instruction-parsing.830424-40')" /> <el-table-column type="index" :label="$t('device.instruction-parsing.830424-40')" />