diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml
index 3284ebf3e..b9cad06f0 100644
--- a/yudao-module-iot/yudao-module-iot-biz/pom.xml
+++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml
@@ -29,6 +29,12 @@
${revision}
+
+ cn.iocoder.boot
+ yudao-module-mes-api
+ ${revision}
+
+
cn.iocoder.boot
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java
index e8da0afd6..d02bc0e3d 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/DeviceController.java
@@ -16,6 +16,7 @@ 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.framework.mqtt.consumer.IMqttservice;
import cn.iocoder.yudao.module.iot.service.device.DeviceService;
+import cn.iocoder.yudao.module.iot.service.device.DeviceProductionDailyReportService;
import cn.iocoder.yudao.module.iot.service.device.TDengineService;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.v3.oas.annotations.Operation;
@@ -55,6 +56,8 @@ public class DeviceController {
@Resource
private TDengineService tDengineService;
@Resource
+ private DeviceProductionDailyReportService deviceProductionDailyReportService;
+ @Resource
private JobService jobService;
// @Resource
@@ -140,6 +143,24 @@ public class DeviceController {
// 导出 Excel
ExcelUtils.write(response, "物联设备.xls", "数据", DeviceRespVO.class,list);
}
+
+ @PostMapping("/production-daily-report/generate")
+ @Operation(summary = "生成设备生产日报 Excel")
+ @PreAuthorize("@ss.hasPermission('iot:device:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public CommonResult generateProductionDailyReport(
+ @Valid @RequestBody DeviceProductionDailyReportGenerateReqVO reqVO) {
+ return success(deviceProductionDailyReportService.generateReport(reqVO));
+ }
+
+ @GetMapping("/production-daily-report/page")
+ @Operation(summary = "获得设备生产日报 Excel 分页")
+ @PreAuthorize("@ss.hasPermission('iot:device:query')")
+ public CommonResult> getProductionDailyReportPage(
+ @Valid DeviceProductionDailyReportPageReqVO reqVO) {
+ return success(deviceProductionDailyReportService.getReportPage(reqVO));
+ }
+
@GetMapping("/deviceList")
// @PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult> deviceList(@Valid DevicePageReqVO pageReqVO) {
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceProductionDailyReportGenerateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceProductionDailyReportGenerateReqVO.java
new file mode 100644
index 000000000..ed84e3194
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceProductionDailyReportGenerateReqVO.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDate;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "管理后台 - 设备生产日报生成 Request VO")
+@Data
+public class DeviceProductionDailyReportGenerateReqVO {
+
+ @Schema(description = "设备 ID 列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]")
+ @NotEmpty(message = "设备 ID 不能为空")
+ private List deviceIds;
+
+ @Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-07-23")
+ @NotNull(message = "日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate reportDate;
+
+ @Schema(description = "班次 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "班次 ID 不能为空")
+ private Long workTeamId;
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceProductionDailyReportPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceProductionDailyReportPageReqVO.java
new file mode 100644
index 000000000..8cc315517
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceProductionDailyReportPageReqVO.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "管理后台 - 设备生产日报分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DeviceProductionDailyReportPageReqVO extends PageParam {
+
+ @Schema(description = "日期", example = "2026-07-23")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate reportDate;
+
+ @Schema(description = "班次 ID", example = "1")
+ private Long workTeamId;
+
+ @Schema(description = "设备 ID,按生成时的设备集合筛选", example = "1")
+ private Long deviceId;
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceProductionDailyReportRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceProductionDailyReportRespVO.java
new file mode 100644
index 000000000..aec4e084e
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/DeviceProductionDailyReportRespVO.java
@@ -0,0 +1,69 @@
+package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - 设备生产日报 Response VO")
+@Data
+public class DeviceProductionDailyReportRespVO {
+
+ @Schema(description = "文件名")
+ private String fileName;
+
+ @Schema(description = "文件访问地址")
+ private String fileUrl;
+
+ @Schema(description = "文件路径")
+ private String filePath;
+
+ @Schema(description = "日期")
+ private LocalDate reportDate;
+
+ @Schema(description = "班次 ID")
+ private Long workTeamId;
+
+ @Schema(description = "班次名称")
+ private String workTeamName;
+
+ @Schema(description = "查询开始时间")
+ private LocalDateTime startTime;
+
+ @Schema(description = "查询结束时间")
+ private LocalDateTime endTime;
+
+ @Schema(description = "设备 ID 列表")
+ private List deviceIds;
+
+ @Schema(description = "设备产能明细")
+ private List devices;
+
+ @Schema(description = "生成时间")
+ private LocalDateTime createTime;
+
+ @Data
+ public static class DeviceCapacityItem {
+
+ @Schema(description = "设备 ID")
+ private Long deviceId;
+ @Schema(description = "设备编码")
+ private String deviceCode;
+ @Schema(description = "设备名称")
+ private String deviceName;
+ @Schema(description = "产能点位编码")
+ private String capacityCode;
+ @Schema(description = "产能点位名称")
+ private String capacityName;
+ @Schema(description = "开始值")
+ private Double startValue;
+ @Schema(description = "结束值")
+ private Double endValue;
+ @Schema(description = "差值")
+ private Double capacityDiff;
+ @Schema(description = "单位")
+ private String unit;
+ }
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportService.java
new file mode 100644
index 000000000..3ad8f2cb2
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportService.java
@@ -0,0 +1,13 @@
+package cn.iocoder.yudao.module.iot.service.device;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceProductionDailyReportGenerateReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceProductionDailyReportPageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceProductionDailyReportRespVO;
+
+public interface DeviceProductionDailyReportService {
+
+ DeviceProductionDailyReportRespVO generateReport(DeviceProductionDailyReportGenerateReqVO reqVO);
+
+ PageResult getReportPage(DeviceProductionDailyReportPageReqVO reqVO);
+}
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
new file mode 100644
index 000000000..6d82107bd
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceProductionDailyReportServiceImpl.java
@@ -0,0 +1,365 @@
+package cn.iocoder.yudao.module.iot.service.device;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper;
+import cn.iocoder.yudao.module.infra.service.file.FileService;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceProductionDailyReportGenerateReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceProductionDailyReportPageReqVO;
+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.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 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.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_NOT_EXISTS;
+
+@Service
+@Validated
+@Slf4j
+public class DeviceProductionDailyReportServiceImpl implements DeviceProductionDailyReportService {
+
+ 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 DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+ @Resource
+ private DeviceMapper deviceMapper;
+ @Resource
+ private DeviceContactModelMapper deviceContactModelMapper;
+ @Resource
+ private TDengineService tDengineService;
+ @Resource
+ private WorkTeamApi workTeamApi;
+ @Resource
+ private FileService fileService;
+ @Resource
+ private FileMapper fileMapper;
+
+ @Override
+ public DeviceProductionDailyReportRespVO generateReport(DeviceProductionDailyReportGenerateReqVO reqVO) {
+ List deviceIds = reqVO.getDeviceIds().stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
+ if (CollUtil.isEmpty(deviceIds)) {
+ throw exception(DEVICE_NOT_EXISTS);
+ }
+
+ Map deviceMap = deviceMapper.selectBatchIds(deviceIds).stream()
+ .collect(Collectors.toMap(DeviceDO::getId, item -> item));
+ if (deviceMap.size() != deviceIds.size()) {
+ throw exception(DEVICE_NOT_EXISTS);
+ }
+
+ WorkTeamRespDTO workTeam = workTeamApi.getWorkTeam(reqVO.getWorkTeamId());
+ if (workTeam == null || workTeam.getStartTime() == null || workTeam.getEndTime() == null) {
+ throw new IllegalArgumentException("Work team does not exist or start/end time is not configured");
+ }
+
+ LocalDateTime startTime = combineShiftTime(reqVO.getReportDate(), workTeam.getStartTime());
+ LocalDateTime endTime = combineShiftTime(reqVO.getReportDate(), workTeam.getEndTime());
+ if (!endTime.isAfter(startTime)) {
+ endTime = endTime.plusDays(1);
+ }
+
+ List items = buildCapacityItems(deviceIds, deviceMap, startTime, endTime);
+ byte[] excelContent = buildExcel(reqVO, workTeam, startTime, endTime, items);
+
+ 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);
+
+ DeviceProductionDailyReportRespVO respVO = buildBaseResp(reqVO.getReportDate(), workTeam, startTime, endTime, deviceIds);
+ respVO.setFileName(file.get("fileName"));
+ respVO.setFileUrl(file.get("fileUrl"));
+ respVO.setFilePath(filePath);
+ respVO.setDevices(items);
+ respVO.setCreateTime(LocalDateTime.now());
+ return respVO;
+ }
+
+ @Override
+ public PageResult getReportPage(DeviceProductionDailyReportPageReqVO reqVO) {
+ LambdaQueryWrapperX queryWrapper = new LambdaQueryWrapperX<>();
+ queryWrapper.like(FileDO::getPath, REPORT_PATH_PREFIX);
+ queryWrapper.orderByDesc(FileDO::getId);
+ if (reqVO.getReportDate() != null) {
+ queryWrapper.like(FileDO::getPath, "/date=" + reqVO.getReportDate() + "/");
+ }
+ if (reqVO.getWorkTeamId() != null) {
+ queryWrapper.like(FileDO::getPath, "/workTeamId=" + reqVO.getWorkTeamId() + "/");
+ }
+ if (reqVO.getDeviceId() != null) {
+ queryWrapper.like(FileDO::getPath, buildDeviceTag(reqVO.getDeviceId()));
+ }
+
+ PageResult pageResult = fileMapper.selectPage(reqVO, queryWrapper);
+ List list = pageResult.getList().stream()
+ .map(this::convertFileToReport)
+ .collect(Collectors.toList());
+ return new PageResult<>(list, pageResult.getTotal());
+ }
+
+ private List buildCapacityItems(List deviceIds,
+ Map deviceMap,
+ LocalDateTime startTime,
+ LocalDateTime endTime) {
+ Map> capacityPointMap = queryCapacityPoints(deviceIds);
+ Map> firstRows = tDengineService.queryDevicesEarliestRow(deviceIds,
+ startTime.format(DATE_TIME_FORMATTER), endTime.format(DATE_TIME_FORMATTER));
+ Map> lastRows = tDengineService.queryDevicesLatestRow(deviceIds,
+ startTime.format(DATE_TIME_FORMATTER), endTime.format(DATE_TIME_FORMATTER));
+
+ List items = new ArrayList<>();
+ for (Long deviceId : deviceIds) {
+ DeviceDO device = deviceMap.get(deviceId);
+ List points = capacityPointMap.getOrDefault(deviceId, Collections.emptyList());
+ if (CollUtil.isEmpty(points)) {
+ 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));
+ }
+ }
+ return items;
+ }
+
+ 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, "产能"))
+ .orderByAsc(DeviceContactModelDO::getDeviceId)
+ .orderByAsc(DeviceContactModelDO::getSort)
+ .orderByAsc(DeviceContactModelDO::getId));
+ return points.stream().collect(Collectors.groupingBy(DeviceContactModelDO::getDeviceId, LinkedHashMap::new, Collectors.toList()));
+ }
+
+ private DeviceProductionDailyReportRespVO.DeviceCapacityItem buildEmptyCapacityItem(DeviceDO device) {
+ DeviceProductionDailyReportRespVO.DeviceCapacityItem item = new DeviceProductionDailyReportRespVO.DeviceCapacityItem();
+ item.setDeviceId(device.getId());
+ item.setDeviceCode(device.getDeviceCode());
+ item.setDeviceName(device.getDeviceName());
+ item.setCapacityDiff(0D);
+ return item;
+ }
+
+ private DeviceProductionDailyReportRespVO.DeviceCapacityItem buildCapacityItem(DeviceDO device, DeviceContactModelDO point,
+ Map firstRow, Map lastRow) {
+ DeviceProductionDailyReportRespVO.DeviceCapacityItem item = new DeviceProductionDailyReportRespVO.DeviceCapacityItem();
+ item.setDeviceId(device.getId());
+ item.setDeviceCode(device.getDeviceCode());
+ item.setDeviceName(device.getDeviceName());
+ item.setCapacityCode(point.getAttributeCode());
+ item.setCapacityName(point.getAttributeName());
+ item.setUnit(point.getDataUnit());
+
+ Double firstValue = getNumberValue(firstRow, point.getAttributeCode());
+ Double lastValue = getNumberValue(lastRow, point.getAttributeCode());
+ item.setStartValue(firstValue);
+ item.setEndValue(lastValue);
+ if (firstValue != null && lastValue != null) {
+ item.setCapacityDiff(BigDecimal.valueOf(lastValue).subtract(BigDecimal.valueOf(firstValue))
+ .setScale(3, RoundingMode.HALF_UP).doubleValue());
+ } else {
+ item.setCapacityDiff(0D);
+ }
+ return item;
+ }
+
+ @SneakyThrows
+ private byte[] buildExcel(DeviceProductionDailyReportGenerateReqVO reqVO, WorkTeamRespDTO workTeam,
+ LocalDateTime startTime, LocalDateTime endTime,
+ List items) {
+ try (InputStream inputStream = new ClassPathResource(TEMPLATE_PATH).getInputStream();
+ 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;
+ 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);
+ }
+ 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());
+ }
+ workbook.write(outputStream);
+ return outputStream.toByteArray();
+ }
+ }
+
+ private DeviceProductionDailyReportRespVO convertFileToReport(FileDO file) {
+ DeviceProductionDailyReportRespVO respVO = new DeviceProductionDailyReportRespVO();
+ respVO.setFileName(file.getName());
+ respVO.setFileUrl(file.getUrl());
+ respVO.setFilePath(file.getPath());
+ respVO.setCreateTime(file.getCreateTime());
+ parsePathMetadata(file.getPath(), respVO);
+ if (respVO.getWorkTeamId() != null) {
+ WorkTeamRespDTO workTeam = workTeamApi.getWorkTeam(respVO.getWorkTeamId());
+ if (workTeam != null) {
+ respVO.setWorkTeamName(workTeam.getTeamName());
+ }
+ }
+ return respVO;
+ }
+
+ private void parsePathMetadata(String path, DeviceProductionDailyReportRespVO respVO) {
+ if (StringUtils.isBlank(path)) {
+ return;
+ }
+ for (String segment : path.split("/")) {
+ if (segment.startsWith("date=")) {
+ respVO.setReportDate(LocalDate.parse(segment.substring("date=".length())));
+ } else if (segment.startsWith("workTeamId=")) {
+ respVO.setWorkTeamId(Long.valueOf(segment.substring("workTeamId=".length())));
+ } else if (segment.startsWith("deviceIds=")) {
+ List deviceIds = new ArrayList<>();
+ for (String id : segment.substring("deviceIds=".length()).split("_")) {
+ if (StringUtils.isNumeric(id)) {
+ deviceIds.add(Long.valueOf(id));
+ }
+ }
+ respVO.setDeviceIds(deviceIds);
+ }
+ }
+ }
+
+ private DeviceProductionDailyReportRespVO buildBaseResp(LocalDate reportDate, WorkTeamRespDTO workTeam,
+ LocalDateTime startTime, LocalDateTime endTime, List deviceIds) {
+ DeviceProductionDailyReportRespVO respVO = new DeviceProductionDailyReportRespVO();
+ respVO.setReportDate(reportDate);
+ respVO.setWorkTeamId(workTeam.getId());
+ respVO.setWorkTeamName(workTeam.getTeamName());
+ respVO.setStartTime(startTime);
+ respVO.setEndTime(endTime);
+ respVO.setDeviceIds(deviceIds);
+ return respVO;
+ }
+
+ private LocalDateTime combineShiftTime(LocalDate date, LocalDateTime workTeamTime) {
+ return LocalDateTime.of(date, LocalTime.from(workTeamTime));
+ }
+
+ private Double getNumberValue(Map row, String code) {
+ if (row == null || StringUtils.isBlank(code)) {
+ return null;
+ }
+ Object value = row.get(code);
+ if (value == null) {
+ value = row.get(code.toLowerCase());
+ }
+ if (value instanceof Number) {
+ return ((Number) value).doubleValue();
+ }
+ if (value != null && StringUtils.isNotBlank(value.toString())) {
+ try {
+ return Double.valueOf(value.toString());
+ } catch (NumberFormatException e) {
+ log.warn("capacity point value is not numeric: code={}, value={}", code, value);
+ }
+ }
+ return null;
+ }
+
+ private String buildShiftText(WorkTeamRespDTO workTeam) {
+ if (StringUtils.isNotBlank(workTeam.getGroupType())) {
+ return workTeam.getGroupType();
+ }
+ return StrUtil.blankToDefault(workTeam.getTeamName(), "Shift:");
+ }
+
+ private Double toHour(LocalDateTime startTime, LocalDateTime endTime) {
+ return BigDecimal.valueOf(java.time.Duration.between(startTime, endTime).toMinutes())
+ .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP).doubleValue();
+ }
+
+ private String joinDeviceIds(List deviceIds) {
+ return deviceIds.stream().sorted(Comparator.naturalOrder()).map(String::valueOf).collect(Collectors.joining("_"));
+ }
+
+ private String buildDeviceTags(List deviceIds) {
+ return deviceIds.stream().sorted(Comparator.naturalOrder()).map(this::buildDeviceTag).collect(Collectors.joining(""));
+ }
+
+ private String buildDeviceTag(Long deviceId) {
+ return "_" + deviceId + "_";
+ }
+
+ private void setCellValue(Sheet sheet, int rowIndex, int columnIndex, Object value) {
+ Row row = sheet.getRow(rowIndex);
+ if (row == null) {
+ row = sheet.createRow(rowIndex);
+ }
+ setCellValue(row, columnIndex, value);
+ }
+
+ private void setCellValue(Row row, int columnIndex, Object value) {
+ Cell cell = row.getCell(columnIndex);
+ if (cell == null) {
+ cell = row.createCell(columnIndex);
+ }
+ if (value instanceof Number) {
+ cell.setCellValue(((Number) value).doubleValue());
+ } else {
+ cell.setCellValue(value == null ? "" : value.toString());
+ }
+ }
+}
+
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
new file mode 100644
index 000000000..ab81f204d
Binary files /dev/null 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-api/src/main/java/cn/iocoder/yudao/module/mes/api/workteam/WorkTeamApi.java b/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/workteam/WorkTeamApi.java
new file mode 100644
index 000000000..e1c0921aa
--- /dev/null
+++ b/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/workteam/WorkTeamApi.java
@@ -0,0 +1,9 @@
+package cn.iocoder.yudao.module.mes.api.workteam;
+
+import cn.iocoder.yudao.module.mes.api.workteam.dto.WorkTeamRespDTO;
+
+public interface WorkTeamApi {
+
+ WorkTeamRespDTO getWorkTeam(Long id);
+
+}
diff --git a/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/workteam/dto/WorkTeamRespDTO.java b/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/workteam/dto/WorkTeamRespDTO.java
new file mode 100644
index 000000000..8155af3fd
--- /dev/null
+++ b/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/workteam/dto/WorkTeamRespDTO.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.mes.api.workteam.dto;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class WorkTeamRespDTO {
+
+ private Long id;
+
+ private String teamName;
+
+ private String groupType;
+
+ private LocalDateTime startTime;
+
+ private LocalDateTime endTime;
+}
diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/api/workteam/WorkTeamApiImpl.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/api/workteam/WorkTeamApiImpl.java
new file mode 100644
index 000000000..19de3958c
--- /dev/null
+++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/api/workteam/WorkTeamApiImpl.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.mes.api.workteam;
+
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.mes.api.workteam.dto.WorkTeamRespDTO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.workteam.WorkTeamDO;
+import cn.iocoder.yudao.module.mes.service.workteam.WorkTeamService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+@Service
+public class WorkTeamApiImpl implements WorkTeamApi {
+
+ @Resource
+ private WorkTeamService workTeamService;
+
+ @Override
+ public WorkTeamRespDTO getWorkTeam(Long id) {
+ WorkTeamDO workTeam = workTeamService.getWorkTeam(id);
+ return BeanUtils.toBean(workTeam, WorkTeamRespDTO.class);
+ }
+}
diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/workteam/WorkTeamController.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/workteam/WorkTeamController.java
index c0b9d81a6..359d428a9 100644
--- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/workteam/WorkTeamController.java
+++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/workteam/WorkTeamController.java
@@ -80,6 +80,14 @@ public class WorkTeamController {
return success(pageResult);
}
+ @GetMapping("/list")
+ @Operation(summary = "获得生产班组列表")
+ @PreAuthorize("@ss.hasPermission('mes:work-team:query')")
+ public CommonResult> getWorkTeamList(@Valid WorkTeamPageReqVO pageReqVO) {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ return success(workTeamService.getWorkTeamPage(pageReqVO).getList());
+ }
+
@GetMapping("/export-excel")
@Operation(summary = "导出生产班组 Excel")
@PreAuthorize("@ss.hasPermission('mes:work-team:export')")
@@ -148,4 +156,4 @@ public class WorkTeamController {
public CommonResult> getUserList2(@RequestParam("orgId") Long orgId,String groupType) {
return success(workTeamService.getUserList2(orgId,groupType));
}
-}
\ No newline at end of file
+}