This commit is contained in:
zt
2025-12-02 15:57:39 +08:00
parent 37c95ab0bd
commit 7816fec7aa
9 changed files with 131 additions and 45 deletions

View File

@ -6,6 +6,8 @@ import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.project.domain.BusProject;
import org.dromara.project.service.IBusProjectService;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -36,17 +38,17 @@ public class DeviceAccessRecordController extends BaseController {
private final IDeviceAccessRecordService deviceAccessRecordService;
private final IBusProjectService projectService;
/**
* 查询设备进出场记录列表
*/
@SaCheckPermission("device:accessRecord:list")
// @SaCheckPermission("device:accessRecord:list")
@GetMapping("/list")
public TableDataInfo<DeviceAccessRecordVo> list(DeviceAccessRecordBo bo, PageQuery pageQuery) {
return deviceAccessRecordService.queryPageList(bo, pageQuery);
}
/**
* 导出设备进出场记录列表
*/
@ -63,17 +65,29 @@ public class DeviceAccessRecordController extends BaseController {
*
* @param id 主键
*/
@SaCheckPermission("device:accessRecord:query")
// @SaCheckPermission("device:accessRecord:query")
@GetMapping("/{id}")
public R<DeviceAccessRecordVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(deviceAccessRecordService.queryById(id));
}
/**
* 子项目列表
*/
@GetMapping("/childProjectList/{projectId}")
public R<List<BusProject>> childProjectId(@PathVariable Long projectId) {
List<BusProject> list = projectService.lambdaQuery()
.select(BusProject::getId, BusProject::getProjectName)
.eq(BusProject::getPId, projectId).list();
return R.ok(list);
}
/**
* 新增设备进出场记录
*/
@SaCheckPermission("device:accessRecord:add")
// @SaCheckPermission("device:accessRecord:add")
@Log(title = "设备进出场记录", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
@ -81,10 +95,12 @@ public class DeviceAccessRecordController extends BaseController {
return toAjax(deviceAccessRecordService.insertByBo(bo));
}
/**
* 修改设备进出场记录
*/
@SaCheckPermission("device:accessRecord:edit")
// @SaCheckPermission("device:accessRecord:edit")
@Log(title = "设备进出场记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()

View File

@ -85,12 +85,18 @@ public class DeviceInfoController extends BaseController {
}
/**
* 设备信息统计
*/
@SaCheckPermission("device:info:list")
@GetMapping("/count/{projectId}")
public R<DeviceInfoCountVo> count(@PathVariable Long projectId) {
public R<Map<String, Integer>> count(@PathVariable Long projectId) {
return R.ok(deviceInfoService.count(projectId));
}
/**
* 设备类型统计
*/
@SaCheckPermission("device:info:list")
@GetMapping("/typeCount/{projectId}")
public R<Map<String, Integer>> typeCount(@PathVariable Long projectId) {

View File

@ -3,6 +3,7 @@ package org.dromara.device.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
@ -56,47 +57,72 @@ public class DeviceTypeController extends BaseController {
@GetMapping("/treeList")
public R<List<DeviceTypeVo>> treeList(DeviceTypeBo bo) {
List<DeviceTypeVo> list = deviceTypeService.queryList(bo);
buildTreeWithAutoRoot(list);
return R.ok(list);
List<DeviceTypeVo> deviceTypeVos = buildTreeWithAutoRoot(list);
return R.ok(deviceTypeVos);
}
public static List<DeviceTypeVo> buildTreeWithAutoRoot(List<DeviceTypeVo> list) {
// 1. 用 Map 缓存所有节点key=节点IDvalue=节点对象)
// 防御性判断:空列表直接返回,避免空指针
if (list == null || list.isEmpty()) {
return new ArrayList<>();
}
// 1. 提取所有有效节点ID用于判断父ID是否存在
Set<Long> validNodeIds = list.stream()
.map(DeviceTypeVo::getId)
.collect(Collectors.toSet());
// 2. 用Map缓存所有节点key=节点IDvalue=节点对象O(1)查找
Map<Long, DeviceTypeVo> nodeMap = list.stream()
.collect(Collectors.toMap(DeviceTypeVo::getId, node -> node));
List<DeviceTypeVo> rootNodes = new ArrayList<>(); // 存储自动识别的顶级节点
List<DeviceTypeVo> orphanNodes = new ArrayList<>(); // 存储孤儿节点(可选:单独处理)
List<DeviceTypeVo> rootNodes = new ArrayList<>();
List<DeviceTypeVo> orphanNodes = new ArrayList<>();
for (DeviceTypeVo node : list) {
Long parentId = node.getParentId();
// 情况1parent_id 为0或NULL → 直接视为顶级节点
boolean isRoot = false;
// 判定是否为顶级节点(满足以下任一条件)
if (parentId == null || parentId == 0) {
// 条件1parentId为0或NULL设计约定的顶级节点
isRoot = true;
} else if (!validNodeIds.contains(parentId)) {
// 条件2parentId不在有效节点ID中孤儿节点视为顶级
isRoot = true;
orphanNodes.add(node);
}
if (isRoot) {
// 顶级节点直接加入根列表,无需挂载
rootNodes.add(node);
} else {
// 情况2查找父节点
// 非顶级节点挂载到父节点的children中
DeviceTypeVo parentNode = nodeMap.get(parentId);
if (parentNode != null) {
// 父节点存在 → 挂载到子节点列表
if (parentNode.getChildren() == null) {
parentNode.setChildren(new ArrayList<>());
}
// 关键:避免重复挂载(判断子节点中是否已存在当前节点)
boolean isAlreadyMounted = parentNode.getChildren().stream()
.anyMatch(child -> child.getId().equals(node.getId()));
if (!isAlreadyMounted) {
parentNode.getChildren().add(node);
} else {
// 父节点不存在 → 视为顶级节点(或加入孤儿节点列表)
rootNodes.add(node);
// 可选:记录孤儿节点,用于后续告警或修复
orphanNodes.add(node);
}
}
}
}
// 可选:打印孤儿节点日志(便于排查数据问题)
// if (!orphanNodes.isEmpty()) {
// System.out.println("警告存在孤儿节点父节点不存在节点ID" +
// orphanNodes.stream().map(DeviceType::getId).collect(Collectors.toList()));
// }
// 打印孤儿节点日志(便于排查数据问题)
if (!orphanNodes.isEmpty()) {
String orphanIds = orphanNodes.stream()
.map(DeviceTypeVo::getId)
.map(String::valueOf)
.collect(Collectors.joining(","));
System.out.println("警告存在孤儿节点父节点不存在节点ID" + orphanIds);
}
return rootNodes;
}

View File

@ -78,5 +78,9 @@ public class DeviceAccessRecordBo extends BaseEntity {
*/
private String details;
/**
* 子项目ID
*/
private Long childProjectId;
}

View File

@ -20,4 +20,8 @@ public class DeviceInfoCountVo {
* 闲置
*/
private Integer idle;
/**
* 报废
*/
private Integer broken;
}

View File

@ -65,7 +65,6 @@ public class DeviceInfoVo implements Serializable {
* 设备类型ID关联设备类型表device_type的id
*/
@ExcelProperty(value = "设备类型ID", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "关=联设备类型表device_type的id")
private Long typeId;
private String typeName;

View File

@ -1,6 +1,5 @@
package org.dromara.device.service;
import org.dromara.device.domain.vo.DeviceInfoCountVo;
import org.dromara.device.domain.vo.DeviceInfoVo;
import org.dromara.device.domain.bo.DeviceInfoBo;
import org.dromara.device.domain.DeviceInfo;
@ -8,7 +7,6 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Collection;
import java.util.List;
@ -75,7 +73,7 @@ public interface IDeviceInfoService extends IService<DeviceInfo>{
/**
* 设备数量统计
*/
DeviceInfoCountVo count(Long projectId);
Map<String, Integer> count(Long projectId);
/**
* 设备类型统计

View File

@ -2,6 +2,7 @@ package org.dromara.device.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@ -10,9 +11,14 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.device.domain.DeviceAccessRecord;
import org.dromara.device.domain.DeviceType;
import org.dromara.device.domain.vo.DeviceInfoCountVo;
import org.dromara.device.service.IDeviceAccessRecordService;
import org.dromara.device.service.IDeviceTypeService;
import org.dromara.system.domain.vo.SysDictDataVo;
import org.dromara.system.service.ISysDictDataService;
import org.dromara.system.service.ISysDictTypeService;
import org.springframework.stereotype.Service;
import org.dromara.device.domain.bo.DeviceInfoBo;
import org.dromara.device.domain.vo.DeviceInfoVo;
@ -37,6 +43,10 @@ public class DeviceInfoServiceImpl extends ServiceImpl<DeviceInfoMapper, DeviceI
private final IDeviceTypeService deviceTypeService;
private final IDeviceAccessRecordService deviceAccessRecordService;
private final ISysDictTypeService dictTypeService;
/**
* 查询设备信息
*
@ -139,7 +149,11 @@ public class DeviceInfoServiceImpl extends ServiceImpl<DeviceInfoMapper, DeviceI
* 保存前的数据校验
*/
private void validEntityBeforeSave(DeviceInfo entity){
//TODO 做一些数据校验,如唯一约束
boolean exists = this.lambdaQuery().eq(DeviceInfo::getDeviceCode, entity.getDeviceCode())
.ne(entity.getId() != null, DeviceInfo::getId, entity.getId()).exists();
if (exists) {
throw new ServiceException("设备编码已存在");
}
}
/**
@ -152,22 +166,27 @@ public class DeviceInfoServiceImpl extends ServiceImpl<DeviceInfoMapper, DeviceI
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
List<DeviceAccessRecord> list = deviceAccessRecordService.lambdaQuery().in(DeviceAccessRecord::getDeviceId, ids).list();
if(CollectionUtil.isNotEmpty(list)){
throw new ServiceException("设备存在进出场记录,不能删除");
}
}
return baseMapper.deleteByIds(ids) > 0;
}
@Override
public DeviceInfoCountVo count(Long projectId) {
List<DeviceInfo> list = this.lambdaQuery().eq(DeviceInfo::getProjectId, projectId).list();
DeviceInfoCountVo countVo = new DeviceInfoCountVo();
countVo.setTotal(list.size());
countVo.setUse((int)list.stream().filter(deviceInfo -> "1".equals(deviceInfo.getStatus())).count());
countVo.setMaintain((int)list.stream().filter(deviceInfo -> "2".equals(deviceInfo.getStatus())).count());
countVo.setIdle((int)list.stream().filter(deviceInfo -> "0".equals(deviceInfo.getStatus())).count());
public Map<String, Integer> count(Long projectId) {
return countVo;
Map<String, Integer> map = new LinkedHashMap<>();
List<SysDictDataVo> deviceStatus = dictTypeService.selectDictDataByType("device_status");
List<DeviceInfo> list = this.lambdaQuery().eq(DeviceInfo::getProjectId, projectId).list();
for (SysDictDataVo status : deviceStatus) {
map.put(status.getDictLabel(), (int)list.stream().filter(deviceInfo -> status.getDictValue().equals(deviceInfo.getStatus())).count());
}
return map;
}
@Override

View File

@ -22,6 +22,7 @@ import org.dromara.device.domain.DeviceType;
import org.dromara.device.mapper.DeviceTypeMapper;
import org.dromara.device.service.IDeviceTypeService;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Collection;
@ -83,6 +84,7 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<DeviceType> lqw = Wrappers.lambdaQuery();
lqw.orderByDesc(DeviceType::getId);
lqw.like(StringUtils.isNotBlank(bo.getTypeCode()), DeviceType::getTypeCode, bo.getTypeCode());
lqw.like(StringUtils.isNotBlank(bo.getTypeName()), DeviceType::getTypeName, bo.getTypeName());
lqw.eq(bo.getParentId() != null, DeviceType::getParentId, bo.getParentId());
lqw.eq(bo.getLevel() != null, DeviceType::getLevel, bo.getLevel());
@ -149,13 +151,25 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
List<Long> checkIds = new ArrayList<>();
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
for (Long id : ids) {
DeviceType deviceType = baseMapper.selectById(id);
if(deviceType == null){
continue;
}
List<DeviceInfo> list = deviceInfoService.lambdaQuery().in(DeviceInfo::getTypeId, ids).list();
String prefix = deviceType.getAncestors() + "," + deviceType.getId();
LambdaQueryWrapper<DeviceType> queryWrapper = new LambdaQueryWrapper<DeviceType>()
.like(DeviceType::getAncestors, prefix)
.orderByAsc(DeviceType::getLevel, DeviceType::getId);
List<DeviceType> list = baseMapper.selectList(queryWrapper);
checkIds.addAll(list.stream().map(DeviceType::getId).toList());
}
List<DeviceInfo> list = deviceInfoService.lambdaQuery().in(DeviceInfo::getTypeId, checkIds).list();
if(CollectionUtil.isNotEmpty(list)){
throw new ServiceException("请先删除该设备类型下的设备");
}
return baseMapper.deleteByIds(ids) > 0;
throw new ServiceException("当前类型或子级类型存在设备");
}
}
return baseMapper.deleteByIds(checkIds) > 0;
}
}