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() 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..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 @@ -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,238 @@ 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; + 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); + 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, 1, buildDeviceText(item.getDeviceName(), item.getDeviceCode())); + 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())); + 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); + Long deviceId = deviceIds.get(index); + setCellValue(row, 0, index + 1); + setCellValue(row, 1, getDeviceText(deviceId)); + 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) { + 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 buildDeviceText(device.getDeviceName(), device.getDeviceCode()); + } + + 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(), alarmLevelLabelMap)) + .collect(Collectors.joining("\n")); + } + + private String buildWarningPointSummary(List warningRecords, Map alarmLevelLabelMap) { + return warningRecords.stream() + .collect(Collectors.groupingBy(this::getWarningRuleKey, LinkedHashMap::new, Collectors.toList())) + .values().stream() + .map(records -> buildWarningRuleSummary(records, alarmLevelLabelMap)) + .collect(Collectors.joining(";")); + } + + private String getWarningRuleKey(DeviceWarinningRecordDO record) { + return getWarningName(record) + "|" + StrUtil.blankToDefault(record.getAlarmLevel(), ""); + } + + private String buildWarningRuleSummary(List warningRecords, Map alarmLevelLabelMap) { + DeviceWarinningRecordDO firstRecord = warningRecords.get(0); + String levelText = buildAlarmLevelText(firstRecord.getAlarmLevel(), alarmLevelLabelMap); + return getWarningName(firstRecord) + warningRecords.size() + "次" + + buildWarningValueRangeText(warningRecords) + levelText + buildWarningTimeRangeText(warningRecords); + } + + private String buildAlarmLevelText(String alarmLevel, Map alarmLevelLabelMap) { + if (StringUtils.isBlank(alarmLevel)) { + return ""; + } + 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) { + 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 +557,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 +580,167 @@ 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 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) { + 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 ab81f204d..551e70a49 100644 Binary files a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/excel/templates/fully-automatic-molding-production-daily-report-template.xlsx and b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/excel/templates/fully-automatic-molding-production-daily-report-template.xlsx differ 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/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)); } 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; 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) { 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)); } 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); }