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.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*; import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission; 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.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit; import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -36,17 +38,17 @@ public class DeviceAccessRecordController extends BaseController {
private final IDeviceAccessRecordService deviceAccessRecordService; private final IDeviceAccessRecordService deviceAccessRecordService;
private final IBusProjectService projectService;
/** /**
* 查询设备进出场记录列表 * 查询设备进出场记录列表
*/ */
@SaCheckPermission("device:accessRecord:list") // @SaCheckPermission("device:accessRecord:list")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo<DeviceAccessRecordVo> list(DeviceAccessRecordBo bo, PageQuery pageQuery) { public TableDataInfo<DeviceAccessRecordVo> list(DeviceAccessRecordBo bo, PageQuery pageQuery) {
return deviceAccessRecordService.queryPageList(bo, pageQuery); return deviceAccessRecordService.queryPageList(bo, pageQuery);
} }
/** /**
* 导出设备进出场记录列表 * 导出设备进出场记录列表
*/ */
@ -63,17 +65,29 @@ public class DeviceAccessRecordController extends BaseController {
* *
* @param id 主键 * @param id 主键
*/ */
@SaCheckPermission("device:accessRecord:query") // @SaCheckPermission("device:accessRecord:query")
@GetMapping("/{id}") @GetMapping("/{id}")
public R<DeviceAccessRecordVo> getInfo(@NotNull(message = "主键不能为空") public R<DeviceAccessRecordVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) { @PathVariable Long id) {
return R.ok(deviceAccessRecordService.queryById(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) @Log(title = "设备进出场记录", businessType = BusinessType.INSERT)
@RepeatSubmit() @RepeatSubmit()
@PostMapping() @PostMapping()
@ -81,10 +95,12 @@ public class DeviceAccessRecordController extends BaseController {
return toAjax(deviceAccessRecordService.insertByBo(bo)); return toAjax(deviceAccessRecordService.insertByBo(bo));
} }
/** /**
* 修改设备进出场记录 * 修改设备进出场记录
*/ */
@SaCheckPermission("device:accessRecord:edit") // @SaCheckPermission("device:accessRecord:edit")
@Log(title = "设备进出场记录", businessType = BusinessType.UPDATE) @Log(title = "设备进出场记录", businessType = BusinessType.UPDATE)
@RepeatSubmit() @RepeatSubmit()
@PutMapping() @PutMapping()

View File

@ -85,12 +85,18 @@ public class DeviceInfoController extends BaseController {
} }
/**
* 设备信息统计
*/
@SaCheckPermission("device:info:list") @SaCheckPermission("device:info:list")
@GetMapping("/count/{projectId}") @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)); return R.ok(deviceInfoService.count(projectId));
} }
/**
* 设备类型统计
*/
@SaCheckPermission("device:info:list") @SaCheckPermission("device:info:list")
@GetMapping("/typeCount/{projectId}") @GetMapping("/typeCount/{projectId}")
public R<Map<String, Integer>> typeCount(@PathVariable Long 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -56,47 +57,72 @@ public class DeviceTypeController extends BaseController {
@GetMapping("/treeList") @GetMapping("/treeList")
public R<List<DeviceTypeVo>> treeList(DeviceTypeBo bo) { public R<List<DeviceTypeVo>> treeList(DeviceTypeBo bo) {
List<DeviceTypeVo> list = deviceTypeService.queryList(bo); List<DeviceTypeVo> list = deviceTypeService.queryList(bo);
buildTreeWithAutoRoot(list); List<DeviceTypeVo> deviceTypeVos = buildTreeWithAutoRoot(list);
return R.ok(list); return R.ok(deviceTypeVos);
} }
public static List<DeviceTypeVo> buildTreeWithAutoRoot(List<DeviceTypeVo> list) { public static List<DeviceTypeVo> buildTreeWithAutoRoot(List<DeviceTypeVo> list) {
// 1. 用 Map 缓存所有节点key=节点IDvalue=节点对象) // 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() Map<Long, DeviceTypeVo> nodeMap = list.stream()
.collect(Collectors.toMap(DeviceTypeVo::getId, node -> node)); .collect(Collectors.toMap(DeviceTypeVo::getId, node -> node));
List<DeviceTypeVo> rootNodes = new ArrayList<>(); // 存储自动识别的顶级节点 List<DeviceTypeVo> rootNodes = new ArrayList<>();
List<DeviceTypeVo> orphanNodes = new ArrayList<>(); // 存储孤儿节点(可选:单独处理) List<DeviceTypeVo> orphanNodes = new ArrayList<>();
for (DeviceTypeVo node : list) { for (DeviceTypeVo node : list) {
Long parentId = node.getParentId(); Long parentId = node.getParentId();
// 情况1parent_id 为0或NULL → 直接视为顶级节点 boolean isRoot = false;
// 判定是否为顶级节点(满足以下任一条件)
if (parentId == null || parentId == 0) { 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); rootNodes.add(node);
} else { } else {
// 情况2查找父节点 // 非顶级节点挂载到父节点的children中
DeviceTypeVo parentNode = nodeMap.get(parentId); DeviceTypeVo parentNode = nodeMap.get(parentId);
if (parentNode != null) { if (parentNode != null) {
// 父节点存在 → 挂载到子节点列表
if (parentNode.getChildren() == null) { if (parentNode.getChildren() == null) {
parentNode.setChildren(new ArrayList<>()); parentNode.setChildren(new ArrayList<>());
} }
parentNode.getChildren().add(node); // 关键:避免重复挂载(判断子节点中是否已存在当前节点)
} else { boolean isAlreadyMounted = parentNode.getChildren().stream()
// 父节点不存在 → 视为顶级节点(或加入孤儿节点列表) .anyMatch(child -> child.getId().equals(node.getId()));
rootNodes.add(node); if (!isAlreadyMounted) {
// 可选:记录孤儿节点,用于后续告警或修复 parentNode.getChildren().add(node);
orphanNodes.add(node); }
} }
} }
} }
// 可选:打印孤儿节点日志(便于排查数据问题) // 打印孤儿节点日志(便于排查数据问题)
// if (!orphanNodes.isEmpty()) { if (!orphanNodes.isEmpty()) {
// System.out.println("警告存在孤儿节点父节点不存在节点ID" + String orphanIds = orphanNodes.stream()
// orphanNodes.stream().map(DeviceType::getId).collect(Collectors.toList())); .map(DeviceTypeVo::getId)
// } .map(String::valueOf)
.collect(Collectors.joining(","));
System.out.println("警告存在孤儿节点父节点不存在节点ID" + orphanIds);
}
return rootNodes; return rootNodes;
} }

