From 3edf8c4fc07f9784e2d04ee7a98692bb457749eb Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 26 十月 2026 16:28:10 +0800
Subject: [PATCH] feat(mes): 新增工艺路线完整导入功能并优化相关验证
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRoutePageReqVO.java | 3
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteService.java | 12
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java | 53 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java | 27
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteCompleteImportWorkbook.java | 87 ++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessSaveReqVO.java | 31
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImplTest.java | 104 +++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteImportExcelVO.java | 30 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java | 76 ++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessService.java | 9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteImportRespVO.java | 24
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java | 9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/product/MesProRouteProductRespVO.java | 18
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java | 25
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteController.java | 89 ++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java | 9
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderProcessDO.java | 19
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteCompleteImportService.java | 243 ++++++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteProcessOutputMapper.java | 47 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java | 21
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderProcessMapper.java | 20
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProductDO.java | 6
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java | 18
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/product/MesProRouteProductSaveReqVO.java | 12
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java | 195 ++++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java | 82 ++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java | 30
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteServiceImpl.java | 177 +++++
sql/mysql/mes_pro_route_import_menu_20261026.sql | 44 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProductController.java | 21
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessOutputDO.java | 72 ++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessOutputVO.java | 37 +
sql/mysql/mes_pro_route_output_attribution_20261026.sql | 56 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteMapper.java | 15
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java | 3
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java | 8
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessDO.java | 11
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java | 32 +
38 files changed, 1,712 insertions(+), 63 deletions(-)
diff --git a/sql/mysql/mes_pro_route_import_menu_20261026.sql b/sql/mysql/mes_pro_route_import_menu_20261026.sql
new file mode 100644
index 0000000..1d6adf7
--- /dev/null
+++ b/sql/mysql/mes_pro_route_import_menu_20261026.sql
@@ -0,0 +1,44 @@
+-- 宸ヨ壓璺嚎瀵煎叆鏉冮檺锛氭櫘閫氬鍏ュ拰瀹屾暣璺嚎鎵归噺瀵煎叆鍏辩敤 mes:pro-route:import銆�
+-- 浼樺厛鎸傚埌鈥滃伐鑹鸿璁� / 宸ヨ壓璺嚎鈥濊彍鍗曪紱鑻ョ洰鏍囧簱鍙湁鐢熶骇妯″潡鏃ц彍鍗曪紝鍒欐寕鍒版棫鑿滃崟銆�
+-- 鍙噸澶嶆墽琛岋紱浠呰嚜鍔ㄦ巿鏉冭秴绾х鐞嗗憳瑙掕壊锛屽叾浠栬鑹茶鍦ㄨ鑹茬鐞嗕腑鎸夐渶鍕鹃�夈��
+
+SET @route_menu_id := COALESCE(
+ (SELECT id FROM system_menu
+ WHERE component LIKE '%process-design/route%' AND type = 2 AND deleted = 0
+ ORDER BY id LIMIT 1),
+ (SELECT id FROM system_menu
+ WHERE component LIKE '%mes/pro/route/index%' AND type = 2 AND deleted = 0
+ ORDER BY id LIMIT 1)
+);
+
+SET @import_menu_id := (
+ SELECT id FROM system_menu
+ WHERE permission = 'mes:pro-route:import' AND deleted = 0
+ ORDER BY id LIMIT 1
+);
+SET @new_menu_id := (SELECT COALESCE(MAX(id), 0) + 1 FROM system_menu);
+
+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 @new_menu_id,
+ '宸ヨ壓璺嚎瀵煎叆', 'mes:pro-route:import', 3, 6, @route_menu_id,
+ '', '#', '', NULL, 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0
+FROM DUAL
+WHERE @route_menu_id IS NOT NULL AND @import_menu_id IS NULL;
+
+SET @import_menu_id := (
+ SELECT id FROM system_menu
+ WHERE permission = 'mes:pro-route:import' AND deleted = 0
+ ORDER BY id LIMIT 1
+);
+
+INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted, tenant_id)
+SELECT r.id, @import_menu_id, '1', NOW(), '1', NOW(), 0, r.tenant_id
+FROM system_role r
+WHERE r.id = 1 AND r.deleted = 0 AND @import_menu_id IS NOT NULL
+ AND NOT EXISTS (
+ SELECT 1 FROM system_role_menu
+ WHERE role_id = r.id AND menu_id = @import_menu_id
+ AND tenant_id = r.tenant_id AND deleted = 0
+ );
diff --git a/sql/mysql/mes_pro_route_output_attribution_20261026.sql b/sql/mysql/mes_pro_route_output_attribution_20261026.sql
new file mode 100644
index 0000000..50bfdbd
--- /dev/null
+++ b/sql/mysql/mes_pro_route_output_attribution_20261026.sql
@@ -0,0 +1,56 @@
+-- 宸ヨ壓璺嚎宸ュ簭浜у嚭鎸変骇鍝佸綊灞炲強榛樿鎶ュ伐搴撲綅锛圡ySQL 8.x锛�
+-- 鍏堟墽琛屾湰鑴氭湰锛屽啀閮ㄧ讲瀵瑰簲鍚庣鐗堟湰锛涘巻鍙插伐鑹鸿矾绾夸骇鍝佸惎鐢ㄥ墠闇�琛ラ綈浠撳簱銆佸簱鍖恒�佸簱浣嶃��
+
+CREATE TABLE IF NOT EXISTS `mes_pro_route_process_output` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '缂栧彿',
+ `route_process_id` bigint NOT NULL COMMENT '宸ヨ壓璺嚎宸ュ簭缂栧彿',
+ `route_id` bigint NOT NULL COMMENT '宸ヨ壓璺嚎缂栧彿',
+ `process_id` bigint NOT NULL COMMENT '宸ュ簭缂栧彿',
+ `product_id` bigint DEFAULT NULL COMMENT '褰掑睘浜у搧缂栧彿锛岀┖涓洪�氱敤',
+ `item_id` bigint NOT NULL COMMENT '浜у嚭鐗╂枡缂栧彿',
+ `sort` int DEFAULT NULL COMMENT '鎺掑簭',
+ `remark` varchar(255) DEFAULT NULL COMMENT '澶囨敞',
+ `creator` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ `updater` varchar(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+ `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+ `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '鏄惁鍒犻櫎',
+ PRIMARY KEY (`id`),
+ KEY `idx_route_process_id` (`route_process_id`),
+ KEY `idx_route_id` (`route_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='MES 宸ヨ壓璺嚎宸ュ簭浜у嚭褰掑睘';
+
+-- 褰撳墠鍒嗘敮淇濈暀鐨勫伐鑹哄弬鏁版ā鏉垮拰宸ュ簭鏀剁巼锛涚洰鏍囧簱鍙兘灏氭湭鎵ц瀵瑰簲鍘嗗彶杩佺Щ銆�
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_route_process' AND column_name = 'param_template_id'),
+ 'SELECT 1', 'ALTER TABLE mes_pro_route_process ADD COLUMN param_template_id BIGINT NULL COMMENT ''宸ヨ壓鍙傛暟妯℃澘缂栧彿'' AFTER process_id');
+PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_route_process' AND column_name = 'yield_rate'),
+ 'SELECT 1', 'ALTER TABLE mes_pro_route_process ADD COLUMN yield_rate DECIMAL(10,4) NULL COMMENT ''宸ュ簭鏀剁巼锛堜骇鍑�/鎶曞叆锛�0~1锛�'' AFTER param_template_id');
+PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_route_product' AND column_name = 'warehouse_id'),
+ 'SELECT 1', 'ALTER TABLE mes_pro_route_product ADD COLUMN warehouse_id BIGINT NULL COMMENT ''鎶ュ伐浜у嚭浠撳簱ID'' AFTER item_id');
+PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_route_product' AND column_name = 'location_id'),
+ 'SELECT 1', 'ALTER TABLE mes_pro_route_product ADD COLUMN location_id BIGINT NULL COMMENT ''鎶ュ伐浜у嚭搴撳尯ID'' AFTER warehouse_id');
+PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_route_product' AND column_name = 'area_id'),
+ 'SELECT 1', 'ALTER TABLE mes_pro_route_product ADD COLUMN area_id BIGINT NULL COMMENT ''鎶ュ伐浜у嚭搴撲綅ID'' AFTER location_id');
+PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_work_order_process' AND column_name = 'param_template_id'),
+ 'SELECT 1', 'ALTER TABLE mes_pro_work_order_process ADD COLUMN param_template_id BIGINT NULL COMMENT ''宸ヨ壓鍙傛暟妯℃澘缂栧彿'' AFTER process_id');
+PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_work_order_process' AND column_name = 'yield_rate'),
+ 'SELECT 1', 'ALTER TABLE mes_pro_work_order_process ADD COLUMN yield_rate DECIMAL(10,4) NULL COMMENT ''宸ュ簭鏀剁巼锛堜骇鍑�/鎶曞叆锛�0~1锛�'' AFTER param_template_id');
+PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_work_order_process' AND column_name = 'warehouse_id'),
+ 'SELECT 1', 'ALTER TABLE mes_pro_work_order_process ADD COLUMN warehouse_id BIGINT NULL COMMENT ''鎶ュ伐浜у嚭浠撳簱ID''');
+PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_work_order_process' AND column_name = 'location_id'),
+ 'SELECT 1', 'ALTER TABLE mes_pro_work_order_process ADD COLUMN location_id BIGINT NULL COMMENT ''鎶ュ伐浜у嚭搴撳尯ID''');
+PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_pro_work_order_process' AND column_name = 'area_id'),
+ 'SELECT 1', 'ALTER TABLE mes_pro_work_order_process ADD COLUMN area_id BIGINT NULL COMMENT ''鎶ュ伐浜у嚭搴撲綅ID''');
+PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteController.java
index 02191d3..648556e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteController.java
@@ -7,11 +7,18 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportExcelVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportRespVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteCompleteImportWorkbook;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRoutePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService;
+import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteCompleteImportService;
+import cn.idev.excel.ExcelWriter;
+import cn.idev.excel.FastExcelFactory;
+import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
@@ -22,8 +29,11 @@
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
@@ -38,6 +48,8 @@
@Resource
private MesProRouteService routeService;
+ @Resource
+ private MesProRouteCompleteImportService completeImportService;
@PostMapping("/create")
@Operation(summary = "鍒涘缓宸ヨ壓璺嚎")
@@ -113,4 +125,81 @@
ExcelUtils.write(response, "宸ヨ壓璺嚎.xls", "鏁版嵁", MesProRouteRespVO.class, data);
}
+ @GetMapping("/get-import-template")
+ @Operation(summary = "鑾峰緱宸ヨ壓璺嚎瀵煎叆妯℃澘")
+ @PreAuthorize("@ss.hasPermission('mes:pro-route:import')")
+ public void importTemplate(HttpServletResponse response) throws IOException {
+ // 鎵嬪姩鍒涘缓瀵煎嚭 demo
+ List<MesProRouteImportExcelVO> list = Collections.singletonList(
+ MesProRouteImportExcelVO.builder()
+ .code("ROUTE001").name("绀轰緥宸ヨ壓璺嚎").description("").remark("").build()
+ );
+ // 杈撳嚭
+ ExcelUtils.write(response, "宸ヨ壓璺嚎瀵煎叆妯℃澘.xls", "宸ヨ壓璺嚎鍒楄〃", MesProRouteImportExcelVO.class, list);
+ }
+
+ @PostMapping("/import")
+ @Operation(summary = "瀵煎叆宸ヨ壓璺嚎")
+ @Parameters({
+ @Parameter(name = "file", description = "Excel 鏂囦欢", required = true),
+ @Parameter(name = "updateSupport", description = "鏄惁鏀寔鏇存柊锛岄粯璁や负 false", example = "true")
+ })
+ @PreAuthorize("@ss.hasPermission('mes:pro-route:import')")
+ public CommonResult<MesProRouteImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
+ @RequestParam(value = "updateSupport", required = false,
+ defaultValue = "false") Boolean updateSupport) throws Exception {
+ List<MesProRouteImportExcelVO> list = ExcelUtils.read(file, MesProRouteImportExcelVO.class);
+ return success(routeService.importRouteList(list, updateSupport));
+ }
+
+ @GetMapping("/get-complete-import-template")
+ @Operation(summary = "鑾峰緱瀹屾暣宸ヨ壓璺嚎鎵归噺瀵煎叆妯℃澘")
+ @PreAuthorize("@ss.hasPermission('mes:pro-route:import')")
+ public void completeImportTemplate(HttpServletResponse response) throws IOException {
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+ response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8("瀹屾暣宸ヨ壓璺嚎瀵煎叆妯℃澘.xlsx"));
+ ExcelWriter writer = FastExcelFactory.write(response.getOutputStream()).autoCloseStream(false).build();
+ try {
+ writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(0, "璺嚎")
+ .head(MesProRouteCompleteImportWorkbook.RouteRow.class).build());
+ writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(1, "浜у搧")
+ .head(MesProRouteCompleteImportWorkbook.ProductRow.class).build());
+ writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(2, "宸ュ簭")
+ .head(MesProRouteCompleteImportWorkbook.ProcessRow.class).build());
+ writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(3, "宸ュ簭浜у嚭")
+ .head(MesProRouteCompleteImportWorkbook.OutputRow.class).build());
+ writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(4, "宸ュ簭鎶曟枡")
+ .head(MesProRouteCompleteImportWorkbook.InputRow.class).build());
+ writer.write(List.of(
+ new MesProRouteCompleteImportWorkbook.GuideRow("璺嚎", "姣忚涓�鏉¤矾绾匡紱璺嚎缂栫爜鍞竴銆傚畬鏁村鍏ヤ粎鏂板锛屽凡鏈夌紪鐮佷細鎶ラ敊锛屽鍏ュ悗榛樿鍋滅敤銆�"),
+ new MesProRouteCompleteImportWorkbook.GuideRow("浜у搧", "璺嚎缂栫爜涓庤矾绾胯〃涓�鑷达紱姣忔潯璺嚎鑷冲皯涓�涓骇鍝併�傜墿鏂欍�佷粨搴撱�佸簱鍖恒�佸簱浣嶅~鍐欏凡鏈変笟鍔$紪鐮侊紱鏃堕棿鍗曚綅濉� MINUTE/HOUR/DAY銆�"),
+ new MesProRouteCompleteImportWorkbook.GuideRow("宸ュ簭", "姣忔潯璺嚎鑷冲皯涓�閬撳伐搴忥紱宸ュ簭缂栫爜椤诲凡瀛樺湪锛屾帓搴忓敮涓�銆傚竷灏斿瓧娈靛~鏄垨鍚︼紱鍏崇郴濉� 0=寮�濮�-寮�濮嬨��1=缁撴潫-缁撴潫銆�2=寮�濮�-缁撴潫銆�3=缁撴潫-寮�濮嬨��"),
+ new MesProRouteCompleteImportWorkbook.GuideRow("宸ュ簭浜у嚭", "鎸夎矾绾跨紪鐮佸拰宸ュ簭缂栫爜鍏宠仈锛涘綊灞炰骇鍝佺紪鐮佺暀绌鸿〃绀哄浜у搧閫氱敤銆傛湯閬撳伐搴忓簲閰嶇疆鍏ㄩ儴鎴愬搧銆�"),
+ new MesProRouteCompleteImportWorkbook.GuideRow("宸ュ簭鎶曟枡", "鎸夎矾绾裤�佸伐搴忓拰褰掑睘浜у搧缂栫爜鍏宠仈锛涙姇鏂欑墿鏂欓』宸插湪璇ヤ骇鍝� BOM 涓��")
+ ), FastExcelFactory.writerSheet(5, "濉啓璇存槑")
+ .head(MesProRouteCompleteImportWorkbook.GuideRow.class).build());
+ } finally {
+ writer.finish();
+ }
+ }
+
+ @PostMapping("/import-complete")
+ @Operation(summary = "鎵归噺瀵煎叆瀹屾暣宸ヨ壓璺嚎锛堝宸ヤ綔琛級")
+ @PreAuthorize("@ss.hasPermission('mes:pro-route:import')")
+ public CommonResult<MesProRouteImportRespVO> importComplete(@RequestParam("file") MultipartFile file) throws IOException {
+ MesProRouteCompleteImportWorkbook workbook = new MesProRouteCompleteImportWorkbook();
+ workbook.setRoutes(readSheet(file, 0, MesProRouteCompleteImportWorkbook.RouteRow.class));
+ workbook.setProducts(readSheet(file, 1, MesProRouteCompleteImportWorkbook.ProductRow.class));
+ workbook.setProcesses(readSheet(file, 2, MesProRouteCompleteImportWorkbook.ProcessRow.class));
+ workbook.setOutputs(readSheet(file, 3, MesProRouteCompleteImportWorkbook.OutputRow.class));
+ workbook.setInputs(readSheet(file, 4, MesProRouteCompleteImportWorkbook.InputRow.class));
+ return success(completeImportService.importRoutes(workbook));
+ }
+
+ private <T> List<T> readSheet(MultipartFile file, int index, Class<T> type) throws IOException {
+ try (InputStream input = file.getInputStream()) {
+ return FastExcelFactory.read(input, type, null).autoCloseStream(false).sheet(index).doReadSync();
+ }
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
index 1bc0813..32de347 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
@@ -6,15 +6,25 @@
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProProcessBomItemVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessOutputVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
+import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -43,6 +53,14 @@
private MesProProcessService processService;
@Resource
private MesMdItemService mdItemService;
+ @Resource
+ private MesProRouteProductService routeProductService;
+ @Resource
+ private MesWmWarehouseService warehouseService;
+ @Resource
+ private MesWmWarehouseLocationService warehouseLocationService;
+ @Resource
+ private MesWmWarehouseAreaService warehouseAreaService;
@PostMapping("/create")
@Operation(summary = "鍒涘缓宸ヨ壓璺嚎宸ュ簭")
@@ -111,7 +129,35 @@
}
}
}
+ // 濉厖宸ヨ壓璺嚎浜у搧涓婇厤缃殑榛樿鎶ュ伐浠撳簱锛堜笅鍗曟椂浣滀负宸ュ簭榛樿鍊硷級
+ fillDefaultOutputWarehouse(result, productId);
return success(result);
+ }
+
+ private void fillDefaultOutputWarehouse(List<MesProRouteProcessRespVO> list, Long productId) {
+ if (CollUtil.isEmpty(list)) {
+ return;
+ }
+ MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(productId);
+ if (routeProduct == null || routeProduct.getWarehouseId() == null) {
+ return;
+ }
+ MesWmWarehouseDO warehouse = warehouseService.getWarehouse(routeProduct.getWarehouseId());
+ MesWmWarehouseLocationDO location = routeProduct.getLocationId() != null
+ ? warehouseLocationService.getWarehouseLocation(routeProduct.getLocationId()) : null;
+ MesWmWarehouseAreaDO area = routeProduct.getAreaId() != null
+ ? warehouseAreaService.getWarehouseArea(routeProduct.getAreaId()) : null;
+ String warehouseName = warehouse != null ? warehouse.getName() : null;
+ String locationName = location != null ? location.getName() : null;
+ String areaName = area != null ? area.getName() : null;
+ for (MesProRouteProcessRespVO vo : list) {
+ vo.setWarehouseId(routeProduct.getWarehouseId());
+ vo.setWarehouseName(warehouseName);
+ vo.setLocationId(routeProduct.getLocationId());
+ vo.setLocationName(locationName);
+ vo.setAreaId(routeProduct.getAreaId());
+ vo.setAreaName(areaName);
+ }
}
@GetMapping("/list-by-route")
@@ -161,6 +207,41 @@
}
Map<Long, MesMdItemDO> outputItemMap = allOutputItemIds.isEmpty()
? Collections.emptyMap() : mdItemService.getItemMap(allOutputItemIds);
+ // 2.1 浜у嚭褰掑睘閰嶇疆锛坧roductId 涓虹┖琛ㄧず澶氫骇鍝侀�氱敤锛�
+ Map<Long, List<MesProRouteProcessOutputVO>> outputsByRouteProcessId = new HashMap<>();
+ Set<Long> routeProcessIds = convertSet(list, MesProRouteProcessDO::getId);
+ routeProcessIds.removeAll(Collections.singleton(null));
+ if (CollUtil.isNotEmpty(routeProcessIds)) {
+ List<MesProRouteProcessOutputDO> outputList = routeProcessService
+ .getRouteProcessOutputListByRouteProcessIds(routeProcessIds);
+ if (CollUtil.isNotEmpty(outputList)) {
+ Set<Long> ownerItemIds = new HashSet<>();
+ outputList.forEach(output -> {
+ if (output.getProductId() != null) {
+ ownerItemIds.add(output.getProductId());
+ }
+ if (output.getItemId() != null) {
+ ownerItemIds.add(output.getItemId());
+ }
+ });
+ Map<Long, MesMdItemDO> ownerItemMap = mdItemService.getItemMap(ownerItemIds);
+ for (MesProRouteProcessOutputDO output : outputList) {
+ MesProRouteProcessOutputVO outputVO = new MesProRouteProcessOutputVO();
+ outputVO.setId(output.getId());
+ outputVO.setRouteProcessId(output.getRouteProcessId());
+ outputVO.setProductId(output.getProductId());
+ outputVO.setItemId(output.getItemId());
+ outputVO.setSort(output.getSort());
+ outputVO.setRemark(output.getRemark());
+ MapUtils.findAndThen(ownerItemMap, output.getItemId(),
+ item -> outputVO.setItemCode(item.getCode()).setItemName(item.getName()));
+ MapUtils.findAndThen(ownerItemMap, output.getProductId(),
+ product -> outputVO.setProductName(product.getName()));
+ outputsByRouteProcessId.computeIfAbsent(output.getRouteProcessId(),
+ key -> new ArrayList<>()).add(outputVO);
+ }
+ }
+ }
// 3. 鎷兼帴 VO
List<MesProRouteProcessRespVO> result = BeanUtils.toBean(list, MesProRouteProcessRespVO.class);
for (int i = 0; i < result.size(); i++) {
@@ -188,6 +269,7 @@
vo.setOutputItemName(firstItem.getName());
}
}
+ vo.setOutputs(outputsByRouteProcessId.getOrDefault(vo.getId(), Collections.emptyList()));
}
return result;
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProductController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProductController.java
index b0eaf5c..bf99a79 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProductController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProductController.java
@@ -10,9 +10,15 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -42,6 +48,12 @@
private MesMdItemService itemService;
@Resource
private MesMdUnitMeasureService unitMeasureService;
+ @Resource
+ private MesWmWarehouseService warehouseService;
+ @Resource
+ private MesWmWarehouseLocationService warehouseLocationService;
+ @Resource
+ private MesWmWarehouseAreaService warehouseAreaService;
@PostMapping("/create")
@Operation(summary = "鍒涘缓宸ヨ壓璺嚎浜у搧")
@@ -96,6 +108,12 @@
convertSet(list, MesProRouteProductDO::getItemId));
Map<Long, MesMdUnitMeasureDO> unitMap = unitMeasureService.getUnitMeasureMap(
convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
+ Map<Long, MesWmWarehouseDO> warehouseMap = warehouseService.getWarehouseMap(
+ convertSet(list, MesProRouteProductDO::getWarehouseId));
+ Map<Long, MesWmWarehouseLocationDO> locationMap = warehouseLocationService.getWarehouseLocationMap(
+ convertSet(list, MesProRouteProductDO::getLocationId));
+ Map<Long, MesWmWarehouseAreaDO> areaMap = warehouseAreaService.getWarehouseAreaMap(
+ convertSet(list, MesProRouteProductDO::getAreaId));
// 2. 鎷兼帴 VO
return BeanUtils.toBean(list, MesProRouteProductRespVO.class, vo -> {
MapUtils.findAndThen(itemMap, vo.getItemId(), item -> {
@@ -104,6 +122,9 @@
MapUtils.findAndThen(unitMap, item.getUnitMeasureId(),
unit -> vo.setUnitName(unit.getName()));
});
+ MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(), warehouse -> vo.setWarehouseName(warehouse.getName()));
+ MapUtils.findAndThen(locationMap, vo.getLocationId(), location -> vo.setLocationName(location.getName()));
+ MapUtils.findAndThen(areaMap, vo.getAreaId(), area -> vo.setAreaName(area.getName()));
});
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteCompleteImportWorkbook.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteCompleteImportWorkbook.java
new file mode 100644
index 0000000..739a777
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteCompleteImportWorkbook.java
@@ -0,0 +1,87 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/** 澶氬伐浣滆〃瀹屾暣宸ヨ壓璺嚎瀵煎叆鏁版嵁锛屾墍鏈夊叧鑱旈�氳繃涓氬姟缂栫爜瀹氫綅銆� */
+@Data
+public class MesProRouteCompleteImportWorkbook {
+
+ private List<RouteRow> routes;
+ private List<ProductRow> products;
+ private List<ProcessRow> processes;
+ private List<OutputRow> outputs;
+ private List<InputRow> inputs;
+
+ @Data
+ public static class RouteRow {
+ @ExcelProperty("璺嚎缂栫爜") private String routeCode;
+ @ExcelProperty("璺嚎鍚嶇О") private String name;
+ @ExcelProperty("璺嚎璇存槑") private String description;
+ @ExcelProperty("澶囨敞") private String remark;
+ }
+
+ @Data
+ public static class ProductRow {
+ @ExcelProperty("璺嚎缂栫爜") private String routeCode;
+ @ExcelProperty("浜у搧鐗╂枡缂栫爜") private String itemCode;
+ @ExcelProperty("浠撳簱缂栫爜") private String warehouseCode;
+ @ExcelProperty("搴撳尯缂栫爜") private String locationCode;
+ @ExcelProperty("搴撲綅缂栫爜") private String areaCode;
+ @ExcelProperty("鐢熶骇鏁伴噺") private Integer quantity;
+ @ExcelProperty("鐢熶骇鐢ㄦ椂") private BigDecimal productionTime;
+ @ExcelProperty("鏃堕棿鍗曚綅") private String timeUnitType;
+ @ExcelProperty("澶囨敞") private String remark;
+ }
+
+ @Data
+ public static class ProcessRow {
+ @ExcelProperty("璺嚎缂栫爜") private String routeCode;
+ @ExcelProperty("宸ュ簭缂栫爜") private String processCode;
+ @ExcelProperty("鎺掑簭") private Integer sort;
+ @ExcelProperty("宸ヨ壓鍙傛暟妯℃澘ID") private Long paramTemplateId;
+ @ExcelProperty("宸ュ簭鏀剁巼") private BigDecimal yieldRate;
+ @ExcelProperty("涓庝笅涓�宸ュ簭鍏崇郴") private Integer linkType;
+ @ExcelProperty("鍑嗗鏃堕棿(鍒嗛挓)") private Integer prepareTime;
+ @ExcelProperty("绛夊緟鏃堕棿(鍒嗛挓)") private Integer waitTime;
+ @ExcelProperty("鐢樼壒鍥鹃鑹�") private String colorCode;
+ @ExcelProperty("鍏抽敭宸ュ簭(鏄�/鍚�)") private String keyFlag;
+ @ExcelProperty("璐ㄦ宸ュ簭(鏄�/鍚�)") private String checkFlag;
+ @ExcelProperty("鍊掑啿(鏄�/鍚�)") private String backflushFlag;
+ @ExcelProperty("澶囨敞") private String remark;
+ }
+
+ @Data
+ public static class OutputRow {
+ @ExcelProperty("璺嚎缂栫爜") private String routeCode;
+ @ExcelProperty("宸ュ簭缂栫爜") private String processCode;
+ @ExcelProperty("浜у嚭鐗╂枡缂栫爜") private String itemCode;
+ @ExcelProperty("褰掑睘浜у搧缂栫爜(绌轰负閫氱敤)") private String productCode;
+ @ExcelProperty("鎺掑簭") private Integer sort;
+ @ExcelProperty("澶囨敞") private String remark;
+ }
+
+ @Data
+ public static class InputRow {
+ @ExcelProperty("璺嚎缂栫爜") private String routeCode;
+ @ExcelProperty("宸ュ簭缂栫爜") private String processCode;
+ @ExcelProperty("褰掑睘浜у搧缂栫爜") private String productCode;
+ @ExcelProperty("鎶曟枡鐗╂枡缂栫爜") private String itemCode;
+ @ExcelProperty("鐢ㄦ枡姣斾緥") private BigDecimal quantity;
+ @ExcelProperty("澶囨敞") private String remark;
+ }
+
+ @Data
+ public static class GuideRow {
+ @ExcelProperty("宸ヤ綔琛�") private String sheet;
+ @ExcelProperty("濉啓璇存槑") private String description;
+
+ public GuideRow(String sheet, String description) {
+ this.sheet = sheet;
+ this.description = description;
+ }
+ }
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteImportExcelVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteImportExcelVO.java
new file mode 100644
index 0000000..6543c9b
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteImportExcelVO.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * MES 宸ヨ壓璺嚎 Excel 瀵煎叆 VO
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class MesProRouteImportExcelVO {
+
+ @ExcelProperty("宸ヨ壓璺嚎缂栫爜")
+ private String code;
+
+ @ExcelProperty("宸ヨ壓璺嚎鍚嶇О")
+ private String name;
+
+ @ExcelProperty("宸ヨ壓璺嚎璇存槑")
+ private String description;
+
+ @ExcelProperty("澶囨敞")
+ private String remark;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteImportRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteImportRespVO.java
new file mode 100644
index 0000000..f89b4d7
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteImportRespVO.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+@Schema(description = "绠$悊鍚庡彴 - MES 宸ヨ壓璺嚎瀵煎叆 Response VO")
+@Data
+@Builder
+public class MesProRouteImportRespVO {
+
+ @Schema(description = "鍒涘缓鎴愬姛鐨勫伐鑹鸿矾绾跨紪鐮佹暟缁�", requiredMode = Schema.RequiredMode.REQUIRED)
+ private List<String> createCodes;
+
+ @Schema(description = "鏇存柊鎴愬姛鐨勫伐鑹鸿矾绾跨紪鐮佹暟缁�", requiredMode = Schema.RequiredMode.REQUIRED)
+ private List<String> updateCodes;
+
+ @Schema(description = "瀵煎叆澶辫触鐨勫伐鑹鸿矾绾块泦鍚堬紝key 涓哄伐鑹鸿矾绾跨紪鐮侊紝value 涓哄け璐ュ師鍥�", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Map<String, String> failureCodes;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRoutePageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRoutePageReqVO.java
index 2c8a50e..cc1e2dd 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRoutePageReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRoutePageReqVO.java
@@ -23,6 +23,9 @@
@Schema(description = "宸ヨ壓璺嚎鍚嶇О", example = "鏍囧噯璺嚎")
private String name;
+ @Schema(description = "鍏宠仈浜у搧鍚嶇О鎴栫紪鐮侊紙妯$硦鎼滅储锛�", example = "榛戞灉鏋告潪")
+ private String productKeyword;
+
@Schema(description = "鐘舵��", example = "0")
private Integer status;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessOutputVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessOutputVO.java
new file mode 100644
index 0000000..0ee4fbb
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessOutputVO.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - MES 宸ヨ壓璺嚎宸ュ簭浜у嚭褰掑睘 Response VO")
+@Data
+public class MesProRouteProcessOutputVO {
+
+ @Schema(description = "缂栧彿", example = "1")
+ private Long id;
+
+ @Schema(description = "宸ヨ壓璺嚎宸ュ簭缂栧彿", example = "1")
+ private Long routeProcessId;
+
+ @Schema(description = "褰掑睘浜у搧缂栧彿锛屼负绌鸿〃绀哄浜у搧閫氱敤")
+ private Long productId;
+
+ @Schema(description = "褰掑睘浜у搧鍚嶇О")
+ private String productName;
+
+ @Schema(description = "浜у嚭鐗╂枡缂栧彿", example = "100")
+ private Long itemId;
+
+ @Schema(description = "浜у嚭鐗╂枡缂栫爜")
+ private String itemCode;
+
+ @Schema(description = "浜у嚭鐗╂枡鍚嶇О")
+ private String itemName;
+
+ @Schema(description = "鎺掑簭", example = "1")
+ private Integer sort;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
index ce2aee4..ae6f012 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
@@ -3,9 +3,9 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
+import java.math.BigDecimal;
@Schema(description = "绠$悊鍚庡彴 - MES 宸ヨ壓璺嚎宸ュ簭 Response VO")
@Data
@@ -19,10 +19,8 @@
@Schema(description = "宸ュ簭缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long processId;
-
@Schema(description = "宸ヨ壓鍙傛暟妯℃澘缂栧彿", example = "1")
private Long paramTemplateId;
-
@Schema(description = "宸ュ簭鏀剁巼锛堜骇鍑�/鎶曞叆锛�0~1锛�", example = "0.98")
private BigDecimal yieldRate;
@@ -77,6 +75,27 @@
@Schema(description = "浜у嚭浜у搧鐗╂枡鍚嶇О锛堥�楀彿鎷兼帴锛�", example = "浜у搧A, 浜у搧B")
private String outputItemNames;
+ @Schema(description = "浜у嚭鐗╂枡褰掑睘閰嶇疆锛坧roductId 涓虹┖琛ㄧず澶氫骇鍝侀�氱敤锛�")
+ private List<MesProRouteProcessOutputVO> outputs;
+
+ @Schema(description = "榛樿鎶ュ伐浠撳簱缂栧彿锛堝彇鑷伐鑹鸿矾绾夸骇鍝佺殑鎶ュ伐浠撳簱閰嶇疆锛�", example = "10")
+ private Long warehouseId;
+
+ @Schema(description = "榛樿鎶ュ伐浠撳簱鍚嶇О")
+ private String warehouseName;
+
+ @Schema(description = "榛樿鎶ュ伐搴撳尯缂栧彿", example = "110")
+ private Long locationId;
+
+ @Schema(description = "榛樿鎶ュ伐搴撳尯鍚嶇О")
+ private String locationName;
+
+ @Schema(description = "榛樿鎶ュ伐搴撲綅缂栧彿", example = "210")
+ private Long areaId;
+
+ @Schema(description = "榛樿鎶ュ伐搴撲綅鍚嶇О")
+ private String areaName;
+
@Schema(description = "澶囨敞")
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessSaveReqVO.java
index 400d5a5..b35c4f5 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessSaveReqVO.java
@@ -4,8 +4,8 @@
import jakarta.validation.constraints.NotNull;
import lombok.Data;
-import java.math.BigDecimal;
import java.util.List;
+import java.math.BigDecimal;
@Schema(description = "绠$悊鍚庡彴 - MES 宸ヨ壓璺嚎宸ュ簭鏂板/淇敼 Request VO")
@Data
@@ -18,16 +18,16 @@
@NotNull(message = "宸ヨ壓璺嚎缂栧彿涓嶈兘涓虹┖")
private Long routeId;
- @Schema(description = "宸ュ簭缂栧彿", example = "1")
+ @Schema(description = "宸ュ簭缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "宸ュ簭缂栧彿涓嶈兘涓虹┖")
private Long processId;
-
@Schema(description = "宸ヨ壓鍙傛暟妯℃澘缂栧彿", example = "1")
private Long paramTemplateId;
-
@Schema(description = "宸ュ簭鏀剁巼锛堜骇鍑�/鎶曞叆锛�0~1锛�", example = "0.98")
private BigDecimal yieldRate;
- @Schema(description = "搴忓彿", example = "1")
+ @Schema(description = "搴忓彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "搴忓彿涓嶈兘涓虹┖")
private Integer sort;
@Schema(description = "涓庝笅涓�閬撳伐搴忓叧绯�", example = "0")
@@ -54,7 +54,28 @@
@Schema(description = "浜у嚭浜у搧鐗╂枡缂栧彿鍒楄〃锛堝閫夛紝涓虹┖鏃堕粯璁ょ敤宸ュ崟鎴愬搧锛�", example = "[100, 101]")
private List<Long> outputItemIds;
+ @Schema(description = "浜у嚭鐗╂枡褰掑睘閰嶇疆锛堜紭鍏堜簬 outputItemIds锛沺roductId 涓虹┖琛ㄧず澶氫骇鍝侀�氱敤锛�")
+ private List<OutputItem> outputs;
+
@Schema(description = "澶囨敞")
private String remark;
+ @Schema(description = "浜у嚭鐗╂枡褰掑睘")
+ @Data
+ public static class OutputItem {
+
+ @Schema(description = "褰掑睘浜у搧缂栧彿锛屼负绌鸿〃绀哄浜у搧閫氱敤")
+ private Long productId;
+
+ @Schema(description = "浜у嚭鐗╂枡缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Long itemId;
+
+ @Schema(description = "鎺掑簭")
+ private Integer sort;
+
+ @Schema(description = "澶囨敞")
+ private String remark;
+
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/product/MesProRouteProductRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/product/MesProRouteProductRespVO.java
index 8df87e9..e9017fe 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/product/MesProRouteProductRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/product/MesProRouteProductRespVO.java
@@ -19,6 +19,24 @@
@Schema(description = "浜у搧鐗╂枡缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long itemId;
+ @Schema(description = "鎶ュ伐浜у嚭浠撳簱缂栧彿")
+ private Long warehouseId;
+
+ @Schema(description = "鎶ュ伐浜у嚭浠撳簱鍚嶇О")
+ private String warehouseName;
+
+ @Schema(description = "鎶ュ伐浜у嚭搴撳尯缂栧彿")
+ private Long locationId;
+
+ @Schema(description = "鎶ュ伐浜у嚭搴撳尯鍚嶇О")
+ private String locationName;
+
+ @Schema(description = "鎶ュ伐浜у嚭搴撲綅缂栧彿")
+ private Long areaId;
+
+ @Schema(description = "鎶ュ伐浜у嚭搴撲綅鍚嶇О")
+ private String areaName;
+
@Schema(description = "浜у搧鐗╂枡缂栫爜")
private String itemCode;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/product/MesProRouteProductSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/product/MesProRouteProductSaveReqVO.java
index db5ecf1..846f6e2 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/product/MesProRouteProductSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/product/MesProRouteProductSaveReqVO.java
@@ -21,6 +21,18 @@
@NotNull(message = "浜у搧鐗╂枡缂栧彿涓嶈兘涓虹┖")
private Long itemId;
+ @Schema(description = "鎶ュ伐浜у嚭浠撳簱缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "鎶ュ伐浜у嚭浠撳簱涓嶈兘涓虹┖")
+ private Long warehouseId;
+
+ @Schema(description = "鎶ュ伐浜у嚭搴撳尯缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "鎶ュ伐浜у嚭搴撳尯涓嶈兘涓虹┖")
+ private Long locationId;
+
+ @Schema(description = "鎶ュ伐浜у嚭搴撲綅缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "鎶ュ伐浜у嚭搴撲綅涓嶈兘涓虹┖")
+ private Long areaId;
+
@Schema(description = "鐢熶骇鏁伴噺", example = "100")
private Integer quantity;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java
index 1cb8944..d75b067 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java
@@ -14,10 +14,16 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -47,6 +53,12 @@
private MesProProcessService processService;
@Resource
private MesMdItemService itemService;
+ @Resource
+ private MesWmWarehouseService warehouseService;
+ @Resource
+ private MesWmWarehouseLocationService warehouseLocationService;
+ @Resource
+ private MesWmWarehouseAreaService warehouseAreaService;
@PostMapping("/create")
@Operation(summary = "鍒涘缓宸ュ崟宸ュ簭")
@@ -150,6 +162,19 @@
}
Map<Long, MesMdItemDO> itemMap = CollUtil.isNotEmpty(allOutputItemIds)
? itemService.getItemMap(allOutputItemIds) : Collections.emptyMap();
+ // 鑾峰彇鎶ュ伐浠撳簱/搴撳尯/搴撲綅
+ Set<Long> warehouseIds = convertSet(list, MesProWorkOrderProcessDO::getWarehouseId);
+ warehouseIds.removeAll(Collections.singleton(null));
+ Set<Long> locationIds = convertSet(list, MesProWorkOrderProcessDO::getLocationId);
+ locationIds.removeAll(Collections.singleton(null));
+ Set<Long> areaIds = convertSet(list, MesProWorkOrderProcessDO::getAreaId);
+ areaIds.removeAll(Collections.singleton(null));
+ Map<Long, MesWmWarehouseDO> warehouseMap = CollUtil.isNotEmpty(warehouseIds)
+ ? warehouseService.getWarehouseMap(warehouseIds) : Collections.emptyMap();
+ Map<Long, MesWmWarehouseLocationDO> locationMap = CollUtil.isNotEmpty(locationIds)
+ ? warehouseLocationService.getWarehouseLocationMap(locationIds) : Collections.emptyMap();
+ Map<Long, MesWmWarehouseAreaDO> areaMap = CollUtil.isNotEmpty(areaIds)
+ ? warehouseAreaService.getWarehouseAreaMap(areaIds) : Collections.emptyMap();
// 2. 鏌ヨ鎶曟枡BOM锛堟寜宸ュ簭鍒嗙粍锛�
Set<Long> workOrderIds = convertSet(list, MesProWorkOrderProcessDO::getWorkOrderId);
@@ -185,6 +210,13 @@
MapUtils.findAndThen(processMap, vo.getNextProcessId(), process -> {
vo.setNextProcessName(process.getName());
});
+ // 鎶ュ伐浠撳簱/搴撳尯/搴撲綅鍚嶇О
+ MapUtils.findAndThen(warehouseMap, vo.getWarehouseId(),
+ warehouse -> vo.setWarehouseName(warehouse.getName()));
+ MapUtils.findAndThen(locationMap, vo.getLocationId(),
+ location -> vo.setLocationName(location.getName()));
+ MapUtils.findAndThen(areaMap, vo.getAreaId(),
+ area -> vo.setAreaName(area.getName()));
// 浜у嚭鐗╂枡锛堟敮鎸佸閫夛級
List<Long> ids = voOutputItemIdsMap.getOrDefault((long) i, Collections.emptyList());
if (!ids.isEmpty()) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java
index c54f6ea..90ccc27 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java
@@ -90,6 +90,24 @@
@Schema(description = "浜у嚭浜у搧鐗╂枡鍚嶇О锛堥�楀彿鎷兼帴锛�", example = "浜у搧A, 浜у搧B")
private String outputItemNames;
+ @Schema(description = "鎶ュ伐浠撳簱缂栧彿", example = "10")
+ private Long warehouseId;
+
+ @Schema(description = "鎶ュ伐浠撳簱鍚嶇О", example = "鎴愬搧浠�")
+ private String warehouseName;
+
+ @Schema(description = "鎶ュ伐搴撳尯缂栧彿", example = "110")
+ private Long locationId;
+
+ @Schema(description = "鎶ュ伐搴撳尯鍚嶇О", example = "鎴愬搧浠撻�氱敤搴撳尯")
+ private String locationName;
+
+ @Schema(description = "鎶ュ伐搴撲綅缂栧彿", example = "210")
+ private Long areaId;
+
+ @Schema(description = "鎶ュ伐搴撲綅鍚嶇О", example = "鎴愬搧浠撻粯璁ゅ簱浣�")
+ private String areaName;
+
@Schema(description = "澶囨敞", example = "澶囨敞")
@ExcelProperty("澶囨敞")
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java
index 6ac741a..ce31f6a 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java
@@ -58,6 +58,15 @@
@Schema(description = "浜у嚭浜у搧鐗╂枡缂栧彿鍒楄〃锛堝閫夛紝涓虹┖鏃堕粯璁ょ敤宸ュ崟鎴愬搧锛�", example = "[100, 101]")
private List<Long> outputItemIds;
+ @Schema(description = "鎶ュ伐浠撳簱缂栧彿", example = "10")
+ private Long warehouseId;
+
+ @Schema(description = "鎶ュ伐搴撳尯缂栧彿", example = "110")
+ private Long locationId;
+
+ @Schema(description = "鎶ュ伐搴撲綅缂栧彿", example = "210")
+ private Long areaId;
+
@Schema(description = "澶囨敞")
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessDO.java
index e430732..c584c40 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessDO.java
@@ -7,7 +7,6 @@
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
-
import java.math.BigDecimal;
/**
@@ -42,15 +41,9 @@
* 鍏宠仈 {@link MesProProcessDO#getId()}
*/
private Long processId;
- /**
- * 宸ヨ壓鍙傛暟妯℃澘缂栧彿
- */
+ /** 宸ヨ壓鍙傛暟妯℃澘缂栧彿 */
private Long paramTemplateId;
- /**
- * 宸ュ簭鏀剁巼锛堜骇鍑�/鎶曞叆锛�0~1锛�
- *
- * 鎺掍骇鏃舵寜姝ゆ敹鐜囦粠鏈亾寰�鍓嶅�掓帹鍚勯亾宸ュ簭鐨勪骇鍑烘暟閲忥紱涓虹┖鎸� 1.0锛堜笉鏀惧ぇ锛夊鐞�
- */
+ /** 宸ュ簭鏀剁巼锛堜骇鍑�/鎶曞叆锛�0~1锛� */
private BigDecimal yieldRate;
/**
* 搴忓彿
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessOutputDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessOutputDO.java
new file mode 100644
index 0000000..39947f6
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessOutputDO.java
@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.mes.dal.dataobject.pro.route;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * MES 宸ヨ壓璺嚎宸ュ簭浜у嚭褰掑睘 DO
+ *
+ * 鍚屼竴閬撳伐搴忕殑浜у嚭鍙互鎸変骇鍝佸尯鍒嗭細褰掑睘浜у搧涓虹┖琛ㄧず璇ヨ矾绾夸笅鎵�鏈変骇鍝侀�氱敤銆�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@TableName("mes_pro_route_process_output")
+@KeySequence("mes_pro_route_process_output_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MesProRouteProcessOutputDO extends BaseDO {
+
+ /**
+ * 缂栧彿
+ */
+ @TableId
+ private Long id;
+ /**
+ * 宸ヨ壓璺嚎宸ュ簭缂栧彿
+ *
+ * 鍏宠仈 {@link MesProRouteProcessDO#getId()}
+ */
+ private Long routeProcessId;
+ /**
+ * 宸ヨ壓璺嚎缂栧彿
+ *
+ * 鍏宠仈 {@link MesProRouteDO#getId()}
+ */
+ private Long routeId;
+ /**
+ * 宸ュ簭缂栧彿
+ *
+ * 鍏宠仈 {@link MesProProcessDO#getId()}
+ */
+ private Long processId;
+ /**
+ * 褰掑睘浜у搧缂栧彿锛屼负绌鸿〃绀哄浜у搧閫氱敤
+ *
+ * 鍏宠仈 {@link MesMdItemDO#getId()}
+ */
+ private Long productId;
+ /**
+ * 浜у嚭鐗╂枡缂栧彿
+ *
+ * 鍏宠仈 {@link MesMdItemDO#getId()}
+ */
+ private Long itemId;
+ /**
+ * 鎺掑簭
+ */
+ private Integer sort;
+ /**
+ * 澶囨敞
+ */
+ private String remark;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProductDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProductDO.java
index 3c34218..07892a1 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProductDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProductDO.java
@@ -40,6 +40,12 @@
* 鍏宠仈 {@link cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO#getId()}
*/
private Long itemId;
+ /** 鎶ュ伐浜у嚭浠撳簱 */
+ private Long warehouseId;
+ /** 鎶ュ伐浜у嚭搴撳尯 */
+ private Long locationId;
+ /** 鎶ュ伐浜у嚭搴撲綅 */
+ private Long areaId;
/**
* 鐢熶骇鏁伴噺
*/
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderProcessDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderProcessDO.java
index 318eb1d..7e757d4 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderProcessDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderProcessDO.java
@@ -102,6 +102,25 @@
*/
private String outputItemId;
/**
+ * 鎶ュ伐浠撳簱缂栧彿
+ *
+ * 涓嬪崟鏃朵粠宸ヨ壓璺嚎浜у搧甯﹀叆锛屽彲鎵嬪伐璋冩暣锛涙姤宸ヤ骇鍑烘寜姝や粨搴撳叆搴�
+ * 鍏宠仈 {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO#getId()}
+ */
+ private Long warehouseId;
+ /**
+ * 鎶ュ伐搴撳尯缂栧彿
+ *
+ * 鍏宠仈 {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO#getId()}
+ */
+ private Long locationId;
+ /**
+ * 鎶ュ伐搴撲綅缂栧彿
+ *
+ * 鍏宠仈 {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO#getId()}
+ */
+ private Long areaId;
+ /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteMapper.java
index 8972079..7987462 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteMapper.java
@@ -5,6 +5,7 @@
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRoutePageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO;
+import cn.hutool.core.util.StrUtil;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@@ -18,12 +19,20 @@
public interface MesProRouteMapper extends BaseMapperX<MesProRouteDO> {
default PageResult<MesProRouteDO> selectPage(MesProRoutePageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX<MesProRouteDO>()
+ LambdaQueryWrapperX<MesProRouteDO> query = new LambdaQueryWrapperX<MesProRouteDO>()
.likeIfPresent(MesProRouteDO::getCode, reqVO.getCode())
.likeIfPresent(MesProRouteDO::getName, reqVO.getName())
.eqIfPresent(MesProRouteDO::getStatus, reqVO.getStatus())
- .betweenIfPresent(MesProRouteDO::getCreateTime, reqVO.getCreateTime())
- .orderByDesc(MesProRouteDO::getId));
+ .betweenIfPresent(MesProRouteDO::getCreateTime, reqVO.getCreateTime());
+ if (StrUtil.isNotBlank(reqVO.getProductKeyword())) {
+ String keyword = reqVO.getProductKeyword().trim();
+ query.apply("EXISTS (SELECT 1 FROM mes_pro_route_product rp "
+ + "JOIN mes_md_item item ON item.id = rp.item_id AND item.deleted = 0 "
+ + "WHERE rp.route_id = mes_pro_route.id AND rp.deleted = 0 "
+ + "AND (item.code LIKE CONCAT('%', {0}, '%') "
+ + "OR item.name LIKE CONCAT('%', {1}, '%')))", keyword, keyword);
+ }
+ return selectPage(reqVO, query.orderByDesc(MesProRouteDO::getId));
}
default MesProRouteDO selectByCode(String code) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteProcessOutputMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteProcessOutputMapper.java
new file mode 100644
index 0000000..46b9af2
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/route/MesProRouteProcessOutputMapper.java
@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.module.mes.dal.mysql.pro.route;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * MES 宸ヨ壓璺嚎宸ュ簭浜у嚭褰掑睘 Mapper
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Mapper
+public interface MesProRouteProcessOutputMapper extends BaseMapperX<MesProRouteProcessOutputDO> {
+
+ default List<MesProRouteProcessOutputDO> selectListByRouteProcessId(Long routeProcessId) {
+ return selectList(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>()
+ .eq(MesProRouteProcessOutputDO::getRouteProcessId, routeProcessId)
+ .orderByAsc(MesProRouteProcessOutputDO::getSort));
+ }
+
+ default List<MesProRouteProcessOutputDO> selectListByRouteProcessIds(Collection<Long> routeProcessIds) {
+ return selectList(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>()
+ .in(MesProRouteProcessOutputDO::getRouteProcessId, routeProcessIds)
+ .orderByAsc(MesProRouteProcessOutputDO::getSort));
+ }
+
+ default List<MesProRouteProcessOutputDO> selectListByRouteId(Long routeId) {
+ return selectList(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>()
+ .eq(MesProRouteProcessOutputDO::getRouteId, routeId)
+ .orderByAsc(MesProRouteProcessOutputDO::getSort));
+ }
+
+ default void deleteByRouteProcessId(Long routeProcessId) {
+ delete(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>()
+ .eq(MesProRouteProcessOutputDO::getRouteProcessId, routeProcessId));
+ }
+
+ default void deleteByRouteId(Long routeId) {
+ delete(new LambdaQueryWrapperX<MesProRouteProcessOutputDO>()
+ .eq(MesProRouteProcessOutputDO::getRouteId, routeId));
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderProcessMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderProcessMapper.java
index c3626a2..247ca56 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderProcessMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderProcessMapper.java
@@ -5,6 +5,7 @@
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@@ -29,6 +30,14 @@
.orderByAsc(MesProWorkOrderProcessDO::getSort));
}
+ default MesProWorkOrderProcessDO selectByWorkOrderIdAndProcessId(Long workOrderId, Long processId) {
+ return selectOne(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>()
+ .eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId)
+ .eq(MesProWorkOrderProcessDO::getProcessId, processId)
+ .orderByAsc(MesProWorkOrderProcessDO::getSort)
+ .last("LIMIT 1"));
+ }
+
default MesProWorkOrderProcessDO selectLastByWorkOrderId(Long workOrderId) {
return selectOne(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>()
.eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId)
@@ -36,6 +45,17 @@
.last("LIMIT 1"));
}
+ /**
+ * 鏇存柊鎶ュ伐浠撳簱锛氬厑璁哥疆绌猴紙updateById 浼氬拷鐣� null 瀛楁锛屾晠鍗曠嫭澶勭悊锛�
+ */
+ default void updateWarehouseById(Long id, Long warehouseId, Long locationId, Long areaId) {
+ update(null, new LambdaUpdateWrapper<MesProWorkOrderProcessDO>()
+ .set(MesProWorkOrderProcessDO::getWarehouseId, warehouseId)
+ .set(MesProWorkOrderProcessDO::getLocationId, locationId)
+ .set(MesProWorkOrderProcessDO::getAreaId, areaId)
+ .eq(MesProWorkOrderProcessDO::getId, id));
+ }
+
default void deleteByWorkOrderId(Long workOrderId) {
delete(new LambdaQueryWrapperX<MesProWorkOrderProcessDO>()
.eq(MesProWorkOrderProcessDO::getWorkOrderId, workOrderId));
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
index 9e011de..7d567b5 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -254,6 +254,7 @@
ErrorCode PRO_ROUTE_ENABLE_NO_KEY_PROCESS = new ErrorCode(1_040_501_003, "宸ヨ壓璺嚎蹇呴』瑕佹湁鍏抽敭宸ュ簭");
ErrorCode PRO_ROUTE_ENABLE_PRODUCT_NO_BOM = new ErrorCode(1_040_501_004, "浜у搧 {} 鏈厤缃伐搴忕殑 BOM 娑堣��");
ErrorCode PRO_ROUTE_IS_ENABLE = new ErrorCode(1_040_501_005, "宸ヨ壓璺嚎宸插惎鐢紝涓嶅厑璁告搷浣�");
+ ErrorCode PRO_ROUTE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_040_501_006, "瀵煎叆宸ヨ壓璺嚎鏁版嵁涓嶈兘涓虹┖");
// ========== MES 鐢熶骇绠$悊-宸ヨ壓璺嚎宸ュ簭锛�1-040-501-100锛� ==========
ErrorCode PRO_ROUTE_PROCESS_NOT_EXISTS = new ErrorCode(1_040_501_100, "宸ヨ壓璺嚎宸ュ簭涓嶅瓨鍦�");
ErrorCode PRO_ROUTE_PROCESS_SORT_DUPLICATE = new ErrorCode(1_040_501_101, "搴忓彿宸插瓨鍦�");
@@ -262,6 +263,13 @@
// ========== MES 鐢熶骇绠$悊-宸ヨ壓璺嚎浜у搧锛�1-040-501-200锛� ==========
ErrorCode PRO_ROUTE_PRODUCT_NOT_EXISTS = new ErrorCode(1_040_501_200, "宸ヨ壓璺嚎浜у搧涓嶅瓨鍦�");
ErrorCode PRO_ROUTE_PRODUCT_ITEM_DUPLICATE = new ErrorCode(1_040_501_201, "姝や骇鍝佸凡閰嶇疆浜嗗伐鑹鸿矾绾�");
+ ErrorCode PRO_ROUTE_PRODUCT_ITEM_TYPE_INVALID = new ErrorCode(1_040_501_202, "鐗╂枡銆恵}銆戠殑涓绘暟鎹被鍨嬪強MES鍒嗙被鍧囬潪鎴愬搧/鍗婃垚鍝侊紝璇峰厛鏍稿鐗╂枡绫诲瀷");
+ ErrorCode PRO_ROUTE_PRODUCT_WAREHOUSE_REQUIRED = new ErrorCode(1_040_501_203, "浜у搧銆恵}銆戞湭閰嶇疆鎶ュ伐鍏ュ簱浠撳簱銆佸簱鍖哄拰搴撲綅");
+ ErrorCode PRO_ROUTE_PROCESS_OUTPUT_REQUIRED = new ErrorCode(1_040_501_204, "宸ュ簭銆恵}銆戞湭閰嶇疆浜у嚭浜у搧");
+ ErrorCode PRO_ROUTE_PROCESS_OUTPUT_NOT_BOUND = new ErrorCode(1_040_501_205, "宸ュ簭浜у嚭浜у搧銆恵}銆戞湭缁戝畾鍒板綋鍓嶅伐鑹鸿矾绾�");
+ ErrorCode PRO_ROUTE_MIDDLE_OUTPUT_INVALID = new ErrorCode(1_040_501_206, "涓棿宸ュ簭銆恵}銆戝彧鑳戒骇鍑哄崐鎴愬搧鍒嗙被浜у搧");
+ ErrorCode PRO_ROUTE_LAST_OUTPUT_INVALID = new ErrorCode(1_040_501_207, "鏈亾宸ュ簭銆恵}銆戝彧鑳戒骇鍑烘垚鍝佸垎绫讳骇鍝侊紝涓斿繀椤诲寘鍚叏閮ㄧ粦瀹氭垚鍝�");
+ ErrorCode PRO_ROUTE_LAST_OUTPUT_ATTRIBUTION_REQUIRED = new ErrorCode(1_040_501_208, "鏈亾宸ュ簭銆恵}銆戠殑鎴愬搧浜у嚭蹇呴』鎸変骇鍝佸綊灞為厤缃紝姣忎釜鎴愬搧褰掑睘鍒拌嚜韬紝涓嶈兘浣跨敤鈥滈�氱敤鈥�");
// ========== MES 鐢熶骇绠$悊-宸ヨ壓璺嚎浜у搧BOM锛�1-040-501-300锛� ==========
ErrorCode PRO_ROUTE_PRODUCT_BOM_NOT_EXISTS = new ErrorCode(1_040_501_300, "宸ヨ壓璺嚎浜у搧 BOM 涓嶅瓨鍦�");
ErrorCode PRO_ROUTE_PRODUCT_BOM_DUPLICATE = new ErrorCode(1_040_501_301, "褰撳墠 BOM 鐗╂枡鍦ㄦ宸ュ簭宸茬粡閰嶇疆杩�");
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
index 5a10283..3cec5b6 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
@@ -117,8 +117,9 @@
// 1. 鏍¢獙
MesProTaskDO task = validateFeedbackData(createReqVO);
- // 2. 鎻掑叆锛堣嚜鍔ㄥ~鍏� itemId锛�
+ // 2. 鎻掑叆锛堜富閿敱鏁版嵁搴撹嚜澧烇紝蹇界暐璇锋眰涓彲鑳芥畫鐣欑殑 id锛涜嚜鍔ㄥ~鍏� itemId锛�
MesProFeedbackDO feedback = BeanUtils.toBean(createReqVO, MesProFeedbackDO.class)
+ .setId(null)
.setStatus(MesProFeedbackStatusEnum.PREPARE.getStatus())
.setItemId(task.getItemId());
feedbackMapper.insert(feedback);
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java
index b68c0de..8ed8807 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java
@@ -540,8 +540,9 @@
}
Long routeId = routeProduct.getRouteId();
- // 2. 鏍¢獙宸ヨ壓璺嚎鏄惁鏈夊伐搴�
- List<MesProRouteProcessDO> routeProcesses = routeProcessService.getRouteProcessListByRouteId(routeId);
+ // 2. 鏍¢獙宸ヨ壓璺嚎鏄惁鏈夊伐搴忥紙鎸変骇鍝佽繃婊ゅ叾瀹冧骇鍝佺殑浜у嚭鐗╂枡锛�
+ List<MesProRouteProcessDO> routeProcesses = routeProcessService
+ .getRouteProcessListByProductId(mps.getProductId());
if (CollUtil.isEmpty(routeProcesses)) {
throw exception(PRO_ROUTE_ENABLE_NO_PROCESS);
}
@@ -581,6 +582,10 @@
.map(Long::valueOf).collect(Collectors.toList()));
}
process.setColorCode(rp.getColorCode());
+ // 甯﹀叆宸ヨ壓璺嚎浜у搧閰嶇疆鐨勬姤宸ヤ粨搴�
+ process.setWarehouseId(routeProduct.getWarehouseId());
+ process.setLocationId(routeProduct.getLocationId());
+ process.setAreaId(routeProduct.getAreaId());
// 甯﹀叆宸ュ簭鎶曟枡BOM
List<MesProRouteProductBomDO> routeBomItems = bomMap != null ? bomMap.get(rp.getProcessId()) : null;
if (CollUtil.isNotEmpty(routeBomItems)) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteCompleteImportService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteCompleteImportService.java
new file mode 100644
index 0000000..fe3d8a8
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteCompleteImportService.java
@@ -0,0 +1,243 @@
+package cn.iocoder.yudao.module.mes.service.pro.route;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteCompleteImportWorkbook;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportRespVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.product.MesProRouteProductSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.productbom.MesProRouteProductBomSaveReqVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.process.MesProProcessMapper;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_ROUTE_IMPORT_LIST_IS_EMPTY;
+
+/** 鎸夎矾绾垮垎浜嬪姟鎵归噺瀵煎叆瀹屾暣閰嶇疆锛涘け璐ヨ矾绾夸笉浼氱暀涓嬮儴鍒嗕骇鍝佹垨宸ュ簭銆� */
+@Service
+public class MesProRouteCompleteImportService {
+ @Resource private TransactionTemplate transactionTemplate;
+ @Resource private MesProRouteService routeService;
+ @Resource private MesProRouteProductService productService;
+ @Resource private MesProRouteProcessService processService;
+ @Resource private MesProRouteProductBomService bomService;
+ @Resource private MesMdItemService itemService;
+ @Resource private MesProProcessMapper processMapper;
+ @Resource private MesWmWarehouseService warehouseService;
+ @Resource private MesWmWarehouseLocationService locationService;
+ @Resource private MesWmWarehouseAreaService areaService;
+
+ public MesProRouteImportRespVO importRoutes(MesProRouteCompleteImportWorkbook book) {
+ if (book.getRoutes() == null || book.getRoutes().isEmpty()) {
+ throw exception(PRO_ROUTE_IMPORT_LIST_IS_EMPTY);
+ }
+ MesProRouteImportRespVO result = MesProRouteImportRespVO.builder()
+ .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>())
+ .failureCodes(new LinkedHashMap<>()).build();
+ Set<String> seen = new HashSet<>();
+ int rowNumber = 1;
+ for (MesProRouteCompleteImportWorkbook.RouteRow row : book.getRoutes()) {
+ rowNumber++;
+ String code = trim(row.getRouteCode());
+ String key = StrUtil.isBlank(code) ? "璺嚎琛ㄧ " + rowNumber + " 琛�" : code;
+ if (StrUtil.isBlank(code) || StrUtil.isBlank(row.getName())) {
+ result.getFailureCodes().put(key, "璺嚎缂栫爜鍜屽悕绉颁笉鑳戒负绌�");
+ continue;
+ }
+ if (!seen.add(code)) {
+ result.getFailureCodes().put("璺嚎琛ㄧ " + rowNumber + " 琛岋紙" + code + "锛�", "瀵煎叆鏂囦欢涓矾绾跨紪鐮侀噸澶�");
+ continue;
+ }
+ try {
+ transactionTemplate.execute(status -> {
+ importOne(book, row, code);
+ return null;
+ });
+ result.getCreateCodes().add(code);
+ } catch (RuntimeException ex) {
+ result.getFailureCodes().put(key, ex.getMessage());
+ }
+ }
+ Set<String> known = book.getRoutes().stream().map(r -> trim(r.getRouteCode()))
+ .collect(Collectors.toSet());
+ collectUnknownRoutes(result.getFailureCodes(), known, "浜у搧", book.getProducts() == null ? List.of() :
+ book.getProducts().stream().map(MesProRouteCompleteImportWorkbook.ProductRow::getRouteCode).toList());
+ collectUnknownRoutes(result.getFailureCodes(), known, "宸ュ簭", book.getProcesses() == null ? List.of() :
+ book.getProcesses().stream().map(MesProRouteCompleteImportWorkbook.ProcessRow::getRouteCode).toList());
+ collectUnknownRoutes(result.getFailureCodes(), known, "宸ュ簭浜у嚭", book.getOutputs() == null ? List.of() :
+ book.getOutputs().stream().map(MesProRouteCompleteImportWorkbook.OutputRow::getRouteCode).toList());
+ collectUnknownRoutes(result.getFailureCodes(), known, "宸ュ簭鎶曟枡", book.getInputs() == null ? List.of() :
+ book.getInputs().stream().map(MesProRouteCompleteImportWorkbook.InputRow::getRouteCode).toList());
+ return result;
+ }
+
+ private void importOne(MesProRouteCompleteImportWorkbook book,
+ MesProRouteCompleteImportWorkbook.RouteRow row, String code) {
+ List<MesProRouteCompleteImportWorkbook.ProductRow> products = rows(book.getProducts()).stream()
+ .filter(r -> code.equals(trim(r.getRouteCode()))).toList();
+ List<MesProRouteCompleteImportWorkbook.ProcessRow> processes = rows(book.getProcesses()).stream()
+ .filter(r -> code.equals(trim(r.getRouteCode())))
+ .sorted(Comparator.comparing(MesProRouteCompleteImportWorkbook.ProcessRow::getSort,
+ Comparator.nullsLast(Comparator.naturalOrder()))).toList();
+ if (products.isEmpty() || processes.isEmpty()) {
+ throw new IllegalArgumentException("鑷冲皯闇�瑕佷竴鏉′骇鍝佸拰涓�閬撳伐搴�");
+ }
+ MesProRouteSaveReqVO route = new MesProRouteSaveReqVO();
+ route.setCode(code);
+ route.setName(trim(row.getName()));
+ route.setDescription(row.getDescription());
+ route.setRemark(row.getRemark());
+ Long routeId = routeService.createRoute(route);
+ Set<String> productCodes = new HashSet<>();
+ for (MesProRouteCompleteImportWorkbook.ProductRow product : products) {
+ String itemCode = trim(product.getItemCode());
+ if (!productCodes.add(itemCode)) {
+ throw new IllegalArgumentException("浜у搧缂栫爜閲嶅锛�" + itemCode);
+ }
+ MesProRouteProductSaveReqVO request = new MesProRouteProductSaveReqVO();
+ request.setRouteId(routeId);
+ request.setItemId(itemId(itemCode));
+ MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(trim(product.getWarehouseCode()));
+ MesWmWarehouseLocationDO location = locationService.getWarehouseLocationByCode(trim(product.getLocationCode()));
+ MesWmWarehouseAreaDO area = areaService.getWarehouseAreaByCode(trim(product.getAreaCode()));
+ if (warehouse == null || location == null || area == null) {
+ throw new IllegalArgumentException("浜у搧 " + itemCode + " 鐨勪粨搴撱�佸簱鍖烘垨搴撲綅缂栫爜涓嶅瓨鍦�");
+ }
+ request.setWarehouseId(warehouse.getId());
+ request.setLocationId(location.getId());
+ request.setAreaId(area.getId());
+ request.setQuantity(product.getQuantity());
+ request.setProductionTime(product.getProductionTime());
+ request.setTimeUnitType(product.getTimeUnitType());
+ request.setRemark(product.getRemark());
+ productService.createRouteProduct(request);
+ }
+ Set<String> processCodes = new HashSet<>();
+ Set<Integer> sorts = new HashSet<>();
+ for (MesProRouteCompleteImportWorkbook.ProcessRow process : processes) {
+ String processCode = trim(process.getProcessCode());
+ if (!processCodes.add(processCode) || process.getSort() == null || !sorts.add(process.getSort())) {
+ throw new IllegalArgumentException("宸ュ簭缂栫爜鎴栨帓搴忛噸澶�/涓虹┖锛�" + processCode);
+ }
+ MesProProcessDO processDO = processMapper.selectByCode(processCode);
+ if (processDO == null) {
+ throw new IllegalArgumentException("宸ュ簭缂栫爜涓嶅瓨鍦細" + processCode);
+ }
+ MesProRouteProcessSaveReqVO request = new MesProRouteProcessSaveReqVO();
+ request.setRouteId(routeId);
+ request.setProcessId(processDO.getId());
+ request.setSort(process.getSort());
+ request.setParamTemplateId(process.getParamTemplateId());
+ request.setYieldRate(process.getYieldRate());
+ request.setLinkType(process.getLinkType());
+ request.setPrepareTime(process.getPrepareTime());
+ request.setWaitTime(process.getWaitTime());
+ request.setColorCode(process.getColorCode());
+ request.setKeyFlag(flag(process.getKeyFlag(), processCode, "鍏抽敭宸ュ簭"));
+ request.setCheckFlag(flag(process.getCheckFlag(), processCode, "璐ㄦ宸ュ簭"));
+ request.setBackflushFlag(flag(process.getBackflushFlag(), processCode, "鍊掑啿"));
+ request.setRemark(process.getRemark());
+ List<MesProRouteProcessSaveReqVO.OutputItem> outputs = new ArrayList<>();
+ for (MesProRouteCompleteImportWorkbook.OutputRow output : rows(book.getOutputs())) {
+ if (!code.equals(trim(output.getRouteCode())) || !processCode.equals(trim(output.getProcessCode()))) {
+ continue;
+ }
+ MesProRouteProcessSaveReqVO.OutputItem value = new MesProRouteProcessSaveReqVO.OutputItem();
+ value.setItemId(itemId(output.getItemCode()));
+ if (StrUtil.isNotBlank(output.getProductCode())) {
+ String ownerCode = trim(output.getProductCode());
+ if (!productCodes.contains(ownerCode)) {
+ throw new IllegalArgumentException("浜у嚭褰掑睘浜у搧鏈叧鑱旇矾绾匡細" + ownerCode);
+ }
+ value.setProductId(itemId(ownerCode));
+ }
+ value.setSort(output.getSort());
+ value.setRemark(output.getRemark());
+ outputs.add(value);
+ }
+ request.setOutputs(outputs);
+ processService.createRouteProcess(request);
+ }
+ for (MesProRouteCompleteImportWorkbook.OutputRow output : rows(book.getOutputs())) {
+ if (code.equals(trim(output.getRouteCode())) && !processCodes.contains(trim(output.getProcessCode()))) {
+ throw new IllegalArgumentException("浜у嚭寮曠敤浜嗘湭閰嶇疆鐨勫伐搴忥細" + output.getProcessCode());
+ }
+ }
+ for (MesProRouteCompleteImportWorkbook.InputRow input : rows(book.getInputs())) {
+ if (!code.equals(trim(input.getRouteCode()))) {
+ continue;
+ }
+ String processCode = trim(input.getProcessCode());
+ String productCode = trim(input.getProductCode());
+ if (!processCodes.contains(processCode) || !productCodes.contains(productCode)) {
+ throw new IllegalArgumentException("鎶曟枡寮曠敤浜嗘湭閰嶇疆鐨勫伐搴忔垨浜у搧锛�" + processCode + "/" + productCode);
+ }
+ MesProRouteProductBomSaveReqVO request = new MesProRouteProductBomSaveReqVO();
+ request.setRouteId(routeId);
+ request.setProcessId(processMapper.selectByCode(processCode).getId());
+ request.setProductId(itemId(productCode));
+ request.setItemId(itemId(input.getItemCode()));
+ request.setQuantity(input.getQuantity());
+ request.setRemark(input.getRemark());
+ bomService.createRouteProductBom(request);
+ }
+ }
+
+ private Long itemId(String code) {
+ String normalized = trim(code);
+ MesMdItemDO item = StrUtil.isBlank(normalized) ? null : itemService.getItemByCode(normalized);
+ if (item == null) {
+ throw new IllegalArgumentException("鐗╂枡缂栫爜涓嶅瓨鍦細" + normalized);
+ }
+ return item.getId();
+ }
+
+ private static boolean flag(String value, String processCode, String field) {
+ String normalized = trim(value);
+ if (StrUtil.isBlank(normalized) || "鍚�".equals(normalized)
+ || "false".equalsIgnoreCase(normalized) || "0".equals(normalized)) {
+ return false;
+ }
+ if ("鏄�".equals(normalized) || "true".equalsIgnoreCase(normalized) || "1".equals(normalized)) {
+ return true;
+ }
+ throw new IllegalArgumentException("宸ュ簭 " + processCode + " 鐨�" + field + "鍙兘濉啓鏄垨鍚�");
+ }
+
+ private static String trim(String value) {
+ return StrUtil.trim(value);
+ }
+
+ private static <T> List<T> rows(List<T> values) {
+ return values == null ? List.of() : values;
+ }
+
+ private static void collectUnknownRoutes(Map<String, String> failures, Set<String> known,
+ String sheet, List<String> codes) {
+ for (String code : codes) {
+ if (!known.contains(trim(code))) {
+ failures.put(sheet + "琛ㄨ矾绾� " + code, "璺嚎琛ㄤ腑涓嶅瓨鍦ㄦ璺嚎缂栫爜");
+ }
+ }
+ }
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java
index 403218c..591ae6d 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java
@@ -67,6 +67,27 @@
List<MesProRouteProcessDO> getRouteProcessListByProductId(Long productId);
/**
+ * 鑾峰緱宸ュ簭鍦ㄦ寚瀹氫骇鍝佷笅鐨勪骇鍑虹墿鏂欑紪鍙�
+ *
+ * 鎸変骇鍑哄綊灞炶繃婊わ細褰掑睘涓虹┖锛堝浜у搧閫氱敤锛夋垨褰掑睘绛変簬鎸囧畾浜у搧鐨勪骇鍑恒��
+ * 鏃犲綊灞炴暟鎹椂杩斿洖 {@code null}锛岃皟鐢ㄦ柟闇�鑷鍥為��锛涙湁褰掑睘鏁版嵁浣嗚浜у搧鏃犱骇鍑烘椂杩斿洖绌哄垪琛ㄣ��
+ *
+ * @param routeProcessId 宸ヨ壓璺嚎宸ュ簭缂栧彿
+ * @param productId 浜у搧缂栧彿
+ * @return 浜у嚭鐗╂枡缂栧彿鍒楄〃锛堟寜鎺掑簭锛夛紝鏃犲綊灞炴暟鎹椂涓� null
+ */
+ List<Long> getOutputItemIdsByRouteProcessId(Long routeProcessId, Long productId);
+
+ /**
+ * 鎸夊伐鑹鸿矾绾垮伐搴忕紪鍙锋壒閲忚幏寰椾骇鍑哄綊灞為厤缃�
+ *
+ * @param routeProcessIds 宸ヨ壓璺嚎宸ュ簭缂栧彿鏁扮粍
+ * @return 浜у嚭褰掑睘鍒楄〃
+ */
+ List<cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO>
+ getRouteProcessOutputListByRouteProcessIds(Collection<Long> routeProcessIds);
+
+ /**
* 鎸変骇鍝佽幏寰楀伐搴忔姇鏂橞OM Map
*
* key = 宸ュ簭ID锛寁alue = 璇ュ伐搴忎笅鐨勬姇鏂橞OM鏄庣粏鍒楄〃
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java
index 4957169..f999ae7 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java
@@ -1,12 +1,15 @@
package cn.iocoder.yudao.module.mes.service.pro.route;
import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProcessMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProcessOutputMapper;
import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
@@ -14,11 +17,16 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -35,6 +43,8 @@
@Resource
private MesProRouteProcessMapper routeProcessMapper;
+ @Resource
+ private MesProRouteProcessOutputMapper routeProcessOutputMapper;
@Resource
@Lazy
@@ -63,13 +73,10 @@
// 2. 鎻掑叆
MesProRouteProcessDO routeProcess = BeanUtils.toBean(createReqVO, MesProRouteProcessDO.class);
- if (CollUtil.isNotEmpty(createReqVO.getOutputItemIds())) {
- routeProcess.setOutputItemId(
- createReqVO.getOutputItemIds().stream()
- .map(String::valueOf)
- .collect(Collectors.joining(",")));
- }
+ routeProcess.setOutputItemId(resolveOutputItemId(createReqVO));
routeProcessMapper.insert(routeProcess);
+ // 2.1 淇濆瓨浜у嚭褰掑睘閰嶇疆
+ saveRouteProcessOutputs(routeProcess, createReqVO);
// 3. 鏇存柊宸ュ簭閾�
rebuildProcessChain(createReqVO.getRouteId());
@@ -81,7 +88,7 @@
public void updateRouteProcess(MesProRouteProcessSaveReqVO updateReqVO) {
// 1.0 宸插惎鐢ㄧ殑宸ヨ壓璺嚎锛屼笉鍏佽鎿嶄綔
routeService.validateRouteNotEnable(updateReqVO.getRouteId());
- // 1.1 鏍¢獙瀛樺湪锛涢儴鍒嗘洿鏂帮紙濡備粎璋冩暣浜у嚭浜у搧锛夋椂鏈紶鐨勫繀濉瓧娈电敤鐜版湁鍊煎洖濉�
+ // 1.1 鏍¢獙瀛樺湪锛涘吋瀹瑰彧璋冩暣浜у嚭閰嶇疆鐨勯儴鍒嗘洿鏂拌姹�
MesProRouteProcessDO existing = validateRouteProcessExists(updateReqVO.getId());
if (updateReqVO.getProcessId() == null) {
updateReqVO.setProcessId(existing.getProcessId());
@@ -98,13 +105,11 @@
// 2. 鏇存柊
MesProRouteProcessDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProcessDO.class);
- if (CollUtil.isNotEmpty(updateReqVO.getOutputItemIds())) {
- updateObj.setOutputItemId(
- updateReqVO.getOutputItemIds().stream()
- .map(String::valueOf)
- .collect(Collectors.joining(",")));
- }
+ // 绌烘暟缁勪篃瑕佹樉寮忓啓鍏ョ┖涓诧紝鍚﹀垯 MyBatis-Plus 榛樿蹇界暐 null锛岀敤鎴锋竻绌轰骇鍑哄搧鍚庢棫閰嶇疆浠嶄細娈嬬暀銆�
+ updateObj.setOutputItemId(resolveOutputItemId(updateReqVO));
routeProcessMapper.updateById(updateObj);
+ // 2.1 瑕嗙洊浜у嚭褰掑睘閰嶇疆
+ saveRouteProcessOutputs(updateObj, updateReqVO);
// 3. 閲嶅缓宸ュ簭閾�
rebuildProcessChain(updateReqVO.getRouteId());
@@ -118,8 +123,9 @@
// 1.2 宸插惎鐢ㄧ殑宸ヨ壓璺嚎锛屼笉鍏佽鎿嶄綔
routeService.validateRouteNotEnable(routeProcess.getRouteId());
- // 2. 鍒犻櫎
+ // 2. 鍒犻櫎锛堝惈浜у嚭褰掑睘閰嶇疆锛�
routeProcessMapper.deleteById(id);
+ routeProcessOutputMapper.deleteByRouteProcessId(id);
// 3. 閲嶅缓宸ュ簭閾�
rebuildProcessChain(routeProcess.getRouteId());
@@ -218,7 +224,165 @@
return Collections.emptyList();
}
// 2. 杩斿洖璇ュ伐鑹鸿矾绾跨殑宸ュ簭鍒楄〃
- return routeProcessMapper.selectListByRouteId(routeProduct.getRouteId());
+ List<MesProRouteProcessDO> list = routeProcessMapper.selectListByRouteId(routeProduct.getRouteId());
+ // 3. 鎸変骇鍑哄綊灞炶繃婊わ細閫氱敤浜у嚭 + 褰撳墠浜у搧涓撳睘浜у嚭
+ Map<Long, List<MesProRouteProcessOutputDO>> outputMap = routeProcessOutputMapper
+ .selectListByRouteId(routeProduct.getRouteId()).stream()
+ .collect(Collectors.groupingBy(MesProRouteProcessOutputDO::getRouteProcessId));
+ for (MesProRouteProcessDO process : list) {
+ List<MesProRouteProcessOutputDO> outputs = outputMap.get(process.getId());
+ if (CollUtil.isEmpty(outputs)) {
+ // 瀛橀噺鏁版嵁鏈媶鍒嗗綊灞炴椂锛岄��鍥炴棫鐨勨�滃墧闄ゅ悓璺嚎鍏跺畠浜у搧鈥濋�昏緫
+ filterOtherProductOutputs(Collections.singletonList(process), routeProduct);
+ continue;
+ }
+ String filtered = outputs.stream()
+ .filter(output -> output.getProductId() == null || output.getProductId().equals(productId))
+ .map(MesProRouteProcessOutputDO::getItemId)
+ .filter(Objects::nonNull)
+ .distinct()
+ .map(String::valueOf)
+ .collect(Collectors.joining(","));
+ process.setOutputItemId(filtered.isEmpty() ? null : filtered);
+ }
+ return list;
+ }
+
+ @Override
+ public List<Long> getOutputItemIdsByRouteProcessId(Long routeProcessId, Long productId) {
+ List<MesProRouteProcessOutputDO> outputs = routeProcessOutputMapper.selectListByRouteProcessId(routeProcessId);
+ if (CollUtil.isEmpty(outputs)) {
+ // 鏈厤缃骇鍑哄綊灞烇紙瀛橀噺鏁版嵁锛夎繑鍥� null锛屼究浜庤皟鐢ㄦ柟鍥為��鑱氬悎浜у嚭
+ return null;
+ }
+ return outputs.stream()
+ .filter(output -> output.getProductId() == null || output.getProductId().equals(productId))
+ .map(MesProRouteProcessOutputDO::getItemId)
+ .filter(Objects::nonNull)
+ .distinct()
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public List<MesProRouteProcessOutputDO> getRouteProcessOutputListByRouteProcessIds(Collection<Long> routeProcessIds) {
+ if (CollUtil.isEmpty(routeProcessIds)) {
+ return Collections.emptyList();
+ }
+ return routeProcessOutputMapper.selectListByRouteProcessIds(routeProcessIds);
+ }
+
+ /**
+ * 璁$畻宸ュ簭浜у嚭鐗╂枡鐨勮仛鍚堜覆锛堥�楀彿鍒嗛殧锛屽幓閲嶏級
+ *
+ * 浼樺厛浣跨敤浜у嚭褰掑睘閰嶇疆锛坥utputs锛夛紝鏈紶鏃跺吋瀹� outputItemIds銆�
+ */
+ private String resolveOutputItemId(MesProRouteProcessSaveReqVO reqVO) {
+ List<Long> itemIds;
+ if (CollUtil.isNotEmpty(reqVO.getOutputs())) {
+ itemIds = reqVO.getOutputs().stream()
+ .filter(Objects::nonNull)
+ .sorted(Comparator.comparing(MesProRouteProcessSaveReqVO.OutputItem::getSort,
+ Comparator.nullsLast(Comparator.naturalOrder())))
+ .map(MesProRouteProcessSaveReqVO.OutputItem::getItemId)
+ .filter(Objects::nonNull)
+ .distinct()
+ .collect(Collectors.toList());
+ } else if (CollUtil.isNotEmpty(reqVO.getOutputItemIds())) {
+ itemIds = reqVO.getOutputItemIds().stream()
+ .filter(Objects::nonNull)
+ .distinct()
+ .collect(Collectors.toList());
+ } else {
+ itemIds = Collections.emptyList();
+ }
+ return itemIds.stream().map(String::valueOf).collect(Collectors.joining(","));
+ }
+
+ /**
+ * 瑕嗙洊淇濆瓨宸ュ簭浜у嚭褰掑睘閰嶇疆
+ */
+ private void saveRouteProcessOutputs(MesProRouteProcessDO routeProcess,
+ MesProRouteProcessSaveReqVO reqVO) {
+ routeProcessOutputMapper.deleteByRouteProcessId(routeProcess.getId());
+ if (CollUtil.isEmpty(reqVO.getOutputs()) && CollUtil.isEmpty(reqVO.getOutputItemIds())) {
+ return;
+ }
+ Set<Long> routeProductIds = routeProductService.getRouteProductListByRouteId(routeProcess.getRouteId())
+ .stream().map(MesProRouteProductDO::getItemId).filter(Objects::nonNull).collect(Collectors.toSet());
+ List<MesProRouteProcessOutputDO> outputs = new ArrayList<>();
+ Set<String> keys = new HashSet<>();
+ int index = 0;
+ if (CollUtil.isNotEmpty(reqVO.getOutputs())) {
+ for (MesProRouteProcessSaveReqVO.OutputItem output : reqVO.getOutputs()) {
+ if (output == null || output.getItemId() == null) {
+ continue;
+ }
+ index++;
+ if (!keys.add(output.getProductId() + "_" + output.getItemId())) {
+ continue;
+ }
+ outputs.add(buildOutput(routeProcess, output.getProductId(), output.getItemId(),
+ output.getSort() != null ? output.getSort() : index, output.getRemark()));
+ }
+ } else {
+ // 鍏煎鍙紶 outputItemIds 鐨勮皟鐢ㄦ柟锛氫骇鍑烘槸璺嚎浜у搧鏃跺綊灞炲埌鑷韩锛屽惁鍒欒涓洪�氱敤
+ for (Long itemId : reqVO.getOutputItemIds()) {
+ if (itemId == null) {
+ continue;
+ }
+ index++;
+ Long productId = routeProductIds.contains(itemId) ? itemId : null;
+ if (!keys.add(productId + "_" + itemId)) {
+ continue;
+ }
+ outputs.add(buildOutput(routeProcess, productId, itemId, index, null));
+ }
+ }
+ if (CollUtil.isNotEmpty(outputs)) {
+ routeProcessOutputMapper.insertBatch(outputs);
+ }
+ }
+
+ private MesProRouteProcessOutputDO buildOutput(MesProRouteProcessDO routeProcess, Long productId,
+ Long itemId, Integer sort, String remark) {
+ return MesProRouteProcessOutputDO.builder()
+ .routeProcessId(routeProcess.getId())
+ .routeId(routeProcess.getRouteId())
+ .processId(routeProcess.getProcessId())
+ .productId(productId)
+ .itemId(itemId)
+ .sort(sort)
+ .remark(remark)
+ .build();
+ }
+
+ /**
+ * 杩囨护浜у嚭鐗╂枡锛氬墧闄ゅ悓涓�宸ヨ壓璺嚎涓嬪叾瀹冧骇鍝佺殑浜у嚭锛屽彧淇濈暀褰撳墠浜у搧鐨勪骇鍑哄拰鍗婃垚鍝佷骇鍑�
+ */
+ private void filterOtherProductOutputs(List<MesProRouteProcessDO> list, MesProRouteProductDO routeProduct) {
+ if (CollUtil.isEmpty(list) || routeProduct.getItemId() == null) {
+ return;
+ }
+ Set<Long> otherProductIds = routeProductService.getRouteProductListByRouteId(routeProduct.getRouteId())
+ .stream()
+ .map(MesProRouteProductDO::getItemId)
+ .filter(itemId -> itemId != null && !itemId.equals(routeProduct.getItemId()))
+ .collect(Collectors.toSet());
+ if (otherProductIds.isEmpty()) {
+ return;
+ }
+ for (MesProRouteProcessDO process : list) {
+ String outputItemId = process.getOutputItemId();
+ if (outputItemId == null || outputItemId.isBlank()) {
+ continue;
+ }
+ String filtered = Arrays.stream(outputItemId.split(","))
+ .map(String::trim)
+ .filter(s -> !s.isEmpty())
+ .filter(s -> !otherProductIds.contains(NumberUtil.parseLong(s, -1L)))
+ .collect(Collectors.joining(","));
+ process.setOutputItemId(filtered.isEmpty() ? null : filtered);
+ }
}
@Override
@@ -243,6 +407,7 @@
@Override
public void deleteRouteProcessByRouteId(Long routeId) {
routeProcessMapper.deleteByRouteId(routeId);
+ routeProcessOutputMapper.deleteByRouteId(routeId);
}
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java
index d5089de..b879221 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java
@@ -4,7 +4,17 @@
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.product.MesProRouteProductSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProductMapper;
+import cn.iocoder.yudao.module.mes.enums.md.MesMdItemTypeEnum;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
+import cn.iocoder.yudao.module.mdm.enums.MdmItemTypeEnum;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -12,6 +22,9 @@
import org.springframework.validation.annotation.Validated;
import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -34,13 +47,25 @@
@Resource
@Lazy
private MesProRouteProductBomService routeProductBomService;
+ @Resource
+ private MesMdItemService itemService;
+ @Resource
+ private MesMdItemTypeService itemTypeService;
+ @Resource
+ private MesWmWarehouseAreaService warehouseAreaService;
+ @Resource
+ private MdmItemApi mdmItemApi;
@Override
public Long createRouteProduct(MesProRouteProductSaveReqVO createReqVO) {
// 1.0 宸插惎鐢ㄧ殑宸ヨ壓璺嚎锛屼笉鍏佽鎿嶄綔
routeService.validateRouteNotEnable(createReqVO.getRouteId());
- // 1.1 鏍¢獙浜у搧鍞竴鎬э紙涓�涓骇鍝佸彧鑳藉叧鑱斾竴鏉″伐鑹鸿矾绾匡級
+ // 1.1 鏍¢獙浜у搧瀛樺湪銆佸惎鐢ㄤ笖绫诲瀷姝g‘
+ validateProductItem(createReqVO.getItemId());
+ // 1.2 鏍¢獙浜у搧鍞竴鎬э紙涓�涓骇鍝佸彧鑳藉叧鑱斾竴鏉″伐鑹鸿矾绾匡級
validateItemUnique(null, createReqVO.getItemId());
+ warehouseAreaService.validateWarehouseAreaExists(createReqVO.getWarehouseId(),
+ createReqVO.getLocationId(), createReqVO.getAreaId());
// 2. 鎻掑叆
MesProRouteProductDO routeProduct = BeanUtils.toBean(createReqVO, MesProRouteProductDO.class);
@@ -53,9 +78,16 @@
// 1.0 宸插惎鐢ㄧ殑宸ヨ壓璺嚎锛屼笉鍏佽鎿嶄綔
routeService.validateRouteNotEnable(updateReqVO.getRouteId());
// 1.1 鏍¢獙瀛樺湪
- validateRouteProductExists(updateReqVO.getId());
- // 1.2 鏍¢獙浜у搧鍞竴鎬�
+ MesProRouteProductDO existing = routeProductMapper.selectById(updateReqVO.getId());
+ validateRouteProductExists(existing);
+ // 鍘嗗彶璺嚎浜у搧鍙兘閲囩敤鏃у垎绫伙紱鍙慨鏀逛粨浣嶇瓑閰嶇疆鏃朵笉閲嶆柊鎷︽埅鍘熸湁鐗╂枡銆�
+ if (ObjUtil.notEqual(existing.getItemId(), updateReqVO.getItemId())) {
+ validateProductItem(updateReqVO.getItemId());
+ }
+ // 1.3 鏍¢獙浜у搧鍞竴鎬�
validateItemUnique(updateReqVO.getId(), updateReqVO.getItemId());
+ warehouseAreaService.validateWarehouseAreaExists(updateReqVO.getWarehouseId(),
+ updateReqVO.getLocationId(), updateReqVO.getAreaId());
// 2. 鏇存柊
MesProRouteProductDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProductDO.class);
@@ -89,6 +121,44 @@
}
}
+ private void validateProductItem(Long itemId) {
+ MesMdItemDO item = itemService.validateItemExistsAndEnable(itemId);
+ Long typeId = item.getItemTypeId();
+ Set<Long> visited = new HashSet<>();
+ while (typeId != null && !MesMdItemTypeDO.PARENT_ID_ROOT.equals(typeId) && visited.add(typeId)) {
+ MesMdItemTypeDO itemType = itemTypeService.getItemType(typeId);
+ if (itemType == null) {
+ break;
+ }
+ // 鍏煎鏃у垎绫伙細鍙兘鏈缃� PRODUCT 鏍囪瘑锛屼篃鏈噰鐢� FG/SF 缂栫爜銆�
+ if (MesMdItemTypeEnum.isProduct(itemType.getItemOrProduct())
+ || "FG".equalsIgnoreCase(itemType.getCode())
+ || "SF".equalsIgnoreCase(itemType.getCode())
+ || isProductCategoryName(itemType.getName())) {
+ return;
+ }
+ typeId = itemType.getParentId();
+ }
+ // MES 鍏煎鍒嗙被鍙兘浠嶆爣璁颁负 ITEM锛涗互 MDM 鐗╂枡涓绘暟鎹殑鎴愬搧/鍗婃垚鍝佺被鍨嬪厹搴曘��
+ CommonResult<MdmItemRespDTO> mdmResult = mdmItemApi.getItemByCode(item.getCode());
+ if (mdmResult != null && mdmResult.isSuccess() && mdmResult.getData() != null) {
+ MdmItemRespDTO mdmItem = mdmResult.getData();
+ Integer mdmType = mdmItem.getItemType();
+ // 鍘嗗彶鍚屾鏇惧皢鍒嗙被 ID 鍐欏叆 itemType锛涗笌 categoryId 鐩稿悓鐨勫�间笉鑳藉綋浣滅被鍨嬫灇涓俱��
+ if (!Objects.equals(mdmItem.getCategoryId(), mdmType == null ? null : mdmType.longValue())
+ && (MdmItemTypeEnum.PRODUCT.getType().equals(mdmType)
+ || MdmItemTypeEnum.SEMI_FINISHED.getType().equals(mdmType))) {
+ return;
+ }
+ }
+ throw exception(PRO_ROUTE_PRODUCT_ITEM_TYPE_INVALID, item.getName());
+ }
+
+ private boolean isProductCategoryName(String name) {
+ return name != null && (name.contains("鍗婃垚鍝�") || name.contains("浜ф垚鍝�")
+ || (name.contains("鎴愬搧") && !name.contains("闈炴垚鍝�")));
+ }
+
private void validateItemUnique(Long id, Long itemId) {
MesProRouteProductDO existing = routeProductMapper.selectByItemId(itemId);
if (existing == null) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteService.java
index a0aa8de..5a1f0a6 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteService.java
@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.mes.service.pro.route;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportExcelVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRoutePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO;
@@ -90,4 +92,14 @@
return convertMap(getRouteList(ids), MesProRouteDO::getId);
}
+ /**
+ * 鎵归噺瀵煎叆宸ヨ壓璺嚎
+ *
+ * @param importList 瀵煎叆宸ヨ壓璺嚎鍒楄〃
+ * @param updateSupport 鏄惁鏀寔鏇存柊
+ * @return 瀵煎叆缁撴灉
+ */
+ MesProRouteImportRespVO importRouteList(List<MesProRouteImportExcelVO> importList,
+ boolean updateSupport);
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteServiceImpl.java
index b0c94a2..a50b366 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteServiceImpl.java
@@ -3,27 +3,46 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportExcelVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRoutePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.MesProRouteSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessOutputDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteMapper;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
+import cn.iocoder.yudao.module.mdm.enums.MdmItemTypeEnum;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Set;
+import java.util.Comparator;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.concurrent.atomic.AtomicInteger;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -52,6 +71,12 @@
@Resource
@Lazy
private MesMdItemService itemService;
+ @Resource
+ private MesMdItemTypeService itemTypeService;
+ @Resource
+ private MesWmWarehouseAreaService warehouseAreaService;
+ @Resource
+ private MdmItemApi mdmItemApi;
@Override
public Long createRoute(MesProRouteSaveReqVO createReqVO) {
@@ -141,13 +166,115 @@
// 3. 鎵�鏈変骇鍝佸繀椤婚厤缃簡 BOM 娑堣��
List<MesProRouteProductDO> productList = routeProductService.getRouteProductListByRouteId(routeId);
for (MesProRouteProductDO product : productList) {
+ MesMdItemDO item = itemService.validateItemExists(product.getItemId());
+ if (product.getWarehouseId() == null || product.getLocationId() == null || product.getAreaId() == null) {
+ throw exception(PRO_ROUTE_PRODUCT_WAREHOUSE_REQUIRED, item.getName());
+ }
+ warehouseAreaService.validateWarehouseAreaExists(product.getWarehouseId(),
+ product.getLocationId(), product.getAreaId());
List<MesProRouteProductBomDO> bomList = routeProductBomService
.getRouteProductBomList(routeId, null, product.getItemId());
if (CollUtil.isEmpty(bomList)) {
- MesMdItemDO item = itemService.validateItemExists(product.getItemId());
throw exception(PRO_ROUTE_ENABLE_PRODUCT_NO_BOM, item.getName());
}
}
+ validateProcessOutputs(processList, productList);
+ }
+
+ private void validateProcessOutputs(List<MesProRouteProcessDO> processList,
+ List<MesProRouteProductDO> productList) {
+ if (CollUtil.isEmpty(productList)) {
+ throw exception(PRO_ROUTE_LAST_OUTPUT_INVALID, processList.get(processList.size() - 1).getProcessId());
+ }
+ Set<Long> boundItemIds = productList.stream().map(MesProRouteProductDO::getItemId).collect(Collectors.toSet());
+ Set<Long> finishedItemIds = productList.stream()
+ .map(MesProRouteProductDO::getItemId)
+ .filter(itemId -> isItemUnderCategory(itemId, "FG"))
+ .collect(Collectors.toSet());
+ MesProRouteProcessDO lastProcess = processList.stream()
+ .max(Comparator.comparing(MesProRouteProcessDO::getSort)).orElseThrow();
+ for (MesProRouteProcessDO process : processList) {
+ Set<Long> outputIds = parseOutputItemIds(process.getOutputItemId());
+ boolean last = process.getId().equals(lastProcess.getId());
+ if (CollUtil.isEmpty(outputIds)) {
+ // 涓棿宸ュ簭涓嶄竴瀹氬舰鎴愬彲鍏ュ簱鐨勭嫭绔嬪崐鎴愬搧锛屽厑璁镐笉閰嶇疆浜у嚭锛涙湯閬撳伐搴忓繀椤讳骇鍑烘垚鍝併��
+ if (last) {
+ throw exception(PRO_ROUTE_LAST_OUTPUT_INVALID, process.getProcessId());
+ }
+ continue;
+ }
+ for (Long outputId : outputIds) {
+ if (!boundItemIds.contains(outputId)) {
+ MesMdItemDO item = itemService.validateItemExists(outputId);
+ throw exception(PRO_ROUTE_PROCESS_OUTPUT_NOT_BOUND, item.getName());
+ }
+ }
+ if (last) {
+ if (finishedItemIds.isEmpty() || !outputIds.equals(finishedItemIds)) {
+ throw exception(PRO_ROUTE_LAST_OUTPUT_INVALID, process.getProcessId());
+ }
+ } else if (outputIds.stream().anyMatch(itemId -> !isItemUnderCategory(itemId, "SF"))) {
+ throw exception(PRO_ROUTE_MIDDLE_OUTPUT_INVALID, process.getProcessId());
+ }
+ }
+ // 4. 鏈亾宸ュ簭鎴愬搧浜у嚭蹇呴』鎸変骇鍝佸綊灞烇紝閬垮厤鈥滈�氱敤鈥濅骇鍑轰覆鍒板叾瀹冧骇鍝佺殑宸ュ崟
+ List<MesProRouteProcessOutputDO> lastOutputs = routeProcessService
+ .getRouteProcessOutputListByRouteProcessIds(Collections.singletonList(lastProcess.getId()));
+ if (CollUtil.isNotEmpty(lastOutputs)) {
+ boolean attributionInvalid = lastOutputs.stream().anyMatch(output ->
+ output.getProductId() == null || !output.getProductId().equals(output.getItemId()));
+ Set<Long> attributedProducts = lastOutputs.stream()
+ .map(MesProRouteProcessOutputDO::getProductId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ if (attributionInvalid || !attributedProducts.containsAll(finishedItemIds)) {
+ throw exception(PRO_ROUTE_LAST_OUTPUT_ATTRIBUTION_REQUIRED, lastProcess.getProcessId());
+ }
+ }
+ }
+
+ private Set<Long> parseOutputItemIds(String value) {
+ if (StrUtil.isBlank(value)) {
+ return Collections.emptySet();
+ }
+ return Arrays.stream(value.split(",")).map(String::trim).filter(StrUtil::isNotBlank)
+ .map(Long::valueOf).collect(Collectors.toSet());
+ }
+
+ private boolean isItemUnderCategory(Long itemId, String rootCode) {
+ MesMdItemDO item = itemService.validateItemExists(itemId);
+ MesMdItemTypeDO type = itemTypeService.getItemType(item.getItemTypeId());
+ while (type != null) {
+ if ("SF".equalsIgnoreCase(type.getCode())
+ || (type.getName() != null && type.getName().contains("鍗婃垚鍝�"))) {
+ return "SF".equals(rootCode);
+ }
+ if ("FG".equalsIgnoreCase(type.getCode()) || isFinishedCategoryName(type.getName())) {
+ return "FG".equals(rootCode);
+ }
+ if (type.getParentId() == null || MesMdItemTypeDO.PARENT_ID_ROOT.equals(type.getParentId())) {
+ break;
+ }
+ type = itemTypeService.getItemType(type.getParentId());
+ }
+ // MES 鍒嗙被鏈娇鐢� FG/SF 缂栫爜鏃讹紝鎸� MDM 涓绘暟鎹殑鐗╂枡绫诲瀷鍒ゅ畾銆�
+ CommonResult<MdmItemRespDTO> mdmResult = mdmItemApi.getItemByCode(item.getCode());
+ if (mdmResult == null || !mdmResult.isSuccess() || mdmResult.getData() == null) {
+ return false;
+ }
+ MdmItemRespDTO mdmItem = mdmResult.getData();
+ Integer mdmType = mdmItem.getItemType();
+ // 鍘嗗彶鍚屾鍙兘鎶婂垎绫� ID 瀛樺湪 itemType锛屼笉鑳芥妸鍒嗙被 2/3 璇垽涓烘垚鍝�/鍗婃垚鍝併��
+ if (Objects.equals(mdmItem.getCategoryId(), mdmType == null ? null : mdmType.longValue())) {
+ return false;
+ }
+ return "FG".equals(rootCode) ? MdmItemTypeEnum.PRODUCT.getType().equals(mdmType)
+ : "SF".equals(rootCode) && MdmItemTypeEnum.SEMI_FINISHED.getType().equals(mdmType);
+ }
+
+ private boolean isFinishedCategoryName(String name) {
+ return name != null && !name.contains("鍗婃垚鍝�") && !name.contains("闈炴垚鍝�")
+ && (name.contains("浜ф垚鍝�") || name.contains("鎴愬搧"));
}
@Override
@@ -181,4 +308,52 @@
return routeMapper.selectByIds(ids);
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public MesProRouteImportRespVO importRouteList(List<MesProRouteImportExcelVO> importList,
+ boolean updateSupport) {
+ // 1. 鏍¢獙闈炵┖
+ if (CollUtil.isEmpty(importList)) {
+ throw exception(PRO_ROUTE_IMPORT_LIST_IS_EMPTY);
+ }
+
+ // 2. 閫愯澶勭悊
+ MesProRouteImportRespVO respVO = MesProRouteImportRespVO.builder()
+ .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>())
+ .failureCodes(new LinkedHashMap<>()).build();
+ AtomicInteger index = new AtomicInteger(1);
+ importList.forEach(importItem -> {
+ int currentIndex = index.getAndIncrement();
+ String key = StrUtil.blankToDefault(importItem.getCode(), "绗� " + currentIndex + " 琛�");
+ if (StrUtil.isBlank(importItem.getCode())) {
+ respVO.getFailureCodes().put(key, "宸ヨ壓璺嚎缂栫爜涓嶈兘涓虹┖");
+ return;
+ }
+ if (StrUtil.isBlank(importItem.getName())) {
+ respVO.getFailureCodes().put(key, "宸ヨ壓璺嚎鍚嶇О涓嶈兘涓虹┖");
+ return;
+ }
+ // 2.1 鍒ゆ柇锛氬垱寤� or 鏇存柊
+ MesProRouteDO existRoute = routeMapper.selectByCode(importItem.getCode());
+ if (existRoute == null) {
+ // 2.1.1 鏂板锛堜笌鎵嬪伐鍒涘缓涓�鑷达細榛樿绂佺敤锛岄�氳繃鍚敤鎺ュ彛鏍¢獙鍚庡惎鐢級
+ MesProRouteDO route = BeanUtils.toBean(importItem, MesProRouteDO.class);
+ route.setId(null);
+ route.setStatus(CommonStatusEnum.DISABLE.getStatus());
+ routeMapper.insert(route);
+ respVO.getCreateCodes().add(importItem.getCode());
+ } else if (updateSupport) {
+ // 2.1.2 鏇存柊锛堢姸鎬佺敱鍚敤/绂佺敤鎺ュ彛鍗曠嫭缁存姢锛屽鍏ヤ笉淇敼鐘舵�侊級
+ MesProRouteDO updateObj = BeanUtils.toBean(importItem, MesProRouteDO.class);
+ updateObj.setId(existRoute.getId());
+ updateObj.setCode(existRoute.getCode());
+ routeMapper.updateById(updateObj);
+ respVO.getUpdateCodes().add(importItem.getCode());
+ } else {
+ respVO.getFailureCodes().put(key, "宸ヨ壓璺嚎缂栫爜宸插瓨鍦�");
+ }
+ });
+ return respVO;
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
index 88956ea..bc9586d 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
@@ -150,11 +150,7 @@
if (itemId == null) {
MesProRouteProcessDO routeProcess = routeProcessService
.getRouteProcessByRouteIdAndProcessId(createReqVO.getRouteId(), createReqVO.getProcessId());
- if (routeProcess != null && routeProcess.getOutputItemId() != null) {
- itemId = Long.valueOf(routeProcess.getOutputItemId().split(",")[0].trim());
- } else {
- itemId = workOrder.getProductId();
- }
+ itemId = resolveTaskItemId(routeProcess, workOrder.getProductId());
}
MesMdItemDO item = itemService.validateItemExists(itemId);
@@ -350,6 +346,25 @@
throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_ROUTE_ENABLE_NO_PROCESS);
}
return routeProcesses;
+ }
+
+ /** 褰撳墠浜у搧鐨勪换鍔′骇鍑猴紝浼樺厛浣跨敤宸ュ簭鐨勪骇鍝佸綊灞為厤缃�� */
+ private Long resolveTaskItemId(MesProRouteProcessDO routeProcess, Long productId) {
+ if (routeProcess == null) {
+ return productId;
+ }
+ if (routeProcess.getId() != null) {
+ List<Long> outputIds = routeProcessService
+ .getOutputItemIdsByRouteProcessId(routeProcess.getId(), productId);
+ if (outputIds != null) {
+ return CollUtil.isNotEmpty(outputIds) ? outputIds.get(0) : productId;
+ }
+ }
+ String outputItemId = routeProcess.getOutputItemId();
+ if (outputItemId != null && !outputItemId.isBlank()) {
+ return Long.valueOf(outputItemId.split(",")[0].trim());
+ }
+ return productId;
}
@Override
@@ -563,7 +578,7 @@
List<Long> taskIds = new java.util.ArrayList<>();
for (MesProRouteProcessDO rp : routeProcesses) {
cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO proc = procMap.get(rp.getProcessId());
- Long itemId = rp.getOutputItemId() != null ? Long.valueOf(rp.getOutputItemId().split(",")[0].trim()) : workOrder.getProductId();
+ Long itemId = resolveTaskItemId(rp, workOrder.getProductId());
cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO item = itemService.validateItemExists(itemId);
MesProTaskDO task = new MesProTaskDO()
.setWorkOrderId(workOrder.getId())
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessService.java
index d1d3ef0..1cfd1e9 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessService.java
@@ -62,6 +62,15 @@
List<MesProWorkOrderProcessDO> getWorkOrderProcessListByWorkOrderId(Long workOrderId);
/**
+ * 鏍规嵁宸ュ崟缂栧彿 + 宸ュ簭缂栧彿鑾峰緱宸ュ崟宸ュ簭
+ *
+ * @param workOrderId 宸ュ崟缂栧彿
+ * @param processId 宸ュ簭缂栧彿
+ * @return 宸ュ崟宸ュ簭
+ */
+ MesProWorkOrderProcessDO getWorkOrderProcessByWorkOrderIdAndProcessId(Long workOrderId, Long processId);
+
+ /**
* 鏍规嵁宸ュ崟缂栧彿鑾峰緱鏈�鍚庝竴閬撳伐搴�
*
* @param workOrderId 宸ュ崟缂栧彿
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
index 55a7581..2364935 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
@@ -7,12 +7,15 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderBomMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderProcessMapper;
import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
+import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -48,6 +51,10 @@
private MesProProcessService processService;
@Resource
private MesProRouteProcessService routeProcessService;
+ @Resource
+ private MesProRouteProductService routeProductService;
+ @Resource
+ private MesWmWarehouseAreaService warehouseAreaService;
@Resource
@Lazy
private MesProWorkOrderBomService workOrderBomService;
@@ -89,6 +96,9 @@
.collect(Collectors.joining(",")));
}
workOrderProcessMapper.updateById(updateObj);
+ // 鎶ュ伐浠撳簱鍏佽缃┖锛屽崟鐙洿鏂�
+ workOrderProcessMapper.updateWarehouseById(updateReqVO.getId(), updateReqVO.getWarehouseId(),
+ updateReqVO.getLocationId(), updateReqVO.getAreaId());
// 3. 閲嶅缓宸ュ簭閾�
rebuildProcessChain(updateReqVO.getWorkOrderId());
@@ -122,6 +132,11 @@
}
@Override
+ public MesProWorkOrderProcessDO getWorkOrderProcessByWorkOrderIdAndProcessId(Long workOrderId, Long processId) {
+ return workOrderProcessMapper.selectByWorkOrderIdAndProcessId(workOrderId, processId);
+ }
+
+ @Override
public MesProWorkOrderProcessDO getLastWorkOrderProcessByWorkOrderId(Long workOrderId) {
return workOrderProcessMapper.selectLastByWorkOrderId(workOrderId);
}
@@ -146,6 +161,8 @@
if (CollUtil.isEmpty(routeProcessList)) {
return Collections.emptyList();
}
+ // 宸ヨ壓璺嚎浜у搧涓婇厤缃殑鎶ュ伐浠撳簱浣滀负宸ュ簭榛樿鍊硷紝涓嬪崟鏃跺彲鍐嶄慨鏀�
+ MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(productId);
// 杞崲涓哄伐鍗曞伐搴� DO锛堜笉璁剧疆 workOrderId锛屽墠绔繚瀛樻椂鍐嶈缃級
List<MesProWorkOrderProcessDO> processList = new ArrayList<>(routeProcessList.size());
for (MesProRouteProcessDO routeProcess : routeProcessList) {
@@ -163,6 +180,9 @@
.checkFlag(routeProcess.getCheckFlag())
.backflushFlag(routeProcess.getBackflushFlag())
.outputItemId(routeProcess.getOutputItemId())
+ .warehouseId(routeProduct != null ? routeProduct.getWarehouseId() : null)
+ .locationId(routeProduct != null ? routeProduct.getLocationId() : null)
+ .areaId(routeProduct != null ? routeProduct.getAreaId() : null)
.remark(routeProcess.getRemark())
.build());
}
@@ -214,6 +234,9 @@
updateObj.setOutputItemId(item.getOutputItemIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
}
workOrderProcessMapper.updateById(updateObj);
+ // 鎶ュ伐浠撳簱鍏佽缃┖锛屽崟鐙洿鏂�
+ workOrderProcessMapper.updateWarehouseById(item.getId(), item.getWarehouseId(),
+ item.getLocationId(), item.getAreaId());
}
}
@@ -279,7 +302,12 @@
throw exception(PRO_WORK_ORDER_PROCESS_DUPLICATE);
}
}
- // 4. 鏍¢獙鍏抽敭宸ュ簭鍞竴
+ // 4. 鏍¢獙鎶ュ伐浠撳簱灞傜骇锛堜粨搴�/搴撳尯/搴撲綅鍚屾椂濉啓鏃舵牎楠屽綊灞炲叧绯伙級
+ if (reqVO.getWarehouseId() != null && reqVO.getLocationId() != null && reqVO.getAreaId() != null) {
+ warehouseAreaService.validateWarehouseAreaExists(reqVO.getWarehouseId(),
+ reqVO.getLocationId(), reqVO.getAreaId());
+ }
+ // 5. 鏍¢獙鍏抽敭宸ュ簭鍞竴
if (Boolean.TRUE.equals(reqVO.getKeyFlag())) {
for (MesProWorkOrderProcessDO process : list) {
if (reqVO.getId() != null && reqVO.getId().equals(process.getId())) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
index 1c45ee4..e8ed301 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java
@@ -8,6 +8,8 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO;
@@ -21,6 +23,8 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
+import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService;
import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
@@ -56,6 +60,10 @@
private MesWmProductProduceDetailService productProduceDetailService;
@Resource
private MesProWorkOrderService workOrderService;
+ @Resource
+ private MesProWorkOrderProcessService workOrderProcessService;
+ @Resource
+ private MesProRouteProductService routeProductService;
@Resource
private MesWmBatchService batchService;
@Resource
@@ -152,10 +160,7 @@
Long batchId = batch != null ? batch.getId() : null;
String batchCode = batch != null ? batch.getCode() : null;
- // 1.5 鑾峰彇铏氭嫙绾胯竟搴�
- MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
- MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
- MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
+ WarehouseTarget outputTarget = resolveOutputTarget(feedback.getWorkOrderId(), feedback.getProcessId(), feedback.getItemId());
// 3. 鏍规嵁鏄惁闇�瑕佹楠屽垎鏀鐞�
if (checkFlag) {
@@ -179,7 +184,7 @@
productProduceLineService.createProductProduceLine(unqualifiedLine);
MesWmProductProduceDetailDO unqualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode,
unqualifiedLine.getId(), unqualifiedQty,
- virtualWarehouse.getId(), virtualLocation.getId(), virtualArea.getId());
+ outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId());
productProduceDetailService.createProductProduceDetail(unqualifiedDetail);
}
// 3.2.2 鍚堟牸鍝佽 + 鏄庣粏
@@ -189,7 +194,7 @@
productProduceLineService.createProductProduceLine(qualifiedLine);
MesWmProductProduceDetailDO qualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode,
qualifiedLine.getId(), qualifiedQty,
- virtualWarehouse.getId(), virtualLocation.getId(), virtualArea.getId());
+ outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId());
productProduceDetailService.createProductProduceDetail(qualifiedDetail);
}
}
@@ -249,13 +254,9 @@
if (produce == null) {
throw exception(WM_PRODUCT_PRODUCE_NOT_EXISTS);
}
- // 1.2 鑾峰彇铏氭嫙绾胯竟搴�
- MesWmWarehouseDO virtualWarehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
- MesWmWarehouseLocationDO virtualLocation = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
- MesWmWarehouseAreaDO virtualArea = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
-
// 2. 閫氳繃 sourceLineId 鐩存帴瀹氫綅寰呮楠岃
MesWmProductProduceLineDO pendingLine = productProduceLineService.validateProductProduceLineExists(sourceLineId);
+ WarehouseTarget outputTarget = resolveOutputTarget(produce.getWorkOrderId(), produce.getProcessId(), pendingLine.getItemId());
// 3A. 鎯呭喌涓�锛氬瓨鍦ㄤ笉鍚堟牸鍝佹暟閲忥紝闇�瑕佹媶鍒嗚
if (unqualifiedQty != null && unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
@@ -272,7 +273,7 @@
.produceId(produce.getId()).lineId(unqualifiedLine.getId())
.itemId(pendingLine.getItemId()).quantity(unqualifiedQty)
.batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
- .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId())
+ .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
.build());
// 3A.2 鍚堟牸鍝侊細澶嶇敤鍘熷緟妫�琛岋紝鏇存柊鏁伴噺鍜岀姸鎬� + 鏄庣粏
pendingLine.setQuantity(ObjectUtil.defaultIfNull(qualifiedQty, BigDecimal.ZERO));
@@ -283,7 +284,7 @@
.produceId(produce.getId()).lineId(pendingLine.getId())
.itemId(pendingLine.getItemId()).quantity(qualifiedQty)
.batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
- .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId())
+ .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
.build());
}
} else {
@@ -294,7 +295,7 @@
.produceId(produce.getId()).lineId(pendingLine.getId())
.itemId(pendingLine.getItemId()).quantity(pendingLine.getQuantity())
.batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode())
- .warehouseId(virtualWarehouse.getId()).locationId(virtualLocation.getId()).areaId(virtualArea.getId())
+ .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId())
.build());
}
@@ -302,4 +303,28 @@
finishProductProduce(produce.getId());
}
+ private WarehouseTarget resolveOutputTarget(Long workOrderId, Long processId, Long itemId) {
+ if (workOrderId != null && processId != null) {
+ MesProWorkOrderProcessDO process = workOrderProcessService
+ .getWorkOrderProcessByWorkOrderIdAndProcessId(workOrderId, processId);
+ if (process != null && process.getWarehouseId() != null
+ && process.getLocationId() != null && process.getAreaId() != null) {
+ areaService.validateWarehouseAreaExists(process.getWarehouseId(), process.getLocationId(), process.getAreaId());
+ return new WarehouseTarget(process.getWarehouseId(), process.getLocationId(), process.getAreaId());
+ }
+ }
+ MesProRouteProductDO product = itemId == null ? null : routeProductService.getRouteProductByItemId(itemId);
+ if (product != null && product.getWarehouseId() != null
+ && product.getLocationId() != null && product.getAreaId() != null) {
+ areaService.validateWarehouseAreaExists(product.getWarehouseId(), product.getLocationId(), product.getAreaId());
+ return new WarehouseTarget(product.getWarehouseId(), product.getLocationId(), product.getAreaId());
+ }
+ MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
+ MesWmWarehouseLocationDO location = locationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION);
+ MesWmWarehouseAreaDO area = areaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA);
+ return new WarehouseTarget(warehouse.getId(), location.getId(), area.getId());
+ }
+
+ private record WarehouseTarget(Long warehouseId, Long locationId, Long areaId) { }
+
}
diff --git a/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImplTest.java b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImplTest.java
new file mode 100644
index 0000000..15d7edb
--- /dev/null
+++ b/yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImplTest.java
@@ -0,0 +1,104 @@
+package cn.iocoder.yudao.module.mes.service.pro.route;
+
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.product.MesProRouteProductSaveReqVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProductMapper;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class MesProRouteProductServiceImplTest {
+
+ @InjectMocks
+ private MesProRouteProductServiceImpl service;
+ @Mock
+ private MesProRouteProductMapper routeProductMapper;
+ @Mock
+ private MesProRouteService routeService;
+ @Mock
+ private MesMdItemService itemService;
+ @Mock
+ private MesMdItemTypeService itemTypeService;
+ @Mock
+ private MesWmWarehouseAreaService warehouseAreaService;
+ @Mock
+ private MdmItemApi mdmItemApi;
+
+ @Test
+ void updateWarehouseForExistingItemDoesNotRevalidateLegacyCategory() {
+ MesProRouteProductSaveReqVO request = request(88L, 815L);
+ MesProRouteProductDO existing = new MesProRouteProductDO().setId(88L).setItemId(815L);
+ when(routeProductMapper.selectById(88L)).thenReturn(existing);
+ when(routeProductMapper.selectByItemId(815L)).thenReturn(existing);
+
+ assertDoesNotThrow(() -> service.updateRouteProduct(request));
+ verifyNoInteractions(itemService, itemTypeService);
+ verify(routeProductMapper).updateById(any(MesProRouteProductDO.class));
+ }
+
+ @Test
+ void createAcceptsSemiFinishedItemInSfCategoryTree() {
+ MesProRouteProductSaveReqVO request = request(null, 815L);
+ when(itemService.validateItemExistsAndEnable(815L))
+ .thenReturn(new MesMdItemDO().setId(815L).setName("灏佽鍗婃垚鍝�").setItemTypeId(23L));
+ when(itemTypeService.getItemType(23L)).thenReturn(new MesMdItemTypeDO()
+ .setId(23L).setCode("PACKAGED").setItemOrProduct("ITEM").setParentId(2L));
+ when(itemTypeService.getItemType(2L)).thenReturn(new MesMdItemTypeDO()
+ .setId(2L).setCode("SF").setParentId(0L));
+
+ assertDoesNotThrow(() -> service.createRouteProduct(request));
+ verify(routeProductMapper).insert(any(MesProRouteProductDO.class));
+ }
+
+ @Test
+ void createRejectsRawMaterialOutsideProductTree() {
+ MesProRouteProductSaveReqVO request = request(null, 815L);
+ when(itemService.validateItemExistsAndEnable(815L))
+ .thenReturn(new MesMdItemDO().setId(815L).setName("鍘熸枡").setItemTypeId(23L));
+ when(itemTypeService.getItemType(23L)).thenReturn(new MesMdItemTypeDO()
+ .setId(23L).setCode("RAW").setItemOrProduct("ITEM").setParentId(0L));
+
+ assertThrows(ServiceException.class, () -> service.createRouteProduct(request));
+ verify(routeProductMapper, never()).insert(any(MesProRouteProductDO.class));
+ }
+
+ @Test
+ void createAcceptsNamedSemiFinishedInLegacyItemCategory() {
+ MesProRouteProductSaveReqVO request = request(null, 815L);
+ MesMdItemDO item = new MesMdItemDO().setId(815L).setCode("74000432")
+ .setName("榛戞灉鏋告潪鍗婃垚鍝�").setItemTypeId(23L);
+ when(itemService.validateItemExistsAndEnable(815L)).thenReturn(item);
+ when(itemTypeService.getItemType(23L)).thenReturn(new MesMdItemTypeDO()
+ .setId(23L).setCode("FOOD").setName("榛戞灉鏋告潪鍗婃垚鍝�")
+ .setItemOrProduct("ITEM").setParentId(0L));
+
+ assertDoesNotThrow(() -> service.createRouteProduct(request));
+ verifyNoInteractions(mdmItemApi);
+ verify(routeProductMapper).insert(any(MesProRouteProductDO.class));
+ }
+
+ private static MesProRouteProductSaveReqVO request(Long id, Long itemId) {
+ MesProRouteProductSaveReqVO request = new MesProRouteProductSaveReqVO();
+ request.setId(id);
+ request.setRouteId(44L);
+ request.setItemId(itemId);
+ request.setWarehouseId(24L);
+ request.setLocationId(9L);
+ request.setAreaId(9L);
+ return request;
+ }
+}
--
Gitblit v1.9.3