Merge pull request 'feat(device): 新增设备类型查询与设备添加功能' (#14) from liwenlong/fys-Multi-tenant:jingquan into jingquan

Reviewed-on: dyf/fys-Multi-tenant#14
This commit is contained in:
dyf
2025-09-30 16:06:10 +08:00
11 changed files with 156 additions and 24 deletions

View File

@ -9,15 +9,24 @@ import com.fuyuanshen.common.log.annotation.Log;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation;
import com.fuyuanshen.common.web.core.BaseController;
import com.fuyuanshen.customer.mapper.CustomerMapper;
import com.fuyuanshen.equipment.domain.DeviceType;
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.equipment.domain.form.DeviceForm;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.service.DeviceService;
import com.fuyuanshen.equipment.service.DeviceTypeService;
import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto;
import com.fuyuanshen.web.service.device.DeviceBJQBizService;
import com.fuyuanshen.web.service.device.DeviceXinghanBizService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* HBY670设备控制类
*/
@ -28,6 +37,7 @@ import org.springframework.web.multipart.MultipartFile;
public class AppDeviceXinghanController extends BaseController {
private final DeviceXinghanBizService appDeviceService;
private final DeviceService deviceService;
/**
* 人员信息登记
*/
@ -113,4 +123,23 @@ public class AppDeviceXinghanController extends BaseController {
return R.ok();
}
@GetMapping(value = "/typeAll")
@Operation(summary = "查询所有设备类型")
public R<List<DeviceType>> queryDeviceTypes() {
List<DeviceType> deviceTypes = appDeviceService.queryDeviceTypes();
return R.ok(deviceTypes);
}
// @Log("新增设备")
@Operation(summary = "新增设备")
@PostMapping(value = "/add")
public R<Void> addDevice(@RequestBody DeviceForm deviceForm) {
try {
appDeviceService.addDevice(deviceForm);
} catch (Exception e) {
return R.fail(e.getMessage());
}
return R.ok();
}
}

View File

@ -64,7 +64,7 @@ public class XinghanBootLogoRule implements MqttMessageRule {
// --- 去重 START ---
String dedupKey = "xd:MSG:LOGO:" + ctx.getDeviceImei() + ":" + respText;
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(10));
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(3));
if (!first) {
log.warn("重复消息丢弃 {}", dedupKey);
return;

View File

@ -57,7 +57,7 @@ public class XinghanSendAlarmMessageRule implements MqttMessageRule {
log.info("设备上报紧急通知握手: {} ", respText);
// --- 去重 START ---
String dedupKey = "xd:ALARM:dedup:" + ctx.getDeviceImei() + ":" + respText;
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(10));
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(3));
if (!first) {
log.warn("重复消息丢弃 {}", dedupKey);
return;

View File

@ -57,7 +57,7 @@ public class XinghanSendMsgRule implements MqttMessageRule {
// --- 去重 START ---
String dedupKey = "xd:MSG:dedup:" + ctx.getDeviceImei() + ":" + respText;
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(10));
boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(3));
if (!first) {
log.warn("重复消息丢弃 {}", dedupKey);
return;

View File

@ -61,7 +61,7 @@ public class DeviceDebugController extends BaseController {
@Log(title = "批量上传文件")
@PostMapping("/addFile")
public R<Void> uploadFile(@Validated @ModelAttribute AppFileDto bo) throws IOException {
return toAjax(deviceDebugService.addFileHash(bo));
return toAjax(deviceDebugService.addFileHash(bo,true));
}
/**
@ -113,7 +113,7 @@ public class DeviceDebugController extends BaseController {
appFileDto.setDeviceIds(new Long[]{ bo.getDeviceId() });
appFileDto.setFileType(1L);
appFileDto.setFiles(bo.getExplanationFiles());
deviceDebugService.addFileHash(appFileDto);
deviceDebugService.addFileHash(appFileDto,false);
}
// 修改上传设备参数
if (bo.getParameterFiles() != null) {
@ -121,8 +121,9 @@ public class DeviceDebugController extends BaseController {
appFileDto.setDeviceIds(new Long[]{ bo.getDeviceId() });
appFileDto.setFileType(2L);
appFileDto.setFiles(bo.getParameterFiles());
deviceDebugService.addFileHash(appFileDto);
deviceDebugService.addFileHash(appFileDto,false);
}
deviceDebugService.delFile(bo.getFileIds());
// 修改操作视频
if (bo.getVideoUrl().isEmpty()) {
AppOperationVideoBo appOperationVideoBo = new AppOperationVideoBo();

View File

@ -31,4 +31,6 @@ public class DeviceDebugEditDto {
* 视频链接
*/
private String videoUrl;
private List<Long> fileIds;
}

View File

@ -47,7 +47,7 @@ public class DeviceDebugService {
* @throws IOException
*/
@Transactional(rollbackFor = Exception.class)
public Boolean addFileHash(AppFileDto bo) throws IOException {
public Boolean addFileHash(AppFileDto bo,Boolean isBatch) throws IOException {
MultipartFile[] files = bo.getFiles();
if (files == null || files.length == 0) {
throw new ServiceException("请选择要上传的文件");
@ -100,7 +100,7 @@ public class DeviceDebugService {
if (CollUtil.isEmpty(bizList)) { // 空集合直接返回
throw new ServiceException("请选择要上传的文件");
}
return appBusinessFileService.insertBatch(bizList);
return appBusinessFileService.insertBatch(bizList,isBatch);
}
public Boolean addVideoList(AppOperationVideoBo bo){
@ -124,6 +124,13 @@ public class DeviceDebugService {
}
return appOperationVideoService.insertBatch(bizList);
}
// 删除文件
public void delFile(List<Long> ids) {
// 3. 删除旧图片
if(ids != null){
appBusinessFileService.deleteWithValidByIds(ids, true);
}
}
/**
* 设备详情

View File

@ -1,6 +1,8 @@
package com.fuyuanshen.web.service.device;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
@ -19,6 +21,10 @@ import com.fuyuanshen.app.domain.vo.AppPersonnelInfoVo;
import com.fuyuanshen.app.mapper.AppPersonnelInfoMapper;
import com.fuyuanshen.app.mapper.AppPersonnelInfoRecordsMapper;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
import com.fuyuanshen.common.core.domain.model.LoginUser;
import com.fuyuanshen.common.core.exception.BadRequestException;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.ImageToCArrayConverter;
import com.fuyuanshen.common.core.utils.MapstructUtils;
@ -27,20 +33,26 @@ 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.DeviceLog;
import com.fuyuanshen.equipment.domain.DeviceType;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.equipment.domain.*;
import com.fuyuanshen.equipment.domain.bo.DeviceAlarmBo;
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
import com.fuyuanshen.equipment.domain.form.DeviceForm;
import com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria;
import com.fuyuanshen.equipment.enums.DeviceActiveStatusEnum;
import com.fuyuanshen.equipment.enums.LightModeEnum;
import com.fuyuanshen.equipment.mapper.DeviceLogMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeGrantsMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeMapper;
import com.fuyuanshen.equipment.service.DeviceAssignmentsService;
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.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.domain.vo.SysRoleVo;
import com.fuyuanshen.web.domain.Dto.DeviceDebugLogoUploadDto;
import com.fuyuanshen.web.domain.Dto.DeviceXinghanInstructDto;
import com.fuyuanshen.web.domain.vo.DeviceXinghanDetailVo;
@ -50,17 +62,19 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
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.equipment.service.impl.DeviceServiceImpl.USER_ID_SEPARATOR;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
@Slf4j
@ -75,6 +89,8 @@ public class DeviceXinghanBizService {
private final DeviceLogMapper deviceLogMapper;
private final AppPersonnelInfoRecordsMapper appPersonnelInfoRecordsMapper;
private final IDeviceAlarmService deviceAlarmService;
private final DeviceTypeGrantsMapper deviceTypeGrantsMapper;
private final DeviceAssignmentsService deviceAssignmentsService;
@Autowired
private ObjectMapper objectMapper;
@ -324,9 +340,9 @@ public class DeviceXinghanBizService {
}
List<String> list = new ArrayList<>();
list.add(bo.getUnitName());
list.add(bo.getName());
list.add(bo.getPosition());
list.add(bo.getUnitName());
list.add(bo.getCode());
RedisUtils.setCacheList(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data", list);
RedisUtils.expire(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data", Duration.ofSeconds(5 * 60L));
@ -677,4 +693,76 @@ public class DeviceXinghanBizService {
return RedisUtils.getCacheObject(deviceOnlineStatusRedisKey)==null;
}
public List<DeviceType> queryDeviceTypes() {
DeviceTypeQueryCriteria criteria = new DeviceTypeQueryCriteria();
return deviceTypeMapper.findAll(criteria);
}
// @Log("新增设备")
public void addDevice(DeviceForm deviceForm) {
if (deviceForm.getDeviceMac() != null && deviceForm.getBluetoothName() == null) {
throw new BadRequestException("请填写蓝牙名称!!!");
}
Device device1 = deviceMapper.selectOne(new QueryWrapper<Device>().eq("device_mac", deviceForm.getDeviceMac()));
if (device1 != null) {
throw new BadRequestException("设备MAC已存在");
}
Device device2 = deviceMapper.selectOne(new QueryWrapper<Device>().eq("device_imei", deviceForm.getDeviceImei()));
if (device2 != null) {
throw new BadRequestException("设备IMEI已存在");
}
DeviceTypeQueryCriteria queryCriteria = new DeviceTypeQueryCriteria();
queryCriteria.setDeviceTypeId(deviceForm.getDeviceType());
queryCriteria.setCustomerId(LoginHelper.getUserId());
DeviceTypeGrants typeGrants = deviceTypeGrantsMapper.selectById(queryCriteria.getDeviceTypeId());
if (typeGrants == null) {
throw new ServiceException("设备类型不存在!!!");
}
DeviceType deviceTypes = deviceTypeMapper.selectById(typeGrants.getDeviceTypeId());
if (deviceTypes == null) {
throw new ServiceException("设备类型不存在!!!");
}
// 转换对象并插入数据库
Device device = new Device();
BeanUtil.copyProperties(deviceForm, device, true);
device.setDeviceNo(createDeviceNo());
AppLoginUser loginUser = AppLoginHelper.getLoginUser();
device.setCurrentOwnerId(loginUser.getUserId());
device.setOriginalOwnerId(loginUser.getUserId());
device.setCreateByName(loginUser.getNickname());
device.setTypeName(deviceTypes.getTypeName());
device.setDeviceType(deviceTypes.getId());
if (device.getDeviceImei() != null) {
device.setPubTopic("A/" + device.getDeviceImei());
device.setSubTopic("B/" + device.getDeviceImei());
}
// 0 未绑定
device.setBindingStatus(0);
deviceMapper.insert(device);
// 新增设备类型记录
DeviceAssignments assignments = new DeviceAssignments();
assignments.setDeviceId(device.getId());
assignments.setAssignedAt(LocalDateTime.now());
// 分配者
assignments.setAssignerId(loginUser.getUserId());
assignments.setAssignerName(loginUser.getUsername());
// 接收者
assignments.setAssigneeId(loginUser.getUserId());
assignments.setActive(DeviceActiveStatusEnum.ACTIVE.getCode());
String lever = USER_ID_SEPARATOR + loginUser.getUserId();
assignments.setLever(lever);
deviceAssignmentsService.save(assignments);
}
private String createDeviceNo() {
String uuidStr = UUID.fastUUID().toString(); // 获取带 - 的标准格式字符串
return uuidStr.replaceAll("-", "");
}
}

View File

@ -57,7 +57,7 @@ public interface IAppBusinessFileService {
* @param bo 批量新增app业务文件
* @return 是否新增成功
*/
Boolean insertBatch(Collection<AppBusinessFile> bo);
Boolean insertBatch(Collection<AppBusinessFile> bo,Boolean isBatch);
/**
* 修改app业务文件

View File

@ -102,16 +102,18 @@ public class AppBusinessFileServiceImpl implements IAppBusinessFileService {
}
@Override
public Boolean insertBatch(Collection<AppBusinessFile> bo) {
public Boolean insertBatch(Collection<AppBusinessFile> bo,Boolean isBatch) {
// 1. 去重后的 businessId 集合
List<Long> businessIds = bo.stream()
.map(AppBusinessFile::getBusinessId)
.distinct()
.toList();
QueryWrapper<AppBusinessFile> queryWrapper = new QueryWrapper<>();
queryWrapper.in("business_id", businessIds);
queryWrapper.eq("file_type", bo.stream().findFirst().orElseThrow().getFileType());
baseMapper.delete(queryWrapper);
if(isBatch){
List<Long> businessIds = bo.stream()
.map(AppBusinessFile::getBusinessId)
.distinct()
.toList();
QueryWrapper<AppBusinessFile> queryWrapper = new QueryWrapper<>();
queryWrapper.in("business_id", businessIds);
queryWrapper.eq("file_type", bo.stream().findFirst().orElseThrow().getFileType());
baseMapper.delete(queryWrapper);
}
return baseMapper.insertBatch(bo);
}

View File

@ -32,6 +32,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="criteria.repairPart != null">
and dr.repair_part like concat('%', TRIM(#{criteria.repairPart}), '%')
</if>
<if test="criteria.repairPerson != null">
and dr.repair_person like concat('%', TRIM(#{criteria.repairPerson}), '%')
</if>
<!-- 时间段条件 -->
<if test="criteria.repairBeginTime != null">
AND dr.repair_time <![CDATA[ >= ]]> #{criteria.repairBeginTime}