From 864037580c3e4d9f8c756b66ef40464d23262e6c Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 20 五月 2026 17:53:42 +0800
Subject: [PATCH] feat(home): 添加生产看板功能并优化订单查询性能
---
doc/20260520_首页生产看板性能优化前端变更文档.md | 107 ++++++++
src/main/java/com/ruoyi/home/controller/HomeController.java | 335 +++++++++++++++++++++++++++
doc/20260520_首页生产看板前端联调文档.md | 160 +++++++++++++
src/main/resources/mapper/production/ProductionOrderMapper.xml | 57 ++++
src/main/java/com/ruoyi/production/mapper/ProductionOrderMapper.java | 13 +
5 files changed, 670 insertions(+), 2 deletions(-)
diff --git "a/doc/20260520_\351\246\226\351\241\265\347\224\237\344\272\247\347\234\213\346\235\277\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md" "b/doc/20260520_\351\246\226\351\241\265\347\224\237\344\272\247\347\234\213\346\235\277\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..aaa6055
--- /dev/null
+++ "b/doc/20260520_\351\246\226\351\241\265\347\224\237\344\272\247\347\234\213\346\235\277\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
@@ -0,0 +1,160 @@
+# 棣栭〉鐢熶骇鐪嬫澘鍓嶇鑱旇皟鏂囨。
+
+鏇存柊鏃堕棿锛�2026-05-20
+妯″潡锛歚/home`锛堥椤碉級
+
+## 1. 鎺ュ彛娓呭崟
+
+1. `GET /home/productionOverview`锛氱敓浜ф�昏
+2. `GET /home/productionRealtimeBoard`锛氱敓浜у疄鏃剁湅鏉�
+3. `GET /home/productionOrderProgress`锛氱敓浜ц鍗曡繘搴�
+4. `GET /home/todayProductionPlan`锛氫粖鏃ョ敓浜ц鍒�
+
+鎵�鏈夋帴鍙g粺涓�杩斿洖 `AjaxResult`锛�
+
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {}
+}
+```
+
+## 2. 鐢熶骇鎬昏
+
+### 2.1 璇锋眰
+
+```http
+GET /home/productionOverview
+```
+
+### 2.2 杩斿洖 `data`
+
+```json
+{
+ "totalOutput": 1280.00,
+ "totalScrap": 25.00,
+ "yieldRate": 98.08
+}
+```
+
+瀛楁璇存槑锛�
+
+- `totalOutput`锛氱疮璁′骇鍑猴紙浠讹紝鍚堟牸鏁帮級
+- `totalScrap`锛氱疮璁℃姤搴燂紙浠讹級
+- `yieldRate`锛氳壇鐜囷紙0-100锛屽墠绔睍绀烘椂鍙嫾鎺� `%`锛�
+
+## 3. 鐢熶骇瀹炴椂鐪嬫澘
+
+### 3.1 璇锋眰
+
+```http
+GET /home/productionRealtimeBoard
+```
+
+### 3.2 杩斿洖 `data`
+
+```json
+{
+ "deviceOee": {
+ "value": 74.00,
+ "compareYesterday": 2.50
+ },
+ "orderAchievementRate": {
+ "value": 81.30,
+ "compareYesterday": -1.20
+ },
+ "defectRate": {
+ "value": 1.40,
+ "compareYesterday": 0.30
+ }
+}
+```
+
+瀛楁璇存槑锛�
+
+- `value`锛氬綋鏃ユ寚鏍囧�硷紙0-100锛�
+- `compareYesterday`锛氳緝鏄ㄦ棩鍙樺寲鍊硷紙鍙鍙礋锛涘墠绔寜姝h礋鍐冲畾绠ご鏂瑰悜鍜岄鑹诧級
+
+## 4. 鐢熶骇璁㈠崟杩涘害
+
+### 4.1 璇锋眰
+
+```http
+GET /home/productionOrderProgress?tab=all&pageNum=1&pageSize=10
+```
+
+鍙傛暟锛�
+
+- `tab`锛歚all` / `inProgress` / `completed` / `paused`
+- `pageNum`锛氶〉鐮侊紙榛樿 `1`锛�
+- `pageSize`锛氭瘡椤垫潯鏁帮紙榛樿 `10`锛屾渶澶� `50`锛�
+
+### 4.2 杩斿洖 `data`
+
+```json
+{
+ "tab": "all",
+ "total": 24,
+ "pageNum": 1,
+ "pageSize": 10,
+ "inProgressCount": 6,
+ "completedCount": 12,
+ "pausedCount": 2,
+ "records": [
+ {
+ "orderNo": "MO-20260518-001",
+ "productName": "鏅鸿兘鎺у埗鍣�",
+ "plannedQuantity": 1000.00,
+ "completedQuantity": 860.00,
+ "completionRate": 86.00,
+ "dueDate": "2026-05-20",
+ "status": 2,
+ "statusLabel": "杩涜涓�"
+ }
+ ]
+}
+```
+
+瀛楁璇存槑锛�
+
+- `completionRate`锛氬畬鎴愮巼锛�0-100锛�
+- `status`锛氬悗绔姸鎬佺爜锛坄1`寰呭紑濮嬶紝`2`杩涜涓紝`3`宸插畬鎴愶紝`4`宸叉殏鍋滐級
+- `statusLabel`锛氱姸鎬佷腑鏂囧睍绀哄��
+
+## 5. 浠婃棩鐢熶骇璁″垝
+
+### 5.1 璇锋眰
+
+```http
+GET /home/todayProductionPlan?limit=4
+```
+
+鍙傛暟锛�
+
+- `limit`锛氳繑鍥炴潯鏁帮紙榛樿 `4`锛屾渶澶� `20`锛�
+
+### 5.2 杩斿洖 `data`
+
+```json
+{
+ "total": 9,
+ "records": [
+ {
+ "orderNo": "MO-20260518-004",
+ "productName": "缁撴瀯浠禔",
+ "plannedQuantity": 1200.00,
+ "dueDate": "2026-05-15",
+ "status": 2,
+ "statusLabel": "杩涜涓�"
+ }
+ ]
+}
+```
+
+## 6. 鍓嶇灞曠ず绾﹀畾
+
+- 鐧惧垎姣斿瓧娈电粺涓�鏄暟鍊硷紙濡� `74.00`锛夛紝鍓嶇鑷鎷兼帴 `%`銆�
+- 鎵�鏈夋暟鍊间繚鐣欎袱浣嶅皬鏁般��
+- `dueDate` 鍙兘涓� `null`锛屽墠绔渶鍏滃簳灞曠ず锛堝 `--`锛夈��
+- `compareYesterday` 姝h礋閮藉彲鑳藉嚭鐜帮紝寤鸿鎸� `>0` 涓婂崌銆乣<0` 涓嬮檷銆乣=0` 鎸佸钩澶勭悊銆�
diff --git "a/doc/20260520_\351\246\226\351\241\265\347\224\237\344\272\247\347\234\213\346\235\277\346\200\247\350\203\275\344\274\230\345\214\226\345\211\215\347\253\257\345\217\230\346\233\264\346\226\207\346\241\243.md" "b/doc/20260520_\351\246\226\351\241\265\347\224\237\344\272\247\347\234\213\346\235\277\346\200\247\350\203\275\344\274\230\345\214\226\345\211\215\347\253\257\345\217\230\346\233\264\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..730dbf1
--- /dev/null
+++ "b/doc/20260520_\351\246\226\351\241\265\347\224\237\344\272\247\347\234\213\346\235\277\346\200\247\350\203\275\344\274\230\345\214\226\345\211\215\347\253\257\345\217\230\346\233\264\346\226\207\346\241\243.md"
@@ -0,0 +1,107 @@
+# 棣栭〉鐢熶骇鐪嬫澘鎬ц兘浼樺寲鍓嶇鍙樻洿鏂囨。
+
+鏇存柊鏃堕棿锛�2026-05-20
+閫傜敤椤甸潰锛氶椤�
+娑夊強鍖哄潡锛�
+
+1. 鐢熶骇璁㈠崟杩涘害
+2. 浠婃棩鐢熶骇璁″垝
+
+## 1. 鏈浼樺寲鐩爣
+
+閽堝澶ф暟鎹噺鍦烘櫙锛堣鍗曟暟閲忓銆佺敓浜у巻鍙查暱锛変紭鍖栨煡璇㈡�ц兘锛岄檷浣庨椤垫帴鍙e搷搴旀椂闂村拰鍐呭瓨鍗犵敤銆�
+
+## 2. 娑夊強鎺ュ彛
+
+1. `GET /home/productionOrderProgress`
+2. `GET /home/todayProductionPlan`
+
+## 3. 鍓嶇鏄惁闇�瑕佹敼浠g爜
+
+缁撹锛�**鏃犲己鍒舵敼鍔紝鎺ュ彛鍏ュ弬涓庤繑鍥炵粨鏋勪繚鎸佸吋瀹�**銆�
+浣犵幇鏈夐〉闈㈠彲浠ョ洿鎺ヨ仈璋冿紝涓嶉渶瑕佹敼瀛楁鏄犲皠銆�
+
+## 4. 鎺ュ彛璇存槑锛堜繚鎸佷笉鍙橈級
+
+### 4.1 鐢熶骇璁㈠崟杩涘害
+
+璇锋眰锛�
+
+```http
+GET /home/productionOrderProgress?tab=all&pageNum=1&pageSize=10
+```
+
+鍙傛暟锛�
+
+- `tab`锛歚all` / `inProgress` / `completed` / `paused`
+- `pageNum`锛氶〉鐮侊紝榛樿 `1`
+- `pageSize`锛氭瘡椤垫潯鏁帮紝榛樿 `10`锛屾渶澶� `50`
+
+杩斿洖 `data`锛堢粨鏋勪笉鍙橈級锛�
+
+```json
+{
+ "tab": "all",
+ "total": 1200,
+ "pageNum": 1,
+ "pageSize": 10,
+ "inProgressCount": 180,
+ "completedCount": 900,
+ "pausedCount": 20,
+ "records": [
+ {
+ "orderNo": "MO-20260518-001",
+ "productName": "鏅鸿兘鎺у埗鍣�",
+ "plannedQuantity": 1000.00,
+ "completedQuantity": 860.00,
+ "completionRate": 86.00,
+ "dueDate": "2026-05-20",
+ "status": 2,
+ "statusLabel": "杩涜涓�"
+ }
+ ]
+}
+```
+
+### 4.2 浠婃棩鐢熶骇璁″垝
+
+璇锋眰锛�
+
+```http
+GET /home/todayProductionPlan?limit=4
+```
+
+鍙傛暟锛�
+
+- `limit`锛氳繑鍥炴潯鏁帮紝榛樿 `4`锛屾渶澶� `20`
+
+杩斿洖 `data`锛堢粨鏋勪笉鍙橈級锛�
+
+```json
+{
+ "total": 230,
+ "records": [
+ {
+ "orderNo": "MO-20260518-004",
+ "productName": "缁撴瀯浠禔",
+ "plannedQuantity": 1200.00,
+ "dueDate": "2026-05-15",
+ "status": 2,
+ "statusLabel": "杩涜涓�"
+ }
+ ]
+}
+```
+
+## 5. 鍚庣浼樺寲鐐癸紙渚涘墠绔煡鎮夛級
+
+1. 璁㈠崟杩涘害涓庝粖鏃ヨ鍒掓敼涓鸿交閲� SQL锛屼粎鏌ヨ棣栭〉蹇呴渶瀛楁銆�
+2. 鍘绘帀浜嗛椤垫煡璇㈣矾寰勪腑涓嶅繀瑕佺殑澶у叧鑱斻�佸浘鐗囧~鍏呭拰瀵硅薄瑁呴厤銆�
+3. 鐘舵�佺粺璁℃敼涓烘暟鎹簱鑱氬悎璁℃暟锛屼笉鍐嶉�愭潯鎷夊彇璁$畻銆�
+4. 鍒嗛〉涓庢潯鏁颁笂闄愪繚鐣欙紙`pageSize <= 50`, `limit <= 20`锛夈��
+
+## 6. 鍓嶇寤鸿
+
+1. 鍒囨崲 `tab` 鏃朵繚鐣欑幇鏈夎皟鐢ㄦ柟寮忓嵆鍙��
+2. `dueDate` 鍙兘涓虹┖锛岀户缁寜 `--` 鍏滃簳灞曠ず銆�
+3. 鐧惧垎姣斿瓧娈典粛涓烘暟鍊硷紝鍓嶇缁х画杩藉姞 `%` 灞曠ず銆�
diff --git a/src/main/java/com/ruoyi/home/controller/HomeController.java b/src/main/java/com/ruoyi/home/controller/HomeController.java
index 8ef87a5..cbe236d 100644
--- a/src/main/java/com/ruoyi/home/controller/HomeController.java
+++ b/src/main/java/com/ruoyi/home/controller/HomeController.java
@@ -1,6 +1,10 @@
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;
@@ -9,17 +13,29 @@
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
@@ -29,9 +45,19 @@
@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")
@@ -179,6 +205,117 @@
}
/********************************************************璐ㄩ噺绫�*****************************************************/
+ @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){
@@ -321,4 +458,198 @@
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);
+ }
+
}
diff --git a/src/main/java/com/ruoyi/production/mapper/ProductionOrderMapper.java b/src/main/java/com/ruoyi/production/mapper/ProductionOrderMapper.java
index e52df0c..fcc9172 100644
--- a/src/main/java/com/ruoyi/production/mapper/ProductionOrderMapper.java
+++ b/src/main/java/com/ruoyi/production/mapper/ProductionOrderMapper.java
@@ -12,6 +12,7 @@
import java.time.LocalDateTime;
import java.util.List;
+import java.util.Map;
/**
* <p>
@@ -39,4 +40,16 @@
Integer countPending(@Param("startDate") String startDate, @Param("endDate") String endDate);
+ List<Map<String, Object>> selectHomeOrderProgressPage(@Param("status") Integer status,
+ @Param("offset") Long offset,
+ @Param("size") Long size);
+
+ Long countHomeOrderProgress(@Param("status") Integer status);
+
+ List<Map<String, Object>> countHomeOrderProgressByStatus();
+
+ List<Map<String, Object>> selectHomeTodayProductionPlan(@Param("size") Long size);
+
+ Long countHomeTodayProductionPlan();
+
}
diff --git a/src/main/resources/mapper/production/ProductionOrderMapper.xml b/src/main/resources/mapper/production/ProductionOrderMapper.xml
index 1a304ef..95679a7 100644
--- a/src/main/resources/mapper/production/ProductionOrderMapper.xml
+++ b/src/main/resources/mapper/production/ProductionOrderMapper.xml
@@ -212,4 +212,61 @@
and ifnull(complete_quantity, 0) < quantity
</select>
+ <select id="selectHomeOrderProgressPage" resultType="java.util.Map">
+ select po.nps_no as orderNo,
+ p.product_name as productName,
+ ifnull(po.quantity, 0) as plannedQuantity,
+ ifnull(po.complete_quantity, 0) as completedQuantity,
+ round(ifnull(po.complete_quantity, 0) / nullif(po.quantity, 0) * 100, 2) as completionRate,
+ po.plan_complete_time as dueDate,
+ po.status as status
+ from production_order po
+ left join product_model pm on po.product_model_id = pm.id
+ left join product p on pm.product_id = p.id
+ <where>
+ <if test="status != null">
+ and po.status = #{status}
+ </if>
+ </where>
+ order by po.id desc
+ limit #{offset}, #{size}
+ </select>
+
+ <select id="countHomeOrderProgress" resultType="java.lang.Long">
+ select count(1)
+ from production_order po
+ <where>
+ <if test="status != null">
+ and po.status = #{status}
+ </if>
+ </where>
+ </select>
+
+ <select id="countHomeOrderProgressByStatus" resultType="java.util.Map">
+ select po.status as status, count(1) as cnt
+ from production_order po
+ where po.status in (2, 3, 4)
+ group by po.status
+ </select>
+
+ <select id="selectHomeTodayProductionPlan" resultType="java.util.Map">
+ select po.nps_no as orderNo,
+ p.product_name as productName,
+ ifnull(po.quantity, 0) as plannedQuantity,
+ po.plan_complete_time as dueDate,
+ po.status as status
+ from production_order po
+ left join product_model pm on po.product_model_id = pm.id
+ left join product p on pm.product_id = p.id
+ where po.status in (1, 2)
+ order by case when po.status = 2 then 0 else 1 end, po.id desc
+ limit #{size}
+ </select>
+
+ <select id="countHomeTodayProductionPlan" resultType="java.lang.Long">
+ select count(1)
+ from production_order po
+ where po.status in (1, 2)
+ </select>
+
</mapper>
--
Gitblit v1.9.3