From d54fbc9d4f8981fa0f7f613b33a28a8cd4f764ea Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期日, 25 十月 2026 16:29:53 +0800
Subject: [PATCH] feat(system): 优化用户搜索功能并完善ERP和MES模块验证逻辑

---
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java                      |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productproduce/MesWmProductProduceServiceImpl.java          |   65 ++++++--
 yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java                 |    3 
 yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java                                   |   18 -
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcSaveReqVO.java                      |    3 
 yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryService.java                           |    8 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java                   |    7 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcSaveReqVO.java                      |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java                              |    8 +
 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/qc/ipqc/vo/MesQcIpqcSaveReqVO.java                    |    3 
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRuleSaveReqVO.java         |   12 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/machinery/MesDvMachineryServiceImpl.java                    |   16 +
 yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApiImpl.java                               |    8 +
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java                 |    2 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProductDO.java                     |    6 
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRuleServiceImpl.java                   |    2 
 yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/event/ProductionCanceledEvent.java                             |   15 ++
 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                   |    9 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java                       |    1 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java  |    5 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestServiceImpl.java                    |    9 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteServiceImpl.java                          |   75 ++++++++++
 yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryServiceImpl.java                       |    5 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcSaveReqVO.java                      |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProductController.java           |   21 +++
 yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java                              |    1 
 yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApi.java                                   |   10 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java                                          |    5 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ProductionFinishedListener.java                  |   16 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/plan/MesCalPlanServiceImpl.java                            |    9 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java          |    9 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/cal/plan/vo/MesCalPlanSaveReqVO.java                  |    3 
 34 files changed, 338 insertions(+), 55 deletions(-)

diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java
index d12da09..d36f73f 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java
@@ -31,7 +31,8 @@
     @Schema(description = "浼樻儬鐜囷紝鐧惧垎姣�", example = "10")
     private BigDecimal discountPercent;
 
-    @Schema(description = "瀹氶噾閲戦", example = "1000.00")
+    @Schema(description = "瀹氶噾閲戦", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000.00")
+    @NotNull(message = "瀹氶噾閲戦涓嶈兘涓虹┖")
     private BigDecimal depositPrice;
 
     @Schema(description = "澶囨敞", example = "澶囨敞")
@@ -90,4 +91,4 @@
 
     }
 
-}
\ No newline at end of file
+}
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
index b90cb00..d6f5fef 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
@@ -97,7 +97,8 @@
 
     @PutMapping("/update-status")
     @Operation(summary = "鏇存柊閿�鍞鍗曠殑鐘舵��")
-    @PreAuthorize("@ss.hasPermission('erp:sale-order:update-status')")
+    @PreAuthorize("@ss.hasPermission('erp:sale-order:update-status') "
+            + "&& (#status != 10 || @ss.hasRole('super_admin'))")
     public CommonResult<Boolean> updateSaleOrderStatus(@RequestParam("id") Long id,
                                                       @RequestParam("status") Integer status) {
         saleOrderService.updateSaleOrderStatus(id, status);
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestServiceImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestServiceImpl.java
index 46a207b..5b86be8 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestServiceImpl.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseRequestServiceImpl.java
@@ -131,6 +131,10 @@
                 .setStatus(ErpPurchaseRequestStatusEnum.DRAFT.getStatus())
                 .setRequestUserId(userId)
                 .setRequestDeptId(deptId));
+        // 鍏煎鏃у鎴风鏈紶瀹氶噾鐨勬儏鍐碉紝鏁版嵁搴撲笌涓氬姟灞傜粺涓�鎸� 0 澶勭悊
+        if (purchaseRequest.getDepositPrice() == null) {
+            purchaseRequest.setDepositPrice(BigDecimal.ZERO);
+        }
         calculateTotalPrice(purchaseRequest, requestItems);
         purchaseRequestMapper.insert(purchaseRequest);
 
@@ -159,6 +163,9 @@
 
         // 4. 鏇存柊鐢宠鍗�
         ErpPurchaseRequestDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseRequestDO.class);
+        if (updateObj.getDepositPrice() == null) {
+            updateObj.setDepositPrice(BigDecimal.ZERO);
+        }
         calculateTotalPrice(updateObj, requestItems);
         purchaseRequestMapper.updateById(updateObj);
 
@@ -605,4 +612,4 @@
                 .setItems(items);
     }
 
-}
\ No newline at end of file
+}
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ProductionFinishedListener.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ProductionFinishedListener.java
index 1381442..f5d1bef 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ProductionFinishedListener.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ProductionFinishedListener.java
@@ -3,6 +3,8 @@
 import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;
 import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderMapper;
 import cn.iocoder.yudao.module.mes.api.event.ProductionFinishedEvent;
+import cn.iocoder.yudao.module.mes.api.event.ProductionCanceledEvent;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.event.EventListener;
@@ -37,4 +39,16 @@
         log.info("[onProductionFinished] 閿�鍞鍗� {} 鐢熶骇鐘舵�佸凡鏇存柊涓哄畬鎴�", event.getSaleOrderId());
     }
 
-}
\ No newline at end of file
+    @EventListener
+    @Async
+    public void onProductionCanceled(ProductionCanceledEvent event) {
+        log.info("[onProductionCanceled] 鏀跺埌鐢熶骇鍙栨秷浜嬩欢锛岄攢鍞鍗旾D: {}", event.getSaleOrderId());
+        // 浣跨敤鏄惧紡 update wrapper 鎵嶈兘鎶婂凡瀛樺湪鐨勫畬鎴愭椂闂寸湡姝f洿鏂颁负 NULL
+        saleOrderMapper.update(null, new LambdaUpdateWrapper<ErpSaleOrderDO>()
+                .eq(ErpSaleOrderDO::getId, event.getSaleOrderId())
+                .set(ErpSaleOrderDO::getProductionStatus, 0)
+                .set(ErpSaleOrderDO::getProductionFinishTime, null));
+        log.info("[onProductionCanceled] 閿�鍞鍗� {} 鐢熶骇鐘舵�佸凡鎭㈠涓烘湭瀹屾垚", event.getSaleOrderId());
+    }
+
+}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRuleSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRuleSaveReqVO.java
index c14b5ed..e775b00 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRuleSaveReqVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRuleSaveReqVO.java
@@ -5,6 +5,7 @@
 
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.AssertTrue;
 import java.math.BigDecimal;
 import java.time.LocalTime;
 
@@ -54,6 +55,17 @@
     @Schema(description = "鍧愭爣绯伙紝浠呮敮鎸� WGS84")
     private String coordinateSystem;
 
+    @AssertTrue(message = "鍚敤瀹氫綅鎵撳崱鏃讹紝鍦扮偣鍚嶇О銆佺粡绾害鍜�10~5000绫虫墦鍗″崐寰勫繀椤诲畬鏁撮厤缃�")
+    public boolean isLocationConfigValid() {
+        if (!Boolean.TRUE.equals(locationRequired)) {
+            return true;
+        }
+        return locationName != null && !locationName.isBlank()
+                && longitude != null && latitude != null && radiusMeters != null
+                && radiusMeters >= 10 && radiusMeters <= 5000
+                && "WGS84".equalsIgnoreCase(coordinateSystem);
+    }
+
     @Schema(description = "鐘舵�侊細0-鍚敤锛�1-绂佺敤", example = "0")
     private Integer status;
 
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java
index 0e41948..b91eded 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java
@@ -447,7 +447,7 @@
         boolean required = Boolean.TRUE.equals(rule.getLocationRequired());
         if (required && (StrUtil.isBlank(rule.getLocationName()) || rule.getLongitude() == null
                 || rule.getLatitude() == null || rule.getRadiusMeters() == null
-                || rule.getRadiusMeters() < 50 || rule.getRadiusMeters() > 5000
+                || rule.getRadiusMeters() < 10 || rule.getRadiusMeters() > 5000
                 || !"WGS84".equalsIgnoreCase(rule.getCoordinateSystem()))) {
             throw exception(ATTENDANCE_LOCATION_RULE_INVALID);
         }
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRuleServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRuleServiceImpl.java
index d80627a..00a15e4 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRuleServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRuleServiceImpl.java
@@ -134,7 +134,7 @@
                 || reqVO.getLatitude().compareTo(BigDecimal.valueOf(90)) > 0)) {
             throw exception(ATTENDANCE_LOCATION_COORDINATE_INVALID);
         }
-        if (reqVO.getRadiusMeters() < 50 || reqVO.getRadiusMeters() > 5000) {
+        if (reqVO.getRadiusMeters() < 10 || reqVO.getRadiusMeters() > 5000) {
             throw exception(ATTENDANCE_LOCATION_RULE_INVALID);
         }
         if (Boolean.TRUE.equals(reqVO.getLocationRequired())
diff --git a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApi.java b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApi.java
index 07dfdb4..b861b55 100644
--- a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApi.java
+++ b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApi.java
@@ -24,6 +24,14 @@
     CommonResult<MdmItemCategoryRespDTO> getItemCategory(Long id);
 
     /**
+     * 鏍规嵁缂栫爜鑾峰彇鐗╂枡鍒嗙被
+     *
+     * @param code 鍒嗙被缂栫爜
+     * @return 鐗╂枡鍒嗙被
+     */
+    CommonResult<MdmItemCategoryRespDTO> getItemCategoryByCode(String code);
+
+    /**
      * 鑾峰彇鐗╂枡鍒嗙被鍒楄〃
      *
      * @param ids 鍒嗙被缂栧彿鍒楄〃
@@ -47,4 +55,4 @@
      */
     CommonResult<MdmItemCategoryRespDTO> getItemCategoryByItemOrProduct(String itemOrProduct);
 
-}
\ No newline at end of file
+}
diff --git a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApiImpl.java b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApiImpl.java
index 0604a97..c5195ef 100644
--- a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApiImpl.java
+++ b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApiImpl.java
@@ -35,6 +35,12 @@
     }
 
     @Override
