20260102:1、新增数据修复页面,支持自动修复与手动修复(B与(A+C)/2的差值超过30%);
2、设备管理-数据调试页面解析数据弹框支持拖动; 3、支持新建产品指令,在产品任意设备下都可新建产品指令,新建之后所属有设备都能看到该产品指令
This commit is contained in:
parent
dd610bc303
commit
ec1c5d7c11
@ -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 {
|
||||||
|
|||||||
@ -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"),
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
||||||
//上报数据时间
|
//上报数据时间
|
||||||
|
|||||||
@ -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<>();
|
||||||
|
|||||||
@ -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");
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,9 @@ public interface DeviceLogMapper {
|
|||||||
*/
|
*/
|
||||||
public List<DeviceLog> selectDeviceLogList(DeviceLog deviceLog);
|
public List<DeviceLog> selectDeviceLogList(DeviceLog deviceLog);
|
||||||
|
|
||||||
|
|
||||||
|
List<DeviceLog> getDataRepairList(DeviceLog deviceLog);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询设备监测数据
|
* 查询设备监测数据
|
||||||
*
|
*
|
||||||
|
|||||||
@ -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删除设备日志
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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)) {
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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) {
|
||||||
|
if (deviceLog.getCreateTime() == null) {
|
||||||
deviceLog.setCreateTime(DateUtils.getNowDate());
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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; // 设备上报
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,8 @@ public interface ILogService {
|
|||||||
**/
|
**/
|
||||||
List<DeviceLog> selectDeviceLogList(DeviceLog deviceLog);
|
List<DeviceLog> selectDeviceLogList(DeviceLog deviceLog);
|
||||||
|
|
||||||
|
List<DeviceLog> getDataRepairList(DeviceLog deviceLog);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询监测数据列表
|
* 查询监测数据列表
|
||||||
**/
|
**/
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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
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
132
vue/src/api/data/repair.js
Normal 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
|
||||||
|
});
|
||||||
|
}
|
||||||
1
vue/src/assets/icons/svg/data-analysis.svg
Normal file
1
vue/src/assets/icons/svg/data-analysis.svg
Normal 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 |
@ -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',
|
||||||
|
|||||||
1207
vue/src/views/dataCenter/dataRepair.vue
Normal file
1207
vue/src/views/dataCenter/dataRepair.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -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')" />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user