| | |
| | | package com.ruoyi.home.controller; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.ruoyi.approve.pojo.ApproveProcess; |
| | | import com.ruoyi.device.mapper.DeviceLedgerMapper; |
| | | import com.ruoyi.device.mapper.DeviceRepairMapper; |
| | | import com.ruoyi.device.pojo.DeviceRepair; |
| | | import com.ruoyi.dto.MapDto; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | |
| | | import com.ruoyi.home.annotation.DefaultType; |
| | | import com.ruoyi.home.dto.*; |
| | | import com.ruoyi.home.service.HomeService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import com.ruoyi.production.mapper.ProductionOrderMapper; |
| | | import com.ruoyi.production.mapper.ProductionProductOutputMapper; |
| | | import com.ruoyi.production.pojo.ProductionOrder; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import lombok.AllArgsConstructor; |
| | | 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.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.text.ParseException; |
| | | import java.time.LocalDate; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author :yys |
| | |
| | | @Tag(name = "é¦é¡µç»è®¡") |
| | | @RequestMapping("/home") |
| | | @AllArgsConstructor |
| | | public class HomeController extends BaseController { |
| | | public class HomeController extends BaseController { |
| | | |
| | | private final HomeService homeService; |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | private final ProductionProductOutputMapper productionProductOutputMapper; |
| | | private final DeviceLedgerMapper deviceLedgerMapper; |
| | | private final DeviceRepairMapper deviceRepairMapper; |
| | | |
| | | private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | private static final Integer ORDER_STATUS_WAIT = 1; |
| | | private static final Integer ORDER_STATUS_RUNNING = 2; |
| | | private static final Integer ORDER_STATUS_COMPLETED = 3; |
| | | private static final Integer ORDER_STATUS_PAUSED = 4; |
| | | |
| | | /********************************************************åºç¡ç±»*****************************************************/ |
| | | @GetMapping("/todos") |
| | |
| | | } |
| | | |
| | | /********************************************************è´¨éç±»*****************************************************/ |
| | | @GetMapping("/productionOverview") |
| | | @Operation(summary = "Production Overview") |
| | | public R productionOverview() { |
| | | LocalDate today = LocalDate.now(); |
| | | Map<String, BigDecimal> totalStats = loadOutputStats(LocalDate.of(2000, 1, 1), today.plusDays(1)); |
| | | BigDecimal totalOutput = totalStats.get("quantity"); |
| | | BigDecimal totalScrap = totalStats.get("scrapQty"); |
| | | BigDecimal yieldRate = calcRate(totalOutput, totalOutput.add(totalScrap)); |
| | | |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("totalOutput", scale(totalOutput)); |
| | | result.put("totalScrap", scale(totalScrap)); |
| | | result.put("yieldRate", scale(yieldRate)); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | @GetMapping("/productionRealtimeBoard") |
| | | @Operation(summary = "Production Realtime Board") |
| | | public R productionRealtimeBoard() { |
| | | LocalDate today = LocalDate.now(); |
| | | LocalDate yesterday = today.minusDays(1); |
| | | |
| | | BigDecimal todayDeviceOee = calcDeviceOee(today); |
| | | BigDecimal yesterdayDeviceOee = calcDeviceOee(yesterday); |
| | | |
| | | BigDecimal todayOrderAchievementRate = calcOrderAchievementRate(today); |
| | | BigDecimal yesterdayOrderAchievementRate = calcOrderAchievementRate(yesterday); |
| | | |
| | | BigDecimal todayDefectRate = calcDefectRate(today); |
| | | BigDecimal yesterdayDefectRate = calcDefectRate(yesterday); |
| | | |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("deviceOee", buildRealtimeMetric(todayDeviceOee, todayDeviceOee.subtract(yesterdayDeviceOee))); |
| | | result.put("orderAchievementRate", buildRealtimeMetric(todayOrderAchievementRate, todayOrderAchievementRate.subtract(yesterdayOrderAchievementRate))); |
| | | result.put("defectRate", buildRealtimeMetric(todayDefectRate, todayDefectRate.subtract(yesterdayDefectRate))); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | @GetMapping("/productionOrderProgress") |
| | | @Operation(summary = "Production Order Progress") |
| | | public R productionOrderProgress(@RequestParam(defaultValue = "all") String tab, |
| | | @RequestParam(defaultValue = "1") Long pageNum, |
| | | @RequestParam(defaultValue = "10") Long pageSize) { |
| | | long safePageNum = pageNum == null || pageNum < 1 ? 1 : pageNum; |
| | | long safePageSize = pageSize == null || pageSize < 1 ? 10 : Math.min(pageSize, 50); |
| | | Integer status = resolveOrderStatus(tab); |
| | | long offset = (safePageNum - 1) * safePageSize; |
| | | List<Map<String, Object>> rawRows = productionOrderMapper.selectHomeOrderProgressPage(status, offset, safePageSize); |
| | | List<Map<String, Object>> records = new ArrayList<>(); |
| | | if (rawRows != null) { |
| | | for (Map<String, Object> rawRow : rawRows) { |
| | | records.add(buildOrderProgressRow(rawRow)); |
| | | } |
| | | } |
| | | |
| | | long inProgressCount = 0L; |
| | | long completedCount = 0L; |
| | | long pausedCount = 0L; |
| | | List<Map<String, Object>> statusCountRows = productionOrderMapper.countHomeOrderProgressByStatus(); |
| | | if (statusCountRows != null) { |
| | | for (Map<String, Object> countRow : statusCountRows) { |
| | | Integer statusKey = toInteger(countRow.get("status")); |
| | | long cnt = toLong(countRow.get("cnt")); |
| | | if (Objects.equals(statusKey, ORDER_STATUS_RUNNING)) { |
| | | inProgressCount = cnt; |
| | | } else if (Objects.equals(statusKey, ORDER_STATUS_COMPLETED)) { |
| | | completedCount = cnt; |
| | | } else if (Objects.equals(statusKey, ORDER_STATUS_PAUSED)) { |
| | | pausedCount = cnt; |
| | | } |
| | | } |
| | | } |
| | | |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("tab", tab); |
| | | result.put("total", toLong(productionOrderMapper.countHomeOrderProgress(status))); |
| | | result.put("pageNum", safePageNum); |
| | | result.put("pageSize", safePageSize); |
| | | result.put("inProgressCount", inProgressCount); |
| | | result.put("completedCount", completedCount); |
| | | result.put("pausedCount", pausedCount); |
| | | result.put("records", records); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | @GetMapping("/todayProductionPlan") |
| | | @Operation(summary = "Today Production Plan") |
| | | public R todayProductionPlan(@RequestParam(defaultValue = "4") Long limit) { |
| | | long safeLimit = limit == null || limit < 1 ? 4 : Math.min(limit, 20); |
| | | List<Map<String, Object>> records = new ArrayList<>(); |
| | | List<Map<String, Object>> rawRows = productionOrderMapper.selectHomeTodayProductionPlan(safeLimit); |
| | | if (rawRows != null) { |
| | | for (Map<String, Object> rawRow : rawRows) { |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | Integer rowStatus = toInteger(rawRow.get("status")); |
| | | row.put("orderNo", rawRow.get("orderNo")); |
| | | row.put("productName", rawRow.get("productName")); |
| | | row.put("plannedQuantity", scale(toBigDecimal(rawRow.get("plannedQuantity")))); |
| | | row.put("dueDate", rawRow.get("dueDate")); |
| | | row.put("status", rowStatus); |
| | | row.put("statusLabel", mapOrderStatusLabel(rowStatus)); |
| | | records.add(row); |
| | | } |
| | | } |
| | | |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("total", toLong(productionOrderMapper.countHomeTodayProductionPlan())); |
| | | result.put("records", records); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | @GetMapping("/rawMaterialDetection") |
| | | @Operation(summary = "åæææ£æµ") |
| | | public R rawMaterialDetection(@DefaultType Integer type){ |
| | |
| | | return R.ok(list); |
| | | } |
| | | |
| | | private Map<String, Object> buildOrderProgressRow(Map<String, Object> rawRow) { |
| | | Map<String, Object> row = new LinkedHashMap<>(); |
| | | Integer rowStatus = toInteger(rawRow.get("status")); |
| | | row.put("orderNo", rawRow.get("orderNo")); |
| | | row.put("productName", rawRow.get("productName")); |
| | | row.put("plannedQuantity", scale(toBigDecimal(rawRow.get("plannedQuantity")))); |
| | | row.put("completedQuantity", scale(toBigDecimal(rawRow.get("completedQuantity")))); |
| | | row.put("completionRate", scale(toBigDecimal(rawRow.get("completionRate")))); |
| | | row.put("dueDate", rawRow.get("dueDate")); |
| | | row.put("status", rowStatus); |
| | | row.put("statusLabel", mapOrderStatusLabel(rowStatus)); |
| | | return row; |
| | | } |
| | | |
| | | private Integer resolveOrderStatus(String tab) { |
| | | if (tab == null) { |
| | | return null; |
| | | } |
| | | String normalized = tab.trim().toLowerCase(); |
| | | if ("inprogress".equals(normalized)) { |
| | | return ORDER_STATUS_RUNNING; |
| | | } |
| | | if ("completed".equals(normalized)) { |
| | | return ORDER_STATUS_COMPLETED; |
| | | } |
| | | if ("paused".equals(normalized)) { |
| | | return ORDER_STATUS_PAUSED; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private String mapOrderStatusLabel(Integer status) { |
| | | if (Objects.equals(status, ORDER_STATUS_WAIT)) { |
| | | return "å¾
å¼å§"; |
| | | } |
| | | if (Objects.equals(status, ORDER_STATUS_RUNNING)) { |
| | | return "è¿è¡ä¸"; |
| | | } |
| | | if (Objects.equals(status, ORDER_STATUS_COMPLETED)) { |
| | | return "已宿"; |
| | | } |
| | | if (Objects.equals(status, ORDER_STATUS_PAUSED)) { |
| | | return "å·²æå"; |
| | | } |
| | | return "æªç¥"; |
| | | } |
| | | |
| | | private Map<String, BigDecimal> loadOutputStats(LocalDate startDate, LocalDate endDateExclusive) { |
| | | String start = startDate.atStartOfDay().format(DATE_TIME_FORMATTER); |
| | | String end = endDateExclusive.atStartOfDay().format(DATE_TIME_FORMATTER); |
| | | |
| | | BigDecimal quantity = BigDecimal.ZERO; |
| | | BigDecimal scrapQty = BigDecimal.ZERO; |
| | | List<Map<String, Object>> rows = productionProductOutputMapper.selectDailyOutputStats(start, end); |
| | | if (rows != null) { |
| | | for (Map<String, Object> row : rows) { |
| | | quantity = quantity.add(toBigDecimal(row.get("quantity"))); |
| | | scrapQty = scrapQty.add(toBigDecimal(row.get("scrapQty"))); |
| | | } |
| | | } |
| | | |
| | | Map<String, BigDecimal> stats = new LinkedHashMap<>(); |
| | | stats.put("quantity", quantity); |
| | | stats.put("scrapQty", scrapQty); |
| | | return stats; |
| | | } |
| | | |
| | | private BigDecimal calcDeviceOee(LocalDate day) { |
| | | long totalDeviceCount = deviceLedgerMapper.selectCount(new LambdaQueryWrapper<>()); |
| | | if (totalDeviceCount <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | Date start = Date.from(day.atStartOfDay(ZoneId.systemDefault()).toInstant()); |
| | | Date end = Date.from(day.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant()); |
| | | |
| | | List<DeviceRepair> repairList = deviceRepairMapper.selectList(new LambdaQueryWrapper<DeviceRepair>() |
| | | .select(DeviceRepair::getDeviceLedgerId) |
| | | .ge(DeviceRepair::getRepairTime, start) |
| | | .lt(DeviceRepair::getRepairTime, end) |
| | | .in(DeviceRepair::getStatus, 0, 3)); |
| | | |
| | | long repairingDeviceCount = repairList == null ? 0 : repairList.stream() |
| | | .map(DeviceRepair::getDeviceLedgerId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .count(); |
| | | |
| | | long availableDeviceCount = Math.max(totalDeviceCount - repairingDeviceCount, 0); |
| | | return new BigDecimal(availableDeviceCount) |
| | | .multiply(new BigDecimal("100")) |
| | | .divide(new BigDecimal(totalDeviceCount), 2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal calcOrderAchievementRate(LocalDate day) { |
| | | List<ProductionOrder> orderList = productionOrderMapper.selectList(new LambdaQueryWrapper<ProductionOrder>() |
| | | .select(ProductionOrder::getQuantity, ProductionOrder::getCompleteQuantity) |
| | | .ge(ProductionOrder::getCreateTime, day.atStartOfDay()) |
| | | .lt(ProductionOrder::getCreateTime, day.plusDays(1).atStartOfDay()) |
| | | .ne(ProductionOrder::getStatus, ORDER_STATUS_PAUSED)); |
| | | |
| | | BigDecimal totalQuantity = BigDecimal.ZERO; |
| | | BigDecimal totalCompleteQuantity = BigDecimal.ZERO; |
| | | if (orderList != null) { |
| | | for (ProductionOrder order : orderList) { |
| | | totalQuantity = totalQuantity.add(zeroIfNull(order.getQuantity())); |
| | | totalCompleteQuantity = totalCompleteQuantity.add(zeroIfNull(order.getCompleteQuantity())); |
| | | } |
| | | } |
| | | return calcRate(totalCompleteQuantity, totalQuantity); |
| | | } |
| | | |
| | | private BigDecimal calcDefectRate(LocalDate day) { |
| | | Map<String, BigDecimal> stats = loadOutputStats(day, day.plusDays(1)); |
| | | BigDecimal quantity = stats.get("quantity"); |
| | | BigDecimal scrapQty = stats.get("scrapQty"); |
| | | return calcRate(scrapQty, quantity.add(scrapQty)); |
| | | } |
| | | |
| | | private Map<String, Object> buildRealtimeMetric(BigDecimal value, BigDecimal change) { |
| | | Map<String, Object> metric = new LinkedHashMap<>(); |
| | | metric.put("value", scale(value)); |
| | | metric.put("compareYesterday", scale(change)); |
| | | return metric; |
| | | } |
| | | |
| | | private BigDecimal calcRate(BigDecimal numerator, BigDecimal denominator) { |
| | | if (denominator == null || denominator.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return zeroIfNull(numerator) |
| | | .multiply(new BigDecimal("100")) |
| | | .divide(denominator, 2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal toBigDecimal(Object value) { |
| | | if (value == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | if (value instanceof BigDecimal) { |
| | | return (BigDecimal) value; |
| | | } |
| | | if (value instanceof Number) { |
| | | return BigDecimal.valueOf(((Number) value).doubleValue()); |
| | | } |
| | | try { |
| | | return new BigDecimal(String.valueOf(value)); |
| | | } catch (Exception ex) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | } |
| | | |
| | | private Integer toInteger(Object value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | if (value instanceof Integer) { |
| | | return (Integer) value; |
| | | } |
| | | if (value instanceof Number) { |
| | | return ((Number) value).intValue(); |
| | | } |
| | | try { |
| | | return Integer.valueOf(String.valueOf(value)); |
| | | } catch (Exception ex) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private long toLong(Object value) { |
| | | if (value == null) { |
| | | return 0L; |
| | | } |
| | | if (value instanceof Long) { |
| | | return (Long) value; |
| | | } |
| | | if (value instanceof Number) { |
| | | return ((Number) value).longValue(); |
| | | } |
| | | try { |
| | | return Long.parseLong(String.valueOf(value)); |
| | | } catch (Exception ex) { |
| | | return 0L; |
| | | } |
| | | } |
| | | |
| | | private BigDecimal zeroIfNull(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private BigDecimal scale(BigDecimal value) { |
| | | return zeroIfNull(value).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | } |