feat(mes): 新增管理驾驶舱与统计分析模块
整合生产/质检/库存/出库/追溯五大维度核心指标统计接口,
提供可视化驾驶舱页面,支持 Excel 多sheet报表导出与打印存档。
Co-Authored-By: Claude <noreply@anthropic.com>
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # 管ç驾驶è±ä¸ç»è®¡åææ¨¡å |
| | | |
| | | ## 1. æ¨¡åæ¦è¿° |
| | | |
| | | å¯¹åºææ éæ±ä¹¦ç¬¬ 12 æ¡ï¼**æ´åç产ãè´¨æ£ãåºåãåºåºã追溯äºå¤§ç»´åº¦æ ¸å¿ææ ï¼æä¾å¯è§åå¾è¡¨å±ç¤ºï¼æ¯æ Excel/PDF æ ¼å¼æ¥è¡¨å¯¼åºåæå°åæ¡£ã** |
| | | |
| | | - äºå¤§ç»´åº¦ï¼ |
| | | - **ç产**ï¼çäº§æ¹æ¬¡ãè¢ç ï¼ä¸è¢ä¸ç ï¼ãæçç ã仿¥æ«ç å
¥åºè¢æ°ãç产è¶å¿ï¼ |
| | | - **è´¨æ£**ï¼ç§ååé¡¹ææ ï¼çº¯åº¦/å度/åè½ç/æ°´åï¼æ£éªç»æåå¸ãåå¼ä¸åæ ¼çï¼ |
| | | - **åºå**ï¼æä»åº/åç§/åºåç¶æï¼æååº/åæ ¼åº/å·²åºåºï¼åå¸ãä½åºåé¢è¦æ°ï¼ |
| | | - **åºåº**ï¼åºåºææ¥è¶å¿ãæç»éååºåº TOP20ï¼ |
| | | - **追溯**ï¼æ¶è´¹è
æ«ç è¶å¿ãç份åå¸ãæ¸ éåå¸ãçªè´§é¢è¦æ±æ»ã |
| | | |
| | | - 导åºè½åï¼ |
| | | - **Excel**ï¼å端 EasyExcelï¼fastexcelï¼çæå¤ sheet å·¥ä½ç°¿ï¼å
± 6 个 sheetï¼é©¾é©¶è±æ»è§ / ç产ç»è®¡ / è´¨æ£ç»è®¡ / åºåç»è®¡ / åºåºç»è®¡ / 追溯ç»è®¡ï¼ï¼ |
| | | - **PDF**ï¼å端 `html2canvas` + `jspdf` å°é©¾é©¶è±æ¥è¡¨åºåçæä¸ºå¯ç´æ¥ä¸è½½ç PDF æä»¶ï¼ |
| | | - **æå°**ï¼å端 `vue3-print-nb` æå°æ¥è¡¨åºåç¨äºåæ¡£ã |
| | | |
| | | ## 2. æ°æ®æºè¦æ±ï¼éè¦ï¼ |
| | | |
| | | **ç§åä¸å¡æ°æ®è¡¨ä½äº `mom-thzy` åº**ï¼å«çäº§æ¹æ¬¡ `mes_pro_batch`ãè¢ç `mes_pro_bag_code`ãæç `mes_pro_pallet`ãç§åè´¨æ£ `mes_qc_seed_quality`ãæ«ç äºä»¶ `mes_trace_consumer_scan_event` çå
¨é¨ä¸å¡è¡¨ï¼ã |
| | | |
| | | > â
å·²å¤çï¼`yudao-server/src/main/resources/application-local.yaml` ä¸»æ°æ®æºå·²æå `jdbc:mysql://192.168.197.128:3306/mom-thzy`ï¼root / sugaozhixing.cnï¼ã驾驶è±åä¸è¢ä¸ç çç§åä¸å¡åè½**å¿
é¡»è¿æ¥ `mom-thzy` åº**æè½æ£å¸¸ç»è®¡ï¼è¯·å¿ååå `ruoyi-vue-pro`ï¼è¯¥åºä¸å«ç§åä¸å¡è¡¨ï¼ã |
| | | |
| | | è¥ç®æ åºç¼ºå°ç§åä¸å¡è¡¨ï¼éå
æ§è¡ `sql/mysql/` ä¸çåå§åèæ¬ï¼ |
| | | - `mes_pro_batch_bag_code_init.sql`ï¼çäº§æ¹æ¬¡/è¢ç /æçï¼ |
| | | - `mes_seed_quality_init.sql`ï¼ç§åè´¨æ£ï¼ |
| | | - `mes_trace_consumer_scan_event.sql`ï¼æ¶è´¹è
æ«ç äºä»¶ï¼ |
| | | - `mes_trace_anti_cross_region_init.sql`ï¼çªè´§é¢è¦ï¼ |
| | | - `mes_pro_scan_inbound_init.sql`ï¼ç产线æ«ç å
¥åºï¼ |
| | | |
| | | ## 3. å端å®ç° |
| | | |
| | | å端ä½äº `yudao-module-mes`ï¼æ°å¢å
ï¼ |
| | | |
| | | | æä»¶ | 说æ | |
| | | |---|---| |
| | | | `controller/admin/dashboard/MesDashboardController.java` | `/mes/dashboard` ç»è®¡æ¥å£ + Excel å¤ sheet å¯¼åº | |
| | | | `controller/admin/dashboard/vo/*.java` | ç»è®¡ååº VO ä¸ Excel å¯¼åº VO | |
| | | | `service/dashboard/MesDashboardService(.java|Impl)` | äºç»´åº¦ç»è®¡èåï¼å«ææ¥è¡¥é¶ï¼ | |
| | | | `dal/mysql/dashboard/MesDashboardMapper.java` | MyBatis Mapper æ¥å£ | |
| | | | `resources/mapper/dashboard/MesDashboardMapper.xml` | æåç»è®¡ SQL | |
| | | |
| | | æ¥å£æ¸
åï¼æé `mes:dashboard:query` / `mes:dashboard:export`ï¼ï¼ |
| | | |
| | | | æ¥å£ | 说æ | |
| | | |---|---| |
| | | | `GET /mes/dashboard/summary` | äºç»´åº¦ KPI æ±æ» | |
| | | | `GET /mes/dashboard/production-trend?days=` | ç产è¶å¿ | |
| | | | `GET /mes/dashboard/quality-statistics` | è´¨æ£ç»´åº¦ç»è®¡ | |
| | | | `GET /mes/dashboard/inventory-statistics` | åºå维度ç»è®¡ | |
| | | | `GET /mes/dashboard/outbound-statistics?days=` | åºåºç»´åº¦ç»è®¡ | |
| | | | `GET /mes/dashboard/trace-statistics?days=` | 追溯维度ç»è®¡ï¼å¤ç¨æ«ç ç»è®¡ä¸çªè´§é¢è¦æå¡ï¼ | |
| | | | `GET /mes/dashboard/export-excel` | Excel å¤ sheet æ¥è¡¨å¯¼åº | |
| | | |
| | | 设计è¦ç¹ï¼ |
| | | - 追溯维度**å¤ç¨** `MesTraceConsumerScanStatisticsService`ã`MesTraceCrossRegionService`ï¼é¿å
éå¤ SQLï¼ |
| | | - åºåä½é¢è¦æ°**å¤ç¨** `MesWmStockWarningService.getCurrentWarnings()`ï¼ |
| | | - ç©ºæ°æ®è¡¨ï¼å¦è´¨æ£/åºå/åºåºå°æ æ°æ®ï¼è¿å 0 æç©ºæ°ç»ï¼å端å¾è¡¨æ£å¸¸æ¸²æï¼Excel 导åºç©º sheet èªå¨è¡¥ãææ æ°æ®ãè¡ï¼ä¿è¯è¡¨å¤´å¯æ¨æã |
| | | |
| | | ## 4. å端å®ç° |
| | | |
| | | å端ä½äº `mom-pro2-before`ï¼æ°å¢ï¼ |
| | | |
| | | | æä»¶ | 说æ | |
| | | |---|---| |
| | | | `src/api/mes/management-cockpit/index.ts` | é©¾é©¶è± APIï¼`requestClient`ï¼ | |
| | | | `src/views/management-cockpit/index.vue` | 驾驶è±ä¸»é¡µé¢ï¼å·¥å
·æ + æ¥è¡¨åºåï¼ | |
| | | | `src/views/management-cockpit/chart-options.ts` | ECharts é
置工å | |
| | | | `src/views/management-cockpit/modules/*.vue` | KPI å¡ + ç产/è´¨æ£/åºå/åºåº/追溯å¾è¡¨ç»ä»¶ | |
| | | |
| | | 页é¢è½åï¼ |
| | | - **å·æ°**ï¼æ´ä½å·æ°å
¨é¨ç»´åº¦æ°æ®ï¼ |
| | | - **å¯¼åº Excel**ï¼`requestClient.download` ä¸è½½åç«¯å¤ sheet å·¥ä½ç°¿ï¼ |
| | | - **å¯¼åº PDF**ï¼`html2canvas` æªåæ¥è¡¨åºå + `jspdf` çæå¯ä¸è½½ PDFï¼ |
| | | - **æå°**ï¼`vue3-print-nb` æå°æ¥è¡¨åºåï¼ç¨äºåæ¡£ã |
| | | |
| | | æ°å¢ä¾èµï¼æ ¹ `package.json`ï¼ï¼`html2canvas`ã`jspdf`ã |
| | | |
| | | ## 5. èå䏿é |
| | | |
| | | æ§è¡ `sql/mysql/mes_dashboard_module_init.sql`ï¼å¹çï¼æ³¨åï¼ |
| | | |
| | | - 顶级ç®å½ãç»è®¡åæãï¼id=602700ï¼å·²å卿¶å¤ç¨ï¼ï¼ |
| | | - 页é¢ã管ç驾驶è±ãï¼id=602701ï¼`component = management-cockpit/index`ï¼ï¼ |
| | | - æé®æé `mes:dashboard:query`ã`mes:dashboard:export`ã |
| | | |
| | | èåç±å端 `system_menu` 卿ä¸åï¼å端 `component` åæ®µæ å°å° `src/views/management-cockpit/index.vue`ã |
| | | |
| | | ## 6. éªè¯ |
| | | |
| | | 1. å端ç¼è¯ï¼`mvn compile -pl yudao-module-mes -am -q` |
| | | 2. åç«¯ç±»åæ£æ¥ï¼`pnpm typecheck`ï¼æ¬æ¬¡æ°å¢æä»¶æ ç±»åéè¯¯ï¼ |
| | | 3. æ§è¡ `mes_dashboard_module_init.sql` åç»å½ç³»ç»ï¼èååºç°ãç»è®¡åæ â 管ç驾驶è±ã |
| | | 4. æ ¸å¯¹äºå¤§ç»´åº¦å¾è¡¨æ°æ®ä¸ `mom-thzy` åºå®é
æ°æ®ä¸è´ |
| | | 5. åå«éªè¯ Excel ä¸è½½ãPDF ä¸è½½ãæå°ä¸ä¸ªåè½ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ============================================= |
| | | -- 管ç驾驶è±ä¸ç»è®¡åææ¨¡å åå§åèæ¬ |
| | | -- å
å«ï¼ã管ç驾驶è±ã页é¢åæé®æéï¼æ¥è¯¢/导åºï¼ |
| | | -- 说æï¼é¡¶çº§ç®å½ãç»è®¡åæã(id=602700) å·²åå¨ï¼æ¬èæ¬ä»
å¨å
¶ä¸æè½½é¡µé¢ã |
| | | -- éç¨åºï¼mom-thzyï¼ç§åä¸å¡ä¸»åºï¼å«ç产/è´¨æ£/åºå/åºåº/追溯å
¨é¨ä¸å¡è¡¨ï¼ |
| | | -- 注æï¼è¥ç®æ åºç¼ºå°ç§åä¸å¡è¡¨ï¼éå
æ§è¡ä»¥ä¸èæ¬ï¼ |
| | | -- mes_pro_batch_bag_code_init.sql ï¼çäº§æ¹æ¬¡/è¢ç /æçï¼ |
| | | -- mes_seed_quality_init.sql ï¼ç§åè´¨æ£ï¼ |
| | | -- mes_trace_consumer_scan_event.sql ï¼æ¶è´¹è
æ«ç äºä»¶ï¼ |
| | | -- mes_trace_anti_cross_region_init.sql ï¼çªè´§é¢è¦ï¼ |
| | | -- mes_pro_scan_inbound_init.sql ï¼ç产线æ«ç å
¥åºï¼ |
| | | -- MySQL 8.4 éç¨ |
| | | -- ============================================= |
| | | |
| | | SET NAMES utf8mb4; |
| | | |
| | | -- ---------------------------- |
| | | -- 1. 页é¢ï¼ç®¡ç驾驶è±ï¼æå°ãç»è®¡åæãç®å½ 602700 ä¸ï¼ |
| | | -- ---------------------------- |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, |
| | | status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT 602701, '管ç驾驶è±', '', 2, 1, 602700, 'management-cockpit', 'ep:monitor', 'management-cockpit/index', 'ManagementCockpit', |
| | | 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM system_menu |
| | | WHERE (id = 602701 OR (parent_id = 602700 AND path = 'management-cockpit' AND deleted = 0)) AND deleted = 0 |
| | | ) |
| | | AND EXISTS (SELECT 1 FROM system_menu WHERE id = 602700 AND deleted = 0); |
| | | |
| | | -- ---------------------------- |
| | | -- 2. æé®æéï¼æ¥è¯¢ / å¯¼åº |
| | | -- ---------------------------- |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, |
| | | status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT 602702, 'é©¾é©¶è±æ¥è¯¢', 'mes:dashboard:query', 3, 1, 602701, '', '', '', '', |
| | | 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM system_menu |
| | | WHERE (id = 602702 OR (parent_id = 602701 AND permission = 'mes:dashboard:query' AND deleted = 0)) AND deleted = 0 |
| | | ); |
| | | |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, |
| | | status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT 602703, '驾驶è±å¯¼åº', 'mes:dashboard:export', 3, 2, 602701, '', '', '', '', |
| | | 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM system_menu |
| | | WHERE (id = 602703 OR (parent_id = 602701 AND permission = 'mes:dashboard:export' AND deleted = 0)) AND deleted = 0 |
| | | ); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ============================================= |
| | | -- 管çé©¾é©¶è± æµè¯æ°æ®èæ¬ |
| | | -- ç®æ åºï¼mom-thzy |
| | | -- ä½ç¨ï¼ä¸ºè´¨æ£ / åºå / åºåº / çªè´§é¢è¦ å个å½å为空ç维度补å
æ¼ç¤ºæ°æ® |
| | | -- å
³èå·²æåºç¡æ°æ®ï¼ä»åº(产ååº/ç©æåº/èæçº¿è¾¹ä»åº)ãåç§(mdm_item#1)ã |
| | | -- ç»éå(crm_customer#2/4/6)ãæ¹æ¬¡(1/102)ãè¢ç (0001~0007) |
| | | -- æ°æ®æ¥æï¼2026-08-28 ~ 2026-09-01 |
| | | -- 注æï¼id ä½¿ç¨ 1001+ 段é¿å
䏿¢ææ°æ®å²çªï¼é夿§è¡å请å
æ¸
çæå¤ä»½ |
| | | -- ============================================= |
| | | |
| | | SET NAMES utf8mb4; |
| | | |
| | | -- ---------------------------- |
| | | -- 1. ç§åè´¨æ£ä¸»è¡¨ï¼7 æ¡ï¼4 åæ ¼ / 2 ä¸åæ ¼ / 1 å¤çä¸ï¼ |
| | | -- ---------------------------- |
| | | INSERT INTO `mes_qc_seed_quality` (id, code, item_id, batch_code, result, reason, inspector_id, inspected_at, creator, create_time, updater, update_time, deleted) VALUES |
| | | (1001, 'PC20260819twoITEM_001-0001', 1, 'PC20260819twoITEM_001', 1, NULL, 1, '2026-08-28 09:00:00', '1', '2026-08-28 09:00:00', '1', '2026-08-28 09:00:00', b'0'), |
| | | (1002, 'PC20260819twoITEM_001-0002', 1, 'PC20260819twoITEM_001', 1, NULL, 111, '2026-08-29 10:00:00', '1', '2026-08-29 10:00:00', '1', '2026-08-29 10:00:00', b'0'), |
| | | (1003, 'PC20260819twoITEM_001-0003', 1, 'PC20260819twoITEM_001', 2, 'åè½çä½äºæ åå¼', 1, '2026-08-29 14:30:00', '1', '2026-08-29 14:30:00', '1', '2026-08-29 14:30:00', b'0'), |
| | | (1004, 'PC20260819twoITEM_001-0004', 1, 'PC20260819twoITEM_001', 1, NULL, 111, '2026-08-30 09:20:00', '1', '2026-08-30 09:20:00', '1', '2026-08-30 09:20:00', b'0'), |
| | | (1005, 'PC20260819twoITEM_001-0005', 1, 'PC20260819twoITEM_001', 0, NULL, 1, NULL, '1', '2026-08-30 11:00:00', '1', '2026-08-30 11:00:00', b'0'), |
| | | (1006, 'PC20260819twoITEM_001-0006', 1, 'PC20260819twoITEM_001', 1, NULL, 1, '2026-08-31 15:00:00', '1', '2026-08-31 15:00:00', '1', '2026-08-31 15:00:00', b'0'), |
| | | (1007, 'PC20260819twoITEM_001-0007', 1, 'PC20260819twoITEM_001', 2, 'æ°´åå«éè¶
æ ', 111, '2026-09-01 09:10:00', '1', '2026-09-01 09:10:00', '1', '2026-09-01 09:10:00', b'0'); |
| | | |
| | | -- ---------------------------- |
| | | -- 2. ç§åè´¨æ£ææ ç»æï¼æ¯æ¡è´¨æ£ 4 项ï¼çº¯åº¦/å度/åè½ç/æ°´åï¼ |
| | | -- æ ååºé´ï¼çº¯åº¦[96,100] å度[98,100] åè½ç[85,100] æ°´å[0,13] |
| | | -- ---------------------------- |
| | | INSERT INTO `mes_qc_seed_quality_indicator` (id, quality_id, indicator_code, value, min_value, max_value, creator, create_time, updater, update_time, deleted) VALUES |
| | | (1001, 1001, 'purity', 98.500000, 96, 100, '1', '2026-08-28 09:00:00', '1', '2026-08-28 09:00:00', b'0'), |
| | | (1002, 1001, 'cleanliness', 99.200000, 98, 100, '1', '2026-08-28 09:00:00', '1', '2026-08-28 09:00:00', b'0'), |
| | | (1003, 1001, 'germination_rate', 92.000000, 85, 100, '1', '2026-08-28 09:00:00', '1', '2026-08-28 09:00:00', b'0'), |
| | | (1004, 1001, 'moisture', 10.500000, 0, 13, '1', '2026-08-28 09:00:00', '1', '2026-08-28 09:00:00', b'0'), |
| | | (1005, 1002, 'purity', 98.100000, 96, 100, '1', '2026-08-29 10:00:00', '1', '2026-08-29 10:00:00', b'0'), |
| | | (1006, 1002, 'cleanliness', 99.000000, 98, 100, '1', '2026-08-29 10:00:00', '1', '2026-08-29 10:00:00', b'0'), |
| | | (1007, 1002, 'germination_rate', 93.500000, 85, 100, '1', '2026-08-29 10:00:00', '1', '2026-08-29 10:00:00', b'0'), |
| | | (1008, 1002, 'moisture', 10.200000, 0, 13, '1', '2026-08-29 10:00:00', '1', '2026-08-29 10:00:00', b'0'), |
| | | (1009, 1003, 'purity', 98.200000, 96, 100, '1', '2026-08-29 14:30:00', '1', '2026-08-29 14:30:00', b'0'), |
| | | (1010, 1003, 'cleanliness', 99.000000, 98, 100, '1', '2026-08-29 14:30:00', '1', '2026-08-29 14:30:00', b'0'), |
| | | (1011, 1003, 'germination_rate', 80.000000, 85, 100, '1', '2026-08-29 14:30:00', '1', '2026-08-29 14:30:00', b'0'), |
| | | (1012, 1003, 'moisture', 11.200000, 0, 13, '1', '2026-08-29 14:30:00', '1', '2026-08-29 14:30:00', b'0'), |
| | | (1013, 1004, 'purity', 98.800000, 96, 100, '1', '2026-08-30 09:20:00', '1', '2026-08-30 09:20:00', b'0'), |
| | | (1014, 1004, 'cleanliness', 99.400000, 98, 100, '1', '2026-08-30 09:20:00', '1', '2026-08-30 09:20:00', b'0'), |
| | | (1015, 1004, 'germination_rate', 95.000000, 85, 100, '1', '2026-08-30 09:20:00', '1', '2026-08-30 09:20:00', b'0'), |
| | | (1016, 1004, 'moisture', 9.800000, 0, 13, '1', '2026-08-30 09:20:00', '1', '2026-08-30 09:20:00', b'0'), |
| | | (1017, 1005, 'purity', 98.000000, 96, 100, '1', '2026-08-30 11:00:00', '1', '2026-08-30 11:00:00', b'0'), |
| | | (1018, 1005, 'cleanliness', 98.800000, 98, 100, '1', '2026-08-30 11:00:00', '1', '2026-08-30 11:00:00', b'0'), |
| | | (1019, 1005, 'germination_rate', 90.000000, 85, 100, '1', '2026-08-30 11:00:00', '1', '2026-08-30 11:00:00', b'0'), |
| | | (1020, 1005, 'moisture', 11.000000, 0, 13, '1', '2026-08-30 11:00:00', '1', '2026-08-30 11:00:00', b'0'), |
| | | (1021, 1006, 'purity', 98.600000, 96, 100, '1', '2026-08-31 15:00:00', '1', '2026-08-31 15:00:00', b'0'), |
| | | (1022, 1006, 'cleanliness', 99.100000, 98, 100, '1', '2026-08-31 15:00:00', '1', '2026-08-31 15:00:00', b'0'), |
| | | (1023, 1006, 'germination_rate', 91.500000, 85, 100, '1', '2026-08-31 15:00:00', '1', '2026-08-31 15:00:00', b'0'), |
| | | (1024, 1006, 'moisture', 10.100000, 0, 13, '1', '2026-08-31 15:00:00', '1', '2026-08-31 15:00:00', b'0'), |
| | | (1025, 1007, 'purity', 97.500000, 96, 100, '1', '2026-09-01 09:10:00', '1', '2026-09-01 09:10:00', b'0'), |
| | | (1026, 1007, 'cleanliness', 98.800000, 98, 100, '1', '2026-09-01 09:10:00', '1', '2026-09-01 09:10:00', b'0'), |
| | | (1027, 1007, 'germination_rate', 90.000000, 85, 100, '1', '2026-09-01 09:10:00', '1', '2026-09-01 09:10:00', b'0'), |
| | | (1028, 1007, 'moisture', 14.500000, 0, 13, '1', '2026-09-01 09:10:00', '1', '2026-09-01 09:10:00', b'0'); |
| | | |
| | | -- ---------------------------- |
| | | -- 3. åºåï¼4 æ¡ï¼åæ ¼åº2 / æååº2ï¼åå¸äºäº§ååº/ç©æåº/èæçº¿è¾¹ä»åºï¼ |
| | | -- åºåç¶æï¼10-æååº 20-åæ ¼åº 30-å·²åºåº |
| | | -- ---------------------------- |
| | | INSERT INTO `mes_wm_material_stock` (id, item_type_id, item_id, batch_id, batch_code, warehouse_id, location_id, area_id, vendor_id, quantity, frozen_quantity, reserved_quantity, in_transit_quantity, stock_state, erp_time, receipt_time, frozen, remark, creator, create_time, updater, update_time, deleted) VALUES |
| | | (1001, NULL, 1, 1, 'PC20260819twoITEM_001', 1, NULL, NULL, NULL, 1000.000000, 0, 0, 0, 20, '2026-08-20 08:00:00', '2026-08-20 08:00:00', b'0', 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-20 08:00:00', '1', '2026-08-20 08:00:00', b'0'), |
| | | (1002, NULL, 1, 102, 'PC20260828twoITEM_001', 1, NULL, NULL, NULL, 500.000000, 0, 0, 0, 20, '2026-08-28 09:00:00', '2026-08-28 09:00:00', b'0', 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-28 09:00:00', '1', '2026-08-28 09:00:00', b'0'), |
| | | (1003, NULL, 1, 1, 'PC20260819twoITEM_001', 2, NULL, NULL, NULL, 800.000000, 0, 0, 0, 10, '2026-08-20 08:30:00', '2026-08-20 08:30:00', b'0', 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-20 08:30:00', '1', '2026-08-20 08:30:00', b'0'), |
| | | (1004, NULL, 1, 102, 'PC20260828twoITEM_001', 3, NULL, NULL, NULL, 300.000000, 0, 0, 0, 10, '2026-08-28 10:00:00', '2026-08-28 10:00:00', b'0', 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-28 10:00:00', '1', '2026-08-28 10:00:00', b'0'); |
| | | |
| | | -- ---------------------------- |
| | | -- 4. éå®åºåºåï¼4 å¼ ï¼3 已宿 + 1 å¾
æ§è¡åºåºï¼ |
| | | -- åºåºç¶æï¼0è稿 1å¾
æ£æµ 2å¾
æ£è´§ 3å¾
æ§è¡åºåº 4已宿 |
| | | -- ---------------------------- |
| | | INSERT INTO `mes_wm_product_sales` (id, code, name, client_id, sale_order_id, notice_id, sales_order_code, sales_date, contact_name, contact_telephone, contact_address, carrier, shipping_number, status, remark, creator, create_time, updater, update_time, deleted) VALUES |
| | | (1001, 'XSCK20260828001', 'éå®åºåºå-åé客æ·', 2, NULL, NULL, NULL, '2026-08-28 09:00:00', 'å¼ ç»ç', '13800000001', 'æ±èçåéå¸', '顺丰', 'SF1001', 4, 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-28 09:00:00', '1', '2026-08-28 09:00:00', b'0'), |
| | | (1002, 'XSCK20260829001', 'éå®åºåºå-å®¢æ·æ²é', 4, NULL, NULL, NULL, '2026-08-29 10:00:00', 'æç»ç', '13800000002', 'æ°çé¿å
è', 'ä¸é', 'ZT2001', 4, 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-29 10:00:00', '1', '2026-08-29 10:00:00', b'0'), |
| | | (1003, 'XSCK20260831001', 'éå®åºåºå-é«çº§', 6, NULL, NULL, NULL, '2026-08-31 11:00:00', 'çç»ç', '13800000003', 'æ°çåä»', 'åé', 'YT3001', 3, 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-31 11:00:00', '1', '2026-08-31 11:00:00', b'0'), |
| | | (1004, 'XSCK20260901001', 'éå®åºåºå-åé客æ·', 2, NULL, NULL, NULL, '2026-09-01 09:30:00', 'å¼ ç»ç', '13800000001', 'æ±èçåéå¸', '顺丰', 'SF1004', 4, 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-09-01 09:30:00', '1', '2026-09-01 09:30:00', b'0'); |
| | | |
| | | -- ---------------------------- |
| | | -- 5. éå®åºåºåè¡ï¼å¯¹åºä¸é¢ 4 å¼ åºåºåï¼ |
| | | -- ---------------------------- |
| | | INSERT INTO `mes_wm_product_sales_line` (id, sales_id, sale_order_item_id, notice_line_id, item_id, batch_id, batch_code, material_stock_id, quantity, oqc_check_flag, oqc_id, quality_status, remark, creator, create_time, updater, update_time, deleted) VALUES |
| | | (1001, 1001, NULL, NULL, 1, 1, 'PC20260819twoITEM_001', NULL, 200.000000, b'0', NULL, NULL, 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-28 09:00:00', '1', '2026-08-28 09:00:00', b'0'), |
| | | (1002, 1001, NULL, NULL, 1, 1, 'PC20260819twoITEM_001', NULL, 150.000000, b'0', NULL, NULL, 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-28 09:00:00', '1', '2026-08-28 09:00:00', b'0'), |
| | | (1003, 1002, NULL, NULL, 1, 102, 'PC20260828twoITEM_001', NULL, 180.000000, b'0', NULL, NULL, 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-29 10:00:00', '1', '2026-08-29 10:00:00', b'0'), |
| | | (1004, 1003, NULL, NULL, 1, 1, 'PC20260819twoITEM_001', NULL, 220.000000, b'0', NULL, NULL, 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-08-31 11:00:00', '1', '2026-08-31 11:00:00', b'0'), |
| | | (1005, 1004, NULL, NULL, 1, 102, 'PC20260828twoITEM_001', NULL, 160.000000, b'0', NULL, NULL, 'é©¾é©¶è±æ¼ç¤ºæ°æ®', '1', '2026-09-01 09:30:00', '1', '2026-09-01 09:30:00', b'0'); |
| | | |
| | | -- ---------------------------- |
| | | -- 6. çªè´§é¢è¦ï¼3 æ¡ï¼è·¨çå¾
å¤ç / è·¨å¸å·²å¤ç / è·¨ç误æ¥ï¼ |
| | | -- ç¶æï¼10-å¾
å¤ç 20-å·²å¤ç 30-误æ¥ï¼ç±»åï¼CROSS_PROVINCE è·¨ç / CROSS_CITY è·¨å¸ |
| | | -- ---------------------------- |
| | | INSERT INTO `mes_trace_cross_region_warning` (id, warning_no, bag_code_id, bag_code, pallet_code_id, pallet_code, batch_id, batch_code, item_id, item_code, item_name, sales_id, sales_code, client_id, client_name, dealer_province, dealer_city, scan_province, scan_city, warning_type, scan_event_id, status, handle_user_id, handle_time, handle_remark, event_time, create_time, update_time, creator, updater, deleted) VALUES |
| | | (1001, 'WARN20260901001', 1, 'PC20260819twoITEM_001-0001', NULL, NULL, 1, 'PC20260819twoITEM_001', 1, '001', 'æµè¯', 1001, 'XSCK20260828001', 2, 'åé客æ·', 'æ°ç', 'é¿å
è', 'æ²³å', 'éå·', 'CROSS_PROVINCE', NULL, 10, NULL, NULL, NULL, '2026-08-30 12:00:00', '2026-08-30 12:00:00', '2026-08-30 12:00:00', '1', '1', b'0'), |
| | | (1002, 'WARN20260901002', NULL, NULL, NULL, NULL, 102, 'PC20260828twoITEM_001', 1, '001', 'æµè¯', 1002, 'XSCK20260829001', 4, 'å®¢æ·æ²é', 'æ°ç', 'åä»', 'æ°ç', 'ä¹é²æ¨é½', 'CROSS_CITY', NULL, 20, 1, '2026-09-01 08:00:00', 'å·²æ ¸å®å¤ç', '2026-08-31 18:00:00', '2026-08-31 18:00:00', '2026-09-01 08:00:00', '1', '1', b'0'), |
| | | (1003, 'WARN20260901003', NULL, NULL, NULL, NULL, 1, 'PC20260819twoITEM_001', 1, '001', 'æµè¯', 1004, 'XSCK20260901001', 2, 'åé客æ·', 'æ°ç', 'é¿å
è', 'çè', 'å
°å·', 'CROSS_PROVINCE', NULL, 30, 1, '2026-09-01 10:00:00', '误æ¥', '2026-09-01 09:00:00', '2026-09-01 09:00:00', '2026-09-01 10:00:00', '1', '1', b'0'); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard; |
| | | |
| | | import cn.idev.excel.ExcelWriter; |
| | | import cn.idev.excel.FastExcelFactory; |
| | | import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardDailyCountRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardGenericExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardIndicatorAvgRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardInventoryRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardNameCountRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardOutboundRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardOverviewExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardProductionExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardProductionTrendRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardQualityRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardSummaryRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardTraceRespVO; |
| | | import cn.iocoder.yudao.module.mes.service.dashboard.MesDashboardService; |
| | | 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 jakarta.servlet.http.HttpServletResponse; |
| | | import jakarta.validation.constraints.Max; |
| | | import jakarta.validation.constraints.Min; |
| | | 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.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±ä¸ç»è®¡åæ |
| | | * <p> |
| | | * æ´åç产ãè´¨æ£ãåºåãåºåºã追溯äºå¤§ç»´åº¦æ ¸å¿ææ ï¼æä¾å¯è§åå¾è¡¨æ°æ®ä¸ Excel æ¥è¡¨å¯¼åºã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Tag(name = "管çåå° - MES 管ç驾驶è±") |
| | | @RestController |
| | | @RequestMapping("/mes/dashboard") |
| | | @Validated |
| | | public class MesDashboardController { |
| | | |
| | | @Resource |
| | | private MesDashboardService dashboardService; |
| | | |
| | | @GetMapping("/summary") |
| | | @Operation(summary = "è·å¾ç®¡ç驾驶è±äºç»´åº¦æ±æ»ç»è®¡") |
| | | @PreAuthorize("@ss.hasPermission('mes:dashboard:query')") |
| | | public CommonResult<MesDashboardSummaryRespVO> getSummary() { |
| | | return success(dashboardService.getSummary()); |
| | | } |
| | | |
| | | @GetMapping("/production-trend") |
| | | @Operation(summary = "è·å¾ç产è¶å¿") |
| | | @Parameter(name = "days", description = "天æ°", example = "30") |
| | | @PreAuthorize("@ss.hasPermission('mes:dashboard:query')") |
| | | public CommonResult<List<MesDashboardProductionTrendRespVO>> getProductionTrend( |
| | | @RequestParam(value = "days", defaultValue = "30") @Min(1) @Max(365) Integer days) { |
| | | return success(dashboardService.getProductionTrend(days)); |
| | | } |
| | | |
| | | @GetMapping("/quality-statistics") |
| | | @Operation(summary = "è·å¾è´¨æ£ç»´åº¦ç»è®¡") |
| | | @PreAuthorize("@ss.hasPermission('mes:dashboard:query')") |
| | | public CommonResult<MesDashboardQualityRespVO> getQualityStatistics() { |
| | | return success(dashboardService.getQualityStatistics()); |
| | | } |
| | | |
| | | @GetMapping("/inventory-statistics") |
| | | @Operation(summary = "è·å¾åºå维度ç»è®¡") |
| | | @PreAuthorize("@ss.hasPermission('mes:dashboard:query')") |
| | | public CommonResult<MesDashboardInventoryRespVO> getInventoryStatistics() { |
| | | return success(dashboardService.getInventoryStatistics()); |
| | | } |
| | | |
| | | @GetMapping("/outbound-statistics") |
| | | @Operation(summary = "è·å¾åºåºç»´åº¦ç»è®¡") |
| | | @Parameter(name = "days", description = "天æ°", example = "30") |
| | | @PreAuthorize("@ss.hasPermission('mes:dashboard:query')") |
| | | public CommonResult<MesDashboardOutboundRespVO> getOutboundStatistics( |
| | | @RequestParam(value = "days", defaultValue = "30") @Min(1) @Max(365) Integer days) { |
| | | return success(dashboardService.getOutboundStatistics(days)); |
| | | } |
| | | |
| | | @GetMapping("/trace-statistics") |
| | | @Operation(summary = "è·å¾è¿½æº¯ç»´åº¦ç»è®¡") |
| | | @Parameter(name = "days", description = "天æ°", example = "30") |
| | | @PreAuthorize("@ss.hasPermission('mes:dashboard:query')") |
| | | public CommonResult<MesDashboardTraceRespVO> getTraceStatistics( |
| | | @RequestParam(value = "days", defaultValue = "30") @Min(1) @Max(365) Integer days) { |
| | | return success(dashboardService.getTraceStatistics(days)); |
| | | } |
| | | |
| | | @GetMapping("/export-excel") |
| | | @Operation(summary = "导åºç®¡çé©¾é©¶è± Excel æ¥è¡¨") |
| | | @ApiAccessLog(operateType = EXPORT) |
| | | @PreAuthorize("@ss.hasPermission('mes:dashboard:export')") |
| | | public void exportExcel(HttpServletResponse response) throws IOException { |
| | | // 1. ç»è£
å维度æ¥è¡¨æ°æ® |
| | | MesDashboardSummaryRespVO summary = dashboardService.getSummary(); |
| | | List<MesDashboardProductionTrendRespVO> productionTrend = dashboardService.getProductionTrend(30); |
| | | MesDashboardQualityRespVO quality = dashboardService.getQualityStatistics(); |
| | | MesDashboardInventoryRespVO inventory = dashboardService.getInventoryStatistics(); |
| | | MesDashboardOutboundRespVO outbound = dashboardService.getOutboundStatistics(30); |
| | | MesDashboardTraceRespVO trace = dashboardService.getTraceStatistics(30); |
| | | |
| | | // 2. å¤ sheet åå
¥ |
| | | ExcelWriter excelWriter = FastExcelFactory.write(response.getOutputStream()) |
| | | .autoCloseStream(false) |
| | | .build(); |
| | | try { |
| | | excelWriter.write(buildOverviewRows(summary), FastExcelFactory.writerSheet("é©¾é©¶è±æ»è§").build()); |
| | | excelWriter.write(buildProductionRows(summary, productionTrend), FastExcelFactory.writerSheet("ç产ç»è®¡").build()); |
| | | excelWriter.write(buildQualityRows(quality), FastExcelFactory.writerSheet("è´¨æ£ç»è®¡").build()); |
| | | excelWriter.write(buildInventoryRows(inventory), FastExcelFactory.writerSheet("åºåç»è®¡").build()); |
| | | excelWriter.write(buildOutboundRows(outbound), FastExcelFactory.writerSheet("åºåºç»è®¡").build()); |
| | | excelWriter.write(buildTraceRows(trace), FastExcelFactory.writerSheet("追溯ç»è®¡").build()); |
| | | } finally { |
| | | excelWriter.finish(); |
| | | excelWriter.close(); |
| | | } |
| | | response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8("管ç驾驶è±ç»è®¡æ¥è¡¨.xls")); |
| | | response.setContentType("application/vnd.ms-excel;charset=UTF-8"); |
| | | } |
| | | |
| | | // ========== Excel è¡ç»è£
========== |
| | | |
| | | private List<MesDashboardOverviewExcelVO> buildOverviewRows(MesDashboardSummaryRespVO summary) { |
| | | List<MesDashboardOverviewExcelVO> rows = new ArrayList<>(); |
| | | addOverview(rows, "æ¹æ¬¡æ»æ°", summary.getBatchTotal()); |
| | | addOverview(rows, "çäº§ä¸æ¹æ¬¡", summary.getBatchProducing()); |
| | | addOverview(rows, "å·²å®ææ¹æ¬¡", summary.getBatchFinished()); |
| | | addOverview(rows, "å·²ä½åºæ¹æ¬¡", summary.getBatchCanceled()); |
| | | addOverview(rows, "è¢ç æ»æ°", summary.getBagCodeTotal()); |
| | | addOverview(rows, "已导åºå°å·è¢ç ", summary.getBagCodePrinted()); |
| | | addOverview(rows, "å·²ä½åºè¢ç ", summary.getBagCodeCanceled()); |
| | | addOverview(rows, "æçç æ»æ°", summary.getPalletTotal()); |
| | | addOverview(rows, "仿¥æ«ç å
¥åºè¢æ°", summary.getTodayInboundBagCount()); |
| | | addOverview(rows, "æ£éªè®°å½æ»æ°", summary.getQualityTotal()); |
| | | addOverview(rows, "åæ ¼æ£éªè®°å½", summary.getQualityPass()); |
| | | addOverview(rows, "ä¸åæ ¼æ£éªè®°å½", summary.getQualityFail()); |
| | | addOverview(rows, "æ£éªåæ ¼ç(%)", summary.getQualityPassRate()); |
| | | addOverview(rows, "å½ååºåæ»é", summary.getStockTotal()); |
| | | addOverview(rows, "æååºåé", summary.getStockTemporary()); |
| | | addOverview(rows, "åæ ¼åºåé", summary.getStockQualified()); |
| | | addOverview(rows, "ä½åºåé¢è¦æ°", summary.getLowStockCount()); |
| | | addOverview(rows, "åºåºåæ»æ°", summary.getOutboundOrderTotal()); |
| | | addOverview(rows, "åºåºæ»é", summary.getOutboundQuantityTotal()); |
| | | addOverview(rows, "仿¥åºåºé", summary.getTodayOutboundQuantity()); |
| | | addOverview(rows, "æ«ç æ»æ°", summary.getScanTotal()); |
| | | addOverview(rows, "仿¥æ«ç æ°", summary.getTodayScanCount()); |
| | | addOverview(rows, "çªè´§é¢è¦æ»æ°", summary.getCrossRegionWarningTotal()); |
| | | addOverview(rows, "å¾
å¤ççªè´§é¢è¦", summary.getCrossRegionPendingCount()); |
| | | return rows; |
| | | } |
| | | |
| | | private List<MesDashboardProductionExcelVO> buildProductionRows(MesDashboardSummaryRespVO summary, |
| | | List<MesDashboardProductionTrendRespVO> trend) { |
| | | List<MesDashboardProductionExcelVO> rows = new ArrayList<>(); |
| | | addProduction(rows, "æ¹æ¬¡", "è稿", summary.getBatchDraft()); |
| | | addProduction(rows, "æ¹æ¬¡", "ç产ä¸", summary.getBatchProducing()); |
| | | addProduction(rows, "æ¹æ¬¡", "已宿", summary.getBatchFinished()); |
| | | addProduction(rows, "æ¹æ¬¡", "å·²ä½åº", summary.getBatchCanceled()); |
| | | addProduction(rows, "è¢ç ", "å·²çæ", summary.getBagCodeGenerated()); |
| | | addProduction(rows, "è¢ç ", "已导åºå°å·", summary.getBagCodePrinted()); |
| | | addProduction(rows, "è¢ç ", "å·²ä½åº", summary.getBagCodeCanceled()); |
| | | addProduction(rows, "æç", "æ»æ°", summary.getPalletTotal()); |
| | | addProduction(rows, "å
¥åº", "仿¥æ«ç å
¥åºè¢æ°", summary.getTodayInboundBagCount()); |
| | | for (MesDashboardProductionTrendRespVO item : trend) { |
| | | addProduction(rows, "è¶å¿-" + item.getDate(), "æ°å¢æ¹æ¬¡", item.getBatchCount()); |
| | | addProduction(rows, "è¶å¿-" + item.getDate(), "æ°å¢è¢ç ", item.getBagCodeCount()); |
| | | addProduction(rows, "è¶å¿-" + item.getDate(), "æ°å¢æç", item.getPalletCount()); |
| | | addProduction(rows, "è¶å¿-" + item.getDate(), "å
¥åºè¢æ°", item.getInboundBagQuantity() == null ? 0L |
| | | : item.getInboundBagQuantity().longValue()); |
| | | } |
| | | return rows; |
| | | } |
| | | |
| | | private List<MesDashboardGenericExcelVO> buildQualityRows(MesDashboardQualityRespVO quality) { |
| | | List<MesDashboardGenericExcelVO> rows = new ArrayList<>(); |
| | | for (MesDashboardNameCountRespVO item : quality.getResultDistribution()) { |
| | | addGeneric(rows, "æ£éªç»æ", item.getName(), item.getCount()); |
| | | } |
| | | for (MesDashboardIndicatorAvgRespVO item : quality.getIndicatorList()) { |
| | | addGeneric(rows, "ææ åå¼", item.getIndicatorName() + "ï¼å¹³åå¼ï¼", item.getAverageValue()); |
| | | addGeneric(rows, "ææ åå¼", item.getIndicatorName() + "ï¼åæ ¼ç%ï¼", item.getPassRate()); |
| | | } |
| | | addEmptyGuard(rows); |
| | | return rows; |
| | | } |
| | | |
| | | private List<MesDashboardGenericExcelVO> buildInventoryRows(MesDashboardInventoryRespVO inventory) { |
| | | List<MesDashboardGenericExcelVO> rows = new ArrayList<>(); |
| | | for (MesDashboardNameCountRespVO item : inventory.getWarehouseDistribution()) { |
| | | addGeneric(rows, "æä»åº", item.getName(), item.getCount()); |
| | | } |
| | | for (MesDashboardNameCountRespVO item : inventory.getItemDistribution()) { |
| | | addGeneric(rows, "æåç§", item.getName(), item.getCount()); |
| | | } |
| | | for (MesDashboardNameCountRespVO item : inventory.getStateDistribution()) { |
| | | addGeneric(rows, "æç¶æ", item.getName(), item.getCount()); |
| | | } |
| | | addEmptyGuard(rows); |
| | | return rows; |
| | | } |
| | | |
| | | private List<MesDashboardGenericExcelVO> buildOutboundRows(MesDashboardOutboundRespVO outbound) { |
| | | List<MesDashboardGenericExcelVO> rows = new ArrayList<>(); |
| | | for (MesDashboardDailyCountRespVO item : outbound.getTrend()) { |
| | | addGeneric(rows, "åºåºè¶å¿", item.getDate(), item.getCount()); |
| | | } |
| | | for (MesDashboardNameCountRespVO item : outbound.getDealerDistribution()) { |
| | | addGeneric(rows, "æç»éå", item.getName(), item.getCount()); |
| | | } |
| | | addEmptyGuard(rows); |
| | | return rows; |
| | | } |
| | | |
| | | private List<MesDashboardGenericExcelVO> buildTraceRows(MesDashboardTraceRespVO trace) { |
| | | List<MesDashboardGenericExcelVO> rows = new ArrayList<>(); |
| | | for (MesDashboardDailyCountRespVO item : trace.getScanTrend()) { |
| | | addGeneric(rows, "æ«ç è¶å¿", item.getDate(), item.getCount()); |
| | | } |
| | | for (MesDashboardNameCountRespVO item : trace.getProvinceDistribution()) { |
| | | addGeneric(rows, "æ«ç ç份", item.getName(), item.getCount()); |
| | | } |
| | | for (MesDashboardNameCountRespVO item : trace.getChannelDistribution()) { |
| | | addGeneric(rows, "æ«ç æ¸ é", item.getName(), item.getCount()); |
| | | } |
| | | if (trace.getCrossRegionSummary() != null) { |
| | | addGeneric(rows, "çªè´§é¢è¦", "æ»æ°", trace.getCrossRegionSummary().getTotal()); |
| | | addGeneric(rows, "çªè´§é¢è¦", "å¾
å¤ç", trace.getCrossRegionSummary().getPending()); |
| | | addGeneric(rows, "çªè´§é¢è¦", "å·²å¤ç", trace.getCrossRegionSummary().getHandled()); |
| | | addGeneric(rows, "çªè´§é¢è¦", "误æ¥", trace.getCrossRegionSummary().getFalseAlarm()); |
| | | addGeneric(rows, "çªè´§é¢è¦", "è·¨ç", trace.getCrossRegionSummary().getCrossProvince()); |
| | | addGeneric(rows, "çªè´§é¢è¦", "è·¨å¸", trace.getCrossRegionSummary().getCrossCity()); |
| | | } |
| | | addEmptyGuard(rows); |
| | | return rows; |
| | | } |
| | | |
| | | /** |
| | | * ç©ºæ°æ®å
åºï¼ä¿è¯æ¯ä¸ª sheet è³å°æä¸è¡ï¼é¿å
FastExcel æ æ³æ¨æè¡¨å¤´ |
| | | */ |
| | | private void addEmptyGuard(List<MesDashboardGenericExcelVO> rows) { |
| | | if (rows.isEmpty()) { |
| | | rows.add(new MesDashboardGenericExcelVO("æç¤º", "ææ æ°æ®", "0")); |
| | | } |
| | | } |
| | | |
| | | private void addOverview(List<MesDashboardOverviewExcelVO> rows, String indicator, Object value) { |
| | | rows.add(new MesDashboardOverviewExcelVO(indicator, value == null ? "0" : value.toString())); |
| | | } |
| | | |
| | | private void addProduction(List<MesDashboardProductionExcelVO> rows, String category, String status, Object count) { |
| | | rows.add(new MesDashboardProductionExcelVO(category, status, count == null ? 0L : ((Number) count).longValue())); |
| | | } |
| | | |
| | | private void addGeneric(List<MesDashboardGenericExcelVO> rows, String category, String name, Object value) { |
| | | rows.add(new MesDashboardGenericExcelVO(category, name, value == null ? "0" : value.toString())); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * 管çåå° - 管çé©¾é©¶è±ææ¥æ°éè¶å¿é¡¹ Response VO |
| | | * <p> |
| | | * ç¨äºåºåºææ¥è¶å¿ã追溯æ«ç ææ¥è¶å¿çã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 管çé©¾é©¶è±ææ¥æ°éè¶å¿é¡¹ Response VO") |
| | | @Data |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class MesDashboardDailyCountRespVO { |
| | | |
| | | @Schema(description = "æ¥æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08-13") |
| | | private String date; |
| | | |
| | | @Schema(description = "æ°é", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") |
| | | private BigDecimal count; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±éç¨å¯¼åº VOï¼ç±»å«-åç§°-æ°é ä¸åï¼ |
| | | * <p> |
| | | * ç¨äºè´¨æ£/åºå/åºåº/追溯维度çåå¸è¡¨å¯¼åºã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Data |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @ExcelIgnoreUnannotated |
| | | public class MesDashboardGenericExcelVO { |
| | | |
| | | @ExcelProperty("ç±»å«") |
| | | private String category; |
| | | |
| | | @ExcelProperty("åç§°") |
| | | private String name; |
| | | |
| | | @ExcelProperty("æ°å¼") |
| | | private String value; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±è´¨æ£ææ åå¼ Response VO |
| | | * <p> |
| | | * ç§ååé¡¹ææ ï¼çº¯åº¦ãå度ãåè½çãæ°´å |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 管ç驾驶è±è´¨æ£ææ åå¼ Response VO") |
| | | @Data |
| | | public class MesDashboardIndicatorAvgRespVO { |
| | | |
| | | @Schema(description = "ææ ç¼ç ", requiredMode = Schema.RequiredMode.REQUIRED, example = "PURITY") |
| | | private String indicatorCode; |
| | | |
| | | @Schema(description = "ææ åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "纯度") |
| | | private String indicatorName; |
| | | |
| | | @Schema(description = "æ£éªæ¬¡æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") |
| | | private Long checkCount; |
| | | |
| | | @Schema(description = "ææ å¹³åå¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "98.500000") |
| | | private BigDecimal averageValue; |
| | | |
| | | @Schema(description = "ææ åæ ¼çï¼%ï¼ï¼è½å¨æ ååºé´å
çå æ¯", requiredMode = Schema.RequiredMode.REQUIRED, example = "90.00") |
| | | private BigDecimal passRate; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±åºå维度ç»è®¡ Response VO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 管ç驾驶è±åºå维度ç»è®¡ Response VO") |
| | | @Data |
| | | public class MesDashboardInventoryRespVO { |
| | | |
| | | @Schema(description = "æä»åºåå¸", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<MesDashboardNameCountRespVO> warehouseDistribution; |
| | | |
| | | @Schema(description = "æåç§åå¸", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<MesDashboardNameCountRespVO> itemDistribution; |
| | | |
| | | @Schema(description = "æåºåç¶æåå¸ï¼æååº/åæ ¼åº/å·²åºåºï¼", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<MesDashboardNameCountRespVO> stateDistribution; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±éç¨åç§°-æ°éåå¸é¡¹ Response VO |
| | | * <p> |
| | | * ç¨äºè´¨æ£ç»æåå¸ãåºåæä»åº/åç§/ç¶æåå¸ãåºåºç»éååå¸ã追溯ç份/æ¸ éåå¸çã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 管ç驾驶è±éç¨åç§°-æ°éåå¸é¡¹ Response VO") |
| | | @Data |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class MesDashboardNameCountRespVO { |
| | | |
| | | @Schema(description = "åç§°ï¼ä»åºå/åç§å/ç¶æå/ç»éåå/ç份çï¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "åæ ¼åº") |
| | | private String name; |
| | | |
| | | @Schema(description = "æ°é", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") |
| | | private Long count; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±åºåºç»´åº¦ç»è®¡ Response VO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 管ç驾驶è±åºåºç»´åº¦ç»è®¡ Response VO") |
| | | @Data |
| | | public class MesDashboardOutboundRespVO { |
| | | |
| | | @Schema(description = "åºåºææ¥è¶å¿", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<MesDashboardDailyCountRespVO> trend; |
| | | |
| | | @Schema(description = "æç»éååºåºåå¸ï¼TOP 20ï¼", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<MesDashboardNameCountRespVO> dealerDistribution; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * 管çåå° - 管çé©¾é©¶è±æ»è§å¯¼åº VOï¼ææ -æ°å¼ 两åï¼ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Data |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @ExcelIgnoreUnannotated |
| | | public class MesDashboardOverviewExcelVO { |
| | | |
| | | @ExcelProperty("ææ ") |
| | | private String indicator; |
| | | |
| | | @ExcelProperty("æ°å¼") |
| | | private String value; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±çäº§ç»´åº¦å¯¼åº VOï¼ç±»å«-ç¶æ-æ°é ä¸åï¼ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Data |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @ExcelIgnoreUnannotated |
| | | public class MesDashboardProductionExcelVO { |
| | | |
| | | @ExcelProperty("ç±»å«") |
| | | private String category; |
| | | |
| | | @ExcelProperty("ç¶æ") |
| | | private String statusName; |
| | | |
| | | @ExcelProperty("æ°é") |
| | | private Long count; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±ç产è¶å¿ Response VO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 管ç驾驶è±ç产è¶å¿ Response VO") |
| | | @Data |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class MesDashboardProductionTrendRespVO { |
| | | |
| | | @Schema(description = "æ¥æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08-13") |
| | | private String date; |
| | | |
| | | @Schema(description = "彿¥æ°å¢æ¹æ¬¡æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long batchCount; |
| | | |
| | | @Schema(description = "彿¥æ°å¢è¢ç æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") |
| | | private Long bagCodeCount; |
| | | |
| | | @Schema(description = "彿¥æ°å¢æçç æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| | | private Long palletCount; |
| | | |
| | | @Schema(description = "彿¥æ«ç å
¥åºè¢æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") |
| | | private BigDecimal inboundBagQuantity; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±è´¨æ£ç»´åº¦ç»è®¡ Response VO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 管ç驾驶è±è´¨æ£ç»´åº¦ç»è®¡ Response VO") |
| | | @Data |
| | | public class MesDashboardQualityRespVO { |
| | | |
| | | @Schema(description = "æ£éªç»æåå¸ï¼å¤çä¸/åæ ¼/ä¸åæ ¼ï¼", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<MesDashboardNameCountRespVO> resultDistribution; |
| | | |
| | | @Schema(description = "åé¡¹ææ åå¼ä¸åæ ¼ç", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<MesDashboardIndicatorAvgRespVO> indicatorList; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±äºç»´åº¦æ±æ»ç»è®¡ Response VO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 管çé©¾é©¶è±æ±æ»ç»è®¡ Response VO") |
| | | @Data |
| | | public class MesDashboardSummaryRespVO { |
| | | |
| | | // ========== ç产维度 ========== |
| | | |
| | | @Schema(description = "æ¹æ¬¡æ»æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "103") |
| | | private Long batchTotal; |
| | | @Schema(description = "èç¨¿æ¹æ¬¡æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long batchDraft; |
| | | @Schema(description = "çäº§ä¸æ¹æ¬¡æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long batchProducing; |
| | | @Schema(description = "å·²å®ææ¹æ¬¡æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long batchFinished; |
| | | @Schema(description = "å·²ä½åºæ¹æ¬¡æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long batchCanceled; |
| | | @Schema(description = "è¢ç æ»æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "1101") |
| | | private Long bagCodeTotal; |
| | | @Schema(description = "å·²çæè¢ç æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long bagCodeGenerated; |
| | | @Schema(description = "已导åºå°å·è¢ç æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long bagCodePrinted; |
| | | @Schema(description = "å·²ä½åºè¢ç æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long bagCodeCanceled; |
| | | @Schema(description = "æçç æ»æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "11") |
| | | private Long palletTotal; |
| | | @Schema(description = "仿¥æ«ç å
¥åºè¢æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long todayInboundBagCount; |
| | | |
| | | // ========== è´¨æ£ç»´åº¦ ========== |
| | | |
| | | @Schema(description = "æ£éªè®°å½æ»æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long qualityTotal; |
| | | @Schema(description = "å¤ç䏿£éªè®°å½æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long qualityPending; |
| | | @Schema(description = "åæ ¼æ£éªè®°å½æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long qualityPass; |
| | | @Schema(description = "ä¸åæ ¼æ£éªè®°å½æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long qualityFail; |
| | | @Schema(description = "æ£éªåæ ¼çï¼%ï¼ï¼å¦ 95.50", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") |
| | | private BigDecimal qualityPassRate; |
| | | |
| | | // ========== åºå维度 ========== |
| | | |
| | | @Schema(description = "å½ååºåæ»é", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private BigDecimal stockTotal; |
| | | @Schema(description = "æååºåé", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private BigDecimal stockTemporary; |
| | | @Schema(description = "åæ ¼åºåé", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private BigDecimal stockQualified; |
| | | @Schema(description = "å·²åºåºæ°é", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private BigDecimal stockOutbound; |
| | | @Schema(description = "å½åä½åºåé¢è¦è®°å½æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long lowStockCount; |
| | | |
| | | // ========== åºåºç»´åº¦ ========== |
| | | |
| | | @Schema(description = "åºåºåæ»æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long outboundOrderTotal; |
| | | @Schema(description = "åºåºæ»é", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private BigDecimal outboundQuantityTotal; |
| | | @Schema(description = "仿¥åºåºé", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private BigDecimal todayOutboundQuantity; |
| | | |
| | | // ========== 追溯维度 ========== |
| | | |
| | | @Schema(description = "æ«ç æ»æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "7") |
| | | private Long scanTotal; |
| | | @Schema(description = "仿¥æ«ç æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long todayScanCount; |
| | | @Schema(description = "æ«ç æåæ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "6") |
| | | private Long scanSuccessCount; |
| | | @Schema(description = "çªè´§é¢è¦æ»æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long crossRegionWarningTotal; |
| | | @Schema(description = "å¾
å¤ççªè´§é¢è¦æ°", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | private Long crossRegionPendingCount; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo; |
| | | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.trace.crossregion.vo.MesTraceCrossRegionSummaryRespVO; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 管çåå° - 管ç驾驶è±è¿½æº¯ç»´åº¦ç»è®¡ Response VO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 管ç驾驶è±è¿½æº¯ç»´åº¦ç»è®¡ Response VO") |
| | | @Data |
| | | public class MesDashboardTraceRespVO { |
| | | |
| | | @Schema(description = "æ¶è´¹è
æ«ç ææ¥è¶å¿", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<MesDashboardDailyCountRespVO> scanTrend; |
| | | |
| | | @Schema(description = "æ«ç ç份åå¸", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<MesDashboardNameCountRespVO> provinceDistribution; |
| | | |
| | | @Schema(description = "æ«ç æ¸ éåå¸ï¼å¾®ä¿¡/æ¯ä»å®/æµè§å¨ï¼", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private List<MesDashboardNameCountRespVO> channelDistribution; |
| | | |
| | | @Schema(description = "çªè´§é¢è¦æ±æ»", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private MesTraceCrossRegionSummaryRespVO crossRegionSummary; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.dashboard; |
| | | |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 管ç驾驶è±ç»è®¡ Mapper |
| | | * <p> |
| | | * æå SQL èåç产ãè´¨æ£ãåºåãåºåºã追溯äºå¤§ç»´åº¦æ ¸å¿ææ ã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Mapper |
| | | public interface MesDashboardMapper { |
| | | |
| | | // ========== ç产维度 ========== |
| | | |
| | | /** æç¶æç»è®¡æ¹æ¬¡æ°éï¼è¿å [{ "status": 10, "count": 5 }] */ |
| | | List<Map<String, Object>> selectBatchCountGroupByStatus(); |
| | | |
| | | /** æç¶æç»è®¡è¢ç æ°éï¼è¿å [{ "status": 10, "count": 5 }] */ |
| | | List<Map<String, Object>> selectBagCodeCountGroupByStatus(); |
| | | |
| | | /** ç»è®¡æçç æ»æ° */ |
| | | Long selectPalletTotal(); |
| | | |
| | | /** ç»è®¡ä»æ¥æ«ç å
¥åºè¢æ°ï¼æ«ç æåäºä»¶æ°éä¹åï¼ */ |
| | | Long selectTodayInboundBagCount(@Param("beginTime") LocalDateTime beginTime); |
| | | |
| | | /** æå¤©è忰墿¹æ¬¡æ°ï¼è¿å [{ "date": "2026-08-13", "count": 1 }] */ |
| | | List<Map<String, Object>> selectDailyBatchCount(@Param("beginTime") LocalDateTime beginTime, |
| | | @Param("endTime") LocalDateTime endTime); |
| | | |
| | | /** æå¤©èåæ°å¢è¢ç æ° */ |
| | | List<Map<String, Object>> selectDailyBagCodeCount(@Param("beginTime") LocalDateTime beginTime, |
| | | @Param("endTime") LocalDateTime endTime); |
| | | |
| | | /** æå¤©è忰墿çç æ° */ |
| | | List<Map<String, Object>> selectDailyPalletCount(@Param("beginTime") LocalDateTime beginTime, |
| | | @Param("endTime") LocalDateTime endTime); |
| | | |
| | | /** æå¤©èåæ«ç å
¥åºè¢æ°ï¼æ«ç æåäºä»¶æ°éä¹åï¼ï¼è¿å [{ "date": "...", "quantity": 100 }] */ |
| | | List<Map<String, Object>> selectDailyInboundBagQuantity(@Param("beginTime") LocalDateTime beginTime, |
| | | @Param("endTime") LocalDateTime endTime); |
| | | |
| | | // ========== è´¨æ£ç»´åº¦ ========== |
| | | |
| | | /** ææ£éªç»æç»è®¡æ°éï¼è¿å [{ "result": 1, "count": 5 }] */ |
| | | List<Map<String, Object>> selectQualityCountGroupByResult(); |
| | | |
| | | /** ææ åå¼ä¸åæ ¼çï¼è¿å [{ "indicatorCode", "checkCount", "averageValue", "passRate" }] */ |
| | | List<Map<String, Object>> selectIndicatorStatistics(); |
| | | |
| | | // ========== åºå维度 ========== |
| | | |
| | | /** æåºåç¶ææ±æ»æ°éï¼è¿å [{ "stockState": 10, "quantity": 100 }] */ |
| | | List<Map<String, Object>> selectStockGroupByState(); |
| | | |
| | | /** æä»åºæ±æ»æ°éï¼è¿å [{ "name": "ä»åºA", "quantity": 100 }] */ |
| | | List<Map<String, Object>> selectStockGroupByWarehouse(); |
| | | |
| | | /** æåç§æ±æ»æ°éï¼è¿å [{ "name": "æ£è±", "quantity": 100 }] */ |
| | | List<Map<String, Object>> selectStockGroupByItem(); |
| | | |
| | | // ========== åºåºç»´åº¦ ========== |
| | | |
| | | /** åºåºæ±æ»ï¼åæ°/æ»é/仿¥åºåºéï¼ï¼è¿ååè¡ Mapï¼{ orderTotal, quantityTotal, todayQuantity } */ |
| | | Map<String, Object> selectOutboundSummary(@Param("beginTime") LocalDateTime beginTime); |
| | | |
| | | /** æå¤©èååºåºéï¼è¿å [{ "date": "...", "quantity": 100 }] */ |
| | | List<Map<String, Object>> selectOutboundDailyQuantity(@Param("beginTime") LocalDateTime beginTime, |
| | | @Param("endTime") LocalDateTime endTime); |
| | | |
| | | /** æç»éåæ±æ»åºåºéï¼TOP 20ï¼ï¼è¿å [{ "name": "ç»éåA", "quantity": 100 }] */ |
| | | List<Map<String, Object>> selectOutboundGroupByClient(); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.dashboard; |
| | | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardInventoryRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardOutboundRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardProductionTrendRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardQualityRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardSummaryRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardTraceRespVO; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 管ç驾驶è±ç»è®¡ Service æ¥å£ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | public interface MesDashboardService { |
| | | |
| | | /** |
| | | * è·å¾ç®¡ç驾驶è±äºç»´åº¦æ±æ»ç»è®¡ |
| | | * |
| | | * @return æ±æ»ç»è®¡ |
| | | */ |
| | | MesDashboardSummaryRespVO getSummary(); |
| | | |
| | | /** |
| | | * è·å¾ç产è¶å¿ï¼è¿ N å¤©ï¼ææ¥ï¼ |
| | | * |
| | | * @param days å¤©æ° |
| | | * @return ç产è¶å¿ |
| | | */ |
| | | List<MesDashboardProductionTrendRespVO> getProductionTrend(Integer days); |
| | | |
| | | /** |
| | | * è·å¾è´¨æ£ç»´åº¦ç»è®¡ |
| | | * |
| | | * @return è´¨æ£ç»è®¡ |
| | | */ |
| | | MesDashboardQualityRespVO getQualityStatistics(); |
| | | |
| | | /** |
| | | * è·å¾åºå维度ç»è®¡ |
| | | * |
| | | * @return åºåç»è®¡ |
| | | */ |
| | | MesDashboardInventoryRespVO getInventoryStatistics(); |
| | | |
| | | /** |
| | | * è·å¾åºåºç»´åº¦ç»è®¡ï¼è¿ N å¤©ï¼ |
| | | * |
| | | * @param days å¤©æ° |
| | | * @return åºåºç»è®¡ |
| | | */ |
| | | MesDashboardOutboundRespVO getOutboundStatistics(Integer days); |
| | | |
| | | /** |
| | | * è·å¾è¿½æº¯ç»´åº¦ç»è®¡ï¼è¿ N å¤©ï¼ |
| | | * |
| | | * @param days å¤©æ° |
| | | * @return 追溯ç»è®¡ |
| | | */ |
| | | MesDashboardTraceRespVO getTraceStatistics(Integer days); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.dashboard; |
| | | |
| | | import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardDailyCountRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardIndicatorAvgRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardInventoryRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardNameCountRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardOutboundRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardProductionTrendRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardQualityRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardSummaryRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dashboard.vo.MesDashboardTraceRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.trace.consumer.vo.MesTraceConsumerScanDistributionReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.trace.consumer.vo.MesTraceConsumerScanDistributionRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.trace.consumer.vo.MesTraceConsumerScanSummaryRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.trace.consumer.vo.MesTraceConsumerScanTrendRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.trace.crossregion.vo.MesTraceCrossRegionSummaryRespVO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.dashboard.MesDashboardMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockStateEnum; |
| | | import cn.iocoder.yudao.module.mes.service.trace.consumer.MesTraceConsumerScanStatisticsService; |
| | | import cn.iocoder.yudao.module.mes.service.trace.crossregion.MesTraceCrossRegionService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warning.MesWmStockWarningService; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 管ç驾驶è±ç»è®¡ Service å®ç°ç±» |
| | | * <p> |
| | | * æ´åç产ãè´¨æ£ãåºåãåºåºã追溯äºå¤§ç»´åº¦æ ¸å¿ææ ã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Service |
| | | public class MesDashboardServiceImpl implements MesDashboardService { |
| | | |
| | | /** ææ ç¼ç -> ææ åç§° */ |
| | | private static final Map<String, String> INDICATOR_NAME_MAP = Map.of( |
| | | "purity", "纯度", |
| | | "cleanliness", "å度", |
| | | "germination_rate", "åè½ç", |
| | | "moisture", "æ°´å"); |
| | | |
| | | @Resource |
| | | private MesDashboardMapper dashboardMapper; |
| | | @Resource |
| | | private MesTraceConsumerScanStatisticsService traceScanStatisticsService; |
| | | @Resource |
| | | private MesTraceCrossRegionService traceCrossRegionService; |
| | | @Resource |
| | | private MesWmStockWarningService stockWarningService; |
| | | |
| | | @Override |
| | | public MesDashboardSummaryRespVO getSummary() { |
| | | MesDashboardSummaryRespVO summary = new MesDashboardSummaryRespVO(); |
| | | |
| | | // 1. ç产维度 |
| | | List<Map<String, Object>> batchStats = dashboardMapper.selectBatchCountGroupByStatus(); |
| | | summary.setBatchTotal(getTotalCount(batchStats)); |
| | | summary.setBatchDraft(getCountByStatus(batchStats, MesProBatchStatusEnum.DRAFT.getStatus())); |
| | | summary.setBatchProducing(getCountByStatus(batchStats, MesProBatchStatusEnum.PRODUCING.getStatus())); |
| | | summary.setBatchFinished(getCountByStatus(batchStats, MesProBatchStatusEnum.FINISHED.getStatus())); |
| | | summary.setBatchCanceled(getCountByStatus(batchStats, MesProBatchStatusEnum.CANCELED.getStatus())); |
| | | |
| | | List<Map<String, Object>> bagCodeStats = dashboardMapper.selectBagCodeCountGroupByStatus(); |
| | | summary.setBagCodeTotal(getTotalCount(bagCodeStats)); |
| | | summary.setBagCodeGenerated(getCountByStatus(bagCodeStats, MesProBagCodeStatusEnum.GENERATED.getStatus())); |
| | | summary.setBagCodePrinted(getCountByStatus(bagCodeStats, MesProBagCodeStatusEnum.PRINTED.getStatus())); |
| | | summary.setBagCodeCanceled(getCountByStatus(bagCodeStats, MesProBagCodeStatusEnum.CANCELED.getStatus())); |
| | | |
| | | summary.setPalletTotal(getLong(dashboardMapper.selectPalletTotal())); |
| | | summary.setTodayInboundBagCount(getLong(dashboardMapper.selectTodayInboundBagCount(LocalDateTimeUtils.getToday()))); |
| | | |
| | | // 2. è´¨æ£ç»´åº¦ |
| | | List<Map<String, Object>> qualityStats = dashboardMapper.selectQualityCountGroupByResult(); |
| | | long qualityPending = getCountByResult(qualityStats, 0); |
| | | long qualityPass = getCountByResult(qualityStats, 1); |
| | | long qualityFail = getCountByResult(qualityStats, 2); |
| | | summary.setQualityTotal(qualityPending + qualityPass + qualityFail); |
| | | summary.setQualityPending(qualityPending); |
| | | summary.setQualityPass(qualityPass); |
| | | summary.setQualityFail(qualityFail); |
| | | summary.setQualityPassRate(calcPercent(qualityPass, qualityPass + qualityFail)); |
| | | |
| | | // 3. åºå维度 |
| | | List<Map<String, Object>> stockStateStats = dashboardMapper.selectStockGroupByState(); |
| | | BigDecimal stockTemporary = getStockQuantityByState(stockStateStats, MesWmStockStateEnum.TEMP.getState()); |
| | | BigDecimal stockQualified = getStockQuantityByState(stockStateStats, MesWmStockStateEnum.QUALIFIED.getState()); |
| | | BigDecimal stockOutbound = getStockQuantityByState(stockStateStats, MesWmStockStateEnum.OUT.getState()); |
| | | summary.setStockTemporary(stockTemporary); |
| | | summary.setStockQualified(stockQualified); |
| | | summary.setStockOutbound(stockOutbound); |
| | | summary.setStockTotal(stockTemporary.add(stockQualified).add(stockOutbound)); |
| | | summary.setLowStockCount((long) stockWarningService.getCurrentWarnings().size()); |
| | | |
| | | // 4. åºåºç»´åº¦ |
| | | Map<String, Object> outboundSummary = dashboardMapper.selectOutboundSummary(LocalDateTimeUtils.getToday()); |
| | | summary.setOutboundOrderTotal(getLong(outboundSummary == null ? null : outboundSummary.get("orderTotal"))); |
| | | summary.setOutboundQuantityTotal(getDecimal(outboundSummary == null ? null : outboundSummary.get("quantityTotal"))); |
| | | summary.setTodayOutboundQuantity(getDecimal(outboundSummary == null ? null : outboundSummary.get("todayQuantity"))); |
| | | |
| | | // 5. 追溯维度 |
| | | MesTraceConsumerScanSummaryRespVO scanAll = traceScanStatisticsService.getSummary(null, null); |
| | | MesTraceConsumerScanSummaryRespVO scanToday = traceScanStatisticsService.getSummary(LocalDateTimeUtils.getToday(), null); |
| | | MesTraceCrossRegionSummaryRespVO crossRegion = traceCrossRegionService.getWarningSummary(null, null); |
| | | summary.setScanTotal(scanAll == null ? 0L : getLong(scanAll.getTotal())); |
| | | summary.setScanSuccessCount(scanAll == null ? 0L : getLong(scanAll.getSuccess())); |
| | | summary.setTodayScanCount(scanToday == null ? 0L : getLong(scanToday.getTotal())); |
| | | summary.setCrossRegionWarningTotal(crossRegion == null ? 0L : getLong(crossRegion.getTotal())); |
| | | summary.setCrossRegionPendingCount(crossRegion == null ? 0L : getLong(crossRegion.getPending())); |
| | | |
| | | return summary; |
| | | } |
| | | |
| | | @Override |
| | | public List<MesDashboardProductionTrendRespVO> getProductionTrend(Integer days) { |
| | | LocalDate endDate = LocalDate.now().plusDays(1); |
| | | LocalDate startDate = endDate.minusDays(days); |
| | | LocalDateTime beginTime = startDate.atStartOfDay(); |
| | | LocalDateTime endTime = endDate.atStartOfDay(); |
| | | |
| | | Map<String, Map<String, Object>> batchMap = toDateMap(dashboardMapper.selectDailyBatchCount(beginTime, endTime)); |
| | | Map<String, Map<String, Object>> bagCodeMap = toDateMap(dashboardMapper.selectDailyBagCodeCount(beginTime, endTime)); |
| | | Map<String, Map<String, Object>> palletMap = toDateMap(dashboardMapper.selectDailyPalletCount(beginTime, endTime)); |
| | | Map<String, Map<String, Object>> inboundMap = toDateMap(dashboardMapper.selectDailyInboundBagQuantity(beginTime, endTime)); |
| | | |
| | | DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| | | List<MesDashboardProductionTrendRespVO> result = new ArrayList<>(); |
| | | for (LocalDate d = startDate; d.isBefore(endDate); d = d.plusDays(1)) { |
| | | String dateStr = d.format(fmt); |
| | | result.add(new MesDashboardProductionTrendRespVO( |
| | | dateStr, |
| | | getLongFromMap(batchMap.get(dateStr), "count"), |
| | | getLongFromMap(bagCodeMap.get(dateStr), "count"), |
| | | getLongFromMap(palletMap.get(dateStr), "count"), |
| | | getDecimalFromMap(inboundMap.get(dateStr), "quantity"))); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public MesDashboardQualityRespVO getQualityStatistics() { |
| | | MesDashboardQualityRespVO vo = new MesDashboardQualityRespVO(); |
| | | |
| | | // 1. æ£éªç»æåå¸ |
| | | List<MesDashboardNameCountRespVO> resultDistribution = new ArrayList<>(); |
| | | for (Map<String, Object> stat : dashboardMapper.selectQualityCountGroupByResult()) { |
| | | int result = ((Number) stat.get("result")).intValue(); |
| | | resultDistribution.add(new MesDashboardNameCountRespVO(qualityResultName(result), getLong(stat.get("count")))); |
| | | } |
| | | vo.setResultDistribution(resultDistribution); |
| | | |
| | | // 2. åé¡¹ææ åå¼ä¸åæ ¼ç |
| | | List<MesDashboardIndicatorAvgRespVO> indicatorList = new ArrayList<>(); |
| | | for (Map<String, Object> stat : dashboardMapper.selectIndicatorStatistics()) { |
| | | MesDashboardIndicatorAvgRespVO item = new MesDashboardIndicatorAvgRespVO(); |
| | | item.setIndicatorCode((String) stat.get("indicatorCode")); |
| | | item.setIndicatorName(indicatorName(item.getIndicatorCode())); |
| | | item.setCheckCount(getLong(stat.get("checkCount"))); |
| | | item.setAverageValue(getDecimal(stat.get("averageValue"))); |
| | | item.setPassRate(getDecimal(stat.get("passRate"))); |
| | | indicatorList.add(item); |
| | | } |
| | | vo.setIndicatorList(indicatorList); |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | public MesDashboardInventoryRespVO getInventoryStatistics() { |
| | | MesDashboardInventoryRespVO vo = new MesDashboardInventoryRespVO(); |
| | | vo.setWarehouseDistribution(toNameCountList(dashboardMapper.selectStockGroupByWarehouse())); |
| | | vo.setItemDistribution(toNameCountList(dashboardMapper.selectStockGroupByItem())); |
| | | // åºåç¶æåå¸ |
| | | List<MesDashboardNameCountRespVO> stateDistribution = new ArrayList<>(); |
| | | for (Map<String, Object> stat : dashboardMapper.selectStockGroupByState()) { |
| | | int stockState = ((Number) stat.get("stockState")).intValue(); |
| | | stateDistribution.add(new MesDashboardNameCountRespVO(stockStateName(stockState), |
| | | getDecimal(stat.get("quantity")).longValue())); |
| | | } |
| | | vo.setStateDistribution(stateDistribution); |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | public MesDashboardOutboundRespVO getOutboundStatistics(Integer days) { |
| | | LocalDate endDate = LocalDate.now().plusDays(1); |
| | | LocalDate startDate = endDate.minusDays(days); |
| | | LocalDateTime beginTime = startDate.atStartOfDay(); |
| | | LocalDateTime endTime = endDate.atStartOfDay(); |
| | | |
| | | MesDashboardOutboundRespVO vo = new MesDashboardOutboundRespVO(); |
| | | // åºåºææ¥è¶å¿ï¼æ¥è¡¥é¶ï¼ |
| | | Map<String, Map<String, Object>> trendMap = toDateMap(dashboardMapper.selectOutboundDailyQuantity(beginTime, endTime)); |
| | | DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| | | List<MesDashboardDailyCountRespVO> trend = new ArrayList<>(); |
| | | for (LocalDate d = startDate; d.isBefore(endDate); d = d.plusDays(1)) { |
| | | String dateStr = d.format(fmt); |
| | | trend.add(new MesDashboardDailyCountRespVO(dateStr, getDecimalFromMap(trendMap.get(dateStr), "quantity"))); |
| | | } |
| | | vo.setTrend(trend); |
| | | // ç»éååºåºåå¸ |
| | | vo.setDealerDistribution(toNameCountList(dashboardMapper.selectOutboundGroupByClient())); |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | public MesDashboardTraceRespVO getTraceStatistics(Integer days) { |
| | | LocalDate endDate = LocalDate.now().plusDays(1); |
| | | LocalDate startDate = endDate.minusDays(days); |
| | | LocalDateTime beginTime = startDate.atStartOfDay(); |
| | | LocalDateTime endTime = endDate.atStartOfDay(); |
| | | |
| | | MesDashboardTraceRespVO vo = new MesDashboardTraceRespVO(); |
| | | |
| | | // 1. æ«ç ææ¥è¶å¿ |
| | | List<MesTraceConsumerScanTrendRespVO> trend = traceScanStatisticsService.getTrend(beginTime, endTime); |
| | | Map<String, Long> trendMap = new LinkedHashMap<>(); |
| | | if (trend != null) { |
| | | for (MesTraceConsumerScanTrendRespVO item : trend) { |
| | | trendMap.put(item.getDate(), getLong(item.getTotal())); |
| | | } |
| | | } |
| | | DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| | | List<MesDashboardDailyCountRespVO> scanTrend = new ArrayList<>(); |
| | | for (LocalDate d = startDate; d.isBefore(endDate); d = d.plusDays(1)) { |
| | | String dateStr = d.format(fmt); |
| | | scanTrend.add(new MesDashboardDailyCountRespVO(dateStr, new BigDecimal(trendMap.getOrDefault(dateStr, 0L)))); |
| | | } |
| | | vo.setScanTrend(scanTrend); |
| | | |
| | | // 2. ç份åå¸ |
| | | vo.setProvinceDistribution(toDistribution(traceScanStatisticsService.getDistribution( |
| | | buildDistributionReqVO("province", beginTime, endTime)))); |
| | | // 3. æ¸ éåå¸ |
| | | vo.setChannelDistribution(toDistribution(traceScanStatisticsService.getDistribution( |
| | | buildDistributionReqVO("channel", beginTime, endTime)))); |
| | | // 4. çªè´§é¢è¦æ±æ» |
| | | vo.setCrossRegionSummary(traceCrossRegionService.getWarningSummary(null, null)); |
| | | return vo; |
| | | } |
| | | |
| | | // ========== å·¥å
·æ¹æ³ ========== |
| | | |
| | | private MesTraceConsumerScanDistributionReqVO buildDistributionReqVO(String groupBy, LocalDateTime beginTime, LocalDateTime endTime) { |
| | | MesTraceConsumerScanDistributionReqVO reqVO = new MesTraceConsumerScanDistributionReqVO(); |
| | | reqVO.setGroupBy(groupBy); |
| | | reqVO.setStartTime(beginTime); |
| | | reqVO.setEndTime(endTime); |
| | | return reqVO; |
| | | } |
| | | |
| | | private List<MesDashboardNameCountRespVO> toDistribution(List<MesTraceConsumerScanDistributionRespVO> distribution) { |
| | | List<MesDashboardNameCountRespVO> result = new ArrayList<>(); |
| | | if (distribution == null) { |
| | | return result; |
| | | } |
| | | for (MesTraceConsumerScanDistributionRespVO item : distribution) { |
| | | result.add(new MesDashboardNameCountRespVO(item.getName(), getLong(item.getCount()))); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private List<MesDashboardNameCountRespVO> toNameCountList(List<Map<String, Object>> stats) { |
| | | List<MesDashboardNameCountRespVO> result = new ArrayList<>(); |
| | | if (stats == null) { |
| | | return result; |
| | | } |
| | | for (Map<String, Object> stat : stats) { |
| | | result.add(new MesDashboardNameCountRespVO((String) stat.get("name"), getDecimal(stat.get("quantity")).longValue())); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private Map<String, Map<String, Object>> toDateMap(List<Map<String, Object>> stats) { |
| | | Map<String, Map<String, Object>> map = new LinkedHashMap<>(); |
| | | if (stats == null) { |
| | | return map; |
| | | } |
| | | for (Map<String, Object> stat : stats) { |
| | | map.put((String) stat.get("date"), stat); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | private Long getCountByStatus(List<Map<String, Object>> stats, Integer status) { |
| | | if (stats == null) { |
| | | return 0L; |
| | | } |
| | | for (Map<String, Object> stat : stats) { |
| | | if (((Number) stat.get("status")).intValue() == status) { |
| | | return getLong(stat.get("count")); |
| | | } |
| | | } |
| | | return 0L; |
| | | } |
| | | |
| | | private Long getCountByResult(List<Map<String, Object>> stats, Integer result) { |
| | | if (stats == null) { |
| | | return 0L; |
| | | } |
| | | for (Map<String, Object> stat : stats) { |
| | | if (((Number) stat.get("result")).intValue() == result) { |
| | | return getLong(stat.get("count")); |
| | | } |
| | | } |
| | | return 0L; |
| | | } |
| | | |
| | | private BigDecimal getStockQuantityByState(List<Map<String, Object>> stats, Integer stockState) { |
| | | if (stats == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | for (Map<String, Object> stat : stats) { |
| | | if (((Number) stat.get("stockState")).intValue() == stockState) { |
| | | return getDecimal(stat.get("quantity")); |
| | | } |
| | | } |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | private Long getTotalCount(List<Map<String, Object>> stats) { |
| | | long total = 0L; |
| | | if (stats != null) { |
| | | for (Map<String, Object> stat : stats) { |
| | | total += getLong(stat.get("count")); |
| | | } |
| | | } |
| | | return total; |
| | | } |
| | | |
| | | private Long getLongFromMap(Map<String, Object> map, String key) { |
| | | return map == null ? 0L : getLong(map.get(key)); |
| | | } |
| | | |
| | | private BigDecimal getDecimalFromMap(Map<String, Object> map, String key) { |
| | | return map == null ? BigDecimal.ZERO : getDecimal(map.get(key)); |
| | | } |
| | | |
| | | private Long getLong(Object obj) { |
| | | if (obj == null) { |
| | | return 0L; |
| | | } |
| | | if (obj instanceof Number) { |
| | | return ((Number) obj).longValue(); |
| | | } |
| | | return Long.valueOf(obj.toString()); |
| | | } |
| | | |
| | | private BigDecimal getDecimal(Object obj) { |
| | | if (obj == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | if (obj instanceof BigDecimal) { |
| | | return (BigDecimal) obj; |
| | | } |
| | | if (obj instanceof Number) { |
| | | return BigDecimal.valueOf(((Number) obj).doubleValue()); |
| | | } |
| | | return new BigDecimal(obj.toString()); |
| | | } |
| | | |
| | | private BigDecimal calcPercent(long pass, long total) { |
| | | if (total == 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return BigDecimal.valueOf(pass) |
| | | .multiply(BigDecimal.valueOf(100)) |
| | | .divide(BigDecimal.valueOf(total), 2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private String indicatorName(String code) { |
| | | return code == null ? "æªç¥ææ " : INDICATOR_NAME_MAP.getOrDefault(code, code); |
| | | } |
| | | |
| | | private String qualityResultName(int result) { |
| | | return switch (result) { |
| | | case 0 -> "å¤çä¸"; |
| | | case 1 -> "åæ ¼"; |
| | | case 2 -> "ä¸åæ ¼"; |
| | | default -> "æªç¥"; |
| | | }; |
| | | } |
| | | |
| | | private String stockStateName(int stockState) { |
| | | return switch (stockState) { |
| | | case 10 -> "æååº"; |
| | | case 20 -> "åæ ¼åº"; |
| | | case 30 -> "å·²åºåº"; |
| | | default -> "æªç¥ç¶æ"; |
| | | }; |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="cn.iocoder.yudao.module.mes.dal.mysql.dashboard.MesDashboardMapper"> |
| | | |
| | | <!-- ========== ç产维度 ========== --> |
| | | |
| | | <!-- æç¶æç»è®¡æ¹æ¬¡æ°é --> |
| | | <select id="selectBatchCountGroupByStatus" resultType="java.util.Map"> |
| | | SELECT status, COUNT(*) AS count |
| | | FROM mes_pro_batch |
| | | WHERE deleted = 0 |
| | | GROUP BY status |
| | | </select> |
| | | |
| | | <!-- æç¶æç»è®¡è¢ç æ°é --> |
| | | <select id="selectBagCodeCountGroupByStatus" resultType="java.util.Map"> |
| | | SELECT status, COUNT(*) AS count |
| | | FROM mes_pro_bag_code |
| | | WHERE deleted = 0 |
| | | GROUP BY status |
| | | </select> |
| | | |
| | | <!-- ç»è®¡æçç æ»æ° --> |
| | | <select id="selectPalletTotal" resultType="java.lang.Long"> |
| | | SELECT COUNT(*) |
| | | FROM mes_pro_pallet |
| | | WHERE deleted = 0 |
| | | </select> |
| | | |
| | | <!-- ç»è®¡ä»æ¥æ«ç å
¥åºè¢æ°ï¼æ«ç æåäºä»¶æ°éä¹åï¼ --> |
| | | <select id="selectTodayInboundBagCount" resultType="java.lang.Long"> |
| | | SELECT IFNULL(ROUND(SUM(quantity)), 0) |
| | | FROM mes_pro_scan_event |
| | | WHERE status = 'SUCCESS' |
| | | AND event_time >= #{beginTime} |
| | | </select> |
| | | |
| | | <!-- æå¤©è忰墿¹æ¬¡æ° --> |
| | | <select id="selectDailyBatchCount" resultType="java.util.Map"> |
| | | SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS date, COUNT(*) AS count |
| | | FROM mes_pro_batch |
| | | WHERE deleted = 0 |
| | | AND create_time >= #{beginTime} |
| | | <if test="endTime != null"> |
| | | AND create_time < #{endTime} |
| | | </if> |
| | | GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d') |
| | | ORDER BY date |
| | | </select> |
| | | |
| | | <!-- æå¤©èåæ°å¢è¢ç æ° --> |
| | | <select id="selectDailyBagCodeCount" resultType="java.util.Map"> |
| | | SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS date, COUNT(*) AS count |
| | | FROM mes_pro_bag_code |
| | | WHERE deleted = 0 |
| | | AND create_time >= #{beginTime} |
| | | <if test="endTime != null"> |
| | | AND create_time < #{endTime} |
| | | </if> |
| | | GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d') |
| | | ORDER BY date |
| | | </select> |
| | | |
| | | <!-- æå¤©è忰墿çç æ° --> |
| | | <select id="selectDailyPalletCount" resultType="java.util.Map"> |
| | | SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS date, COUNT(*) AS count |
| | | FROM mes_pro_pallet |
| | | WHERE deleted = 0 |
| | | AND create_time >= #{beginTime} |
| | | <if test="endTime != null"> |
| | | AND create_time < #{endTime} |
| | | </if> |
| | | GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d') |
| | | ORDER BY date |
| | | </select> |
| | | |
| | | <!-- æå¤©èåæ«ç å
¥åºè¢æ°ï¼æ«ç æåäºä»¶æ°éä¹åï¼ --> |
| | | <select id="selectDailyInboundBagQuantity" resultType="java.util.Map"> |
| | | SELECT DATE_FORMAT(event_time, '%Y-%m-%d') AS date, |
| | | IFNULL(SUM(quantity), 0) AS quantity |
| | | FROM mes_pro_scan_event |
| | | WHERE status = 'SUCCESS' |
| | | AND event_time >= #{beginTime} |
| | | <if test="endTime != null"> |
| | | AND event_time < #{endTime} |
| | | </if> |
| | | GROUP BY DATE_FORMAT(event_time, '%Y-%m-%d') |
| | | ORDER BY date |
| | | </select> |
| | | |
| | | <!-- ========== è´¨æ£ç»´åº¦ ========== --> |
| | | |
| | | <!-- ææ£éªç»æç»è®¡æ°éï¼0å¤çä¸ 1åæ ¼ 2ä¸åæ ¼ï¼ --> |
| | | <select id="selectQualityCountGroupByResult" resultType="java.util.Map"> |
| | | SELECT result, COUNT(*) AS count |
| | | FROM mes_qc_seed_quality |
| | | WHERE deleted = 0 |
| | | GROUP BY result |
| | | </select> |
| | | |
| | | <!-- ææ åå¼ä¸åæ ¼çï¼å¼è½å¨æ ååºé´ [min_value, max_value] å
è§ä¸ºåæ ¼ï¼ --> |
| | | <select id="selectIndicatorStatistics" resultType="java.util.Map"> |
| | | SELECT indicator_code, |
| | | COUNT(*) AS checkCount, |
| | | ROUND(AVG(value), 6) AS averageValue, |
| | | IFNULL(ROUND(SUM(CASE WHEN value >= min_value AND value <= max_value THEN 1 ELSE 0 END) |
| | | / COUNT(*) * 100, 2), 0) AS passRate |
| | | FROM mes_qc_seed_quality_indicator |
| | | WHERE deleted = 0 |
| | | GROUP BY indicator_code |
| | | ORDER BY indicator_code |
| | | </select> |
| | | |
| | | <!-- ========== åºå维度 ========== --> |
| | | |
| | | <!-- æåºåç¶ææ±æ»æ°éï¼10æååº 20åæ ¼åº 30å·²åºåºï¼ --> |
| | | <select id="selectStockGroupByState" resultType="java.util.Map"> |
| | | SELECT IFNULL(stock_state, -1) AS stockState, |
| | | IFNULL(SUM(quantity), 0) AS quantity |
| | | FROM mes_wm_material_stock |
| | | WHERE deleted = 0 |
| | | GROUP BY stock_state |
| | | </select> |
| | | |
| | | <!-- æä»åºæ±æ»æ°é --> |
| | | <select id="selectStockGroupByWarehouse" resultType="java.util.Map"> |
| | | SELECT IFNULL(w.name, 'æªç¥ä»åº') AS name, |
| | | IFNULL(SUM(s.quantity), 0) AS quantity |
| | | FROM mes_wm_material_stock s |
| | | LEFT JOIN mes_wm_warehouse w ON w.id = s.warehouse_id AND w.deleted = 0 |
| | | WHERE s.deleted = 0 |
| | | GROUP BY s.warehouse_id |
| | | ORDER BY quantity DESC |
| | | </select> |
| | | |
| | | <!-- æåç§æ±æ»æ°é --> |
| | | <select id="selectStockGroupByItem" resultType="java.util.Map"> |
| | | SELECT IFNULL(i.name, 'æªç¥åç§') AS name, |
| | | IFNULL(SUM(s.quantity), 0) AS quantity |
| | | FROM mes_wm_material_stock s |
| | | LEFT JOIN mes_md_item i ON i.id = s.item_id AND i.deleted = 0 |
| | | WHERE s.deleted = 0 |
| | | GROUP BY s.item_id |
| | | ORDER BY quantity DESC |
| | | </select> |
| | | |
| | | <!-- ========== åºåºç»´åº¦ ========== --> |
| | | |
| | | <!-- åºåºæ±æ»ï¼åæ°/æ»é/仿¥åºåºéï¼ --> |
| | | <select id="selectOutboundSummary" resultType="java.util.Map"> |
| | | SELECT COUNT(DISTINCT s.id) AS orderTotal, |
| | | IFNULL(SUM(l.quantity), 0) AS quantityTotal, |
| | | IFNULL(SUM(CASE WHEN s.sales_date >= #{beginTime} THEN l.quantity ELSE 0 END), 0) AS todayQuantity |
| | | FROM mes_wm_product_sales s |
| | | LEFT JOIN mes_wm_product_sales_line l ON l.sales_id = s.id AND l.deleted = 0 |
| | | WHERE s.deleted = 0 |
| | | </select> |
| | | |
| | | <!-- æå¤©èååºåºé --> |
| | | <select id="selectOutboundDailyQuantity" resultType="java.util.Map"> |
| | | SELECT DATE_FORMAT(s.sales_date, '%Y-%m-%d') AS date, |
| | | IFNULL(SUM(l.quantity), 0) AS quantity |
| | | FROM mes_wm_product_sales s |
| | | LEFT JOIN mes_wm_product_sales_line l ON l.sales_id = s.id AND l.deleted = 0 |
| | | WHERE s.deleted = 0 |
| | | AND s.sales_date >= #{beginTime} |
| | | <if test="endTime != null"> |
| | | AND s.sales_date < #{endTime} |
| | | </if> |
| | | GROUP BY DATE_FORMAT(s.sales_date, '%Y-%m-%d') |
| | | ORDER BY date |
| | | </select> |
| | | |
| | | <!-- æç»éåæ±æ»åºåºéï¼TOP 20ï¼ --> |
| | | <select id="selectOutboundGroupByClient" resultType="java.util.Map"> |
| | | SELECT COALESCE(NULLIF(c.name, ''), CONCAT('ç»éå#', s.client_id), CONCAT('客æ·ID:', s.client_id)) AS name, |
| | | IFNULL(SUM(l.quantity), 0) AS quantity |
| | | FROM mes_wm_product_sales s |
| | | LEFT JOIN mes_wm_product_sales_line l ON l.sales_id = s.id AND l.deleted = 0 |
| | | LEFT JOIN crm_customer c ON c.id = s.client_id AND c.deleted = 0 |
| | | WHERE s.deleted = 0 |
| | | GROUP BY s.client_id, c.name |
| | | ORDER BY quantity DESC |
| | | LIMIT 20 |
| | | </select> |
| | | |
| | | </mapper> |