View File

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

View File

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

View File

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

View File

@ -1,6 +1,5 @@
package org.dromara.device.service; package org.dromara.device.service;
import org.dromara.device.domain.vo.DeviceInfoCountVo;
import org.dromara.device.domain.vo.DeviceInfoVo; import org.dromara.device.domain.vo.DeviceInfoVo;
import org.dromara.device.domain.bo.DeviceInfoBo; import org.dromara.device.domain.bo.DeviceInfoBo;
import org.dromara.device.domain.DeviceInfo; 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 org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Collection; import java.util.Collection;
import java.util.List; 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 cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 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.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo; 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.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.device.domain.DeviceAccessRecord;
import org.dromara.device.domain.DeviceType; import org.dromara.device.domain.DeviceType;
import org.dromara.device.domain.vo.DeviceInfoCountVo; import org.dromara.device.domain.vo.DeviceInfoCountVo;
import org.dromara.device.service.IDeviceAccessRecordService;
import org.dromara.device.service.IDeviceTypeService; 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.springframework.stereotype.Service;
import org.dromara.device.domain.bo.DeviceInfoBo; import org.dromara.device.domain.bo.DeviceInfoBo;
import org.dromara.device.domain.vo.DeviceInfoVo; import org.dromara.device.domain.vo.DeviceInfoVo;
@ -37,6 +43,10 @@ public class DeviceInfoServiceImpl extends ServiceImpl<DeviceInfoMapper, DeviceI
private final IDeviceTypeService deviceTypeService; 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){ 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 @Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(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; return baseMapper.deleteByIds(ids) > 0;
} }
@Override @Override
public DeviceInfoCountVo count(Long projectId) { public Map<String, Integer> 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());
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 @Override

View File

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