Merge remote-tracking branch 'origin/6170' into 6170

# Conflicts:
#	fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java
This commit is contained in:
2025-08-23 16:40:42 +08:00
12 changed files with 688 additions and 12 deletions

View File

@ -3,6 +3,8 @@ package com.fuyuanshen.web.controller.device;
import java.util.List;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
@ -27,6 +29,7 @@ import com.fuyuanshen.equipment.service.IDeviceGroupService;
* @author Lion Li
* @date 2025-08-08
*/
@Tag(name = "web:设备分组", description = "web:设备分组")
@Validated
@RequiredArgsConstructor
@RestController
@ -39,6 +42,7 @@ public class DeviceGroupController extends BaseController {
/**
* 查询设备分组列表
*/
@Operation(summary = "查询设备分组列表")
@SaCheckPermission("fys-equipment:group:list")
@GetMapping("/list")
public R<List<DeviceGroupVo>> list(DeviceGroupBo bo) {
@ -64,6 +68,7 @@ public class DeviceGroupController extends BaseController {
*
* @param id 主键
*/
@Operation(summary = "获取设备分组详细信息")
@SaCheckPermission("fys-equipment:group:query")
@GetMapping("/{id}")
public R<DeviceGroupVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
@ -74,6 +79,7 @@ public class DeviceGroupController extends BaseController {
/**
* 新增设备分组
*/
@Operation(summary = "新增设备分组")
@SaCheckPermission("fys-equipment:group:add")
@Log(title = "设备分组", businessType = BusinessType.INSERT)
@RepeatSubmit()
@ -86,6 +92,7 @@ public class DeviceGroupController extends BaseController {
/**
* 修改设备分组
*/
@Operation(summary = "修改设备分组")
@SaCheckPermission("fys-equipment:group:edit")
@Log(title = "设备分组", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@ -99,6 +106,7 @@ public class DeviceGroupController extends BaseController {
*
* @param ids 主键串
*/
@Operation(summary = "删除设备分组")
@SaCheckPermission("fys-equipment:group:remove")
@Log(title = "设备分组", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")

View File

@ -0,0 +1,269 @@
package com.fuyuanshen.web.service.device;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fuyuanshen.app.domain.AppPersonnelInfo;
import com.fuyuanshen.app.domain.bo.AppPersonnelInfoBo;
import com.fuyuanshen.app.domain.dto.AppDeviceLogoUploadDto;
import com.fuyuanshen.app.domain.dto.DeviceInstructDto;
import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo;
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.ImageToCArrayConverter;
import com.fuyuanshen.common.core.utils.MapstructUtils;
import com.fuyuanshen.common.core.utils.ObjectUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.json.utils.JsonUtils;
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.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.global.mqtt.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.time.Duration;
import java.util.*;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.buildArr;
import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.generateFixedBitmapData;
import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_BOOT_LOGO_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceXinghanBizService {
private final DeviceMapper deviceMapper;
private final AppPersonnelInfoMapper appPersonnelInfoMapper;
private final DeviceTypeMapper deviceTypeMapper;
private final MqttGateway mqttGateway;
private final DeviceLogMapper deviceLogMapper;
/**
* 所有档位的描述表
* key : 指令类型,如 "ins_DetectGrade"、"ins_LightGrade" ……
* value : Map<Integer,String> 值 -> 描述
*/
private static final Map<String, Map<Integer, String>> GRADE_DESC = Map.of(
"ins_DetectGrade", Map.of(1, "低档", 2, "中档", 3, "高档"),
"ins_LightGrade", Map.of(1, "强光", 2, "弱光"),
"ins_SOSGrade", Map.of(1, "爆闪模式", 2, "红蓝模式"),
"ins_ShakeBit", Map.of(0, "未静止报警", 1, "正在静止报警")
// 再加 4、5、6…… 档,直接往 Map 里塞即可
);
/**
* 根据指令类型和值,返回中文描述
*/
private static String resolveGradeDesc(String type, int value) {
return GRADE_DESC.getOrDefault(type, Map.of())
.getOrDefault(value, "关闭");
}
/**
* 设置静电预警档位
*/
public void upDetectGradeSettings(DeviceInstructDto dto) {
sendCommand(dto, "ins_DetectGrade","静电预警档位");
}
/**
* 设置照明档位
*/
public void upLightGradeSettings(DeviceInstructDto dto) {
sendCommand(dto, "ins_LightGrade","照明档位");
}
/**
* 设置SOS档位
*/
public void upSOSGradeSettings(DeviceInstructDto dto) {
sendCommand(dto, "ins_SOSGrade","SOS档位");
}
/**
* 设置强制报警
*/
public void upShakeBitSettings(DeviceInstructDto dto) {
sendCommand(dto, "ins_ShakeBit","强制报警");
}
/**
* 上传设备logo
*/
public void uploadDeviceLogo(AppDeviceLogoUploadDto bo) {
try {
Device device = deviceMapper.selectById(bo.getDeviceId());
if (device == null) {
throw new ServiceException("设备不存在");
}
if (isDeviceOffline(device.getDeviceImei())) {
throw new ServiceException("设备已断开连接:" + device.getDeviceName());
}
MultipartFile file = bo.getFile();
byte[] largeData = ImageToCArrayConverter.convertImageToCArray(file.getInputStream(), 160, 80, 25600);
log.info("长度:" + largeData.length);
log.info("原始数据大小: {} 字节", largeData.length);
int[] ints = convertHexToDecimal(largeData);
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() +DEVICE_BOOT_LOGO_KEY_PREFIX, Arrays.toString(ints), Duration.ofSeconds(5 * 60L));
Map<String, Object> payload = Map.of("ins_PicTrans",
Collections.singletonList(0));
String topic = MqttConstants.GLOBAL_PUB_KEY + device.getDeviceImei();
String json = JsonUtils.toJsonString(payload);
try {
mqttGateway.sendMsgToMqtt(topic, 1, json);
} catch (Exception e) {
log.error("上传开机画面失败, topic={}, payload={}", topic, json, e);
throw new ServiceException("上传LOGO失败" + e.getMessage());
}
log.info("发送上传开机画面到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),json);
recordDeviceLog(device.getId(), device.getDeviceName(), "上传开机画面", "上传开机画面", AppLoginHelper.getUserId());
} catch (Exception e){
throw new ServiceException("发送指令失败");
}
}
/**
* 人员登记
* @param bo
*/
public boolean registerPersonInfo(AppPersonnelInfoBo bo) {
Long deviceId = bo.getDeviceId();
Device deviceObj = deviceMapper.selectById(deviceId);
if (deviceObj == null) {
throw new RuntimeException("请先将设备入库!!!");
}
if (isDeviceOffline(deviceObj.getDeviceImei())) {
throw new ServiceException("设备已断开连接:" + deviceObj.getDeviceName());
}
QueryWrapper<AppPersonnelInfo> qw = new QueryWrapper<AppPersonnelInfo>()
.eq("device_id", deviceId);
List<AppPersonnelInfoVo> appPersonnelInfoVos = appPersonnelInfoMapper.selectVoList(qw);
List<String> list = new ArrayList<>();
list.add(bo.getUnitName());
list.add(bo.getName());
list.add(bo.getPosition());
list.add(bo.getCode());
RedisUtils.setCacheList(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data", list);
Map<String, Object> payload = Map.of("ins_TexTrans",
Collections.singletonList(0));
String topic = MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei();
String json = JsonUtils.toJsonString(payload);
try {
mqttGateway.sendMsgToMqtt(topic, 1, json);
} catch (Exception e) {
log.error("人员信息登记失败, topic={}, payload={}", topic, json, e);
throw new ServiceException("人员信息登记失败:" + e.getMessage());
}
log.info("发送人员信息登记到设备消息=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY + deviceObj.getDeviceImei(), bo);
recordDeviceLog(deviceId, deviceObj.getDeviceName(), "人员信息登记", JSON.toJSONString(bo), AppLoginHelper.getUserId());
if (ObjectUtils.length(appPersonnelInfoVos) == 0) {
AppPersonnelInfo appPersonnelInfo = MapstructUtils.convert(bo, AppPersonnelInfo.class);
return appPersonnelInfoMapper.insertOrUpdate(appPersonnelInfo);
} else {
UpdateWrapper<AppPersonnelInfo> uw = new UpdateWrapper<>();
uw.eq("device_id", deviceId)
.set("name", bo.getName())
.set("position", bo.getPosition())
.set("unit_name", bo.getUnitName())
.set("code", bo.getCode());
return appPersonnelInfoMapper.update(null, uw) > 0;
}
}
/* ---------------------------------- 私有通用方法 ---------------------------------- */
private void sendCommand(DeviceInstructDto dto,
String payloadKey,String deviceAction) {
long deviceId = dto.getDeviceId();
Device device = deviceMapper.selectById(deviceId);
if (device == null) {
throw new ServiceException("设备不存在");
}
if (isDeviceOffline(device.getDeviceImei())) {
throw new ServiceException("设备已断开连接:" + device.getDeviceName());
}
Integer value = Integer.parseInt(dto.getInstructValue());
Map<String, Object> payload = Map.of(payloadKey,
Collections.singletonList(value));
String topic = MqttConstants.GLOBAL_PUB_KEY + device.getDeviceImei();
String json = JsonUtils.toJsonString(payload);
try {
mqttGateway.sendMsgToMqtt(topic, 1, json);
} catch (Exception e) {
log.error("发送指令失败, topic={}, payload={}", topic, json, e);
throw new ServiceException("发送指令失败:" + e.getMessage());
}
log.info("发送指令成功 => topic:{}, payload:{}", topic, json);
String content = resolveGradeDesc("ins_DetectGrade", value);
recordDeviceLog(device.getId(),
device.getDeviceName(),
deviceAction,
content,
AppLoginHelper.getUserId());
}
private boolean isDeviceOffline(String imei) {
// 原方法名语义相反,这里取反,使含义更清晰
return getDeviceStatus(imei);
}
/**
* 记录设备操作日志
* @param deviceId 设备ID
* @param content 日志内容
* @param operator 操作人
*/
private void recordDeviceLog(Long deviceId,String deviceName, String deviceAction, String content, Long operator) {
try {
// 创建设备日志实体
com.fuyuanshen.equipment.domain.DeviceLog deviceLog = new com.fuyuanshen.equipment.domain.DeviceLog();
deviceLog.setDeviceId(deviceId);
deviceLog.setDeviceAction(deviceAction);
deviceLog.setContent(content);
deviceLog.setCreateBy(operator);
deviceLog.setDeviceName(deviceName);
deviceLog.setCreateTime(new Date());
// 插入日志记录
deviceLogMapper.insert(deviceLog);
} catch (Exception e) {
log.error("记录设备操作日志失败: {}", e.getMessage(), e);
}
}
private boolean getDeviceStatus(String deviceImei) {
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
return StringUtils.isBlank(deviceOnlineStatusRedisKey);
}
}