From 62eea2d16aa87aa5902f1ab1587966c8d7a748f3 Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Wed, 4 Feb 2026 11:04:36 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix=EF=BC=9A=E4=BC=98=E5=8C=96=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E6=95=B0=E6=8D=AE=E5=88=86=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/DeviceController.java | 8 +- .../iot/service/device/DeviceService.java | 2 +- .../iot/service/device/DeviceServiceImpl.java | 121 +++++++++++------- .../iot/service/device/TDengineService.java | 90 +++++++++++++ 4 files changed, 172 insertions(+), 49 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java index eb4430162..77feb66b4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java @@ -183,11 +183,13 @@ public class DeviceController { @GetMapping("/historyRecord") @Operation(summary = "历史记录查询") // @PreAuthorize("@ss.hasPermission('iot:device:query')") - public CommonResult>> historyRecord(@RequestParam("deviceId") Long deviceId, + public CommonResult>> historyRecord(@RequestParam("deviceId") Long deviceId, @RequestParam(name = "collectionStartTime", required = false) String collectionStartTime, - @RequestParam(name = "collectionEndTime", required = false) String collectionEndTime + @RequestParam(name = "collectionEndTime", required = false) String collectionEndTime, + @RequestParam(name = "pageNo", required = false, defaultValue = "1") Integer page, + @RequestParam(name = "pageSize", required = false, defaultValue = "10") Integer pageSize ) throws JsonProcessingException { - List> deviceContactModelDO=deviceService.historyRecord(deviceId,collectionStartTime,collectionEndTime); + PageResult> deviceContactModelDO=deviceService.historyRecord(deviceId,collectionStartTime,collectionEndTime,page,pageSize); return success(deviceContactModelDO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java index 02e04e3f5..7589d79b4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java @@ -123,7 +123,7 @@ public interface DeviceService { Map>> singleDevice(Long deviceId) throws JsonProcessingException; - List> historyRecord(Long deviceId,String collectionStartTime, String collectionEndTime); + PageResult> historyRecord(Long deviceId,String collectionStartTime, String collectionEndTime,Integer page,Integer size); Map> createDeviceDataMap(Long deviceId); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java index 88994ab24..9efc6a552 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java @@ -108,6 +108,8 @@ public class DeviceServiceImpl implements DeviceService { @Resource private DeviceOperationRecordMapper deviceOperationRecordMapper; + + @Override @Transactional(rollbackFor = Exception.class) public DeviceDO createDevice(DeviceSaveReqVO createReqVO) { @@ -689,78 +691,107 @@ public class DeviceServiceImpl implements DeviceService { } @Override - public List> historyRecord(Long deviceId,String collectionStartTime, String collectionEndTime) { - + public PageResult> historyRecord(Long deviceId, String collectionStartTime, String collectionEndTime,Integer page, + Integer pageSize) { List> resultList = new ArrayList<>(); + if (deviceId == null) { + return null; + } try { // 1. 获取设备数据列表 - List> deviceDataList = tdengineService.getstDeviceDataOrderByTimeDesc(deviceId,collectionStartTime,collectionEndTime,null); + List> deviceDataList = tdengineService.getstDeviceDataOrderByTimeDescPage( + deviceId, collectionStartTime, collectionEndTime, page, pageSize + ); - // 2. 获取属性类型映射 + long total = tdengineService.queryDeviceDataTotal(deviceId, collectionStartTime, collectionEndTime); + + + if (deviceDataList.isEmpty()) { + return null; + } + + // 2. 获取属性类型映射 (ID -> Name) 并构建缓存,避免重复数据库查询 Map idToNameMap = deviceAttributeTypeMapper.selectList() .stream() - .collect(Collectors.toMap( - DeviceAttributeTypeDO::getId, - DeviceAttributeTypeDO::getName - )); + .collect(Collectors.toMap(DeviceAttributeTypeDO::getId, DeviceAttributeTypeDO::getName)); + + // 额外缓存:数字ID -> 名称,防止同一个ID多次查询数据库 + Map attributeTypeCache = new HashMap<>(); - // 3. 遍历每个时间点的数据 + ObjectMapper objectMapper = new ObjectMapper(); + + // 3. 遍历每条设备数据 for (Map deviceData : deviceDataList) { String queryDataJson = (String) deviceData.get("queryData"); Timestamp timestamp = (Timestamp) deviceData.get("timestamp"); - if (StringUtils.isNotBlank(queryDataJson) && timestamp != null) { - List> dataList = new ObjectMapper().readValue( - queryDataJson, - new TypeReference>>() {} - ); - - // 按属性类型分组 - Map>> groupedData = new LinkedHashMap<>(); + if (StringUtils.isBlank(queryDataJson) || timestamp == null) { + continue; // 跳过无效数据 + } - for (Map data : dataList) { - String attributeTypeName = "其他"; - String typeStr = (String) data.get("attributeType"); - if (typeStr != null) { - try { - attributeTypeName = typeStr; - } catch (Exception e) { - attributeTypeName = "未知"; - } - } + List> dataList = objectMapper.readValue( + queryDataJson, new TypeReference>>() {} + ); - Map simplifiedData = new HashMap<>(); - simplifiedData.put("addressValue", data.get("addressValue")); - simplifiedData.put("attributeName", data.get("attributeName")); + // 4. 按属性类型分组 + Map>> groupedData = new LinkedHashMap<>(); - groupedData - .computeIfAbsent(attributeTypeName, k -> new ArrayList<>()) - .add(simplifiedData); - } + for (Map data : dataList) { + String attributeTypeName = "其他"; + Object typeObj = data.get("attributeType"); - // 创建当前时间点的Map - Map timePointData = new LinkedHashMap<>(); + if (typeObj != null) { + String typeStr = typeObj.toString(); - // 添加属性分组 - for (Map.Entry>> entry : groupedData.entrySet()) { - timePointData.put(entry.getKey(), entry.getValue()); + try { + // 尝试当作数字 ID + Long typeId = Long.parseLong(typeStr); + + // 先从缓存查 + if (attributeTypeCache.containsKey(typeId)) { + attributeTypeName = attributeTypeCache.get(typeId); + } else { + // 查数据库 + String nameFromDb = idToNameMap.get(typeId); + if (nameFromDb != null) { + attributeTypeName = nameFromDb; + } else { + attributeTypeName = typeStr; // 查不到就用原值 + } + attributeTypeCache.put(typeId, attributeTypeName); + } + } catch (NumberFormatException e) { + // 不是数字,直接用原字符串 + attributeTypeName = typeStr; + } } - // 添加收集时间 - timePointData.put("collectTime", timestamp.toString()); + // 构建属性数据 Map + Map simplifiedData = new HashMap<>(); + simplifiedData.put("addressValue", data.get("addressValue")); + simplifiedData.put("attributeName", data.get("attributeName")); - resultList.add(timePointData); + groupedData.computeIfAbsent(attributeTypeName, k -> new ArrayList<>()) + .add(simplifiedData); } + + // 5. 构建时间点 Map + Map timePointData = new LinkedHashMap<>(groupedData); + timePointData.put("collectTime", timestamp.toString()); + + resultList.add(timePointData); } + return new PageResult<>(resultList, total); } catch (Exception e) { - System.out.println("处理设备数据时发生异常: " + e.getMessage()); + log.error("处理设备数据时发生异常", e); + return PageResult.empty(); + } + } - return resultList; - } private void validateDeviceAttributeExists(Long id) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java index 5f05f7ba9..588df44be 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/TDengineService.java @@ -266,6 +266,96 @@ public class TDengineService { } } + + /** + * 查询指定设备表的全部数据并按时间倒序排序 + * @param id 设备ID + * @return 设备数据列表,按时间戳倒序排列 + */ + @DS("tdengine") + public List> getstDeviceDataOrderByTimeDescPage(Long id,String StartTime, String EndTime,Integer page, + Integer pageSize) { + String tableName = "d_" + id; + StringBuilder sqlBuilder = new StringBuilder(); + List params = new ArrayList<>(); + + sqlBuilder.append("SELECT ts, query_data FROM besure.").append(tableName).append(" WHERE 1=1"); + + if (StartTime != null) { + // 直接将时间字符串拼接到SQL中 + sqlBuilder.append(" AND ts >= '").append(StartTime).append("'"); + } + + if (EndTime != null) { + sqlBuilder.append(" AND ts <= '").append(EndTime).append("'"); + } + + sqlBuilder.append(" ORDER BY ts DESC"); + + // 分页处理 + if (page != null && pageSize != null && page > 0 && pageSize > 0) { + int offset = (page - 1) * pageSize; + sqlBuilder.append(" LIMIT ").append(offset).append(",").append(pageSize); + } + + try { + return jdbcTemplate.query(sqlBuilder.toString(), new RowMapper>() { + @Override + public Map mapRow(ResultSet rs, int rowNum) throws SQLException { + Map result = new HashMap<>(); + result.put("timestamp", rs.getTimestamp("ts")); + result.put("deviceId", id); + + // 读取 query_data(二进制字段) + byte[] blob = rs.getBytes("query_data"); + result.put("queryData", decodeQueryData(blob)); + + return result; + } + }); + } catch (Exception e) { +// log.error("查询设备" + id + "的最新数据时发生异常", e); +// e.printStackTrace(); + return Collections.singletonList(createEmptyResult(id)); + } + } + + + /** + * 查询指定设备历史数据的总条数 + * + * @param deviceId 设备ID + * @param startTime 开始时间,可为空 + * @param endTime 结束时间,可为空 + * @return 总记录数 + */ + @DS("tdengine") + public long queryDeviceDataTotal(Long deviceId, String startTime, String endTime) { + if (deviceId == null) { + return 0L; + } + + String tableName = "d_" + deviceId; + StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM besure." + tableName + " WHERE 1=1"); + + if (startTime != null) { + countSql.append(" AND ts >= '").append(startTime).append("'"); + } + + if (endTime != null) { + countSql.append(" AND ts <= '").append(endTime).append("'"); + } + + try { + Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class); + return total != null ? total : 0L; + } catch (Exception e) { + log.error("查询设备 {} 总条数异常", deviceId, e); + return 0L; + } + } + + /** * 从 TDengine 中查询设备边缘数据 * From baadf3c1fea323ea32d6508e56a368d1bf54ca87 Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Wed, 4 Feb 2026 11:34:38 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix=EF=BC=9A=E4=BC=98=E5=8C=96=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=91=8A=E8=AD=A6=E9=A1=B5=E9=9D=A2=E5=88=86=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/controller/admin/device/DeviceController.java | 10 ++++++++++ .../devicepointrules/DevicePointRulesController.java | 9 +++++++++ .../yudao/module/iot/service/device/DeviceService.java | 2 ++ .../module/iot/service/device/DeviceServiceImpl.java | 7 +++++++ .../devicepointrules/DevicePointRulesService.java | 1 + .../devicepointrules/DevicePointRulesServiceImpl.java | 7 +++++++ 6 files changed, 36 insertions(+) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java index 77feb66b4..6b81da185 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java @@ -214,6 +214,16 @@ public class DeviceController { return success(deviceService.getDeviceAttributePage(pageParam, deviceModelAttributePageReqVO)); } + + @GetMapping("/device-attribute/list") + @Operation(summary = "获得设备属性列表") + @Parameter(name = "deviceId", description = "设备id") + @PreAuthorize("@ss.hasPermission('iot:device:query')") + public CommonResult> getDeviceAttributeList(@RequestParam(name = "deviceId") Long deviceId) { + return success(deviceService.getDeviceAttributeList(deviceId)); + } + + @PostMapping("/device-attribute/create") @Operation(summary = "创建设备属性") @PreAuthorize("@ss.hasPermission('iot:device:create')") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/DevicePointRulesController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/DevicePointRulesController.java index 1debf7096..6077d9ebd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/DevicePointRulesController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicepointrules/DevicePointRulesController.java @@ -104,4 +104,13 @@ public class DevicePointRulesController { BeanUtils.toBean(list, DevicePointRulesRespVO.class)); } + @GetMapping("/getList") + @Operation(summary = "获得点位规则列表") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-point-rules:query')") + public CommonResult> getList(@RequestParam("id") Long id) { + List devicePointRules = devicePointRulesService.getList(id); + return success(devicePointRules); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java index 7589d79b4..c7267219b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceService.java @@ -136,4 +136,6 @@ public interface DeviceService { DeviceOperationStatusRespVO getDeviceOperationalStatus(); List> getMultiDeviceAttributes(String deviceIds); + + List getDeviceAttributeList(Long deviceId); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java index 9efc6a552..4f15ac678 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java @@ -975,4 +975,11 @@ public class DeviceServiceImpl implements DeviceService { return result; } + + @Override + public List getDeviceAttributeList(Long deviceId) { + return deviceContactModelMapper.selectList(Wrappers.lambdaQuery() + .eq(DeviceContactModelDO::getDeviceId,deviceId) + .orderByDesc(DeviceContactModelDO::getId)); + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesService.java index 234c16615..ed1db9f5c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesService.java @@ -52,4 +52,5 @@ public interface DevicePointRulesService { */ PageResult getDevicePointRulesPage(DevicePointRulesPageReqVO pageReqVO); + List getList(Long id); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesServiceImpl.java index 40e0a0d9d..6e5f86f6e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicepointrules/DevicePointRulesServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.service.devicepointrules; import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.springframework.stereotype.Service; import javax.annotation.Resource; import org.springframework.validation.annotation.Validated; @@ -78,4 +79,10 @@ public class DevicePointRulesServiceImpl implements DevicePointRulesService { return devicePointRulesMapper.selectPage(pageReqVO); } + @Override + public List getList(Long id) { + return devicePointRulesMapper.selectList(Wrappers.lambdaQuery() + .eq(DevicePointRulesDO::getDeviceId,id).eq(DevicePointRulesDO::getIdentifier,"ALARM").orderByDesc(DevicePointRulesDO::getDeviceId)); + } + } \ No newline at end of file From 1b9e55c492d8a9c84aaf43634a74c2f3f3ec84c6 Mon Sep 17 00:00:00 2001 From: 86158 <461356067@qq.com> Date: Wed, 4 Feb 2026 17:21:28 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E=E4=BA=86?= =?UTF-8?q?=E9=87=87=E9=9B=86=E8=AE=BE=E5=A4=87=E6=A8=A1=E5=9E=8B-?= =?UTF-8?q?=E9=87=87=E9=9B=86=E7=82=B9=E3=80=81=E8=AE=BE=E5=A4=87=E7=AE=A1?= =?UTF-8?q?=E7=90=86-=E8=AE=BE=E5=A4=87=E5=85=B3=E9=94=AE=E4=BB=B6?= =?UTF-8?q?=E3=80=81=E5=A4=87=E4=BB=B6=E7=AE=A1=E7=90=86-=E5=A4=87?= =?UTF-8?q?=E4=BB=B6=E4=BF=A1=E6=81=AF=E7=9A=84=E6=95=B0=E6=8D=AE=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/product/ErpProductController.java | 52 ++++++- .../vo/product/ErpProductImportExcelVO.java | 68 ++++++++ .../vo/product/ErpProductImportRespVO.java | 44 ++++++ .../product/ErpProductCategoryMapper.java | 4 + .../dal/mysql/product/ErpProductMapper.java | 5 + .../product/ErpProductCategoryService.java | 8 + .../ErpProductCategoryServiceImpl.java | 5 + .../service/product/ErpProductService.java | 11 ++ .../product/ErpProductServiceImpl.java | 145 +++++++++++++++++- .../product/ErpProductUnitService.java | 7 + .../product/ErpProductUnitServiceImpl.java | 4 + .../DeviceModelAttributeController.java | 60 ++++++++ .../vo/DeviceModelAttributeImportExcelVO.java | 61 ++++++++ .../vo/DeviceModelAttributeImportRespVO.java | 28 ++++ .../vo/DeviceModelAttributeRespVO.java | 2 +- .../DeviceModelAttributeMapper.java | 4 + .../DeviceModelAttributeService.java | 8 + .../DeviceModelAttributeServiceImpl.java | 114 +++++++++++++- .../CriticalComponentController.java | 42 +++++ .../vo/CriticalComponentImportExcelVO.java | 40 +++++ .../vo/CriticalComponentImportRespVO.java | 44 ++++++ .../CriticalComponentMapper.java | 5 + .../CriticalComponentService.java | 5 + .../CriticalComponentServiceImpl.java | 59 +++++++ 24 files changed, 816 insertions(+), 9 deletions(-) create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductImportExcelVO.java create mode 100644 yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductImportRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportExcelVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportRespVO.java create mode 100644 yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/vo/CriticalComponentImportExcelVO.java create mode 100644 yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/vo/CriticalComponentImportRespVO.java diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java index 364189968..83635f5fc 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductController.java @@ -8,9 +8,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.category.ErpProductCategoryListReqVO; -import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO; -import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; -import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.*; import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO; import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper; @@ -19,14 +17,17 @@ import cn.iocoder.yudao.module.erp.service.product.ErpProductCategoryService; import cn.iocoder.yudao.module.erp.service.product.ErpProductService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.IOException; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -169,4 +170,49 @@ public class ErpProductController { pageResult.getList()); } + @GetMapping("/get-import-template") + @Operation(summary = "获取产品导入模板") + @ApiAccessLog(operateType = EXPORT) + public void getImportTemplate(HttpServletResponse response) throws IOException { + // 准备模板数据 + List list = new ArrayList<>(); + list.add(ErpProductImportExcelVO.builder() + .name("备件1") + .barCode("BZ001") + .unitName("个") + .status(0) // 启用状态 + .standard("红色") + .remark("测试产品") + .expiryDay(30) + .safetyNumber(new BigDecimal("10.00")) + .build()); + list.add(ErpProductImportExcelVO.builder() + .name("备件2") + .barCode("BZ002") + .unitName("个") + .status(0) // 启用状态 + .standard("标准") + .remark("备件说明") + .expiryDay(30) + .safetyNumber(new BigDecimal("5.00")) + .build()); + // 输出 + ExcelUtils.write(response, "产品导入模板.xls", "数据", ErpProductImportExcelVO.class, list); + } + + @PostMapping("/import") + @Operation(summary = "导入产品") + @Parameters({ + @Parameter(name = "file", description = "Excel 文件", required = true), + @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true") + }) + public CommonResult importExcel( + @RequestParam("file") MultipartFile file, + @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception { + // 1. 读取 Excel 数据 + List list = ExcelUtils.read(file, ErpProductImportExcelVO.class); + // 2. 调用 Service 并返回结果 + return success(productService.importProductList(list, updateSupport)); + } + } \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductImportExcelVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductImportExcelVO.java new file mode 100644 index 000000000..7aa223bca --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductImportExcelVO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * ERP 产品 Excel 导入 VO + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) +public class ErpProductImportExcelVO { + + @ExcelProperty("产品名称") + private String name; + + @ExcelProperty("产品条码") + private String barCode; + +// @ExcelIgnore +// private String categoryName; + + @ExcelIgnore + private String subCategoryName; + + @ExcelProperty("单位") + private String unitName; + + @ExcelProperty("产品状态") + private Integer status; + + @ExcelProperty("产品规格") + private String standard; + + @ExcelProperty("产品备注") + private String remark; + + @ExcelProperty("保质期天数") + private Integer expiryDay; + + @ExcelProperty("预警库存") + private BigDecimal safetyNumber; + + // 以下是辅助字段,不在Excel中显示 + @ExcelIgnore + private Long categoryId; // 分类ID + + @ExcelIgnore + private Long subCategoryId; // 子分类ID + + @ExcelIgnore + private Long unitId; // 单位ID + + @ExcelIgnore + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductImportRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductImportRespVO.java new file mode 100644 index 000000000..4e886265b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductImportRespVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.erp.controller.admin.product.vo.product; + +import lombok.Builder; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * ERP 产品 Excel 导入响应 VO + */ +@Data +@Builder +public class ErpProductImportRespVO { + + /** + * 成功导入的产品名称列表 + */ + private List createNames; + + /** + * 更新的产品名称列表 + */ + private List updateNames; + + /** + * 失败的产品名称列表,key: 产品名称,value: 失败原因 + */ + private Map failureNames; + + public ErpProductImportRespVO() { + this.createNames = new ArrayList<>(); + this.updateNames = new ArrayList<>(); + this.failureNames = new LinkedHashMap<>(); + } + + public ErpProductImportRespVO(List createNames, List updateNames, Map failureNames) { + this.createNames = createNames; + this.updateNames = updateNames; + this.failureNames = failureNames; + } +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductCategoryMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductCategoryMapper.java index 70bbc6429..987d14844 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductCategoryMapper.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/product/ErpProductCategoryMapper.java @@ -27,6 +27,10 @@ public interface ErpProductCategoryMapper extends BaseMapperX { .in("category_id" ,categoryIds)); } + default List selectListByNameIn(Collection names) { + return selectList(new LambdaQueryWrapperX().in(ErpProductDO::getName, names)); + } + } \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryService.java index 629f58bd3..00a4cc143 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryService.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryService.java @@ -76,4 +76,12 @@ public interface ErpProductCategoryService { return convertMap(getProductCategoryList(ids), ErpProductCategoryDO::getId); } + /** + * 根据名称获取产品分类 + * + * @param name 分类名称 + * @return 产品分类 + */ + ErpProductCategoryDO getProductCategoryByName(String name); + } \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryServiceImpl.java index 70062c0bb..0b1b2384a 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryServiceImpl.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductCategoryServiceImpl.java @@ -146,4 +146,9 @@ public class ErpProductCategoryServiceImpl implements ErpProductCategoryService return erpProductCategoryMapper.selectBatchIds(ids); } + @Override + public ErpProductCategoryDO getProductCategoryByName(String name) { + return erpProductCategoryMapper.selectByName(name); + } + } \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductService.java index ac7005db1..66bc03d8f 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductService.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductService.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.erp.service.product; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductImportExcelVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductImportRespVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO; @@ -123,4 +125,13 @@ public interface ErpProductService { return convertMap(getProductList(ids), ErpProductDO::getId); } List selectByCategorys(List categoryIds); + + /** + * 导入产品列表 + * + * @param importProducts 导入的产品列表 + * @param isUpdateSupport 是否支持更新 + * @return 导入结果 + */ + ErpProductImportRespVO importProductList(List importProducts, boolean isUpdateSupport); } \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java index a053e5d3e..cfbc400fe 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java @@ -1,10 +1,15 @@ package cn.iocoder.yudao.module.erp.service.product; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductImportExcelVO; +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductImportRespVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO; @@ -16,17 +21,23 @@ import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductCategoryMapper; import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper; import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMapper; import cn.iocoder.yudao.module.erp.service.stock.ErpStockService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.springframework.context.annotation.Lazy; +import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import javax.validation.ConstraintViolationException; +import javax.validation.Valid; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; @@ -178,11 +189,15 @@ public class ErpProductServiceImpl implements ErpProductService { } Map categoryMap = productCategoryService.getProductCategoryMap( convertSet(list, ErpProductDO::getCategoryId)); + Map subCategoryMap = productCategoryService.getProductCategoryMap( + convertSet(list, ErpProductDO::getSubCategoryId)); Map unitMap = productUnitService.getProductUnitMap( convertSet(list, ErpProductDO::getUnitId)); return BeanUtils.toBean(list, ErpProductRespVO.class, product -> { MapUtils.findAndThen(categoryMap, product.getCategoryId(), category -> product.setCategoryName(category.getName())); + MapUtils.findAndThen(subCategoryMap, product.getSubCategoryId(), + subCategory -> product.setSubCategoryName(subCategory.getName())); MapUtils.findAndThen(unitMap, product.getUnitId(), unit -> product.setUnitName(unit.getName())); }); @@ -206,4 +221,126 @@ public class ErpProductServiceImpl implements ErpProductService { List selectByCategorys(List categoryIds){ return productMapper.selectByCategorys(categoryIds); } + + @Override + @Transactional(rollbackFor = Exception.class) + public ErpProductImportRespVO importProductList(List importProducts, boolean isUpdateSupport) { + // 1. 参数校验 + if (CollUtil.isEmpty(importProducts)) { + throw exception(PRODUCT_NOT_EXISTS); + } + + // 2. 初始化返回结果 + ErpProductImportRespVO respVO = ErpProductImportRespVO.builder() + .createNames(new ArrayList<>()) + .updateNames(new ArrayList<>()) + .failureNames(new LinkedHashMap<>()) + .build(); + + // 3. 检查Excel中是否有重复的产品名称 + Set excelNames = new HashSet<>(); + Set duplicateNamesInExcel = new HashSet<>(); + for (ErpProductImportExcelVO importProduct : importProducts) { + if (!excelNames.add(importProduct.getName())) { + duplicateNamesInExcel.add(importProduct.getName()); + } + } + + // 记录Excel中重复的产品名称 + for (String duplicateName : duplicateNamesInExcel) { + respVO.getFailureNames().put(duplicateName, "Excel中存在重复的产品名称: " + duplicateName); + } + + // 4. 获取数据库中已存在的产品 + Set uniqueNamesToCheck = new HashSet<>(excelNames); + uniqueNamesToCheck.removeAll(duplicateNamesInExcel); // 排除Excel中的重复项 + + List existingProducts = new ArrayList<>(); + if (!uniqueNamesToCheck.isEmpty()) { + existingProducts = productMapper.selectListByNameIn(uniqueNamesToCheck); + } + + Map existingProductMap = existingProducts.stream() + .collect(Collectors.toMap(ErpProductDO::getName, item -> item)); + + // 5. 遍历导入数据 + for (ErpProductImportExcelVO importProduct : importProducts) { + // 如果已在失败列表中,则跳过 + if (respVO.getFailureNames().containsKey(importProduct.getName())) { + continue; + } + + try { + // 5.1 先处理分类和单位ID,确保它们被设置 + ErpProductDO productDOForConversion = convertToProductDO(importProduct); + + // 5.2 转换为保存对象 + ProductSaveReqVO saveReqVO = BeanUtils.toBean(importProduct, ProductSaveReqVO.class); + + // 5.3 设置saveReqVO中的ID值,以通过验证 + saveReqVO.setCategoryId(productDOForConversion.getCategoryId()); + saveReqVO.setUnitId(productDOForConversion.getUnitId()); + + // 5.4 字段校验 + ValidationUtils.validate(saveReqVO); + } catch (ConstraintViolationException ex) { + respVO.getFailureNames().put(importProduct.getName(), ex.getMessage()); + continue; + } + + // 5.2 检查是否已存在 + ErpProductDO existProduct = existingProductMap.get(importProduct.getName()); + if (existProduct == null) { + try { + // 5.3 创建新记录 + ErpProductDO newProduct = convertToProductDO(importProduct); + // 设置创建时间为当前时间 + newProduct.setCreateTime(LocalDateTime.now()); + productMapper.insert(newProduct); + respVO.getCreateNames().add(importProduct.getName()); + } catch (DuplicateKeyException e) { + // 处理并发插入或其他原因导致的重复键异常 + respVO.getFailureNames().put(importProduct.getName(), "该产品已存在"); + } + } else if (isUpdateSupport) { + // 5.4 更新已有记录 + ErpProductDO updateProduct = convertToProductDO(importProduct); + updateProduct.setId(existProduct.getId()); + // 保持原有创建时间不变 + updateProduct.setCreateTime(existProduct.getCreateTime()); + productMapper.updateById(updateProduct); + respVO.getUpdateNames().add(importProduct.getName()); + } else { + // 5.5 已存在且不支持更新 + respVO.getFailureNames().put(importProduct.getName(), "该产品已存在"); + } + } + + return respVO; + } + + private ErpProductDO convertToProductDO(ErpProductImportExcelVO importProduct) { + ErpProductDO productDO = BeanUtils.toBean(importProduct, ErpProductDO.class); + + // 直接设置分类ID为5 + productDO.setCategoryId(5L); + + // 设置固定子分类名称为"备件" + productDO.setSubCategoryName("备件"); + // 查找对应的子分类ID + ErpProductCategoryDO subCategoryDO = productCategoryService.getProductCategoryByName("备件"); + if (subCategoryDO != null) { + productDO.setSubCategoryId(subCategoryDO.getId()); + } + + // 处理单位名称 + if (StrUtil.isNotBlank(importProduct.getUnitName())) { + ErpProductUnitDO unitDO = productUnitService.getProductUnitByName(importProduct.getUnitName()); + if (unitDO != null) { + productDO.setUnitId(unitDO.getId()); + } + } + + return productDO; + } } \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitService.java index 10dcb3dbd..082031b6c 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitService.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitService.java @@ -92,4 +92,11 @@ public interface ErpProductUnitService { */ List getProductUnitList(); + /** + * 根据名称获取产品单位 + * + * @param name 单位名称 + * @return 产品单位 + */ + ErpProductUnitDO getProductUnitByName(String name); } \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitServiceImpl.java index 22b1eee5a..868ba7ed0 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitServiceImpl.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductUnitServiceImpl.java @@ -125,4 +125,8 @@ public class ErpProductUnitServiceImpl implements ErpProductUnitService { return productUnitMapper.selectList(); } + @Override + public ErpProductUnitDO getProductUnitByName(String name) { + return productUnitMapper.selectByName(name); + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/DeviceModelAttributeController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/DeviceModelAttributeController.java index cf7ba4b02..71bfb7a49 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/DeviceModelAttributeController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/DeviceModelAttributeController.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute; import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO; +import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelattribute.DeviceModelAttributeMapper; +import io.swagger.v3.oas.annotations.Parameters; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import org.springframework.validation.annotation.Validated; @@ -30,6 +32,7 @@ import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; import cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo.*; import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO; import cn.iocoder.yudao.module.iot.service.devicemodelattribute.DeviceModelAttributeService; +import org.springframework.web.multipart.MultipartFile; @Tag(name = "管理后台 - 采集设备模型-点位管理") @RestController @@ -39,6 +42,8 @@ public class DeviceModelAttributeController { @Resource private DeviceModelAttributeService deviceModelAttributeService; + @Resource + private DeviceModelAttributeMapper deviceModelAttributeMapper; @PostMapping("/create") @Operation(summary = "创建采集设备模型-点位管理") @@ -120,4 +125,59 @@ public class DeviceModelAttributeController { List> deviceModelAttribute = deviceModelAttributeService.operationAnalysisDetails(deviceId,modelId,collectionStartTime,collectionEndTime); return success(deviceModelAttribute); } + + /////////////// 以下是新增的代码 /////////////// + @GetMapping("/get-import-template") + @Operation(summary = "获取采集设备模型-点位管理导入模板") + @PreAuthorize("@ss.hasPermission('iot:device-model-attribute:import')") + @ApiAccessLog(operateType = EXPORT) + public void getImportTemplate(HttpServletResponse response) throws IOException { + // 准备模板数据 + List list = new ArrayList<>(); + list.add(DeviceModelAttributeImportExcelVO.builder() + .attributeCode("test") + .attributeName("温度") +// .attributeType("1") + .typeName("报警信息") + .dataType("int64") + .address("AI0") + .dataUnit("s") + .ratio(1.0) + .remark("温度传感器") + .build()); + list.add(DeviceModelAttributeImportExcelVO.builder() + .attributeCode("humidity_001") + .attributeName("湿度") +// .attributeType("2") + .typeName("能源参数") + .dataType("int64") + .address("AI1") + .dataUnit("s") + .ratio(1.0) + .remark("湿度传感器") + .build()); + // 输出 + ExcelUtils.write(response, "采集设备模型-点位管理导入模板.xls", "数据", DeviceModelAttributeImportExcelVO.class, list); + } + + + @PostMapping("/import") + @Operation(summary = "导入设备采集点位") // 对应 User 的 "导入用户" + @Parameters({ + @Parameter(name = "file", description = "Excel 文件", required = true), + @Parameter(name = "deviceModelId", description = "设备型号ID", required = true, example = "1024"), + @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true") + }) // 完全复用你的 Parameters 注解格式 + @PreAuthorize("@ss.hasPermission('system:device-model-attribute:import')") // 对齐 User 的权限码风格 + public CommonResult importExcel( + @RequestParam("file") MultipartFile file, + @RequestParam("deviceModelId") Long deviceModelId, + @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception { + // 1. 读取 Excel 数据 - 完全复用你的 ExcelUtils.read 调用方式 + List list = ExcelUtils.read(file, DeviceModelAttributeImportExcelVO.class); + // 2. 调用 Service 并返回结果 - 完全复用你的 success 包装方式 + return success(deviceModelAttributeService.importAttributeList(list, deviceModelId, updateSupport)); + } + + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportExcelVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportExcelVO.java new file mode 100644 index 000000000..9c37662ab --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportExcelVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo; + +//import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +//import cn.iocoder.yudao.module.system.enums.DictTypeConstants; // 沿用你项目中的字典枚举 +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 设备采集点位 Excel 导入 VO + * 【完全对齐 UserImportExcelVO 注解风格和结构】 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) // 完全复用你的配置,避免导入问题 +public class DeviceModelAttributeImportExcelVO { + + @ExcelProperty("点位编码") // 对应 User 的 "登录名称" + private String attributeCode; + + @ExcelProperty("点位名称") // 对应 User 的 "用户名称" + private String attributeName; + + // 点位类型,不在Excel中显示 + @ExcelIgnore + private String attributeType; + + @ExcelProperty("点位类型名称") + private String typeName; + + @ExcelProperty(value = "数据类型") // 对应 User 的 "用户性别" +// , converter = DictConvert.class +// @DictFormat("device_data_type") // 替换为你项目中数据类型的字典编码,对齐 User 的 DictFormat 用法 + private String dataType; + + @ExcelProperty("寄存器地址") + private String address; + + @ExcelProperty("单位") // 对应 User 的 "用户邮箱" + private String dataUnit; + + @ExcelProperty("倍率") + private Double ratio; + + @ExcelProperty("备注") + private String remark; + + // 设备型号ID,不在Excel中显示,由前端传入 + @ExcelIgnore + private Long deviceModelId; + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportRespVO.java new file mode 100644 index 000000000..c4cb03898 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeImportRespVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.iot.controller.admin.devicemodelattribute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * 管理后台 - 设备采集点位导入 Response VO + * 【完全对齐 UserImportRespVO 注解风格和结构】 + */ +@Schema(description = "管理后台 - 设备采集点位导入 Response VO") +@Data +@Builder +public class DeviceModelAttributeImportRespVO { + + @Schema(description = "创建成功的点位编码数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List createCodes; // 对应 User 的 createUsernames,替换为点位唯一标识(编码) + + @Schema(description = "更新成功的点位编码数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List updateCodes; // 对应 User 的 updateUsernames,替换为点位唯一标识(编码) + + @Schema(description = "导入失败的点位集合,key 为点位编码,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) + private Map failureCodes; // 对应 User 的 failureUsernames,替换为点位唯一标识(编码) + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeRespVO.java index 4d02dabfe..b82a719a4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/devicemodelattribute/vo/DeviceModelAttributeRespVO.java @@ -27,7 +27,7 @@ public class DeviceModelAttributeRespVO { @Schema(description = "点位类型", example = "1") @ExcelProperty("点位类型") - private Long attributeType; + private String attributeType; @Schema(description = "类型名称", example = "1") @ExcelProperty("类型名称") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelattribute/DeviceModelAttributeMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelattribute/DeviceModelAttributeMapper.java index f33cb9ad7..16eb683b8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelattribute/DeviceModelAttributeMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/devicemodelattribute/DeviceModelAttributeMapper.java @@ -65,4 +65,8 @@ public interface DeviceModelAttributeMapper extends BaseMapperX> operationAnalysisDetails(Long deviceId, Long modelId,String collectionStartTime, String collectionEndTime); List getDeviceModelAttributeList(Long id); + /** + * 导入设备采集点位列表 + * 【对应 UserService 的 importUserList 方法】 + */ + DeviceModelAttributeImportRespVO importAttributeList(List importAttributes, Long deviceModelId, boolean isUpdateSupport); + + + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelattribute/DeviceModelAttributeServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelattribute/DeviceModelAttributeServiceImpl.java index 4c672d748..521bccf30 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelattribute/DeviceModelAttributeServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicemodelattribute/DeviceModelAttributeServiceImpl.java @@ -1,7 +1,11 @@ package cn.iocoder.yudao.module.iot.service.devicemodelattribute; +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO; import cn.iocoder.yudao.module.iot.dal.mysql.deviceattributetype.DeviceAttributeTypeMapper; +import cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.iot.service.device.TDengineService; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.fasterxml.jackson.core.type.TypeReference; @@ -10,6 +14,8 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import javax.validation.ConstraintViolationException; + import org.springframework.validation.annotation.Validated; import org.springframework.transaction.annotation.Transactional; @@ -36,7 +42,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @Service @Validated @Slf4j -public class DeviceModelAttributeServiceImpl implements DeviceModelAttributeService { + public class DeviceModelAttributeServiceImpl implements DeviceModelAttributeService { @Resource private DeviceModelAttributeMapper deviceModelAttributeMapper; @@ -207,4 +213,110 @@ public class DeviceModelAttributeServiceImpl implements DeviceModelAttributeServ } + @Override + @Transactional(rollbackFor = Exception.class) // 完全复用你的事务注解,异常回滚所有导入 + public DeviceModelAttributeImportRespVO importAttributeList(List importAttributes, Long deviceModelId, boolean isUpdateSupport) { + // 1.1 参数校验 - 完全复刻 User 的空列表校验逻辑 + if (CollUtil.isEmpty(importAttributes)) { + throw exception(DEVICE_MODEL_ATTRIBUTE_NOT_EXISTS); + } + // 1.2 校验设备型号ID是否存在 + if (deviceModelId == null) { + throw exception(DEVICE_MODEL_NOT_EXISTS); + } + + // 2. 构建点位类型名称到ID的映射 + List attributeTypes = deviceAttributeTypeMapper.selectList(); + Map typeNameToIdMap = attributeTypes.stream() + .collect(Collectors.toMap(DeviceAttributeTypeDO::getName, type -> String.valueOf(type.getId()))); + + // 3. 初始化返回 VO - 完全复刻 User 的 builder 方式,使用 LinkedHashMap 保持顺序 + DeviceModelAttributeImportRespVO respVO = DeviceModelAttributeImportRespVO.builder() + .createCodes(new ArrayList<>()) + .updateCodes(new ArrayList<>()) + .failureCodes(new LinkedHashMap<>()) + .build(); + + // 4. 遍历,逐个创建 or 更新 - 完全复刻 User 的 forEach 逻辑 + importAttributes.forEach(importAttribute -> { + // 4.1 设置设备型号ID + importAttribute.setDeviceModelId(deviceModelId); + + // 4.2 处理点位类型转换 + String typeName = importAttribute.getTypeName(); + if (StringUtils.isBlank(typeName)) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), "点位类型名称不能为空"); + return; + } + String attributeTypeId = typeNameToIdMap.get(typeName); + if (StringUtils.isBlank(attributeTypeId)) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), "点位类型名称无效: " + typeName); + return; + } + // 设置点位类型ID + importAttribute.setAttributeType(attributeTypeId); + + // 4.3.1 校验字段是否符合要求 - 完全复刻 User 的 ValidationUtils.validate 调用 + try { + // 注意:如果有 DeviceModelAttributeSaveReqVO,替换为该 VO;无则直接校验 importAttribute + ValidationUtils.validate(BeanUtils.toBean(importAttribute, DeviceModelAttributeSaveReqVO.class)); + } catch (ConstraintViolationException ex) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), ex.getMessage()); + return; + } + + // 4.3.2 业务规则校验 - 完全复刻 User 的 validateUserForCreateOrUpdate 逻辑 + try { + validateAttributeForCreateOrUpdate(deviceModelId, importAttribute.getAttributeCode()); + } catch (ServiceException ex) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), ex.getMessage()); + return; + } + + // 4.4.1 判断如果不存在,进行插入 - 完全复刻 User 的 selectByUsername 逻辑 + DeviceModelAttributeDO existAttribute = deviceModelAttributeMapper.selectByAttributeCode(importAttribute.getAttributeCode()); + if (existAttribute == null) { + // 创建DO对象并设置点位类型名称 + DeviceModelAttributeDO deviceModelAttribute = BeanUtils.toBean(importAttribute, DeviceModelAttributeDO.class); + deviceModelAttribute.setTypeName(typeName); // 设置点位类型名称 + deviceModelAttributeMapper.insert(deviceModelAttribute); + respVO.getCreateCodes().add(importAttribute.getAttributeCode()); + return; + } + + // 4.4.2 如果存在,判断是否允许更新 - 完全复刻 User 的更新逻辑 + if (!isUpdateSupport) { + respVO.getFailureCodes().put(importAttribute.getAttributeCode(), DEVICE_MODEL_ATTRIBUTE_NOT_EXISTS.getMsg()); + return; + } + + // 创建更新对象并设置点位类型名称 + DeviceModelAttributeDO updateAttribute = BeanUtils.toBean(importAttribute, DeviceModelAttributeDO.class); + updateAttribute.setId(existAttribute.getId()); // 复用已有数据的 ID + updateAttribute.setTypeName(typeName); // 设置点位类型名称 + deviceModelAttributeMapper.updateById(updateAttribute); + respVO.getUpdateCodes().add(importAttribute.getAttributeCode()); + }); + + return respVO; + } + + /** + * 校验设备采集点位创建/更新的业务规则 + * 【对应 User 的 validateUserForCreateOrUpdate 方法】 + */ + private void validateAttributeForCreateOrUpdate(Long deviceModelId, String code) { + // 1. 校验设备型号是否存在(可根据你的业务补充,对应 User 的部门校验) + if (deviceModelId == null) { + throw exception(ErrorCodeConstants.DEVICE_MODEL_NOT_EXISTS); // 替换为你项目中的设备型号不存在错误码 + } + // 2. 可补充其他业务校验(如数据类型合法性、单位长度等) + } + + + + + + + } \ No newline at end of file diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/CriticalComponentController.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/CriticalComponentController.java index b84d86a75..88a7300f2 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/CriticalComponentController.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/CriticalComponentController.java @@ -32,6 +32,9 @@ import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; import cn.iocoder.yudao.module.mes.controller.admin.criticalcomponent.vo.*; import cn.iocoder.yudao.module.mes.dal.dataobject.criticalcomponent.CriticalComponentDO; import cn.iocoder.yudao.module.mes.service.criticalcomponent.CriticalComponentService; +import org.springframework.web.multipart.MultipartFile; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; @Tag(name = "管理后台 - 设备关键件") @RestController @@ -128,4 +131,43 @@ public class CriticalComponentController { ExcelUtils.write(response, fileName, "数据", CriticalComponentExcelVO.class,list); } + @GetMapping("/get-import-template") + @Operation(summary = "获取设备关键件导入模板") + @PreAuthorize("@ss.hasPermission('mes:critical-component:import')") + @ApiAccessLog(operateType = EXPORT) + public void getImportTemplate(HttpServletResponse response) throws IOException { + // 准备模板数据 + List list = new ArrayList<>(); + list.add(CriticalComponentImportExcelVO.builder() + .code("TEST001") + .name("电机") + .description("主驱动电机") + .remark("西门子品牌") + .build()); + list.add(CriticalComponentImportExcelVO.builder() + .code("TEST002") + .name("传感器") + .description("温度传感器") + .remark("欧姆龙品牌") + .build()); + // 输出 + ExcelUtils.write(response, "设备关键件导入模板.xls", "数据", CriticalComponentImportExcelVO.class, list); + } + + @PostMapping("/import") + @Operation(summary = "导入设备关键件") + @Parameters({ + @Parameter(name = "file", description = "Excel 文件", required = true), + @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true") + }) + @PreAuthorize("@ss.hasPermission('mes:critical-component:import')") + public CommonResult importExcel( + @RequestParam("file") MultipartFile file, + @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception { + // 1. 读取 Excel 数据 + List list = ExcelUtils.read(file, CriticalComponentImportExcelVO.class); + // 2. 调用 Service 并返回结果 + return success(criticalComponentService.importCriticalComponentList(list, updateSupport)); + } + } \ No newline at end of file diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/vo/CriticalComponentImportExcelVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/vo/CriticalComponentImportExcelVO.java new file mode 100644 index 000000000..83a3fe85e --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/vo/CriticalComponentImportExcelVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.mes.controller.admin.criticalcomponent.vo; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + * 设备关键件 Excel 导入 VO + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) +public class CriticalComponentImportExcelVO { + + @ExcelProperty("编码") + private String code; + + @ExcelProperty("名称") + private String name; + + @ExcelProperty("描述") + private String description; + + @ExcelProperty("备注") + private String remark; + + // 创建时间,不在Excel中显示,自动生成 + @ExcelIgnore + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/vo/CriticalComponentImportRespVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/vo/CriticalComponentImportRespVO.java new file mode 100644 index 000000000..fc6210235 --- /dev/null +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/criticalcomponent/vo/CriticalComponentImportRespVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.mes.controller.admin.criticalcomponent.vo; + +import lombok.Builder; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 设备关键件 Excel 导入响应 VO + */ +@Data +@Builder +public class CriticalComponentImportRespVO { + + /** + * 成功导入的编码列表 + */ + private List createCodes; + + /** + * 更新的编码列表 + */ + private List updateCodes; + + /** + * 失败的编码列表,key: 编码,value: 失败原因 + */ + private Map failureCodes; + + public CriticalComponentImportRespVO() { + this.createCodes = new ArrayList<>(); + this.updateCodes = new ArrayList<>(); + this.failureCodes = new LinkedHashMap<>(); + } + + public CriticalComponentImportRespVO(List createCodes, List updateCodes, Map failureCodes) { + this.createCodes = createCodes; + this.updateCodes = updateCodes; + this.failureCodes = failureCodes; + } +} \ No newline at end of file diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/criticalcomponent/CriticalComponentMapper.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/criticalcomponent/CriticalComponentMapper.java index 2a36b6101..fb07bb3fb 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/criticalcomponent/CriticalComponentMapper.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/criticalcomponent/CriticalComponentMapper.java @@ -6,6 +6,7 @@ import java.util.stream.Collectors; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO; import cn.iocoder.yudao.module.mes.dal.dataobject.criticalcomponent.CriticalComponentDO; import cn.iocoder.yudao.module.mes.dal.dataobject.deviceledger.DeviceLedgerDO; import com.alibaba.excel.util.StringUtils; @@ -43,4 +44,8 @@ public interface CriticalComponentMapper extends BaseMapperX exportDeviceComponent(Long id); + /** + * 导入设备关键件列表 + */ + CriticalComponentImportRespVO importCriticalComponentList(List importComponents, boolean isUpdateSupport); + } \ No newline at end of file diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/criticalcomponent/CriticalComponentServiceImpl.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/criticalcomponent/CriticalComponentServiceImpl.java index 61fae0695..d7f5d5b09 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/criticalcomponent/CriticalComponentServiceImpl.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/criticalcomponent/CriticalComponentServiceImpl.java @@ -1,16 +1,23 @@ package cn.iocoder.yudao.module.mes.service.criticalcomponent; +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.module.mes.dal.dataobject.deviceledger.DeviceLedgerDO; import cn.iocoder.yudao.module.mes.dal.dataobject.repairtems.RepairTemsDO; import cn.iocoder.yudao.module.mes.dal.mysql.deviceledger.DeviceLedgerMapper; import cn.iocoder.yudao.module.mes.dal.mysql.repairtems.RepairTemsMapper; +import cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants; import com.alibaba.excel.util.StringUtils; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import javax.validation.ConstraintViolationException; + +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; +import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; @@ -183,4 +190,56 @@ public class CriticalComponentServiceImpl implements CriticalComponentService { return BeanUtils.toBean(componentList,CriticalComponentExcelVO.class); } + + @Override + @Transactional(rollbackFor = Exception.class) + public CriticalComponentImportRespVO importCriticalComponentList(List importComponents, boolean isUpdateSupport) { + // 1. 参数校验 + if (CollUtil.isEmpty(importComponents)) { + throw exception(ErrorCodeConstants.CRITICAL_COMPONENT_NOT_EXISTS); + } + + // 2. 初始化返回结果 + CriticalComponentImportRespVO respVO = CriticalComponentImportRespVO.builder() + .createCodes(new ArrayList<>()) + .updateCodes(new ArrayList<>()) + .failureCodes(new LinkedHashMap<>()) + .build(); + + // 3. 遍历导入数据 + importComponents.forEach(importComponent -> { + try { + // 3.1 字段校验 + ValidationUtils.validate(BeanUtils.toBean(importComponent, CriticalComponentSaveReqVO.class)); + } catch (ConstraintViolationException ex) { + respVO.getFailureCodes().put(importComponent.getCode(), ex.getMessage()); + return; + } + + // 3.2 检查是否已存在 + CriticalComponentDO existComponent = criticalComponentMapper.selectByCode(importComponent.getCode()); + if (existComponent == null) { + // 3.3 创建新记录 + CriticalComponentDO newComponent = BeanUtils.toBean(importComponent, CriticalComponentDO.class); + // 设置创建时间为当前时间 + newComponent.setCreateTime(LocalDateTime.now()); + criticalComponentMapper.insert(newComponent); + respVO.getCreateCodes().add(importComponent.getCode()); + } else if (isUpdateSupport) { + // 3.4 更新已有记录 + CriticalComponentDO updateComponent = BeanUtils.toBean(importComponent, CriticalComponentDO.class); + updateComponent.setId(existComponent.getId()); + // 保持原有创建时间不变 + updateComponent.setCreateTime(existComponent.getCreateTime()); + criticalComponentMapper.updateById(updateComponent); + respVO.getUpdateCodes().add(importComponent.getCode()); + } else { + // 3.5 已存在且不支持更新 + respVO.getFailureCodes().put(importComponent.getCode(), "该关键件已存在"); + } + }); + + return respVO; + } + } \ No newline at end of file