Merge branch 'dyf-device' into 6170

This commit is contained in:
2025-09-26 09:33:07 +08:00
17 changed files with 304 additions and 41 deletions

View File

@ -263,18 +263,21 @@ public class AppAuthController {
@GetMapping("/version")
public R<List<SysDictDataVo>> getAppVersion() {
List<SysDictDataVo> list = dictTypeService.selectDictDataByType("app_version");
list.forEach(d -> {
String[] arr = d.getRemark().split("\\|");
d.setDictLabel(d.getDictLabel()); // ios/android
d.setDictValue(arr[0]); // 版本号
d.setRemark(arr[1]); // 下载地址
// 1. 安全拆分
String[] arr = d.getRemark() == null ? new String[0] : d.getRemark().split("\\|");
if (arr.length < 2) { // 格式不对
log.warn("字典数据 app_version 格式非法dictLabel={}, remark={}", d.getDictLabel(), d.getRemark());
d.setDictValue(""); // 或者 d.setDictValue(d.getDictValue());
d.setRemark(""); // 下载地址留空
return; // 跳过
}
// 2. 正常赋值
d.setDictValue(arr[0].trim()); // 版本号
d.setRemark(arr[1].trim()); // 下载地址
});
// 只保留方法体:筛选 label=ios 且版本号 ≥ 2.5.0 的列表
// List<SysDictDataVo> result = list.stream()
// .peek(d -> { String[] a = d.getRemark().split("\\|"); d.setDictValue(a[0]); d.setRemark(a[1]); })
// .filter(d -> "ios".equalsIgnoreCase(d.getDictLabel()))
// .filter(d -> VersionComparator.INSTANCE.compare(d.getDictValue(), "2.5.0") >= 0)
// .toList();
return R.ok(list);
}

View File

@ -10,6 +10,7 @@ import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation;
import com.fuyuanshen.common.web.core.BaseController;
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto;
import com.fuyuanshen.web.service.device.DeviceBJQBizService;
import com.fuyuanshen.web.service.device.DeviceXinghanBizService;
import lombok.RequiredArgsConstructor;
@ -70,7 +71,7 @@ public class AppDeviceXinghanController extends BaseController {
*/
@Log(title = "xinghan指令-静电预警档位")
@PostMapping("/DetectGradeSettings")
public R<Void> DetectGradeSettings(@RequestBody DeviceInstructDto params) {
public R<Void> DetectGradeSettings(@RequestBody DeviceXinghanInstructDto params) {
// params 转 JSONObject
appDeviceService.upDetectGradeSettings(params);
return R.ok();
@ -82,7 +83,7 @@ public class AppDeviceXinghanController extends BaseController {
*/
@Log(title = "xinghan指令-照明档位")
@PostMapping("/LightGradeSettings")
public R<Void> LightGradeSettings(@RequestBody DeviceInstructDto params) {
public R<Void> LightGradeSettings(@RequestBody DeviceXinghanInstructDto params) {
// params 转 JSONObject
appDeviceService.upLightGradeSettings(params);
return R.ok();
@ -94,7 +95,7 @@ public class AppDeviceXinghanController extends BaseController {
*/
@Log(title = "xinghan指令-SOS档位s")
@PostMapping("/SOSGradeSettings")
public R<Void> SOSGradeSettings(@RequestBody DeviceInstructDto params) {
public R<Void> SOSGradeSettings(@RequestBody DeviceXinghanInstructDto params) {
// params 转 JSONObject
appDeviceService.upSOSGradeSettings(params);
return R.ok();
@ -106,7 +107,7 @@ public class AppDeviceXinghanController extends BaseController {
*/
@Log(title = "xinghan指令-静止报警状态")
@PostMapping("/ShakeBitSettings")
public R<Void> ShakeBitSettings(@RequestBody DeviceInstructDto params) {
public R<Void> ShakeBitSettings(@RequestBody DeviceXinghanInstructDto params) {
// params 转 JSONObject
appDeviceService.upShakeBitSettings(params);
return R.ok();

View File

@ -61,5 +61,14 @@ public class MqttXinghanJson {
*/
@JsonProperty("sta_latitude")
public String stalatitude;
/**
* 第十二键值对系统现状0关机1仅充电2开机未充电,3开机且充电
*/
@JsonProperty("sta_system")
public String stasystem;
/**
* 电量百分比(适配控制列表显示)
*/
public String batteryPercentage;
}

View File

@ -1,33 +1,42 @@
package com.fuyuanshen.global.mqtt.rule.xinghan;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.core.utils.date.DurationUtils;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.bo.DeviceAlarmBo;
import com.fuyuanshen.equipment.domain.vo.DeviceAlarmVo;
import com.fuyuanshen.equipment.mapper.DeviceAlarmMapper;
import com.fuyuanshen.equipment.service.DeviceService;
import com.fuyuanshen.equipment.service.IDeviceAlarmService;
import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil;
import com.fuyuanshen.equipment.utils.map.LngLonUtil;
import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.base.MqttXinghanJson;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.constants.XingHanCommandTypeConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import com.fuyuanshen.web.enums.AlarmTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
@ -57,6 +66,9 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
@Autowired
private ObjectMapper objectMapper;
private final IDeviceAlarmService deviceAlarmService;
private final DeviceService deviceService;
private final DeviceAlarmMapper deviceAlarmMapper;
@Override
public void execute(MqttRuleContext context) {
@ -65,7 +77,7 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
// Latitude, longitude
//主灯档位,激光灯档位,电量百分比,充电状态,电池剩余续航时间
MqttXinghanJson deviceStatus = objectMapper.convertValue(context.getPayloadDict(), MqttXinghanJson.class);
deviceStatus.setBatteryPercentage(deviceStatus.getStaPowerPercent().toString());
// 发送设备状态和位置信息到Redis
asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),deviceStatus);
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
@ -99,12 +111,113 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
// 异步发送经纬度到Redis
asyncSendLocationToRedisWithFuture(deviceImei, deviceStatus.getStalatitude(), deviceStatus.getStalongitude());
// 保存报警信息
saveAlarm(deviceImei,deviceStatus);
} catch (Exception e) {
log.error("异步发送设备信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
}
});
}
/**
* 入口保存报警SOS 与 Shake 完全独立)
*/
public void saveAlarm(String deviceImei, MqttXinghanJson status) {
int sos = Optional.ofNullable(status.getStaSOSGrade()).orElse(0);
// 1. 处理 SOS 报警
handleSingleAlarm(deviceImei,
sos > 0,
AlarmTypeEnum.SOS);
int shake = Optional.ofNullable(status.getStaShakeBit()).orElse(0);
// 2. 处理 Shake 报警
handleSingleAlarm(deviceImei,
shake > 0,
AlarmTypeEnum.SHAKE);
}
/**
* 通用:对单个报警源的“开始/结束”生命周期管理
*/
private void handleSingleAlarm(String deviceImei, boolean nowAlarming, AlarmTypeEnum type) {
String redisKey = buildAlarmRedisKey(deviceImei, type);
Long alarmId = RedisUtils.getCacheObject(redisKey);
// ---------- 情况 1当前正在报警 ----------
if (nowAlarming) {
// 已存在未结束报警 -> 什么都不做(同一条报警)
if (alarmId != null) {
// key 还在 -> 同一条报警,只续期
RedisUtils.setCacheObject(redisKey, alarmId, Duration.ofMinutes(10));
return;
}
// 不存在 -> 新建
DeviceAlarmBo bo = createAlarmBo(deviceImei, type);
deviceAlarmService.insertByBo(bo);
RedisUtils.setCacheObject(redisKey, bo.getId(), Duration.ofMinutes(10)); // 5分钟后结束过期
return;
}
// ---------- 情况 2当前不报警 ----------
if (alarmId != null) {
// 结束它
finishAlarm(alarmId);
RedisUtils.deleteObject(redisKey);
}
}
/**
* 结束报警:写库
*/
private void finishAlarm(Long alarmId) {
DeviceAlarmVo vo = deviceAlarmService.queryById(alarmId);
if (vo == null || vo.getTreatmentState() == 0) {
return; // 已处理或已被删
}
DeviceAlarmBo bo = BeanUtil.toBean(vo, DeviceAlarmBo.class);
bo.setFinishTime(new Date());
bo.setDurationTime(DurationUtils.getDurationBetween(vo.getStartTime(), bo.getFinishTime()));
bo.setTreatmentState(0); // 已处理
deviceAlarmService.updateByBo(bo);
}
/**
* 新建报警 Bo
*/
private DeviceAlarmBo createAlarmBo(String deviceImei, AlarmTypeEnum type) {
Device device = deviceService.selectDeviceByImei(deviceImei);
if (device == null) {
return null;
}
DeviceAlarmBo bo = new DeviceAlarmBo();
bo.setDeviceId(device.getId());
bo.setDeviceImei(deviceImei);
bo.setDeviceAction(2); // 自动报警
bo.setStartTime(new Date());
bo.setTreatmentState(1); // 未处理
bo.setTenantId(device.getTenantId());
// 报警内容
bo.setContent("自动报警:" + type.getDesc());
// 位置
String location = RedisUtils.getCacheObject(
GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX);
if (StrUtil.isNotBlank(location)) {
bo.setLocation(JSONObject.parseObject(location).getString("address"));
}
return bo;
}
/**
* key 构建
*/
private String buildAlarmRedisKey(String deviceImei, AlarmTypeEnum type) {
return StrUtil.format("{}{}{}{}:alarm_id",
GLOBAL_REDIS_KEY, DEVICE_KEY_PREFIX, deviceImei, type.getSuffix());
}
/**
* 异步发送位置信息到Redis使用CompletableFuture
*

View File

@ -10,6 +10,7 @@ import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation;
import com.fuyuanshen.common.web.core.BaseController;
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto;
import com.fuyuanshen.web.domain.vo.DeviceXinghanDetailVo;
import com.fuyuanshen.web.service.device.DeviceXinghanBizService;
import jakarta.validation.constraints.NotNull;
@ -80,7 +81,7 @@ public class DeviceXinghanController extends BaseController {
* 3,2,1,0,分别表示高档/中档/低挡/关闭
*/
@PostMapping("/DetectGradeSettings")
public R<Void> DetectGradeSettings(@RequestBody DeviceInstructDto params) {
public R<Void> DetectGradeSettings(@RequestBody DeviceXinghanInstructDto params) {
// params 转 JSONObject
deviceXinghanBizService.upDetectGradeSettings(params);
return R.ok();
@ -91,7 +92,7 @@ public class DeviceXinghanController extends BaseController {
* 照明档位2,1,0,分别表示弱光/强光/关闭
*/
@PostMapping("/LightGradeSettings")
public R<Void> LightGradeSettings(@RequestBody DeviceInstructDto params) {
public R<Void> LightGradeSettings(@RequestBody DeviceXinghanInstructDto params) {
// params 转 JSONObject
deviceXinghanBizService.upLightGradeSettings(params);
return R.ok();
@ -102,7 +103,7 @@ public class DeviceXinghanController extends BaseController {
* SOS档位2,1,0, 分别表示红蓝模式/爆闪模式/关闭
*/
@PostMapping("/SOSGradeSettings")
public R<Void> SOSGradeSettings(@RequestBody DeviceInstructDto params) {
public R<Void> SOSGradeSettings(@RequestBody DeviceXinghanInstructDto params) {
// params 转 JSONObject
deviceXinghanBizService.upSOSGradeSettings(params);
return R.ok();
@ -113,7 +114,7 @@ public class DeviceXinghanController extends BaseController {
* 静止报警状态0-未静止报警1-正在静止报警。
*/
@PostMapping("/ShakeBitSettings")
public R<Void> ShakeBitSettings(@RequestBody DeviceInstructDto params) {
public R<Void> ShakeBitSettings(@RequestBody DeviceXinghanInstructDto params) {
// params 转 JSONObject
deviceXinghanBizService.upShakeBitSettings(params);
return R.ok();

View File

@ -0,0 +1,15 @@
package com.fuyuanshen.web.domain.Dto;
import lombok.Data;
@Data
public class DeviceXinghanInstructDto {
private Long deviceId;
private String deviceImei;
/**
* 下发指令
*/
private String instructValue;
private Boolean isBluetooth = false;
}

View File

@ -0,0 +1,17 @@
package com.fuyuanshen.web.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 报警类型枚举
*/
@AllArgsConstructor
@Getter
public enum AlarmTypeEnum {
SOS("_sos", "SOS报警"),
SHAKE("_shake", "静止报警");
private final String suffix;
private final String desc;
}

View File

@ -10,18 +10,15 @@ import com.fuyuanshen.app.service.IAppBusinessFileService;
import com.fuyuanshen.app.service.IAppOperationVideoService;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.service.DeviceService;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.service.ISysOssService;
import com.fuyuanshen.web.domain.vo.DeviceInfoVo;
import com.fuyuanshen.web.util.FileHashUtil;
import io.swagger.v3.oas.annotations.Operation;
import com.fuyuanshen.equipment.utils.FileHashUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;

View File

@ -1,6 +1,7 @@
package com.fuyuanshen.web.service.device;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -28,17 +29,21 @@ import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceType;
import com.fuyuanshen.equipment.domain.bo.DeviceAlarmBo;
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.equipment.enums.LightModeEnum;
import com.fuyuanshen.equipment.mapper.DeviceLogMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeMapper;
import com.fuyuanshen.equipment.service.IDeviceAlarmService;
import com.fuyuanshen.global.mqtt.base.MqttXinghanJson;
import com.fuyuanshen.global.mqtt.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
import com.fuyuanshen.web.domain.Dto.DeviceDebugLogoUploadDto;
import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto;
import com.fuyuanshen.web.domain.vo.DeviceXinghanDetailVo;
import com.fuyuanshen.web.enums.AlarmTypeEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -68,6 +73,7 @@ public class DeviceXinghanBizService {
private final MqttGateway mqttGateway;
private final DeviceLogMapper deviceLogMapper;
private final AppPersonnelInfoRecordsMapper appPersonnelInfoRecordsMapper;
private final IDeviceAlarmService deviceAlarmService;
@Autowired
private ObjectMapper objectMapper;
@ -95,28 +101,39 @@ public class DeviceXinghanBizService {
/**
* 设置静电预警档位
*/
public void upDetectGradeSettings(DeviceInstructDto dto) {
public void upDetectGradeSettings(DeviceXinghanInstructDto dto) {
sendCommand(dto, "ins_DetectGrade","静电预警档位");
}
/**
* 设置照明档位
*/
public void upLightGradeSettings(DeviceInstructDto dto) {
public void upLightGradeSettings(DeviceXinghanInstructDto dto) {
sendCommand(dto, "ins_LightGrade","照明档位");
}
/**
* 设置SOS档位
*/
public void upSOSGradeSettings(DeviceInstructDto dto) {
sendCommand(dto, "ins_SOSGrade","SOS档位");
public void upSOSGradeSettings(DeviceXinghanInstructDto dto) {
if(dto.getIsBluetooth()){
long deviceId = dto.getDeviceId();
// 1. 使用Optional简化空值检查使代码更简洁
Device device = Optional.ofNullable(deviceMapper.selectById(deviceId))
.orElseThrow(() -> new ServiceException("设备不存在"));
int sosGrade = Integer.parseInt(dto.getInstructValue());
// 6. 新建报警信息
createAlarm(device.getId(),device.getDeviceImei(),"ins_SOSGrade",sosGrade);
}else {
sendCommand(dto, "ins_SOSGrade","SOS档位");
}
}
/**
* 设置强制报警
*/
public void upShakeBitSettings(DeviceInstructDto dto) {
public void upShakeBitSettings(DeviceXinghanInstructDto dto) {
sendCommand(dto, "ins_ShakeBit","强制报警");
}
@ -465,7 +482,7 @@ public class DeviceXinghanBizService {
* @param payloadKey 指令负载数据的键名
* @param deviceAction 设备操作类型描述
*/
private void sendCommand(DeviceInstructDto dto, String payloadKey, String deviceAction) {
private void sendCommand(DeviceXinghanInstructDto dto, String payloadKey, String deviceAction) {
long deviceId = dto.getDeviceId();
// 1. 使用Optional简化空值检查使代码更简洁
@ -509,6 +526,9 @@ public class DeviceXinghanBizService {
deviceAction,
content,
AppLoginHelper.getUserId());
// 6. 新建报警信息
createAlarm(device.getId(),deviceImei,payloadKey,value);
}
// private boolean isDeviceOffline(String imei) {
@ -516,6 +536,48 @@ public class DeviceXinghanBizService {
// return getDeviceStatus(imei);
// }
/**
* 新建报警 Bo
*/
private void createAlarm(Long deviceId, String deviceImei,
String payloadKey, int value) {
// 这里直接放你原来的 createAlarmBo 全部逻辑
if (!"ins_SOSGrade".equals(payloadKey) || value == 0) {
return;
}
AlarmTypeEnum type = value == 1 ? AlarmTypeEnum.SOS : AlarmTypeEnum.SHAKE;
String redisKey = buildAlarmRedisKey(deviceImei, type);
Long alarmId = RedisUtils.getCacheObject(redisKey);
// 已存在未结束报警 -> 什么都不做(同一条报警)
if (alarmId != null) {
// key 还在 -> 同一条报警,只续期
RedisUtils.setCacheObject(redisKey, alarmId, Duration.ofMinutes(10));
return;
}
// 不存在 -> 新建
DeviceAlarmBo bo = new DeviceAlarmBo();
bo.setDeviceId(deviceId);
bo.setDeviceImei(deviceImei);
bo.setDeviceAction(0); // 强制报警
bo.setStartTime(new Date());
bo.setTreatmentState(1); // 未处理
bo.setContent("强制报警:" + type.getDesc());
String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX);
if (StrUtil.isNotBlank(location)) {
bo.setLocation(JSONObject.parseObject(location).getString("address"));
}
deviceAlarmService.insertByBo(bo);
RedisUtils.setCacheObject(redisKey, bo.getId(), Duration.ofMinutes(10));
}
/**
* key 构建
*/
private String buildAlarmRedisKey(String deviceImei, AlarmTypeEnum type) {
return StrUtil.format("{}{}{}{}:alarm_id",
GLOBAL_REDIS_KEY, DEVICE_KEY_PREFIX, deviceImei, type.getSuffix());
}
/**
* 记录设备操作日志
* @param deviceId 设备ID

View File

@ -11,6 +11,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.multipart.MultipartFile;
@ -70,4 +72,6 @@ public class DeviceRepairRecordsBo extends BaseEntity {
@Schema(title = "维修后图片")
@JsonIgnore
private MultipartFile afterFile;
private List<Long> imageIds;
}

View File

@ -22,9 +22,6 @@ public class DeviceExcelExportDTO {
// @ExcelProperty("ID")
// private Long id;
@ExcelProperty("设备类型")
@ColumnWidth(20)
private String typeName;
// @ExcelProperty("设备类型")
// private Long deviceType;
@ -55,12 +52,25 @@ public class DeviceExcelExportDTO {
@ColumnWidth(20)
private String deviceImei;
@ExcelProperty("设备类型")
@ColumnWidth(20)
private String typeName;
// @ExcelProperty("经度")
// private String longitude;
// @ExcelProperty("纬度")
// private String latitude;
/**
* 绑定状态
* 0 未绑定
* 1 已绑定
*/
@ExcelProperty("绑定状态")
@ColumnWidth(20)
private String bindingStatus;
@ExcelProperty("备注")
@ColumnWidth(30)
private String remark;

View File

@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.multipart.MultipartFile;
import java.util.Date;
@ -51,8 +52,10 @@ public class DeviceRepairRecordsQueryCriteria extends BaseEntity {
private String repairPerson;
@Schema(title = "维修开始时间")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date repairBeginTime;
@Schema(title = "维修结束时间")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date repairEndTime;
@Schema(title = "所属客户")

View File

@ -53,7 +53,9 @@ public class DeviceExportService {
dto.setTypeName(device.getTypeName());
dto.setCreateBy(device.getCreateByName());
Integer deviceStatus = device.getDeviceStatus();
Integer bindingStatus = device.getBindingStatus();
// dto.setDeviceStatus(deviceStatus == 1 ? "正常" : "失效");
dto.setBindingStatus(bindingStatus == 1 ? "已绑定" : "未绑定");
// 时间戳转换
dto.setCreateTime(DateUtils.format(device.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));

View File

@ -18,6 +18,7 @@ import com.fuyuanshen.equipment.domain.vo.DeviceRepairImagesVo;
import com.fuyuanshen.equipment.enums.RepairImageType;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceRepairImagesMapper;
import com.fuyuanshen.equipment.utils.FileHashUtil;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.service.ISysOssService;
import lombok.RequiredArgsConstructor;
@ -32,6 +33,7 @@ import com.fuyuanshen.equipment.service.IDeviceRepairRecordsService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.*;
/**
@ -178,6 +180,10 @@ public class DeviceRepairRecordsServiceImpl extends ServiceImpl<DeviceRepairReco
if (!updated) {
return false;
}
// 3. 删除旧图片
if(bo.getImageIds() != null){
imagesMapper.deleteByIds(bo.getImageIds());
}
// 3. 收集需要保存的图片
List<DeviceRepairImages> images = new ArrayList<>(2);
@ -200,12 +206,28 @@ public class DeviceRepairRecordsServiceImpl extends ServiceImpl<DeviceRepairReco
if (file == null || file.isEmpty()) {
return;
}
SysOssVo ossVo = ossService.upload(file);
// 1. 计算文件哈希
String hash = null;
try {
hash = FileHashUtil.hash(file);
} catch (IOException e) {
throw new RuntimeException(e);
}
// 2. 先根据 hash 查库(秒传)
SysOssVo exist = ossService.selectByHash(hash);
if (exist == null) {
// 2.2 不存在,真正上传
exist = ossService.upload(file);
// 2.3 把 hash 写回记录(供下次去重)
ossService.updateHashById(exist.getOssId(), hash);
}
DeviceRepairImages image = new DeviceRepairImages();
image.setRecordId(recordId);
image.setImageType(imageType);
image.setImageUrl(ossVo.getUrl());
image.setImageUrl(exist.getUrl());
list.add(image);
}

View File

@ -1,4 +1,4 @@
package com.fuyuanshen.web.util;
package com.fuyuanshen.equipment.utils;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

View File

@ -29,9 +29,10 @@
and da.treatment_state = #{bo.treatmentState}
</if>
<if test="bo.queryTime1 != null and bo.queryTime2 != null ">
and da.start_time between #{bo.queryTime1} and #{bo.queryTime2}
and da.start_time BETWEEN #{bo.queryTime1} AND #{bo.queryTime2}
</if>
</where>
order by da.create_time DESC
</select>

View File

@ -23,6 +23,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
FROM device_repair_records dr
JOIN device d ON dr.device_id = d.id
<where>
<if test="criteria.searchValue != null">
and d.device_name like concat('%', TRIM(#{criteria.searchValue}), '%')
</if>
<if test="criteria.deviceId != null">
and dr.device_id = #{criteria.deviceId}
</if>