From 86e4f4f7601312c017d92931f8189588a83fda4f Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Mon, 27 Jul 2026 10:21:25 +0800 Subject: [PATCH 1/8] =?UTF-8?q?fix:=E9=85=8D=E6=96=B9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/product/ErpProductCategoryController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java index 8654d9080..0e3eeee9c 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/ErpProductCategoryController.java @@ -102,7 +102,7 @@ public class ErpProductCategoryController { @GetMapping("/simple-list") @Operation(summary = "获得产品分类精简列表", description = "只包含被开启的分类,主要用于前端的下拉选项") - public CommonResult> getProductCategorySimpleList(@RequestParam("type") Integer type) { + public CommonResult> getProductCategorySimpleList(@RequestParam(value = "type",required = false) Integer type) { List list = productCategoryService.getProductCategoryList( new ErpProductCategoryListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()).setType(type)); return success(convertList(list, category -> new ErpProductCategoryRespVO() From 8bd1e2cda51425ba2880e6e354a7b0c91e2e26cd Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Mon, 27 Jul 2026 10:44:29 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=8F=B0=E8=B4=A6=E7=BB=B4=E4=BF=AE=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/deviceledger/vo/DeviceLedgerPageRespVO.java | 3 +++ .../mes/service/deviceledger/DeviceLedgerServiceImpl.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceLedgerPageRespVO.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceLedgerPageRespVO.java index ae59537df..2480a886b 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceLedgerPageRespVO.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/deviceledger/vo/DeviceLedgerPageRespVO.java @@ -24,6 +24,9 @@ public class DeviceLedgerPageRespVO { @Schema(description = "停用数量 (deviceStatus=1)") private Long disabledCount; + @Schema(description = "维修数量 (deviceStatus=2)") + private Long repairCount; + @Schema(description = "已排产数量 (isScheduled=1)") private Long scheduledCount; diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerServiceImpl.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerServiceImpl.java index bca1a2984..a50fd6d04 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerServiceImpl.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/deviceledger/DeviceLedgerServiceImpl.java @@ -520,6 +520,7 @@ public class DeviceLedgerServiceImpl implements DeviceLedgerService { respVO.setAllCount(0L); respVO.setNormalCount(0L); respVO.setDisabledCount(0L); + respVO.setRepairCount(0L); respVO.setScheduledCount(0L); respVO.setUnscheduledCount(0L); return respVO; @@ -537,6 +538,7 @@ public class DeviceLedgerServiceImpl implements DeviceLedgerService { respVO.setAllCount(0L); respVO.setNormalCount(0L); respVO.setDisabledCount(0L); + respVO.setRepairCount(0L); respVO.setScheduledCount(0L); respVO.setUnscheduledCount(0L); return respVO; @@ -545,6 +547,7 @@ public class DeviceLedgerServiceImpl implements DeviceLedgerService { respVO.setAllCount((long) allList.size()); respVO.setNormalCount(allList.stream().filter(item -> item.getDeviceStatus() != null && item.getDeviceStatus() == 0).count()); respVO.setDisabledCount(allList.stream().filter(item -> item.getDeviceStatus() != null && item.getDeviceStatus() == 1).count()); + respVO.setRepairCount(allList.stream().filter(item -> item.getDeviceStatus() != null && item.getDeviceStatus() == 2).count()); respVO.setScheduledCount(allList.stream().filter(item -> item.getIsScheduled() != null && item.getIsScheduled() == 1).count()); respVO.setUnscheduledCount(allList.stream().filter(item -> item.getIsScheduled() != null && item.getIsScheduled() == 0).count()); return respVO; From 142d2b8b857fa53665ad4463d4967aa068217e19 Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Mon, 27 Jul 2026 14:31:21 +0800 Subject: [PATCH 3/8] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9=E6=A8=A1=E5=85=B7?= =?UTF-8?q?=E7=BB=B4=E4=BF=AE=E4=BF=AE=E6=94=B9=E7=8A=B6=E6=80=81=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../moldrepair/MoldRepairServiceImpl.java | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/moldrepair/MoldRepairServiceImpl.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/moldrepair/MoldRepairServiceImpl.java index c6a707226..3eec3280e 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/moldrepair/MoldRepairServiceImpl.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/moldrepair/MoldRepairServiceImpl.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.common.dal.dataobject.mold.MoldBrandDO; import cn.iocoder.yudao.module.common.dal.dataobject.mold.MoldDO; import cn.iocoder.yudao.module.common.dal.mysql.mold.MoldBrandMapper; +import cn.iocoder.yudao.module.common.enums.MoldBrandStatusEnum; import cn.iocoder.yudao.module.erp.controller.admin.autocode.util.AutoCodeUtil; import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.dashboard.EventStatisticsVO; import cn.iocoder.yudao.module.common.controller.admin.moldrepair.enums.RepairResultEnum; @@ -91,6 +92,7 @@ public class MoldRepairServiceImpl implements MoldRepairService { // 插入 MoldRepairDO moldRepair = BeanUtils.toBean(createReqVO, MoldRepairDO.class); moldRepairMapper.insert(moldRepair); + updateMoldBrandStatus(moldRepair.getMoldId(), MoldBrandStatusEnum.REPAIRING.getStatus()); sendCreateNotifyMessage(moldRepair); // 插入子表 @@ -124,7 +126,7 @@ public class MoldRepairServiceImpl implements MoldRepairService { @Transactional(rollbackFor = Exception.class) public void updateMoldRepair(MoldRepairSaveReqVO updateReqVO) { // 校验存在 - validateMoldRepairExists(updateReqVO.getId()); + MoldRepairDO oldMoldRepair = validateMoldRepairExists(updateReqVO.getId()); //编码重复判断 Long count = moldRepairMapper.selectCount(new LambdaQueryWrapper() @@ -140,6 +142,8 @@ public class MoldRepairServiceImpl implements MoldRepairService { // 更新 MoldRepairDO updateObj = BeanUtils.toBean(updateReqVO, MoldRepairDO.class); moldRepairMapper.updateById(updateObj); + Long moldId = updateReqVO.getMoldId() != null ? updateReqVO.getMoldId() : oldMoldRepair.getMoldId(); + updateMoldBrandStatusByRepairResult(moldId, updateReqVO.getRepairStatus()); // 更新子表 // updateMoldRepairLineList(updateReqVO.getId(), updateReqVO.getMoldRepairLines()); @@ -159,10 +163,12 @@ public class MoldRepairServiceImpl implements MoldRepairService { } - private void validateMoldRepairExists(Long id) { - if (moldRepairMapper.selectById(id) == null) { + private MoldRepairDO validateMoldRepairExists(Long id) { + MoldRepairDO moldRepair = moldRepairMapper.selectById(id); + if (moldRepair == null) { throw exception(MOLD_REPAIR_NOT_EXISTS); } + return moldRepair; } @Override @@ -304,6 +310,7 @@ public class MoldRepairServiceImpl implements MoldRepairService { moldRepairDO.setConfirmDate(updateReqVO.getConfirmDate()); moldRepairDO.setRepairResult(updateReqVO.getRepairResult()); moldRepairMapper.updateById(moldRepairDO); + updateMoldBrandStatusByRepairResult(moldRepairDO.getMoldId(), updateReqVO.getRepairResult()); batchUpdateTicketResults(updateReqVO.getUpdateReqVOList()); } @@ -457,14 +464,49 @@ public class MoldRepairServiceImpl implements MoldRepairService { .anyMatch(vo -> vo.getResult() != null && vo.getResult().equals(JobResultEnum.FAIL.getCode())); if (hasFail) { moldRepairDO.setRepairStatus(JobResultEnum.FAIL.getCode()); + updateMoldBrandStatus(moldRepairDO.getMoldId(), MoldBrandStatusEnum.REPAIRING.getStatus()); } else { moldRepairDO.setRepairStatus(JobResultEnum.PASS.getCode()); + updateMoldBrandStatus(moldRepairDO.getMoldId(), MoldBrandStatusEnum.STANDBY.getStatus()); } moldRepairMapper.updateById(moldRepairDO); } + private void updateMoldBrandStatusByRepairResult(Long moldId, String repairResult) { + if (StringUtils.isEmpty(repairResult)) { + return; + } + Integer status = isRepairPassed(repairResult) + ? MoldBrandStatusEnum.STANDBY.getStatus() + : MoldBrandStatusEnum.REPAIRING.getStatus(); + updateMoldBrandStatus(moldId, status); + } + + private boolean isRepairPassed(String repairResult) { + String value = repairResult.trim(); + return RepairResultEnum.PASS.getCode().toString().equals(value) + || RepairResultEnum.PASS.getDesc().equalsIgnoreCase(value) + || JobResultEnum.PASS.getCode().toString().equals(value) + || "PASS".equalsIgnoreCase(value) + || "通过".equals(value); + } + + private void updateMoldBrandStatus(Long moldId, Integer status) { + if (moldId == null || status == null) { + return; + } + MoldBrandDO moldBrand = moldBrandMapper.selectById(moldId); + if (moldBrand == null) { + throw exception(MOLD_SET_NOT_EXISTS); + } + MoldBrandDO updateObj = new MoldBrandDO(); + updateObj.setId(moldId); + updateObj.setStatus(status); + moldBrandMapper.updateById(updateObj); + } + private void validateBatchUpdateData(List updateReqVOList) { From c1a770ef17ae5d90f5aabceeed1b3b333b4e2ffc Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Mon, 27 Jul 2026 16:22:17 +0800 Subject: [PATCH 4/8] =?UTF-8?q?fix:app=E8=8F=9C=E5=8D=95=E5=AF=BC=E8=88=AA?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/usernavmenu/UserNavMenuController.java | 7 +++++-- .../service/usernavmenu/UserNavMenuService.java | 2 +- .../service/usernavmenu/UserNavMenuServiceImpl.java | 12 ++++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usernavmenu/UserNavMenuController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usernavmenu/UserNavMenuController.java index cd309354a..ef72af6eb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usernavmenu/UserNavMenuController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usernavmenu/UserNavMenuController.java @@ -29,6 +29,8 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + @Tag(name = "管理后台 - App用户导航菜单配置") @RestController @RequestMapping("/system/user-nav-menu") @@ -56,8 +58,9 @@ public class UserNavMenuController { @PutMapping("/update-list") @Operation(summary = "批量更新") // @PreAuthorize("@ss.hasPermission('system:user-nav-menu:update')") - public CommonResult updateUserNavMenuList(@Valid @RequestBody List updateReqVOList) { - userNavMenuService.updateUserNavMenuList(updateReqVOList); + public CommonResult updateUserNavMenuList(@RequestParam(value = "userId", required = false) Long userId, + @Valid @RequestBody(required = false) List updateReqVOList) { + userNavMenuService.updateUserNavMenuList(userId != null ? userId : getLoginUserId(), updateReqVOList); return success(true); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/usernavmenu/UserNavMenuService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/usernavmenu/UserNavMenuService.java index 9d73a4b45..d364f6fc8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/usernavmenu/UserNavMenuService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/usernavmenu/UserNavMenuService.java @@ -55,7 +55,7 @@ public interface UserNavMenuService { List getUserNavMenuList(@Valid UserNavMenuPageReqVO listReqVO); - void updateUserNavMenuList(@Valid List updateReqVOList); + void updateUserNavMenuList(Long userId, @Valid List updateReqVOList); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/usernavmenu/UserNavMenuServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/usernavmenu/UserNavMenuServiceImpl.java index 04192a49d..4c10dc6eb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/usernavmenu/UserNavMenuServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/usernavmenu/UserNavMenuServiceImpl.java @@ -47,17 +47,21 @@ public class UserNavMenuServiceImpl implements UserNavMenuService { } @Override - public void updateUserNavMenuList(List updateReqVOList) { + public void updateUserNavMenuList(Long userId, List updateReqVOList) { if (updateReqVOList == null || updateReqVOList.isEmpty()) { + if (userId == null) { + throw new IllegalArgumentException("userId cannot be empty when update menu list is empty"); + } + userNavMenuMapper.deleteByUserId(userId); return; } - Long userId = updateReqVOList.get(0).getUserId(); + Long targetUserId = userId != null ? userId : updateReqVOList.get(0).getUserId(); for (UserNavMenuSaveReqVO updateReqVO : updateReqVOList) { - if (!Objects.equals(userId, updateReqVO.getUserId())) { + if (!Objects.equals(targetUserId, updateReqVO.getUserId())) { throw exception(USER_NAV_MENU_NOT_EXISTS); } } - userNavMenuMapper.deleteByUserId(userId); + userNavMenuMapper.deleteByUserId(targetUserId); List insertList = BeanUtils.toBean(updateReqVOList, UserNavMenuDO.class); insertList.forEach(userNavMenuMapper::insert); } From d7db3e0f8cf0212c1997245673c33ab27c31ca1e Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Mon, 27 Jul 2026 16:41:29 +0800 Subject: [PATCH 5/8] =?UTF-8?q?fix:=E6=B7=BB=E5=8A=A0=E5=AD=97=E5=85=B8?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/controller/admin/dict/DictDataController.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java index 83afcd1c7..eef064c79 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java @@ -66,9 +66,10 @@ public class DictDataController { @GetMapping(value = {"/list-all-simple", "simple-list"}) @Operation(summary = "获得全部字典数据列表", description = "一般用于管理后台缓存字典数据在本地") // 无需添加权限认证,因为前端全局都需要 - public CommonResult> getSimpleDictDataList() { + public CommonResult> getSimpleDictDataList( + @RequestParam(value = "dictType", required = false) String dictType) { List list = dictDataService.getDictDataList( - CommonStatusEnum.ENABLE.getStatus(), null); + CommonStatusEnum.ENABLE.getStatus(), dictType); return success(BeanUtils.toBean(list, DictDataSimpleRespVO.class)); } From 5e99f8d3dc50c3665ac12deda86b8a4947a2754c Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Mon, 27 Jul 2026 16:45:14 +0800 Subject: [PATCH 6/8] =?UTF-8?q?fix:=E4=BB=BB=E5=8A=A1=E5=8D=95=E8=B6=8B?= =?UTF-8?q?=E5=8A=BF=E7=BB=9F=E8=AE=A1=E6=94=BE=E5=BC=80=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/mes/controller/admin/task/TaskController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/TaskController.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/TaskController.java index 1cd4ec17e..eadea3a28 100644 --- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/TaskController.java +++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/task/TaskController.java @@ -202,7 +202,7 @@ public class TaskController { @GetMapping("/trend") @Operation(summary = "任务单趋势统计") - @PreAuthorize("@ss.hasPermission('mes:task:query')") +// @PreAuthorize("@ss.hasPermission('mes:task:query')") public CommonResult getTaskTrend(@Valid TaskTrendReqVO reqVO) { return success(taskService.getTaskTrend(reqVO)); } From f6aad2752e6a49128196268e27293487d35aa610 Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Mon, 27 Jul 2026 17:22:28 +0800 Subject: [PATCH 7/8] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E4=BA=A7=E8=83=BD?= =?UTF-8?q?=E6=8A=A5=E8=A1=A8=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mqtt/consumer/IotMqttRuntimeCache.java | 2 +- .../mqtt/consumer/MqttDataHandler.java | 1 - ...eviceProductionDailyReportServiceImpl.java | 374 ++++++++++++++++-- .../iot/service/device/TDengineService.java | 34 ++ ...ding-production-daily-report-template.xlsx | Bin 8600 -> 5798 bytes 5 files changed, 379 insertions(+), 32 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/IotMqttRuntimeCache.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/IotMqttRuntimeCache.java index 1fd3f241a..8818c17bb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/IotMqttRuntimeCache.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/IotMqttRuntimeCache.java @@ -122,7 +122,7 @@ public class IotMqttRuntimeCache { } List parsedRules = new ArrayList<>(); for (DevicePointRulesDO rule : rules) { - if (StringUtils.isBlank(rule.getFieldRule())) { + if (StringUtils.isBlank(rule.getAlarmLevel()) || StringUtils.isBlank(rule.getFieldRule())) { continue; } try { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/MqttDataHandler.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/MqttDataHandler.java index 048f301f3..f3ba88628 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/MqttDataHandler.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/MqttDataHandler.java @@ -398,7 +398,6 @@ public class MqttDataHandler extends SuperConsumer { attributeCode, processedValue, JSON.toJSONString(pointRulesRespVO)); handleMatchedSuccessRule(cachedRule.getRule(), pointRulesRespVO, processedValue, device, attributeCode, point); - break; } else { log.debug("规则不匹配: modelId={}, value={}, rule={}", attributeCode, processedValue, diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportServiceImpl.java index 6d82107bd..7d5795e95 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportServiceImpl.java @@ -12,24 +12,31 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceProductionDa import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceProductionDailyReportRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.devicewarinningrecord.DeviceWarinningRecordDO; import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper; import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper; import cn.iocoder.yudao.module.mes.api.workteam.WorkTeamApi; import cn.iocoder.yudao.module.mes.api.workteam.dto.WorkTeamRespDTO; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellRangeAddress; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.math.BigDecimal; @@ -57,8 +64,10 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD private static final String TEMPLATE_PATH = "excel/templates/fully-automatic-molding-production-daily-report-template.xlsx"; private static final String REPORT_PATH_PREFIX = "iot/device/production-daily-report"; + private static final String ALARM_LEVEL_DICT_TYPE = "alarm_registration"; private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss"); @Resource private DeviceMapper deviceMapper; @@ -69,6 +78,8 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD @Resource private WorkTeamApi workTeamApi; @Resource + private DictDataApi dictDataApi; + @Resource private FileService fileService; @Resource private FileMapper fileMapper; @@ -96,15 +107,19 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD if (!endTime.isAfter(startTime)) { endTime = endTime.plusDays(1); } + log.info("[production-daily-report] generate request: reportDate={}, workTeamId={}, workTeamName={}, startTime={}, endTime={}, deviceIds={}", + reqVO.getReportDate(), reqVO.getWorkTeamId(), workTeam.getTeamName(), startTime, endTime, deviceIds); List items = buildCapacityItems(deviceIds, deviceMap, startTime, endTime); - byte[] excelContent = buildExcel(reqVO, workTeam, startTime, endTime, items); + byte[] excelContent = buildExcel(reqVO, workTeam, startTime, endTime, items, deviceIds); String fileName = String.format("全自动成型机生产日报表_%s_%s_%s.xlsx", reqVO.getReportDate().format(DateTimeFormatter.BASIC_ISO_DATE), reqVO.getWorkTeamId(), System.currentTimeMillis()); String filePath = String.format("%s/date=%s/workTeamId=%s/deviceIds=%s/deviceTags=%s/%s", REPORT_PATH_PREFIX, reqVO.getReportDate(), reqVO.getWorkTeamId(), joinDeviceIds(deviceIds), buildDeviceTags(deviceIds), fileName); Map file = fileService.createFile(fileName, filePath, excelContent); + log.info("[production-daily-report] file created: fileName={}, filePath={}, fileUrl={}, contentLength={}", + file.get("fileName"), filePath, file.get("fileUrl"), excelContent.length); DeviceProductionDailyReportRespVO respVO = buildBaseResp(reqVO.getReportDate(), workTeam, startTime, endTime, deviceIds); respVO.setFileName(file.get("fileName")); @@ -141,7 +156,7 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD Map deviceMap, LocalDateTime startTime, LocalDateTime endTime) { - Map> capacityPointMap = queryCapacityPoints(deviceIds); + Map capacityPointMap = queryCapacityPoints(deviceIds); Map> firstRows = tDengineService.queryDevicesEarliestRow(deviceIds, startTime.format(DATE_TIME_FORMATTER), endTime.format(DATE_TIME_FORMATTER)); Map> lastRows = tDengineService.queryDevicesLatestRow(deviceIds, @@ -150,31 +165,28 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD List items = new ArrayList<>(); for (Long deviceId : deviceIds) { DeviceDO device = deviceMap.get(deviceId); - List points = capacityPointMap.getOrDefault(deviceId, Collections.emptyList()); - if (CollUtil.isEmpty(points)) { + DeviceContactModelDO point = capacityPointMap.get(deviceId); + if (point == null) { items.add(buildEmptyCapacityItem(device)); continue; } Map firstRow = firstRows.get(deviceId); Map lastRow = lastRows.get(deviceId); - for (DeviceContactModelDO point : points) { - items.add(buildCapacityItem(device, point, firstRow, lastRow)); - } + items.add(buildCapacityItem(device, point, firstRow, lastRow)); } return items; } - private Map> queryCapacityPoints(List deviceIds) { + private Map queryCapacityPoints(List deviceIds) { List points = deviceContactModelMapper.selectList( Wrappers.lambdaQuery() .in(DeviceContactModelDO::getDeviceId, deviceIds) - .and(wrapper -> wrapper.like(DeviceContactModelDO::getTypeName, "产能") - .or().like(DeviceContactModelDO::getAttributeType, "产能") - .or().like(DeviceContactModelDO::getAttributeName, "产能")) + .eq(DeviceContactModelDO::getTypeName, "产能") .orderByAsc(DeviceContactModelDO::getDeviceId) - .orderByAsc(DeviceContactModelDO::getSort) - .orderByAsc(DeviceContactModelDO::getId)); - return points.stream().collect(Collectors.groupingBy(DeviceContactModelDO::getDeviceId, LinkedHashMap::new, Collectors.toList())); + .orderByDesc(DeviceContactModelDO::getCreateTime) + .orderByDesc(DeviceContactModelDO::getId)); + return points.stream().collect(Collectors.toMap(DeviceContactModelDO::getDeviceId, item -> item, (first, second) -> first, + LinkedHashMap::new)); } private DeviceProductionDailyReportRespVO.DeviceCapacityItem buildEmptyCapacityItem(DeviceDO device) { @@ -212,36 +224,190 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD @SneakyThrows private byte[] buildExcel(DeviceProductionDailyReportGenerateReqVO reqVO, WorkTeamRespDTO workTeam, LocalDateTime startTime, LocalDateTime endTime, - List items) { + List items, List deviceIds) { try (InputStream inputStream = new ClassPathResource(TEMPLATE_PATH).getInputStream(); - Workbook workbook = WorkbookFactory.create(inputStream); + Workbook workbook = WorkbookFactory.create(inputStream); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { Sheet sheet = workbook.getSheetAt(0); - setCellValue(sheet, 2, 0, "日期:" + LocalDate.now().format(DATE_FORMATTER)); - setCellValue(sheet, 2, 4, "班次:" + StrUtil.blankToDefault(workTeam.getTeamName(), "")); - setCellValue(sheet, 19, 9, "审核:"); - - int rowIndex = 5; + log.info("[production-daily-report] template loaded: path={}, sheetName={}, mergedRegions={}", + TEMPLATE_PATH, sheet.getSheetName(), sheet.getMergedRegions()); + String dateText = "日期:" + reqVO.getReportDate().format(DATE_FORMATTER); + String shiftText = "班次:" + StrUtil.blankToDefault(workTeam.getTeamName(), ""); + setHeaderValue(sheet, "日期:", dateText, 2, 0); + setHeaderValue(sheet, "班次:", shiftText, 2, 4); + log.info("[production-daily-report] header cells after write: A3={}, C3={}, E3={}", + getCellValue(sheet, 2, 0), getCellValue(sheet, 2, 2), getCellValue(sheet, 2, 4)); + + int rowIndex = 4; + int exceptionStartRowIndex = 5; + Row templateRow = sheet.getRow(rowIndex); + if (items.size() > 1) { + sheet.shiftRows(exceptionStartRowIndex, sheet.getLastRowNum(), items.size() - 1, true, false); + } for (int index = 0; index < items.size(); index++) { - if (rowIndex + index > 9) { - sheet.shiftRows(10, sheet.getLastRowNum(), 1, true, false); - } Row row = sheet.getRow(rowIndex + index); if (row == null) { row = sheet.createRow(rowIndex + index); + copyRowStyle(templateRow, row, 5); } DeviceProductionDailyReportRespVO.DeviceCapacityItem item = items.get(index); setCellValue(row, 0, index + 1); setCellValue(row, 1, StrUtil.blankToDefault(item.getDeviceCode(), item.getDeviceName())); - setCellValue(row, 4, item.getCapacityName()); - setCellValue(row, 20, item.getCapacityDiff()); - setCellValue(row, 21, toHour(startTime, endTime)); - setCellValue(row, 22, startTime.toLocalTime() + "-" + endTime.toLocalTime()); - setCellValue(row, 24, workTeam.getTeamName()); + setCellValue(row, 2, buildCapacityPointText(item)); + setCellValue(row, 3, item.getCapacityDiff()); + setCellValue(row, 4, startTime.toLocalTime() + "-" + endTime.toLocalTime()); } + fillWarningDescription(sheet, rowIndex + items.size() + 2, deviceIds, startTime, endTime); workbook.write(outputStream); - return outputStream.toByteArray(); + byte[] bytes = outputStream.toByteArray(); + logGeneratedHeader(bytes); + return bytes; + } + } + + private void fillWarningDescription(Sheet sheet, int warningRowIndex, List deviceIds, + LocalDateTime startTime, LocalDateTime endTime) { + List warningRecords = tDengineService.selectDeviceWarningList(deviceIds, startTime, endTime); + Map> warningMap = warningRecords.stream() + .collect(Collectors.groupingBy(DeviceWarinningRecordDO::getDeviceId, LinkedHashMap::new, Collectors.toList())); + Row templateRow = sheet.getRow(warningRowIndex); + for (int index = 0; index < deviceIds.size(); index++) { + Row row = sheet.getRow(warningRowIndex + index); + if (row == null) { + row = sheet.createRow(warningRowIndex + index); + copyRowStyle(templateRow, row, 5); + mergeCellsIfAbsent(sheet, warningRowIndex + index, 2, 4); + } + Long deviceId = deviceIds.get(index); + setCellValue(row, 0, index + 1); + setCellValue(row, 1, getDeviceText(deviceId)); + String warningDescription = buildWarningDescription(warningMap.getOrDefault(deviceId, Collections.emptyList())); + setCellValue(row, 2, warningDescription); + setWrapText(row, 2); + row.setHeightInPoints(Math.max(row.getHeightInPoints(), 18F * Math.max(1, warningDescription.split("\n", -1).length))); + } + } + + private String buildCapacityPointText(DeviceProductionDailyReportRespVO.DeviceCapacityItem item) { + String capacityName = item.getCapacityName(); + String capacityCode = item.getCapacityCode(); + if (StringUtils.isBlank(capacityName)) { + return StrUtil.blankToDefault(capacityCode, ""); + } + if (StringUtils.isBlank(capacityCode) || StringUtils.equals(capacityName, capacityCode)) { + return capacityName; + } + return capacityName + "(" + capacityCode + ")"; + } + + private String getDeviceText(Long deviceId) { + DeviceDO device = deviceMapper.selectById(deviceId); + if (device == null) { + return ""; + } + return StrUtil.blankToDefault(device.getDeviceCode(), device.getDeviceName()); + } + + private String buildWarningDescription(List warningRecords) { + if (CollUtil.isEmpty(warningRecords)) { + return ""; } + return warningRecords.stream() + .collect(Collectors.groupingBy(this::getWarningPointName, LinkedHashMap::new, Collectors.toList())) + .entrySet().stream() + .map(entry -> entry.getKey() + ":" + buildWarningPointSummary(entry.getValue())) + .collect(Collectors.joining("\n")); + } + + private String buildWarningPointSummary(List warningRecords) { + return warningRecords.stream() + .collect(Collectors.groupingBy(this::getWarningRuleKey, LinkedHashMap::new, Collectors.toList())) + .values().stream() + .map(this::buildWarningRuleSummary) + .collect(Collectors.joining(";")); + } + + private String getWarningRuleKey(DeviceWarinningRecordDO record) { + return getWarningName(record) + "|" + StrUtil.blankToDefault(record.getAlarmLevel(), ""); + } + + private String buildWarningRuleSummary(List warningRecords) { + DeviceWarinningRecordDO firstRecord = warningRecords.get(0); + String levelText = buildAlarmLevelText(firstRecord.getAlarmLevel()); + return getWarningName(firstRecord) + warningRecords.size() + "次" + + buildWarningValueRangeText(warningRecords) + levelText + buildWarningTimeRangeText(warningRecords); + } + + private String buildAlarmLevelText(String alarmLevel) { + if (StringUtils.isBlank(alarmLevel)) { + return ""; + } + DictDataRespDTO dictData = dictDataApi.getDictData(ALARM_LEVEL_DICT_TYPE, alarmLevel); + return ",等级=" + (dictData != null && StringUtils.isNotBlank(dictData.getLabel()) ? dictData.getLabel() : alarmLevel); + } + + private String getWarningPointName(DeviceWarinningRecordDO record) { + return StrUtil.blankToDefault(record.getModelName(), + StrUtil.blankToDefault(record.getRuleName(), StrUtil.blankToDefault(record.getRule(), "未知点位"))); + } + + private String getWarningName(DeviceWarinningRecordDO record) { + return StrUtil.blankToDefault(record.getRuleName(), StrUtil.blankToDefault(record.getRule(), "告警")); + } + + private String buildWarningValueRangeText(List warningRecords) { + List values = warningRecords.stream() + .map(this::parseWarningValue) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (CollUtil.isEmpty(values)) { + return ""; + } + double minValue = values.stream().min(Double::compareTo).orElse(0D); + double maxValue = values.stream().max(Double::compareTo).orElse(0D); + if (BigDecimal.valueOf(minValue).compareTo(BigDecimal.valueOf(maxValue)) == 0) { + return ",值=" + formatNumber(minValue); + } + return ",值=" + formatNumber(minValue) + "~" + formatNumber(maxValue); + } + + private Double parseWarningValue(DeviceWarinningRecordDO record) { + if (StringUtils.isBlank(record.getAddressValue())) { + return null; + } + try { + return Double.valueOf(record.getAddressValue()); + } catch (NumberFormatException e) { + return null; + } + } + + private String buildWarningTimeRangeText(List warningRecords) { + List times = warningRecords.stream() + .map(DeviceWarinningRecordDO::getCreateTime) + .filter(Objects::nonNull) + .sorted() + .collect(Collectors.toList()); + if (CollUtil.isEmpty(times)) { + return ""; + } + LocalDateTime startTime = times.get(0); + LocalDateTime endTime = times.get(times.size() - 1); + if (Objects.equals(startTime, endTime)) { + return ",时间=" + formatWarningTime(startTime, startTime); + } + return ",时间=" + formatWarningTime(startTime, endTime) + "~" + formatWarningTime(endTime, startTime); + } + + private String formatWarningTime(LocalDateTime time, LocalDateTime compareTime) { + if (Objects.equals(time.toLocalDate(), compareTime.toLocalDate())) { + return time.format(TIME_FORMATTER); + } + return time.format(DATE_TIME_FORMATTER); + } + + private String formatNumber(Double value) { + return BigDecimal.valueOf(value).stripTrailingZeros().toPlainString(); } private DeviceProductionDailyReportRespVO convertFileToReport(FileDO file) { @@ -343,6 +509,11 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD } private void setCellValue(Sheet sheet, int rowIndex, int columnIndex, Object value) { + CellRangeAddress mergedRegion = getMergedRegion(sheet, rowIndex, columnIndex); + if (mergedRegion != null) { + rowIndex = mergedRegion.getFirstRow(); + columnIndex = mergedRegion.getFirstColumn(); + } Row row = sheet.getRow(rowIndex); if (row == null) { row = sheet.createRow(rowIndex); @@ -361,5 +532,148 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD cell.setCellValue(value == null ? "" : value.toString()); } } + + private void setWrapText(Row row, int columnIndex) { + Cell cell = row.getCell(columnIndex); + if (cell == null) { + return; + } + CellStyle cellStyle = row.getSheet().getWorkbook().createCellStyle(); + cellStyle.cloneStyleFrom(cell.getCellStyle()); + cellStyle.setWrapText(true); + cell.setCellStyle(cellStyle); + } + + private void copyRowStyle(Row sourceRow, Row targetRow, int columnCount) { + if (sourceRow == null || targetRow == null) { + return; + } + targetRow.setHeight(sourceRow.getHeight()); + for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) { + Cell sourceCell = sourceRow.getCell(columnIndex); + if (sourceCell == null) { + continue; + } + Cell targetCell = targetRow.getCell(columnIndex); + if (targetCell == null) { + targetCell = targetRow.createCell(columnIndex); + } + targetCell.setCellStyle(sourceCell.getCellStyle()); + } + } + + private void mergeCellsIfAbsent(Sheet sheet, int rowIndex, int firstColumn, int lastColumn) { + CellRangeAddress newRegion = new CellRangeAddress(rowIndex, rowIndex, firstColumn, lastColumn); + for (CellRangeAddress mergedRegion : sheet.getMergedRegions()) { + if (mergedRegion.intersects(newRegion)) { + return; + } + } + sheet.addMergedRegion(newRegion); + } + + private void setHeaderValue(Sheet sheet, String label, String value, int fallbackRowIndex, int fallbackColumnIndex) { + Cell labelCell = findCellByPrefix(sheet, label); + if (labelCell != null) { + CellRangeAddress mergedRegion = getMergedRegion(sheet, labelCell.getRowIndex(), labelCell.getColumnIndex()); + log.info("[production-daily-report] set header by label: label={}, foundCell={}, rowIndex={}, columnIndex={}, mergedRegion={}, value={}", + label, labelCell.getAddress().formatAsString(), labelCell.getRowIndex(), labelCell.getColumnIndex(), mergedRegion, value); + setStringCellValueForce(sheet, labelCell.getRowIndex(), labelCell.getColumnIndex(), value); + return; + } + CellRangeAddress mergedRegion = getMergedRegion(sheet, fallbackRowIndex, fallbackColumnIndex); + log.info("[production-daily-report] set header by fallback: label={}, fallbackRowIndex={}, fallbackColumnIndex={}, mergedRegion={}, value={}", + label, fallbackRowIndex, fallbackColumnIndex, mergedRegion, value); + setStringCellValueForce(sheet, fallbackRowIndex, fallbackColumnIndex, value); + } + + private void setStringCellValueForce(Sheet sheet, int rowIndex, int columnIndex, String value) { + CellRangeAddress mergedRegion = getMergedRegion(sheet, rowIndex, columnIndex); + int firstRowIndex = mergedRegion != null ? mergedRegion.getFirstRow() : rowIndex; + int firstColumnIndex = mergedRegion != null ? mergedRegion.getFirstColumn() : columnIndex; + Row row = sheet.getRow(firstRowIndex); + if (row == null) { + row = sheet.createRow(firstRowIndex); + } + + Cell oldCell = row.getCell(firstColumnIndex); + org.apache.poi.ss.usermodel.CellStyle cellStyle = oldCell != null ? oldCell.getCellStyle() : null; + if (oldCell != null) { + row.removeCell(oldCell); + } + Cell newCell = row.createCell(firstColumnIndex, CellType.STRING); + if (cellStyle != null) { + newCell.setCellStyle(cellStyle); + } + newCell.setCellValue(value); + + if (mergedRegion != null) { + for (int column = mergedRegion.getFirstColumn() + 1; column <= mergedRegion.getLastColumn(); column++) { + Cell mergedCell = row.getCell(column); + if (mergedCell != null) { + mergedCell.setCellValue(""); + } + } + } + log.info("[production-daily-report] force set header cell: targetRowIndex={}, targetColumnIndex={}, mergedRegion={}, value={}, actualValue={}", + firstRowIndex, firstColumnIndex, mergedRegion, value, getCellValue(sheet, firstRowIndex, firstColumnIndex)); + } + + @SneakyThrows + private void logGeneratedHeader(byte[] bytes) { + try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(bytes))) { + Sheet sheet = workbook.getSheetAt(0); + log.info("[production-daily-report] generated bytes header check: A3={}, C3={}, E3={}, mergedRegions={}", + getCellValue(sheet, 2, 0), getCellValue(sheet, 2, 2), getCellValue(sheet, 2, 4), sheet.getMergedRegions()); + } + } + + private String getCellValue(Sheet sheet, int rowIndex, int columnIndex) { + Row row = sheet.getRow(rowIndex); + Cell cell = row != null ? row.getCell(columnIndex) : null; + if (cell == null) { + CellRangeAddress mergedRegion = getMergedRegion(sheet, rowIndex, columnIndex); + if (mergedRegion != null) { + row = sheet.getRow(mergedRegion.getFirstRow()); + cell = row != null ? row.getCell(mergedRegion.getFirstColumn()) : null; + } + } + if (row == null) { + return null; + } + if (cell == null) { + return null; + } + if (cell.getCellType() == CellType.STRING) { + return cell.getStringCellValue(); + } + if (cell.getCellType() == CellType.NUMERIC) { + return String.valueOf(cell.getNumericCellValue()); + } + if (cell.getCellType() == CellType.BOOLEAN) { + return String.valueOf(cell.getBooleanCellValue()); + } + return cell.toString(); + } + + private Cell findCellByPrefix(Sheet sheet, String label) { + for (Row row : sheet) { + for (Cell cell : row) { + if (cell.getCellType() == CellType.STRING && StringUtils.startsWith(cell.getStringCellValue(), label)) { + return cell; + } + } + } + return null; + } + + private CellRangeAddress getMergedRegion(Sheet sheet, int rowIndex, int columnIndex) { + for (CellRangeAddress mergedRegion : sheet.getMergedRegions()) { + if (mergedRegion.isInRange(rowIndex, columnIndex)) { + return mergedRegion; + } + } + return null; + } } 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 8c5409b1f..5c579f585 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 @@ -2662,6 +2662,40 @@ public class TDengineService { } } + /** + * 查询设备在指定时间段内的告警记录 + */ + @DS("tdengine") + public List selectDeviceWarningList(List deviceIds, LocalDateTime startTime, LocalDateTime endTime) { + if (CollectionUtils.isEmpty(deviceIds)) { + return Collections.emptyList(); + } + + String placeholders = deviceIds.stream().map(item -> "?").collect(Collectors.joining(",")); + StringBuilder sql = new StringBuilder(); + List params = new ArrayList<>(); + sql.append("SELECT * FROM ").append(tdengineTable("iot_device_warning_record")).append(" ") + .append("WHERE deleted = 0 ") + .append("AND device_id IN (").append(placeholders).append(") "); + params.addAll(deviceIds); + if (startTime != null) { + sql.append("AND create_time >= ? "); + params.add(Timestamp.valueOf(startTime)); + } + if (endTime != null) { + sql.append("AND create_time <= ? "); + params.add(Timestamp.valueOf(endTime)); + } + sql.append("ORDER BY device_id ASC, create_time ASC"); + + try { + return jdbcTemplate.query(sql.toString(), params.toArray(), (rs, rowNum) -> mapRowToWarningRecordDO(rs)); + } catch (Exception e) { + log.error("查询设备时间段告警记录失败: deviceIds={}, startTime={}, endTime={}", deviceIds, startTime, endTime, e); + return Collections.emptyList(); + } + } + /** * 映射查询结果 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/excel/templates/fully-automatic-molding-production-daily-report-template.xlsx b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/excel/templates/fully-automatic-molding-production-daily-report-template.xlsx index ab81f204d592b48d5a8ee08582f9b6ac7de9f380..551e70a49383fdba9a0248f1b398e557980eb03a 100644 GIT binary patch delta 3463 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 delta 6229 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 Date: Mon, 27 Jul 2026 17:51:28 +0800 Subject: [PATCH 8/8] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E4=BA=A7=E8=83=BD?= =?UTF-8?q?=E6=8A=A5=E8=A1=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...eviceProductionDailyReportServiceImpl.java | 93 ++++++++++++++++--- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportServiceImpl.java index 7d5795e95..8d0a79a49 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportServiceImpl.java @@ -229,6 +229,7 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD Workbook workbook = WorkbookFactory.create(inputStream); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { Sheet sheet = workbook.getSheetAt(0); + setReportColumnWidths(sheet); log.info("[production-daily-report] template loaded: path={}, sheetName={}, mergedRegions={}", TEMPLATE_PATH, sheet.getSheetName(), sheet.getMergedRegions()); String dateText = "日期:" + reqVO.getReportDate().format(DATE_FORMATTER); @@ -252,7 +253,7 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD } DeviceProductionDailyReportRespVO.DeviceCapacityItem item = items.get(index); setCellValue(row, 0, index + 1); - setCellValue(row, 1, StrUtil.blankToDefault(item.getDeviceCode(), item.getDeviceName())); + setCellValue(row, 1, buildDeviceText(item.getDeviceName(), item.getDeviceCode())); setCellValue(row, 2, buildCapacityPointText(item)); setCellValue(row, 3, item.getCapacityDiff()); setCellValue(row, 4, startTime.toLocalTime() + "-" + endTime.toLocalTime()); @@ -270,22 +271,32 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD List warningRecords = tDengineService.selectDeviceWarningList(deviceIds, startTime, endTime); Map> warningMap = warningRecords.stream() .collect(Collectors.groupingBy(DeviceWarinningRecordDO::getDeviceId, LinkedHashMap::new, Collectors.toList())); + Map alarmLevelLabelMap = buildAlarmLevelLabelMap(); Row templateRow = sheet.getRow(warningRowIndex); for (int index = 0; index < deviceIds.size(); index++) { Row row = sheet.getRow(warningRowIndex + index); if (row == null) { row = sheet.createRow(warningRowIndex + index); copyRowStyle(templateRow, row, 5); - mergeCellsIfAbsent(sheet, warningRowIndex + index, 2, 4); } + mergeCellsIfAbsent(sheet, warningRowIndex + index, 2, 4); Long deviceId = deviceIds.get(index); setCellValue(row, 0, index + 1); setCellValue(row, 1, getDeviceText(deviceId)); - String warningDescription = buildWarningDescription(warningMap.getOrDefault(deviceId, Collections.emptyList())); + String warningDescription = buildWarningDescription(warningMap.getOrDefault(deviceId, Collections.emptyList()), alarmLevelLabelMap); setCellValue(row, 2, warningDescription); setWrapText(row, 2); row.setHeightInPoints(Math.max(row.getHeightInPoints(), 18F * Math.max(1, warningDescription.split("\n", -1).length))); } + removeRowsFromEnd(sheet, warningRowIndex + deviceIds.size()); + } + + private void setReportColumnWidths(Sheet sheet) { + sheet.setColumnWidth(0, 8 * 256); + sheet.setColumnWidth(1, 32 * 256); + sheet.setColumnWidth(2, 52 * 256); + sheet.setColumnWidth(3, 14 * 256); + sheet.setColumnWidth(4, 22 * 256); } private String buildCapacityPointText(DeviceProductionDailyReportRespVO.DeviceCapacityItem item) { @@ -305,25 +316,35 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD if (device == null) { return ""; } - return StrUtil.blankToDefault(device.getDeviceCode(), device.getDeviceName()); + return buildDeviceText(device.getDeviceName(), device.getDeviceCode()); } - private String buildWarningDescription(List warningRecords) { + private String buildDeviceText(String deviceName, String deviceCode) { + if (StringUtils.isBlank(deviceName)) { + return StrUtil.blankToDefault(deviceCode, ""); + } + if (StringUtils.isBlank(deviceCode)) { + return deviceName; + } + return deviceName + "(" + deviceCode + ")"; + } + + private String buildWarningDescription(List warningRecords, Map alarmLevelLabelMap) { if (CollUtil.isEmpty(warningRecords)) { return ""; } return warningRecords.stream() .collect(Collectors.groupingBy(this::getWarningPointName, LinkedHashMap::new, Collectors.toList())) .entrySet().stream() - .map(entry -> entry.getKey() + ":" + buildWarningPointSummary(entry.getValue())) + .map(entry -> entry.getKey() + ":" + buildWarningPointSummary(entry.getValue(), alarmLevelLabelMap)) .collect(Collectors.joining("\n")); } - private String buildWarningPointSummary(List warningRecords) { + private String buildWarningPointSummary(List warningRecords, Map alarmLevelLabelMap) { return warningRecords.stream() .collect(Collectors.groupingBy(this::getWarningRuleKey, LinkedHashMap::new, Collectors.toList())) .values().stream() - .map(this::buildWarningRuleSummary) + .map(records -> buildWarningRuleSummary(records, alarmLevelLabelMap)) .collect(Collectors.joining(";")); } @@ -331,19 +352,46 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD return getWarningName(record) + "|" + StrUtil.blankToDefault(record.getAlarmLevel(), ""); } - private String buildWarningRuleSummary(List warningRecords) { + private String buildWarningRuleSummary(List warningRecords, Map alarmLevelLabelMap) { DeviceWarinningRecordDO firstRecord = warningRecords.get(0); - String levelText = buildAlarmLevelText(firstRecord.getAlarmLevel()); + String levelText = buildAlarmLevelText(firstRecord.getAlarmLevel(), alarmLevelLabelMap); return getWarningName(firstRecord) + warningRecords.size() + "次" + buildWarningValueRangeText(warningRecords) + levelText + buildWarningTimeRangeText(warningRecords); } - private String buildAlarmLevelText(String alarmLevel) { + private String buildAlarmLevelText(String alarmLevel, Map alarmLevelLabelMap) { if (StringUtils.isBlank(alarmLevel)) { return ""; } - DictDataRespDTO dictData = dictDataApi.getDictData(ALARM_LEVEL_DICT_TYPE, alarmLevel); - return ",等级=" + (dictData != null && StringUtils.isNotBlank(dictData.getLabel()) ? dictData.getLabel() : alarmLevel); + String alarmLevelLabel = alarmLevelLabelMap.get(normalizeAlarmLevel(alarmLevel)); + return ",等级=" + StrUtil.blankToDefault(alarmLevelLabel, alarmLevel); + } + + private Map buildAlarmLevelLabelMap() { + List dictDataList = dictDataApi.getDictDataList(ALARM_LEVEL_DICT_TYPE); + if (CollUtil.isEmpty(dictDataList)) { + return Collections.emptyMap(); + } + Map alarmLevelLabelMap = new LinkedHashMap<>(); + for (DictDataRespDTO dictData : dictDataList) { + if (dictData == null || StringUtils.isBlank(dictData.getValue())) { + continue; + } + alarmLevelLabelMap.put(normalizeAlarmLevel(dictData.getValue()), dictData.getLabel()); + } + return alarmLevelLabelMap; + } + + private String normalizeAlarmLevel(String alarmLevel) { + String value = StringUtils.trimToEmpty(alarmLevel); + if (StringUtils.isBlank(value)) { + return ""; + } + try { + return BigDecimal.valueOf(Double.parseDouble(value)).stripTrailingZeros().toPlainString(); + } catch (NumberFormatException e) { + return value; + } } private String getWarningPointName(DeviceWarinningRecordDO record) { @@ -572,6 +620,25 @@ public class DeviceProductionDailyReportServiceImpl implements DeviceProductionD sheet.addMergedRegion(newRegion); } + private void removeRowsFromEnd(Sheet sheet, int startRowIndex) { + int lastRowIndex = sheet.getLastRowNum(); + if (startRowIndex > lastRowIndex) { + return; + } + for (int index = sheet.getNumMergedRegions() - 1; index >= 0; index--) { + CellRangeAddress mergedRegion = sheet.getMergedRegion(index); + if (mergedRegion.getFirstRow() >= startRowIndex) { + sheet.removeMergedRegion(index); + } + } + for (int rowIndex = lastRowIndex; rowIndex >= startRowIndex; rowIndex--) { + Row row = sheet.getRow(rowIndex); + if (row != null) { + sheet.removeRow(row); + } + } + } + private void setHeaderValue(Sheet sheet, String label, String value, int fallbackRowIndex, int fallbackColumnIndex) { Cell labelCell = findCellByPrefix(sheet, label); if (labelCell != null) {