+    public CommonResult<MdmItemCategoryRespDTO> getItemCategoryByCode(String code) {
+        MdmItemCategoryDO itemCategory = itemCategoryService.getItemCategoryByCode(code);
+        return success(BeanUtils.toBean(itemCategory, MdmItemCategoryRespDTO.class));
+    }
+
+    @Override
     public CommonResult<List<MdmItemCategoryRespDTO>> getItemCategoryList(Collection<Long> ids) {
         List<MdmItemCategoryDO> list = itemCategoryService.getItemCategoryList(ids);
         return success(BeanUtils.toBean(list, MdmItemCategoryRespDTO.class));
@@ -52,4 +58,4 @@
         return success(BeanUtils.toBean(itemCategory, MdmItemCategoryRespDTO.class));
     }
 
-}
\ No newline at end of file
+}
diff --git a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryService.java b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryService.java
index aa16c1a..9dd0b7c 100644
--- a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryService.java
+++ b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryService.java
@@ -58,6 +58,14 @@
     MdmItemCategoryDO getItemCategory(Long id);
 
     /**
+     * 鏍规嵁缂栫爜鑾峰緱鐗╂枡鍒嗙被
+     *
+     * @param code 鍒嗙被缂栫爜
+     * @return 鐗╂枡鍒嗙被
+     */
+    MdmItemCategoryDO getItemCategoryByCode(String code);
+
+    /**
      * 鏍¢獙鐗╂枡鍒嗙被瀛樺湪
      *
      * @param id 缂栧彿
diff --git a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryServiceImpl.java b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryServiceImpl.java
index 2616981..9f4854e 100644
--- a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryServiceImpl.java
+++ b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryServiceImpl.java
@@ -115,6 +115,11 @@
     }
 
     @Override
+    public MdmItemCategoryDO getItemCategoryByCode(String code) {
+        return itemCategoryMapper.selectByCode(code);
+    }
+
+    @Override
     public PageResult<MdmItemCategoryDO> getItemCategoryPage(MdmItemCategoryPageReqVO pageReqVO) {
         return itemCategoryMapper.selectPage(pageReqVO);
     }
diff --git a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java
index 5ce6e3e..c8ced1b 100644
--- a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java
+++ b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java
@@ -5,10 +5,8 @@
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemImportExcelVO;
 import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemImportReqVO;
 import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemImportRespVO;
@@ -28,12 +26,9 @@
 import cn.iocoder.yudao.module.mdm.service.category.MdmItemCategoryService;
 import cn.iocoder.yudao.module.mdm.service.unit.MdmUnitMeasureService;
 import cn.iocoder.yudao.module.mes.api.item.MesMdItemApi;
-import cn.iocoder.yudao.module.mes.api.item.MesMdItemTypeApi;
 import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSyncReqDTO;
-import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemTypeRespDTO;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import jakarta.annotation.Resource;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -78,10 +73,6 @@
     private MesMdItemApi mesMdItemApi;
     @Resource
     private MdmItemBatchConfigMapper itemBatchConfigMapper;
-
-    @Resource
-    private MesMdItemTypeApi mesMdItemTypeApi;
-
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -152,8 +143,11 @@
         // 瑙f瀽鍒嗙被 id -> code锛堜娇鐢� MDM 鏈湴鍒嗙被鏈嶅姟锛�
         String itemTypeCode = null;
         if (reqVO.getCategoryId() != null) {
-            CommonResult<MesMdItemTypeRespDTO> itemType = mesMdItemTypeApi.getItemType(reqVO.getCategoryId());
-            itemTypeCode = itemType.getData().getCode();
+            MdmItemCategoryDO category = itemCategoryService.getItemCategory(reqVO.getCategoryId());
+            if (category == null) {
+                throw exception(MDM_ITEM_CATEGORY_NOT_EXISTS);
+            }
+            itemTypeCode = category.getCode();
         }
         // 缁勮鍚屾 DTO
         MesMdItemSyncReqDTO syncReqDTO = new MesMdItemSyncReqDTO();
@@ -545,4 +539,4 @@
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/event/ProductionCanceledEvent.java b/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/event/ProductionCanceledEvent.java
new file mode 100644
index 0000000..6f83314
--- /dev/null
+++ b/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/event/ProductionCanceledEvent.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.mes.api.event;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * 閿�鍞鍗曞叧鑱旂殑鐢熶骇璁″垝鍙栨秷浜嬩欢銆�
+ */
+@Data
+@AllArgsConstructor
+public class ProductionCanceledEvent {
+
+    private Long saleOrderId;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/cal/plan/vo/MesCalPlanSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/cal/plan/vo/MesCalPlanSaveReqVO.java
index a7d9af5..bea7a12 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/cal/plan/vo/MesCalPlanSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/cal/plan/vo/MesCalPlanSaveReqVO.java
@@ -14,8 +14,7 @@
     @Schema(description = "璁″垝缂栧彿", example = "1024")
     private Long id;
 
-    @Schema(description = "璁″垝缂栫爜", requiredMode = Schema.RequiredMode.REQUIRED, example = "PLAN001")
-    @NotEmpty(message = "璁″垝缂栫爜涓嶈兘涓虹┖")
+    @Schema(description = "璁″垝缂栫爜锛堟柊澧炴椂涓虹┖鍒欑敱绯荤粺鑷姩鐢熸垚锛�", example = "PLAN001")
     private String code;
 
     @Schema(description = "璁″垝鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024骞存帓鐝鍒�")
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/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/qc/ipqc/vo/MesQcIpqcSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcSaveReqVO.java
index 433765d..043715d 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcSaveReqVO.java
@@ -97,7 +97,8 @@
 
     // ========== 妫�楠� ==========
 
-    @Schema(description = "妫�娴嬬粨鏋�", example = "1")
+    @Schema(description = "妫�娴嬬粨鏋�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "妫�娴嬬粨鏋滀笉鑳戒负绌�")
     private Integer checkResult;
 
     @Schema(description = "妫�娴嬫棩鏈�")
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcSaveReqVO.java
index b08e9d1..76a067c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcSaveReqVO.java
@@ -78,7 +78,8 @@
     @NotNull(message = "妫�娴嬫棩鏈熶笉鑳戒负绌�")
     private LocalDateTime inspectDate;
 
-    @Schema(description = "妫�娴嬬粨鏋�", example = "1")
+    @Schema(description = "妫�娴嬬粨鏋�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "妫�娴嬬粨鏋滀笉鑳戒负绌�")
     private Integer checkResult;
 
     @Schema(description = "鏉ユ枡鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcSaveReqVO.java
index 98c20eb..5ad7031 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcSaveReqVO.java
@@ -74,7 +74,8 @@
 
     // ========== 妫�楠� ==========
 
-    @Schema(description = "妫�娴嬬粨鏋�", example = "1")
+    @Schema(description = "妫�娴嬬粨鏋�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "妫�娴嬬粨鏋滀笉鑳戒负绌�")
     private Integer checkResult;
 
     @Schema(description = "鍑鸿揣鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcSaveReqVO.java
index ae9629d..b79227c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcSaveReqVO.java
@@ -75,7 +75,8 @@
 
     // ========== 妫�楠� ==========
 
-    @Schema(description = "妫�娴嬬粨鏋�", example = "1")
+    @Schema(description = "妫�娴嬬粨鏋�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "妫�娴嬬粨鏋滀笉鑳戒负绌�")
     private Integer checkResult;
 
     @Schema(description = "妫�娴嬫棩鏈�", requiredMode = Schema.RequiredMode.REQUIRED)
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/enums/ErrorCodeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
index 126c12e..e9d01f3 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
@@ -289,6 +289,11 @@
     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, "宸ヨ壓璺嚎鍙兘鍏宠仈浜у搧绫诲瀷鐗╂枡");
+    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, "鏈亾宸ュ簭銆恵}銆戝彧鑳戒骇鍑烘垚鍝佸垎绫讳骇鍝侊紝涓斿繀椤诲寘鍚叏閮ㄧ粦瀹氭垚鍝�");
     // ========== 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/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
index 195b2f1..39df74f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
@@ -28,6 +28,7 @@
     WM_PACKAGE_CODE("WM_PACKAGE_CODE", "瑁呯鍗曠紪鐮�"),
     WM_BATCH_CODE("WM_BATCH_CODE", "鎵规缂栫爜"),
     PRO_MPS_CODE("PRO_MPS_CODE", "鐢熶骇璁″垝缂栫爜"),
+    CAL_PLAN_CODE("CAL_PLAN_CODE", "鎺掔彮璁″垝缂栫爜"),
     PRO_MAINTENANCE_PLAN_CODE("PRO_MAINTENANCE_PLAN_CODE", "鐢靛姏浣滀笟璁″垝缂栫爜"),
     PRO_TASK_CODE("PRO_TASK_CODE", "鐢熶骇浠诲姟缂栫爜"),
     QC_IQC_CODE("QC_IQC_CODE", "鏉ユ枡妫�楠屽崟缂栫爜"),
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/plan/MesCalPlanServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/plan/MesCalPlanServiceImpl.java
index df5b27b..0ddf88b 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/plan/MesCalPlanServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/plan/MesCalPlanServiceImpl.java
@@ -13,7 +13,9 @@
 import cn.iocoder.yudao.module.mes.dal.mysql.cal.plan.MesCalPlanMapper;
 import cn.iocoder.yudao.module.mes.enums.cal.MesCalPlanStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.cal.MesCalShiftTypeEnum;
+import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
 import cn.iocoder.yudao.module.mes.service.cal.team.MesCalTeamShiftService;
+import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
 import jakarta.annotation.Resource;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -51,10 +53,17 @@
     @Resource
     @Lazy
     private MesCalTeamShiftService teamShiftService;
+    @Resource
+    private MesMdAutoCodeRecordService autoCodeRecordService;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createPlan(MesCalPlanSaveReqVO createReqVO) {
+        // 鍓嶇閫氬父浼氶鐢熸垚缂栫爜锛涙帴鍙d晶浠嶆彁渚涘厹搴曪紝閬垮厤绉诲姩绔�佸鍏ュ鐨勮皟鐢ㄦ柟婕忎紶鍚庢祦绋嬩腑鏂��
+        if (StrUtil.isBlank(createReqVO.getCode())) {
+            createReqVO.setCode(autoCodeRecordService.generateAutoCode(
+                    MesMdAutoCodeRuleCodeEnum.CAL_PLAN_CODE.getCode()));
+        }
         // 1. 鏍¢獙缂栫爜鍞竴
         validatePlanCodeUnique(null, createReqVO.getCode());
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/machinery/MesDvMachineryServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/machinery/MesDvMachineryServiceImpl.java
index 45568db..b11c9f8 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/machinery/MesDvMachineryServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/machinery/MesDvMachineryServiceImpl.java
@@ -8,6 +8,8 @@
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
 import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemCreateReqDTO;
+import cn.iocoder.yudao.module.mdm.api.category.MdmItemCategoryApi;
+import cn.iocoder.yudao.module.mdm.api.category.dto.MdmItemCategoryRespDTO;
 import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
 import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
 import cn.iocoder.yudao.module.mdm.enums.MdmItemTypeEnum;
@@ -104,6 +106,8 @@
     // ========== 璁惧鐗╂枡鑱斿姩渚濊禆 ==========
     @Resource
     private MdmItemApi mdmItemApi;
+    @Resource
+    private MdmItemCategoryApi mdmItemCategoryApi;
     @Resource
     private MdmUnitMeasureApi mdmUnitMeasureApi;
     @Resource
@@ -382,9 +386,15 @@
      * @return MDM 鐗╂枡缂栧彿锛�= MES 鐗╂枡缂栧彿锛�
      */
     private Long createEquipmentMaterial(String code, String name, String specification, String remark) {
-        // 1. 瑙f瀽鈥滆澶団�濈被鍨嬶紙MES 渚э級涓庘�滃彴鈥濆崟浣嶏紙MDM 渚� + MES 渚э級锛岀己澶卞垯鎻愮ず涓绘暟鎹湭鍒濆鍖�
+        // 1. 鍒嗗埆鎸夌紪鐮佽В鏋� MDM 鍒嗙被銆丮ES 绫诲瀷鍜屼袱渚у崟浣嶏紝绂佹渚濊禆涓ゅ琛ㄤ富閿伆濂戒竴鑷淬��
         MesMdItemTypeDO itemType = mesMdItemTypeService.getItemTypeByCode(EQUIPMENT_ITEM_TYPE_CODE);
         if (itemType == null) {
+            throw exception(DV_MACHINERY_EQUIP_MASTER_MISSING);
+        }
+        CommonResult<MdmItemCategoryRespDTO> categoryResult =
+                mdmItemCategoryApi.getItemCategoryByCode(EQUIPMENT_ITEM_TYPE_CODE);
+        MdmItemCategoryRespDTO mdmCategory = categoryResult.isSuccess() ? categoryResult.getData() : null;
+        if (mdmCategory == null) {
             throw exception(DV_MACHINERY_EQUIP_MASTER_MISSING);
         }
         if (mesMdUnitMeasureService.getUnitMeasureByCode(EQUIPMENT_UNIT_CODE) == null) {
@@ -396,13 +406,13 @@
             throw exception(DV_MACHINERY_EQUIP_MASTER_MISSING);
         }
 
-        // 2. 鍒涘缓 MDM 鐗╂枡锛涘垎绫讳娇鐢ㄢ�滆澶団�濆垎绫伙紙鍏� id 涓� MES 绫诲瀷 id 瀵归綈锛岀敱鍒濆鍖栬剼鏈繚璇侊級
+        // 2. 鍒涘缓 MDM 鐗╂枡锛涘垎绫诲彇 MDM 鑷韩涓婚敭锛屽厑璁镐笉鍚岀幆澧冧腑涓ゅ鍒嗙被琛ㄧ殑涓婚敭涓嶄竴鑷淬��
         MdmItemCreateReqDTO createReqDTO = new MdmItemCreateReqDTO();
         createReqDTO.setCode(code);
         createReqDTO.setName(name);
         createReqDTO.setSpecification(specification);
         createReqDTO.setRemark(remark);
-        createReqDTO.setCategoryId(itemType.getId());
+        createReqDTO.setCategoryId(mdmCategory.getId());
         createReqDTO.setUnitMeasureId(mdmUnit.getId());
         createReqDTO.setItemType(MdmItemTypeEnum.EQUIPMENT.getType());
         createReqDTO.setStatus(0);
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 81ce0e4..60962e5 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
@@ -10,6 +10,7 @@
 import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsImportRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsSaveReqVO;
+import cn.iocoder.yudao.module.mes.api.event.ProductionCanceledEvent;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderSaveReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsMergeItemDO;
@@ -42,6 +43,7 @@
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
@@ -95,6 +97,8 @@
     private MesProRouteProcessService routeProcessService;
     @Resource
     private MesProWorkOrderProcessService workOrderProcessService;
+    @Resource
+    private ApplicationEventPublisher eventPublisher;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -308,6 +312,10 @@
         mpsMapper.updateById(new MesProMpsDO()
                 .setId(id)
                 .setStatus(MesProMpsStatusEnum.CANCELED.getStatus()));
+        // 鏉ユ簮浜庨攢鍞鍗曟椂锛屽悓姝ユ仮澶嶉攢鍞鍗曠敓浜х姸鎬侊紝鍏佽閲嶆柊鍒涘缓鐢熶骇璁″垝
+        if (mps.getSaleOrderId() != null) {
+            eventPublisher.publishEvent(new ProductionCanceledEvent(mps.getSaleOrderId()));
+        }
     }
 
     @Override
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 f88d950..9671e6e 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
@@ -92,12 +92,11 @@
 
         // 2. 鏇存柊
         MesProRouteProcessDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProcessDO.class);
-        if (CollUtil.isNotEmpty(updateReqVO.getOutputItemIds())) {
-            updateObj.setOutputItemId(
+        // 绌烘暟缁勪篃瑕佹樉寮忓啓鍏ョ┖涓诧紝鍚﹀垯 MyBatis-Plus 榛樿蹇界暐 null锛岀敤鎴锋竻绌轰骇鍑哄搧鍚庢棫閰嶇疆浠嶄細娈嬬暀銆�
+        updateObj.setOutputItemId(CollUtil.isEmpty(updateReqVO.getOutputItemIds()) ? "" :
                 updateReqVO.getOutputItemIds().stream()
-                    .map(String::valueOf)
-                    .collect(Collectors.joining(",")));
-        }
+                        .map(String::valueOf)
+                        .collect(Collectors.joining(",")));
         routeProcessMapper.updateById(updateObj);
 
         // 3. 閲嶅缓宸ュ簭閾�
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 c19f6b7..77e3b0c 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
@@ -10,6 +10,7 @@
 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 jakarta.annotation.Resource;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -43,6 +44,8 @@
     private MesMdItemService itemService;
     @Resource
     private MesMdItemTypeService itemTypeService;
+    @Resource
+    private MesWmWarehouseAreaService warehouseAreaService;
 
     @Override
     public Long createRouteProduct(MesProRouteProductSaveReqVO createReqVO) {
@@ -52,6 +55,8 @@
         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);
@@ -69,6 +74,8 @@
         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);
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 72698b7..e30fe31 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
@@ -16,8 +16,11 @@
 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 jakarta.annotation.Resource;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -29,6 +32,10 @@
 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.stream.Collectors;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -58,6 +65,10 @@
     @Resource
     @Lazy
     private MesMdItemService itemService;
+    @Resource
+    private MesMdItemTypeService itemTypeService;
+    @Resource
+    private MesWmWarehouseAreaService warehouseAreaService;
 
     @Override
     public Long createRoute(MesProRouteSaveReqVO createReqVO) {
@@ -147,13 +158,75 @@
         // 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());
+            if (CollUtil.isEmpty(outputIds)) {
+                throw exception(PRO_ROUTE_PROCESS_OUTPUT_REQUIRED, process.getProcessId());
+            }
+            for (Long outputId : outputIds) {
+                if (!boundItemIds.contains(outputId)) {
+                    MesMdItemDO item = itemService.validateItemExists(outputId);
+                    throw exception(PRO_ROUTE_PROCESS_OUTPUT_NOT_BOUND, item.getName());
+                }
+            }
+            if (process.getId().equals(lastProcess.getId())) {
+                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());
+            }
+        }
+    }
+
+    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 (rootCode.equals(type.getCode())) {
+                return true;
+            }
+            if (type.getParentId() == null || MesMdItemTypeDO.PARENT_ID_ROOT.equals(type.getParentId())) {
+                return false;
+            }
+            type = itemTypeService.getItemType(type.getParentId());
+        }
+        return false;
     }
 
     @Override
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 d70dea9..8a33ace 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
@@ -9,11 +9,13 @@
 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;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productproduce.MesWmProductProduceMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductProduceStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
@@ -23,6 +25,7 @@
 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.productreceipt.MesWmProductReceiptService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
@@ -52,6 +55,8 @@
 
     @Resource
     private MesWmProductProduceMapper productProduceMapper;
+    @Resource
+    private MesProFeedbackMapper feedbackMapper;
 
     @Resource
     private MesWmProductProduceLineService productProduceLineService;
@@ -61,6 +66,8 @@
     private MesProWorkOrderService workOrderService;
     @Resource
     private MesProWorkOrderProcessService workOrderProcessService;
+    @Resource
+    private MesProRouteProductService routeProductService;
     @Resource
     private MesWmProductReceiptService productReceiptService;
     @Resource
@@ -86,7 +93,7 @@
         }
 
         // 2. 鍒涘缓搴撳瓨浜嬪姟
-        createTransactionList(id, lines);
+        createTransactionList(produce, lines);
 
         // 3. 鏇存柊鍏ュ簱鍗曠姸鎬�
         productProduceMapper.updateById(new MesWmProductProduceDO()
@@ -100,7 +107,11 @@
         }
     }
 
-    private void createTransactionList(Long produceId, List<MesWmProductProduceLineDO> lines) {
+    private void createTransactionList(MesWmProductProduceDO produce, List<MesWmProductProduceLineDO> lines) {
+        MesProFeedbackDO feedback = produce.getFeedbackId() != null
+                ? feedbackMapper.selectById(produce.getFeedbackId()) : null;
+        String bizCode = feedback != null && cn.hutool.core.util.StrUtil.isNotBlank(feedback.getCode())
+                ? feedback.getCode() : "PRODUCE-" + produce.getId();
         for (MesWmProductProduceLineDO line : lines) {
             // 1. 鏍¢獙姣忚鏄庣粏鏁伴噺涔嬪拰绛変簬琛屾暟閲�
             List<MesWmProductProduceDetailDO> details = productProduceDetailService.getProductProduceDetailListByLineId(
@@ -117,8 +128,8 @@
                         .setQuantity(detail.getQuantity())
                         .setBatchId(detail.getBatchId()).setBatchCode(detail.getBatchCode())
                         .setWarehouseId(detail.getWarehouseId()).setLocationId(detail.getLocationId()).setAreaId(detail.getAreaId())
-                        .setBizType(MesBizTypeConstants.WM_PRODUCT_PRODUCE).setBizId(produceId)
-                        .setBizCode("").setBizLineId(line.getId()));
+                        .setBizType(MesBizTypeConstants.WM_PRODUCT_PRODUCE).setBizId(produce.getId())
+                        .setBizCode(bizCode).setBizLineId(line.getId()));
             }
         }
     }
@@ -127,6 +138,11 @@
     @Transactional(rollbackFor = Exception.class)
     public MesWmProductProduceDO generateProductProduce(MesProFeedbackDO feedback, boolean checkFlag,
                                                          MesProFeedbackApproveReqVO approveReqVO) {
+        // 涓�涓姤宸ュ彧鑳界敓鎴愪竴寮犵敓浜т骇鍑哄崟锛涘鎵规帴鍙i噸璇曟椂鐩存帴杩斿洖鏃㈡湁鍗曟嵁
+        MesWmProductProduceDO existingProduce = productProduceMapper.selectByFeedbackId(feedback.getId());
+        if (existingProduce != null) {
+            return existingProduce;
+        }
         // 0. 鏌ヨ鍏宠仈鐨勫伐鍗曪紙鐢ㄤ簬鑾峰彇 clientId 绛変俊鎭級
         MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(feedback.getWorkOrderId());
 
@@ -166,10 +182,8 @@
         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);
+        // 1.5 浼樺厛浣跨敤宸ヨ壓璺嚎浜у搧缁戝畾鐨勬姤宸ヤ粨搴擄紱鍘嗗彶鏁版嵁鏈厤缃椂鍥為��铏氭嫙绾胯竟搴撱��
+        WarehouseTarget outputTarget = resolveOutputTarget(feedback.getItemId());
 
         // 3. 鏍规嵁鏄惁闇�瑕佹楠屽垎鏀鐞�
         if (checkFlag) {
@@ -189,7 +203,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 鍚堟牸鍝佽 + 鏄庣粏
@@ -199,7 +213,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);
             }
         }
@@ -259,13 +273,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(pendingLine.getItemId());
 
         // 3A. 鎯呭喌涓�锛氬瓨鍦ㄤ笉鍚堟牸鍝佹暟閲忥紝闇�瑕佹媶鍒嗚
         if (unqualifiedQty != null && unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) {
@@ -282,7 +292,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));
@@ -293,7 +303,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 {
@@ -304,7 +314,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());
         }
 
@@ -312,4 +322,23 @@
         finishProductProduce(produce.getId());
     }
 
+    private WarehouseTarget resolveOutputTarget(Long itemId) {
+        MesProRouteProductDO routeProduct = itemId != null
+                ? routeProductService.getRouteProductByItemId(itemId) : null;
+        if (routeProduct != null && routeProduct.getWarehouseId() != null
+                && routeProduct.getLocationId() != null && routeProduct.getAreaId() != null) {
+            areaService.validateWarehouseAreaExists(routeProduct.getWarehouseId(),
+                    routeProduct.getLocationId(), routeProduct.getAreaId());
+            return new WarehouseTarget(routeProduct.getWarehouseId(), routeProduct.getLocationId(), routeProduct.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/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
index dbdf2cc..8730d3e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productreceipt/MesWmProductReceiptServiceImpl.java
@@ -81,6 +81,8 @@
     private MesWmWarehouseLocationService locationService;
     @Resource
     private MesWmWarehouseAreaService areaService;
+    @Resource
+    private cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceDetailService productProduceDetailService;
 
     @Override
     public Long createProductReceipt(MesWmProductReceiptSaveReqVO createReqVO) {
@@ -143,8 +145,13 @@
             MesMdItemDO item = mdItemService.getItem(produceLine.getItemId());
             Long mdmItemId = item != null && item.getMdmItemId() != null
                     ? item.getMdmItemId() : produceLine.getItemId();
+            List<cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDetailDO> details =
+                    productProduceDetailService.getProductProduceDetailListByLineId(produceLine.getId());
+            Long warehouseId = CollUtil.isNotEmpty(details) ? details.get(0).getWarehouseId() : virtualWarehouse.getId();
+            Long locationId = CollUtil.isNotEmpty(details) ? details.get(0).getLocationId() : virtualLocation.getId();
+            Long areaId = CollUtil.isNotEmpty(details) ? details.get(0).getAreaId() : virtualArea.getId();
             MesWmMaterialStockDO stock = materialStockService.getMaterialStockByCompositeKey(
-                    mdmItemId, virtualWarehouse.getId(), virtualLocation.getId(), virtualArea.getId(),
+                    mdmItemId, warehouseId, locationId, areaId,
                     produceLine.getBatchId(), null);
             productReceiptLineMapper.insert(MesWmProductReceiptLineDO.builder()
                     .receiptId(receipt.getId())
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java
index 7dadb51..115b706 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java
@@ -22,6 +22,9 @@
     @Schema(description = "鐢ㄦ埛璐﹀彿锛屾ā绯婂尮閰�", example = "yudao")
     private String username;
 
+    @Schema(description = "鐢ㄦ埛鏄电О锛屾ā绯婂尮閰�", example = "绠$悊鍛�")
+    private String nickname;
+
     @Schema(description = "鎵嬫満鍙风爜锛屾ā绯婂尮閰�", example = "yudao")
     private String mobile;
 
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java
index c0c9be8..a24a610 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java
@@ -28,6 +28,7 @@
     default PageResult<AdminUserDO> selectPage(UserPageReqVO reqVO, Collection<Long> deptIds, Collection<Long> userIds) {
         return selectPage(reqVO, new LambdaQueryWrapperX<AdminUserDO>()
                 .likeIfPresent(AdminUserDO::getUsername, reqVO.getUsername())
+                .likeIfPresent(AdminUserDO::getNickname, reqVO.getNickname())
                 .likeIfPresent(AdminUserDO::getMobile, reqVO.getMobile())
                 .eqIfPresent(AdminUserDO::getStatus, reqVO.getStatus())
                 .betweenIfPresent(AdminUserDO::getCreateTime, reqVO.getCreateTime())

--
Gitblit v1.9.3