From beb66826d7ca7faae90d03b5e0d21c882f5c9750 Mon Sep 17 00:00:00 2001
From: zss <zss@example.com>
Date: 星期三, 11 三月 2026 13:03:08 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_宁夏_中盛建材' into dev_宁夏_中盛建材

---
 src/main/java/com/ruoyi/productionPlan/mapper/ProductionPlanMapper.java            |    7 
 src/main/java/com/ruoyi/productionPlan/pojo/ProductionPlan.java                    |   34 ++++
 src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java      |   12 
 src/main/java/com/ruoyi/productionPlan/controller/ProductionPlanController.java    |   30 +++
 src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java |  147 +++++++++++++++++++-
 src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanDto.java                  |   20 ++
 src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanSummaryDto.java           |   70 ++++++++++
 doc/宁夏-中盛建材.sql                                                                    |   12 +
 src/main/resources/mapper/productionPlan/ProductionPlanMapper.xml                  |   29 ++++
 src/main/java/com/ruoyi/productionPlan/service/ProductionPlanService.java          |   18 ++
 src/main/java/com/ruoyi/productionPlan/enums/DataSourceTypeEnum.java               |    4 
 src/main/java/com/ruoyi/production/pojo/ProductOrder.java                          |   37 ++---
 12 files changed, 372 insertions(+), 48 deletions(-)

diff --git "a/doc/\345\256\201\345\244\217-\344\270\255\347\233\233\345\273\272\346\235\220.sql" "b/doc/\345\256\201\345\244\217-\344\270\255\347\233\233\345\273\272\346\235\220.sql"
index ba7e338..ab5e37f 100644
--- "a/doc/\345\256\201\345\244\217-\344\270\255\347\233\233\345\273\272\346\235\220.sql"
+++ "b/doc/\345\256\201\345\244\217-\344\270\255\347\233\233\345\273\272\346\235\220.sql"
@@ -82,3 +82,15 @@
     `update_user` int NULL DEFAULT NULL,
     PRIMARY KEY (`id`)
     ) COMMENT = '鑳芥簮绫诲瀷-鑳借�楁妱琛ㄦ槑缁哶闄勪欢';
+
+alter table product_order
+drop column sales_ledger_id,
+drop column sale_ledger_product_id,
+drop column product_model_id;
+
+alter table production_plan
+    add assigned_quantity DECIMAL(10, 4) default 0 not null COMMENT '涓嬪彂鏁伴噺';
+
+alter table product_order
+    add plan_complete_time datetime(0) NULL DEFAULT NULL COMMENT '璁″垝瀹屾垚鏃堕棿',
+    add combine_production_plan_ids varchar(500) default '' not null;
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductOrder.java b/src/main/java/com/ruoyi/production/pojo/ProductOrder.java
index 5286cd3..0e06718 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductOrder.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductOrder.java
@@ -11,6 +11,7 @@
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
+import java.util.List;
 
 @Data
 @TableName("product_order")
@@ -20,24 +21,6 @@
 
     @TableId(value = "id", type = IdType.AUTO)
     private Long id;
-
-    /**
-     * 閿�鍞彴璐d
-     */
-    @ApiModelProperty(value = "閿�鍞彴璐d")
-    private Long salesLedgerId;
-
-    /**
-     * 閿�鍞彴璐︿骇鍝乮d(sales_ledger_product)
-     */
-    @ApiModelProperty(value = "閿�鍞彴璐︿骇鍝乮d")
-    private Long saleLedgerProductId;
-
-    /**
-     * 浜у搧瑙勬牸id
-     */
-    @ApiModelProperty(value = "浜у搧瑙勬牸id")
-    private Long productModelId;
 
     /**
      * 妯$増鐨勫伐鑹鸿矾绾縤d
@@ -51,6 +34,20 @@
     @ApiModelProperty(value = "鐢熶骇璁㈠崟鍙�")
     @Excel(name = "鐢熶骇璁㈠崟鍙�")
     private String npsNo;
+
+    /**
+     * 鍚堝苟鐢熶骇璁″垝ids
+     */
+    @ApiModelProperty(value = "鍚堝苟鐢熶骇璁″垝ids")
+    private String combineProductionPlanIds;
+
+    /**
+     * 璁″垝瀹屾垚鏃堕棿
+     */
+    @ApiModelProperty(value = "璁″垝瀹屾垚鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime planCompleteTime;
 
     /**
      * 绉熸埛id
@@ -73,7 +70,6 @@
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime updateTime;
-
 
     /**
      * 闇�姹傛暟閲�
@@ -100,7 +96,4 @@
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime endTime;
-
-
-
 }
diff --git a/src/main/java/com/ruoyi/productionPlan/controller/ProductionPlanController.java b/src/main/java/com/ruoyi/productionPlan/controller/ProductionPlanController.java
index af1965b..5f655c9 100644
--- a/src/main/java/com/ruoyi/productionPlan/controller/ProductionPlanController.java
+++ b/src/main/java/com/ruoyi/productionPlan/controller/ProductionPlanController.java
@@ -5,14 +5,15 @@
 import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
 import com.ruoyi.framework.web.domain.AjaxResult;
 import com.ruoyi.productionPlan.dto.ProductionPlanDto;
+import com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto;
 import com.ruoyi.productionPlan.service.ProductionPlanService;
+import com.ruoyi.staff.dto.StaffLeaveDto;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import java.util.List;
 
 /**
  * <br>
@@ -38,9 +39,32 @@
     }
 
     @GetMapping("/loadProdData")
+    @ApiOperation("鎷夊彇閿�鍞敓浜ц鍒�")
     @Log(title = "鎷夊彇閿�鍞敓浜ц鍒�", businessType = BusinessType.INSERT)
     public AjaxResult loadProdData() {
         productionPlanService.loadProdData();
         return AjaxResult.success();
     }
+
+    @PostMapping("/combine")
+    @Log(title = "鍚堝苟鐢熶骇璁″垝", businessType = BusinessType.INSERT)
+    @ApiOperation("鍚堝苟鐢熶骇璁″垝")
+    public AjaxResult combine(@RequestBody ProductionPlanDto productionPlanDto) {
+        return AjaxResult.success(productionPlanService.combine(productionPlanDto));
+    }
+
+    @PostMapping("")
+    @Log(title = "鍒涘缓鐢熶骇璁″垝", businessType = BusinessType.INSERT)
+    @ApiOperation("鍒涘缓鐢熶骇璁″垝")
+    public AjaxResult add(@RequestBody ProductionPlanDto productionPlanDto) {
+        return AjaxResult.success(productionPlanService.add(productionPlanDto));
+    }
+
+    @GetMapping("/summaryByProductType")
+    @ApiOperation("鎸夌収浜у搧绫诲埆姹囨�荤粺璁¢渶姹傞噺")
+    @Log(title = "鎸夌収浜у搧绫诲埆姹囨�荤粺璁¢渶姹傞噺", businessType = BusinessType.OTHER)
+    public AjaxResult summaryByProductType(ProductionPlanSummaryDto query) {
+        List<ProductionPlanSummaryDto> list = productionPlanService.summaryByProductType(query);
+        return AjaxResult.success(list);
+    }
 }
diff --git a/src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanDto.java b/src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanDto.java
index b65004d..e416d5b 100644
--- a/src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanDto.java
+++ b/src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanDto.java
@@ -1,6 +1,26 @@
 package com.ruoyi.productionPlan.dto;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.productionPlan.pojo.ProductionPlan;
 
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
 public class ProductionPlanDto extends ProductionPlan {
+    @ApiModelProperty(value = "鐢熶骇璁″垝id闆嗗悎")
+    private List<Long> ids;
+
+    @ApiModelProperty(value = "涓嬪彂鏁伴噺")
+    private BigDecimal totalAssignedQuantity;
+
+    @ApiModelProperty(value = "璁″垝瀹屾垚鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime planCompleteTime;
 }
diff --git a/src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanSummaryDto.java b/src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanSummaryDto.java
new file mode 100644
index 0000000..0831d89
--- /dev/null
+++ b/src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanSummaryDto.java
@@ -0,0 +1,70 @@
+package com.ruoyi.productionPlan.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * <br>
+ * 閿�鍞敓浜ч渶姹� - 浜у搧绫诲瀷姹囨�籇TO
+ * </br>
+ *
+ * @author deslrey
+ * @version 1.0
+ * @since 2026/03/11 10:24
+ */
+@Data
+@ApiModel("鐢熶骇闇�姹備骇鍝佹眹鎬�")
+public class ProductionPlanSummaryDto {
+
+    /**
+     * 鐗╂枡缂栫爜
+     */
+    @ApiModelProperty("鐗╂枡缂栫爜")
+    private String materialCode;
+
+    /**
+     * 浜у搧鍚嶇О
+     */
+    @ApiModelProperty("浜у搧鍚嶇О")
+    private String productName;
+
+    /**
+     * 浜у搧瑙勬牸
+     */
+    @ApiModelProperty("浜у搧瑙勬牸")
+    private String productSpec;
+
+    /**
+     * 浜у搧闀垮害
+     */
+    @ApiModelProperty("浜у搧闀垮害")
+    private Integer length;
+
+    /**
+     * 浜у搧瀹藉害
+     */
+    @ApiModelProperty("浜у搧瀹藉害")
+    private Integer width;
+
+    /**
+     * 浜у搧楂樺害
+     */
+    @ApiModelProperty("浜у搧楂樺害")
+    private Integer height;
+
+    /**
+     * 姹囨�诲潡鏁�
+     */
+    @ApiModelProperty("姹囨�诲潡鏁�")
+    private Integer quantity;
+
+    /**
+     * 姹囨�绘柟鏁�
+     */
+    @ApiModelProperty("姹囨�绘柟鏁�")
+    private BigDecimal volume;
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/productionPlan/enums/DataSourceTypeEnum.java b/src/main/java/com/ruoyi/productionPlan/enums/DataSourceTypeEnum.java
index d14ccc7..10edbd4 100644
--- a/src/main/java/com/ruoyi/productionPlan/enums/DataSourceTypeEnum.java
+++ b/src/main/java/com/ruoyi/productionPlan/enums/DataSourceTypeEnum.java
@@ -14,8 +14,8 @@
 @Getter
 public enum DataSourceTypeEnum {
 
-    SALES_ORDER(1, "閿�鍞鍗�"),
-    PRODUCTION_FORECAST(2, "鐢熶骇棰勬祴");
+    SALES_ORDER(1, "鍚屾"),
+    PRODUCTION_FORECAST(2, "鏂板");
 
     private final Integer code;
     private final String desc;
diff --git a/src/main/java/com/ruoyi/productionPlan/mapper/ProductionPlanMapper.java b/src/main/java/com/ruoyi/productionPlan/mapper/ProductionPlanMapper.java
index b7b863b..d6a7577 100644
--- a/src/main/java/com/ruoyi/productionPlan/mapper/ProductionPlanMapper.java
+++ b/src/main/java/com/ruoyi/productionPlan/mapper/ProductionPlanMapper.java
@@ -4,9 +4,11 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.productionPlan.dto.ProductionPlanDto;
+import com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto;
 import com.ruoyi.productionPlan.pojo.ProductionPlan;
-import com.ruoyi.staff.dto.StaffLeaveDto;
 import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 /**
  * <br>
@@ -19,4 +21,7 @@
  */
 public interface ProductionPlanMapper extends BaseMapper<ProductionPlan> {
     IPage<ProductionPlanDto> listPage(Page page, @Param("c") ProductionPlanDto productionPlanDto);
+
+    List<ProductionPlanSummaryDto> selectSummaryByProductType(ProductionPlanSummaryDto query);
+
 }
diff --git a/src/main/java/com/ruoyi/productionPlan/pojo/ProductionPlan.java b/src/main/java/com/ruoyi/productionPlan/pojo/ProductionPlan.java
index 45bbda3..1bd9b0c 100644
--- a/src/main/java/com/ruoyi/productionPlan/pojo/ProductionPlan.java
+++ b/src/main/java/com/ruoyi/productionPlan/pojo/ProductionPlan.java
@@ -4,6 +4,7 @@
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.math.BigDecimal;
@@ -31,146 +32,177 @@
     /**
      * 琛ㄥ崟瀹炰緥ID
      */
+    @ApiModelProperty("琛ㄥ崟瀹炰緥ID")
     private String formInstanceId;
 
     /**
      * 娴佹按鍙�
      */
+    @ApiModelProperty("娴佹按鍙�")
     private String serialNo;
 
     /**
      * 鐢宠鍗曠紪鍙�
      */
+    @ApiModelProperty("鐢宠鍗曠紪鍙�")
     private String applyNo;
 
     /**
      * 瀹㈡埛鍚嶇О
      */
+    @ApiModelProperty("瀹㈡埛鍚嶇О")
     private String customerName;
 
     /**
      * 鐗╂枡缂栫爜
      */
+    @ApiModelProperty("鐗╂枡缂栫爜")
     private String materialCode;
 
     /**
      * 浜у搧鍚嶇О
      */
+    @ApiModelProperty("浜у搧鍚嶇О")
     private String productName;
 
     /**
      * 浜у搧瑙勬牸
      */
+    @ApiModelProperty("浜у搧瑙勬牸")
     private String productSpec;
 
     /**
      * 闀�
      */
+    @ApiModelProperty("闀�")
     private Integer length;
 
     /**
      * 瀹�
      */
+    @ApiModelProperty("瀹�")
     private Integer width;
 
     /**
      * 楂�
      */
+    @ApiModelProperty("楂�")
     private Integer height;
 
     /**
      * 鍧楁暟
      */
+    @ApiModelProperty("鍧楁暟")
     private Integer quantity;
 
     /**
      * 鏂规暟
      */
+    @ApiModelProperty("鏂规暟")
     private BigDecimal volume;
 
     /**
      * 寮哄害
      */
+    @ApiModelProperty("寮哄害")
     private String strength;
 
     /**
      * 寮�濮嬫棩鏈�
      */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty("寮�濮嬫棩鏈�")
     private LocalDateTime startDate;
 
     /**
      * 缁撴潫鏃ユ湡
      */
+    @ApiModelProperty("缁撴潫鏃ユ湡")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime endDate;
 
     /**
      * 鎻愪氦浜�
      */
+    @ApiModelProperty("鎻愪氦浜�")
     private String submitter;
 
     /**
      * 鎻愪氦浜虹粍缁�
      */
+    @ApiModelProperty("鎻愪氦浜虹粍缁�")
     private String submitOrg;
 
     /**
      * 澶囨敞1
      */
+    @ApiModelProperty("澶囨敞1")
     private String remarkOne;
 
     /**
      * 澶囨敞2
      */
+    @ApiModelProperty("澶囨敞2")
     private String remarkTwo;
 
     /**
      * 鍒涘缓浜�
      */
+    @ApiModelProperty("鍒涘缓浜�")
     private String creatorName;
 
     /**
      * 淇敼浜�
      */
+    @ApiModelProperty("淇敼浜�")
     private String modifierName;
 
     /**
      * 琛ㄥ崟鍒涘缓鏃堕棿
      */
+    @ApiModelProperty("琛ㄥ崟鍒涘缓鏃堕棿")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime formCreatedTime;
 
     /**
      * 琛ㄥ崟淇敼鏃堕棿
      */
+    @ApiModelProperty("琛ㄥ崟淇敼鏃堕棿")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime formModifiedTime;
 
     /**
      * 鏁版嵁鍚屾绫诲瀷锛�1=鎵嬪姩 2=瀹氭椂浠诲姟
      */
+    @ApiModelProperty("鏁版嵁鍚屾绫诲瀷锛�1=鎵嬪姩 2=瀹氭椂浠诲姟")
     private Integer dataSyncType;
 
     /**
-     * 鏁版嵁鏉ユ簮绫诲瀷锛�1=閿�鍞鍗� 2=閿�鍞娴�
+     * 鏁版嵁鏉ユ簮绫诲瀷锛�1=鍚屾 2=鏂板
      */
+    @ApiModelProperty("鏁版嵁鏉ユ簮绫诲瀷锛�1=鍚屾 2=鏂板")
     private Integer dataSourceType;
 
     /**
      * 鏁版嵁搴撳垱寤烘椂闂�
      */
+    @ApiModelProperty("鏁版嵁搴撳垱寤烘椂闂�")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime createTime;
 
     /**
      * 鏁版嵁搴撴洿鏂版椂闂�
      */
+    @ApiModelProperty("鏁版嵁搴撴洿鏂版椂闂�")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime updateTime;
 
     /**
      * 褰撳墠鏇存柊鏁伴噺
      */
+    @ApiModelProperty("totalCount")
     private Integer totalCount;
+
+    @ApiModelProperty(value = "涓嬪彂鏁伴噺")
+    private BigDecimal assignedQuantity;
 }
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/productionPlan/service/ProductionPlanService.java b/src/main/java/com/ruoyi/productionPlan/service/ProductionPlanService.java
index 7d26b9b..069c0c3 100644
--- a/src/main/java/com/ruoyi/productionPlan/service/ProductionPlanService.java
+++ b/src/main/java/com/ruoyi/productionPlan/service/ProductionPlanService.java
@@ -4,7 +4,10 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ruoyi.productionPlan.dto.ProductionPlanDto;
+import com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto;
 import com.ruoyi.productionPlan.pojo.ProductionPlan;
+
+import java.util.List;
 
 /**
  * <br>
@@ -27,4 +30,19 @@
      * 瀹氭椂鍚屾
      */
     void syncProdDataJob();
+
+     /**
+     * 鍚堝苟鐢熶骇璁″垝
+     */
+    boolean combine(ProductionPlanDto productionPlanDto);
+
+    /**
+     * 鍒涘缓鐢熶骇璁″垝
+     */
+    boolean add(ProductionPlanDto productionPlanDto);
+
+    /**
+     * 鎸夌収浜у搧绫诲埆姹囨�荤粺璁¢渶姹傞噺
+     */
+    List<ProductionPlanSummaryDto> summaryByProductType(ProductionPlanSummaryDto query);
 }
diff --git a/src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java b/src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java
index 9cdd367..994e8c0 100644
--- a/src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java
+++ b/src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java
@@ -7,10 +7,14 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.basic.pojo.Product;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.http.HttpUtils;
 import com.ruoyi.framework.config.AliDingConfig;
+import com.ruoyi.production.pojo.ProductOrder;
+import com.ruoyi.production.service.ProductOrderService;
 import com.ruoyi.productionPlan.dto.ProductionPlanDto;
+import com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto;
 import com.ruoyi.productionPlan.mapper.ProductionPlanMapper;
 import com.ruoyi.productionPlan.pojo.ProductionPlan;
 import com.ruoyi.productionPlan.service.ProductionPlanService;
@@ -19,6 +23,7 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.time.LocalDateTime;
@@ -26,9 +31,11 @@
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+
+import static com.ruoyi.productionPlan.enums.DataSourceTypeEnum.PRODUCTION_FORECAST;
 
 /**
  * <br>
@@ -49,6 +56,9 @@
 
     @Autowired
     private ProductionPlanMapper productionPlanMapper;
+
+    @Autowired
+    private ProductOrderService productOrderService;
 
     /**
      * 鍚屾閿侊紝纭繚鎵嬪姩鍜屽畾鏃朵换鍔′笉鍚屾椂鎵ц
@@ -74,6 +84,83 @@
     @Override
     public void syncProdDataJob() {
         syncProdData(2);
+    }
+
+    /**
+     * 鍚堝苟鐢熶骇璁″垝
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean combine(ProductionPlanDto productionPlanDto) {
+        if (productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) {
+            return false;
+        }
+
+        //  鏌ヨ涓荤敓浜ц鍒�
+        List<ProductionPlan> plans = productionPlanMapper.selectBatchIds(productionPlanDto.getIds());
+
+        //  鏍¢獙鏄惁瀛樺湪涓嶅悓鐨勪骇鍝佸悕绉�
+        String firstProductName = plans.get(0).getProductName();
+        if (plans.stream().anyMatch(p -> !p.getProductName().equals(firstProductName))) {
+            log.warn("鍚堝苟澶辫触锛屽瓨鍦ㄤ笉鍚岀殑浜у搧鍚嶇О");
+            return false;
+        }
+
+        // 鏍¢獙鏄惁瀛樺湪涓嶅悓鐨勪骇鍝佽鏍�
+        String firstProductSpec = plans.get(0).getProductSpec();
+        if (plans.stream().anyMatch(p -> !p.getProductSpec().equals(firstProductSpec))) {
+            log.warn("鍚堝苟澶辫触锛屽瓨鍦ㄤ笉鍚岀殑浜у搧瑙勬牸");
+            return false;
+        }
+
+
+        // 鍙犲姞鏂规暟
+        BigDecimal totalVolume = plans.stream()
+                .map(ProductionPlan::getVolume)
+                .filter(v -> v != null)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        // 鍒ゆ柇涓嬪彂鏁伴噺鏄惁澶т簬绛変簬鏂规暟
+        if (productionPlanDto.getTotalAssignedQuantity().compareTo(totalVolume) > 0) {
+            log.warn("鍚堝苟澶辫触锛屼笅鍙戞暟閲忎笉鑳藉ぇ浜庢柟鏁�");
+            return false;
+        }
+
+        // 鏍规嵁涓嬪彂鏁伴噺锛屼粠绗竴涓敓浜ц鍒掑紑濮嬪垎閰嶆柟鏁�
+        BigDecimal assignedVolume = BigDecimal.ZERO;
+        for (ProductionPlan plan : plans) {
+            BigDecimal volume = plan.getVolume();
+            if (volume == null) {
+                continue;
+            }
+
+            if (assignedVolume.add(volume).compareTo(productionPlanDto.getTotalAssignedQuantity()) >= 0) {
+                // 鏈�鍚庝竴涓鍒掞紝鍒嗛厤鍓╀綑鏂规暟
+                plan.setAssignedQuantity(productionPlanDto.getTotalAssignedQuantity().subtract(assignedVolume));
+                break;
+            }
+
+            // 鍒嗛厤褰撳墠璁″垝鏂规暟
+            plan.setAssignedQuantity(volume);
+            productionPlanMapper.updateById(plan);
+            assignedVolume = assignedVolume.add(volume);
+        }
+
+        // 鍒涘缓鐢熶骇璁㈠崟
+        ProductOrder productOrder = new ProductOrder();
+        String combineIds = StringUtils.join(productionPlanDto.getIds(), ",");
+        productOrder.setCombineProductionPlanIds(combineIds);
+        productOrder.setQuantity(productionPlanDto.getTotalAssignedQuantity());
+        productOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime());
+        productOrderService.addProductOrder(productOrder);
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean add(ProductionPlanDto productionPlanDto) {
+        productionPlanDto.setDataSourceType(PRODUCTION_FORECAST.getCode());
+        productionPlanMapper.insert(productionPlanDto);
+        return true;
     }
 
     /**
@@ -133,8 +220,8 @@
                 List<ProductionPlan> list = parseProductionPlans(dataArr, dataSyncType, totalCount);
                 if (!list.isEmpty()) {
                     //  澶勭悊鏇存柊鎴栨柊澧�
-                    processSaveOrUpdate(list);
-                    totalSynced += list.size();
+                    int affected = processSaveOrUpdate(list);
+                    totalSynced += affected;
                 }
 
                 //  鍒ゆ柇鏄惁杩樻湁涓嬩竴椤�
@@ -277,19 +364,48 @@
         return list;
     }
 
-    private void processSaveOrUpdate(List<ProductionPlan> list) {
+    private int processSaveOrUpdate(List<ProductionPlan> list) {
+        if (list == null || list.isEmpty()) {
+            return 0;
+        }
+        int affected = 0;
+
+        //  鍘婚噸 formInstanceId
+        Set<String> formIds = list.stream()
+                .map(ProductionPlan::getFormInstanceId)
+                .collect(Collectors.toSet());
+
+        //  鏌ヨ鏁版嵁搴撳凡鏈夋暟鎹�
+        List<ProductionPlan> existList = this.list(new LambdaQueryWrapper<ProductionPlan>().in(ProductionPlan::getFormInstanceId, formIds));
+
+        //  Map (formInstanceId + materialCode)
+        Map<String, ProductionPlan> existMap = new HashMap<>();
+        for (ProductionPlan p : existList) {
+            String key = p.getFormInstanceId() + "_" + p.getMaterialCode();
+            existMap.put(key, p);
+        }
+
+        //  閬嶅巻鍚屾鏁版嵁
         for (ProductionPlan plan : list) {
-            LambdaQueryWrapper<ProductionPlan> queryWrapper = new LambdaQueryWrapper<>();
-            queryWrapper.eq(ProductionPlan::getFormInstanceId, plan.getFormInstanceId())
-                    .eq(ProductionPlan::getMaterialCode, plan.getMaterialCode());
-            ProductionPlan existing = this.getOne(queryWrapper);
-            if (existing != null) {
-                plan.setId(existing.getId());
-                this.updateById(plan);
-            } else {
+            String key = plan.getFormInstanceId() + "_" + plan.getMaterialCode();
+            ProductionPlan exist = existMap.get(key);
+            if (exist == null) {
+                // 鏂板
                 this.save(plan);
+                affected++;
+                log.info("鏂板鏁版嵁 formInstanceId={}, materialCode={}", plan.getFormInstanceId(), plan.getMaterialCode());
+            } else {
+                // 鍒ゆ柇鏄惁闇�瑕佹洿鏂�
+                if (exist.getFormModifiedTime() == null || !exist.getFormModifiedTime().equals(plan.getFormModifiedTime())) {
+                    plan.setId(exist.getId());
+                    plan.setCreateTime(exist.getCreateTime());
+                    this.updateById(plan);
+                    affected++;
+                    log.info("鏇存柊鏁版嵁 formInstanceId={}, materialCode={}", plan.getFormInstanceId(), plan.getMaterialCode());
+                }
             }
         }
+        return affected;
     }
 
     private LocalDateTime parseUtcTime(String utcString) {
@@ -304,4 +420,9 @@
             return null;
         }
     }
+
+    @Override
+    public List<ProductionPlanSummaryDto> summaryByProductType(ProductionPlanSummaryDto query) {
+        return baseMapper.selectSummaryByProductType(query);
+    }
 }
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
index 68b52cb..373beb4 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -267,9 +267,9 @@
      */
     public void addProductionData(SalesLedgerProduct salesLedgerProduct) {
         ProductOrder productOrder = new ProductOrder();
-        productOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
-        productOrder.setProductModelId(salesLedgerProduct.getProductModelId());
-        productOrder.setSaleLedgerProductId(salesLedgerProduct.getId());
+//        productOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
+//        productOrder.setProductModelId(salesLedgerProduct.getProductModelId());
+//        productOrder.setSaleLedgerProductId(salesLedgerProduct.getId());
         String string = productOrderServiceImpl.generateNextOrderNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
         productOrder.setNpsNo(string);
         productOrder.setQuantity(salesLedgerProduct.getQuantity());//闇�姹傛暟閲�
@@ -340,7 +340,7 @@
         //鎵归噺鏌ヨproductOrder
         List<ProductOrder> productOrders = productOrderMapper.selectList(
                 new LambdaQueryWrapper<ProductOrder>()
-                        .in(ProductOrder::getSaleLedgerProductId, productIds)
+//                        .in(ProductOrder::getSaleLedgerProductId, productIds)
         );
         if (!org.springframework.util.CollectionUtils.isEmpty(productOrders)) {
             List<Long> orderIds = productOrders.stream()
@@ -421,8 +421,8 @@
                     .in(ProductProcessRoute::getProductOrderId, orderIds));
 
             // 鎵归噺鍒犻櫎productOrder
-            productOrderMapper.delete(new LambdaQueryWrapper<ProductOrder>()
-                    .in(ProductOrder::getSaleLedgerProductId, productIds));
+//            productOrderMapper.delete(new LambdaQueryWrapper<ProductOrder>()
+//                    .in(ProductOrder::getSaleLedgerProductId, productIds));
         }
     }
 
diff --git a/src/main/resources/mapper/productionPlan/ProductionPlanMapper.xml b/src/main/resources/mapper/productionPlan/ProductionPlanMapper.xml
index 1fc9456..0b205d6 100644
--- a/src/main/resources/mapper/productionPlan/ProductionPlanMapper.xml
+++ b/src/main/resources/mapper/productionPlan/ProductionPlanMapper.xml
@@ -44,4 +44,33 @@
         WHERE 1 = 1
     </select>
 
+    <select id="selectSummaryByProductType" resultType="com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto">
+        SELECT
+        material_code,
+        product_name,
+        product_spec,
+        length,
+        width,
+        height,
+        COALESCE(SUM(quantity),0) AS quantity,
+        COALESCE(SUM(volume),0) AS volume
+        FROM production_plan
+        <where>
+            <if test="materialCode != null and materialCode != ''">
+                AND material_code LIKE CONCAT('%', #{materialCode}, '%')
+            </if>
+
+            <if test="productName != null and productName != ''">
+                AND product_name LIKE CONCAT('%', #{productName}, '%')
+            </if>
+        </where>
+        GROUP BY
+        material_code,
+        product_name,
+        product_spec,
+        length,
+        width,
+        height
+    </select>
+
 </mapper>
\ No newline at end of file

--
Gitblit v1.9.3