diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/MesHomeStatisticsController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/MesHomeStatisticsController.java new file mode 100644 index 0000000..9653601 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/MesHomeStatisticsController.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.mes.controller.admin.statistics; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.mes.controller.admin.statistics.vo.MesHomeProductionTrendRespVO; +import cn.iocoder.yudao.module.mes.controller.admin.statistics.vo.MesHomeSummaryRespVO; +import cn.iocoder.yudao.module.mes.controller.admin.statistics.vo.MesHomeWorkOrderStatusRespVO; +import cn.iocoder.yudao.module.mes.service.statistics.MesHomeStatisticsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - MES 首页统计") +@RestController +@RequestMapping("/mes/home-statistics") +@Validated +public class MesHomeStatisticsController { + + @Resource + private MesHomeStatisticsService homeStatisticsService; + + @GetMapping("/summary") + @Operation(summary = "获得首页汇总统计") + @PreAuthorize("@ss.hasPermission('mes:statistics:query')") + public CommonResult getHomeSummary() { + return success(homeStatisticsService.getHomeSummary()); + } + + @GetMapping("/work-order-status") + @Operation(summary = "获得工单状态分布") + @PreAuthorize("@ss.hasPermission('mes:statistics:query')") + public CommonResult> getWorkOrderStatusDistribution() { + return success(homeStatisticsService.getWorkOrderStatusDistribution()); + } + + @GetMapping("/production-trend") + @Operation(summary = "获得生产趋势") + @Parameter(name = "days", description = "天数", example = "7") + @PreAuthorize("@ss.hasPermission('mes:statistics:query')") + public CommonResult> getProductionTrend( + @RequestParam(value = "days", defaultValue = "7") Integer days) { + return success(homeStatisticsService.getProductionTrend(days)); + } + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/vo/MesHomeProductionTrendRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/vo/MesHomeProductionTrendRespVO.java new file mode 100644 index 0000000..c6cf044 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/vo/MesHomeProductionTrendRespVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.mes.controller.admin.statistics.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - MES 首页生产趋势 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MesHomeProductionTrendRespVO { + + @Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-04-05") + private String date; + + @Schema(description = "产量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234") + private BigDecimal quantity; + + @Schema(description = "合格品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200") + private BigDecimal qualifiedQuantity; + + @Schema(description = "不良品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "34") + private BigDecimal unqualifiedQuantity; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/vo/MesHomeSummaryRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/vo/MesHomeSummaryRespVO.java new file mode 100644 index 0000000..d442f6c --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/vo/MesHomeSummaryRespVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.mes.controller.admin.statistics.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Schema(description = "管理后台 - MES 首页汇总统计 Response VO") +@Data +public class MesHomeSummaryRespVO { + + // ========== 工单统计 ========== + + @Schema(description = "进行中工单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "12") + private Long workOrderActiveCount; + + @Schema(description = "待排产工单数(草稿)", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + private Long workOrderPrepareCount; + + @Schema(description = "已完成工单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "30") + private Long workOrderFinishedCount; + + // ========== 产量统计 ========== + + @Schema(description = "今日报工总产量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234") + private BigDecimal todayOutput; + + @Schema(description = "昨日报工总产量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1100") + private BigDecimal yesterdayOutput; + + // ========== 质量统计 ========== + + @Schema(description = "今日合格品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200") + private BigDecimal todayQualifiedQuantity; + + @Schema(description = "今日不良品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "34") + private BigDecimal todayUnqualifiedQuantity; + + // ========== 设备统计 ========== + + @Schema(description = "设备总数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + private Long machineryTotal; + + @Schema(description = "运行中设备数", requiredMode = Schema.RequiredMode.REQUIRED, example = "15") + private Long machineryProducing; + + @Schema(description = "停机设备数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") + private Long machineryStop; + + @Schema(description = "维护中设备数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Long machineryMaintenance; + + // ========== 异常/待办统计 ========== + + @Schema(description = "未处置安灯报警数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") + private Long andonActiveCount; + + @Schema(description = "未完成维修工单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Long repairActiveCount; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/vo/MesHomeWorkOrderStatusRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/vo/MesHomeWorkOrderStatusRespVO.java new file mode 100644 index 0000000..ccbd92b --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/statistics/vo/MesHomeWorkOrderStatusRespVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.mes.controller.admin.statistics.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - MES 首页工单状态分布 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MesHomeWorkOrderStatusRespVO { + + @Schema(description = "状态值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer status; + + @Schema(description = "状态名", requiredMode = Schema.RequiredMode.REQUIRED, example = "草稿") + private String statusName; + + @Schema(description = "工单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "12") + private Long count; + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/statistics/MesHomeStatisticsMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/statistics/MesHomeStatisticsMapper.java new file mode 100644 index 0000000..ea78a4c --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/statistics/MesHomeStatisticsMapper.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.mes.dal.mysql.statistics; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * MES 首页统计 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface MesHomeStatisticsMapper { + + /** + * 按状态统计工单数量 + * + * @return [{ "status": 0, "count": 5 }, ...] + */ + List> selectWorkOrderCountGroupByStatus(); + + /** + * 统计报工产量合计 + * + * @param beginTime >= 开始时间 + * @param endTime < 结束时间 + * @return [feedbackQuantity, qualifiedQuantity, unqualifiedQuantity] + */ + Map selectFeedbackSummary(@Param("beginTime") LocalDateTime beginTime, + @Param("endTime") LocalDateTime endTime); + + /** + * 按设备状态统计设备台数 + * + * @return [{ "status": 1, "count": 3 }, ...] + */ + List> selectMachineryCountGroupByStatus(); + + /** + * 统计未处置安灯记录数 + * + * @return 数量 + */ + Long selectAndonActiveCount(); + + /** + * 统计未完成维修工单数(草稿 + 维修中 + 待验收) + * + * @return 数量 + */ + Long selectRepairActiveCount(); + + /** + * 按天聚合报工产量(近 N 天) + * + * @param beginTime >= 开始时间 + * @param endTime < 结束时间 + * @return [{ "date": "2026-04-05", "quantity": 1234, "qualifiedQuantity": 1200, "unqualifiedQuantity": 34 }, ...] + */ + List> selectDailyFeedbackTrend(@Param("beginTime") LocalDateTime beginTime, + @Param("endTime") LocalDateTime endTime); + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/statistics/MesHomeStatisticsService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/statistics/MesHomeStatisticsService.java new file mode 100644 index 0000000..ac46d5a --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/statistics/MesHomeStatisticsService.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.mes.service.statistics; + +import cn.iocoder.yudao.module.mes.controller.admin.statistics.vo.MesHomeProductionTrendRespVO; +import cn.iocoder.yudao.module.mes.controller.admin.statistics.vo.MesHomeSummaryRespVO; +import cn.iocoder.yudao.module.mes.controller.admin.statistics.vo.MesHomeWorkOrderStatusRespVO; + +import java.util.List; + +/** + * MES 首页统计 Service 接口 + * + * @author 芋道源码 + */ +public interface MesHomeStatisticsService { + + /** + * 获得首页汇总统计 + * + * @return 汇总统计数据 + */ + MesHomeSummaryRespVO getHomeSummary(); + + /** + * 获得工单状态分布 + * + * @return 各状态的工单数量列表 + */ + List getWorkOrderStatusDistribution(); + + /** + * 获得生产趋势(近 N 天) + * + * @param days 天数 + * @return 每天的产量数据 + */ + List getProductionTrend(Integer days); + +} diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/statistics/MesHomeStatisticsServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/statistics/MesHomeStatisticsServiceImpl.java new file mode 100644 index 0000000..4d6afa5 --- /dev/null +++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/statistics/MesHomeStatisticsServiceImpl.java @@ -0,0 +1,168 @@ +package cn.iocoder.yudao.module.mes.service.statistics; + +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.module.mes.controller.admin.statistics.vo.MesHomeProductionTrendRespVO; +import cn.iocoder.yudao.module.mes.controller.admin.statistics.vo.MesHomeSummaryRespVO; +import cn.iocoder.yudao.module.mes.controller.admin.statistics.vo.MesHomeWorkOrderStatusRespVO; +import cn.iocoder.yudao.module.mes.dal.mysql.statistics.MesHomeStatisticsMapper; +import cn.iocoder.yudao.module.mes.enums.dv.MesDvMachineryStatusEnum; +import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; + +/** + * MES 首页统计 Service 实现类 + * + * @author 芋道源码 + */ +@Service +public class MesHomeStatisticsServiceImpl implements MesHomeStatisticsService { + + @Resource + private MesHomeStatisticsMapper homeStatisticsMapper; + + @Override + public MesHomeSummaryRespVO getHomeSummary() { + MesHomeSummaryRespVO summary = new MesHomeSummaryRespVO(); + + // 1. 工单统计 + List> workOrderStats = homeStatisticsMapper.selectWorkOrderCountGroupByStatus(); + long activeCount = 0L; + long prepareCount = 0L; + long finishedCount = 0L; + for (Map stat : workOrderStats) { + int status = ((Number) stat.get("status")).intValue(); + long count = ((Number) stat.get("count")).longValue(); + if (status == MesProWorkOrderStatusEnum.CONFIRMED.getStatus()) { + activeCount = count; + } else if (status == MesProWorkOrderStatusEnum.PREPARE.getStatus()) { + prepareCount = count; + } else if (status == MesProWorkOrderStatusEnum.FINISHED.getStatus()) { + finishedCount = count; + } + } + summary.setWorkOrderActiveCount(activeCount); + summary.setWorkOrderPrepareCount(prepareCount); + summary.setWorkOrderFinishedCount(finishedCount); + + // 2. 今日产量 + LocalDateTime today = LocalDateTimeUtils.getToday(); + LocalDateTime yesterday = LocalDateTimeUtils.getYesterday(); + Map todayFeedback = homeStatisticsMapper.selectFeedbackSummary(today, null); + summary.setTodayOutput(getDecimal(todayFeedback, "feedbackQuantity")); + summary.setTodayQualifiedQuantity(getDecimal(todayFeedback, "qualifiedQuantity")); + summary.setTodayUnqualifiedQuantity(getDecimal(todayFeedback, "unqualifiedQuantity")); + + // 3. 昨日产量 + Map yesterdayFeedback = homeStatisticsMapper.selectFeedbackSummary(yesterday, today); + summary.setYesterdayOutput(getDecimal(yesterdayFeedback, "feedbackQuantity")); + + // 4. 设备统计 + List> machineryStats = homeStatisticsMapper.selectMachineryCountGroupByStatus(); + long total = 0L; + long producing = 0L; + long stop = 0L; + long maintenance = 0L; + for (Map stat : machineryStats) { + int status = ((Number) stat.get("status")).intValue(); + long count = ((Number) stat.get("count")).longValue(); + total += count; + if (status == MesDvMachineryStatusEnum.PRODUCING.getStatus()) { + producing = count; + } else if (status == MesDvMachineryStatusEnum.STOP.getStatus()) { + stop = count; + } else if (status == MesDvMachineryStatusEnum.MAINTENANCE.getStatus()) { + maintenance = count; + } + } + summary.setMachineryTotal(total); + summary.setMachineryProducing(producing); + summary.setMachineryStop(stop); + summary.setMachineryMaintenance(maintenance); + + // 5. 异常/待办 + summary.setAndonActiveCount(homeStatisticsMapper.selectAndonActiveCount()); + summary.setRepairActiveCount(homeStatisticsMapper.selectRepairActiveCount()); + + return summary; + } + + @Override + public List getWorkOrderStatusDistribution() { + List> stats = homeStatisticsMapper.selectWorkOrderCountGroupByStatus(); + List result = new ArrayList<>(); + for (MesProWorkOrderStatusEnum statusEnum : MesProWorkOrderStatusEnum.values()) { + long count = 0L; + for (Map stat : stats) { + if (((Number) stat.get("status")).intValue() == statusEnum.getStatus()) { + count = ((Number) stat.get("count")).longValue(); + break; + } + } + result.add(new MesHomeWorkOrderStatusRespVO(statusEnum.getStatus(), statusEnum.getName(), count)); + } + return result; + } + + @Override + public List getProductionTrend(Integer days) { + LocalDate endDate = LocalDate.now().plusDays(1); + LocalDate startDate = endDate.minusDays(days); + LocalDateTime beginTime = startDate.atStartOfDay(); + LocalDateTime endTime = endDate.atStartOfDay(); + + // 查询数据库 + List> dbData = homeStatisticsMapper.selectDailyFeedbackTrend(beginTime, endTime); + Map> dateMap = new LinkedHashMap<>(); + for (Map row : dbData) { + dateMap.put((String) row.get("date"), row); + } + + // 补全所有日期(无数据的日期填 0) + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + List result = new ArrayList<>(); + for (LocalDate d = startDate; d.isBefore(endDate); d = d.plusDays(1)) { + String dateStr = d.format(fmt); + Map row = dateMap.get(dateStr); + MesHomeProductionTrendRespVO vo = new MesHomeProductionTrendRespVO(); + vo.setDate(dateStr); + if (row != null) { + vo.setQuantity(getDecimalFromObj(row.get("quantity"))); + vo.setQualifiedQuantity(getDecimalFromObj(row.get("qualifiedQuantity"))); + vo.setUnqualifiedQuantity(getDecimalFromObj(row.get("unqualifiedQuantity"))); + } else { + vo.setQuantity(BigDecimal.ZERO); + vo.setQualifiedQuantity(BigDecimal.ZERO); + vo.setUnqualifiedQuantity(BigDecimal.ZERO); + } + result.add(vo); + } + return result; + } + + // ========== 工具方法 ========== + + private BigDecimal getDecimal(Map map, String key) { + if (map == null || map.get(key) == null) { + return BigDecimal.ZERO; + } + return map.get(key); + } + + private BigDecimal getDecimalFromObj(Object obj) { + if (obj == null) { + return BigDecimal.ZERO; + } + if (obj instanceof BigDecimal) { + return (BigDecimal) obj; + } + return new BigDecimal(obj.toString()); + } + +} diff --git a/yudao-module-mes/src/main/resources/mapper/statistics/MesHomeStatisticsMapper.xml b/yudao-module-mes/src/main/resources/mapper/statistics/MesHomeStatisticsMapper.xml new file mode 100644 index 0000000..e3f6804 --- /dev/null +++ b/yudao-module-mes/src/main/resources/mapper/statistics/MesHomeStatisticsMapper.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + +