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

---
 src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java |  201 ++++++++++++--------------------------------------
 1 files changed, 49 insertions(+), 152 deletions(-)

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 b04cf5a..698199c 100644
--- a/src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java
+++ b/src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java
@@ -1,6 +1,5 @@
 package com.ruoyi.productionPlan.service.impl;
 
-import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -12,10 +11,9 @@
 import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
-import com.ruoyi.common.utils.http.HttpUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.config.AliDingConfig;
-import com.ruoyi.production.pojo.ProductMaterial;
+import com.ruoyi.framework.util.AliDingUtils;
 import com.ruoyi.production.pojo.ProductMaterialSku;
 import com.ruoyi.production.pojo.ProductOrder;
 import com.ruoyi.production.service.ProductMaterialService;
@@ -24,6 +22,7 @@
 import com.ruoyi.productionPlan.dto.ProductionPlanDto;
 import com.ruoyi.productionPlan.dto.ProductionPlanImportDto;
 import com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto;
+import com.ruoyi.productionPlan.enums.DataSourceTypeEnum;
 import com.ruoyi.productionPlan.mapper.ProductOrderPlanMapper;
 import com.ruoyi.productionPlan.mapper.ProductionPlanMapper;
 import com.ruoyi.productionPlan.pojo.ProductOrderPlan;
@@ -37,15 +36,13 @@
 
 import javax.servlet.http.HttpServletResponse;
 import java.math.BigDecimal;
-import java.nio.charset.StandardCharsets;
-import java.time.*;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Collectors;
 
-import static com.ruoyi.productionPlan.enums.DataSourceTypeEnum.PRODUCTION_FORECAST;
 
 /**
  * <br>
@@ -120,20 +117,20 @@
 
         //  鏍¢獙鏄惁瀛樺湪涓嶅悓鐨勪骇鍝佸悕绉�
         String firstProductName = plans.get(0).getProductName();
-        if (plans.stream().anyMatch(p -> !p.getProductName().equals(firstProductName))) {
+        if (plans.stream().anyMatch(p -> p.getProductName() == null || !p.getProductName().equals(firstProductName))) {
             throw new BaseException("鍚堝苟澶辫触锛屽瓨鍦ㄤ笉鍚岀殑浜у搧鍚嶇О");
         }
 
         // 鏍¢獙鏄惁瀛樺湪涓嶅悓鐨勪骇鍝佽鏍�
         String firstProductSpec = plans.get(0).getSpecification();
-        if (plans.stream().anyMatch(p -> !p.getSpecification().equals(firstProductSpec))) {
+        if (plans.stream().anyMatch(p -> p.getSpecification() == null || !p.getSpecification().equals(firstProductSpec))) {
             throw new BaseException("鍚堝苟澶辫触锛屽瓨鍦ㄤ笉鍚岀殑浜у搧瑙勬牸");
         }
 
         // 鍙犲姞鍓╀綑鏂规暟
         BigDecimal totalRemainingVolume = plans.stream()
                 .map(ProductionPlan::getRemainingVolume)
-                .filter(v -> v != null)
+                .filter(Objects::nonNull)
                 .reduce(BigDecimal.ZERO, BigDecimal::add);
         // 鍒ゆ柇涓嬪彂鏁伴噺鏄惁澶т簬绛変簬鍓╀綑鏂规暟
         if (productionPlanDto.getTotalAssignedQuantity().compareTo(totalRemainingVolume) > 0) {
@@ -167,6 +164,9 @@
                 // 鏈�鍚庝竴涓鍒掞紝鍒嗛厤鍓╀綑鏂规暟
                 BigDecimal lastRemainingVolume = productionPlanDto.getTotalAssignedQuantity().subtract(assignedVolume);
                 plan.setStatus(1);
+                if (lastRemainingVolume.compareTo(BigDecimal.ZERO) <= 0) {
+                    plan.setStatus(2);
+                }
                 plan.setAssignedQuantity(plan.getAssignedQuantity().add(lastRemainingVolume));
                 productOrderPlan.setAssignedQuantity(lastRemainingVolume);
                 productionPlanMapper.updateById(plan);
@@ -176,6 +176,9 @@
 
             // 鍒嗛厤褰撳墠璁″垝鏂规暟
             plan.setStatus(1);
+            if (remainingVolume.compareTo(BigDecimal.ZERO) <= 0) {
+                plan.setStatus(2);
+            }
             plan.setAssignedQuantity(plan.getAssignedQuantity().add(remainingVolume));
             productOrderPlan.setAssignedQuantity(remainingVolume);
             // 鏇存柊鐢熶骇璁″垝
@@ -190,7 +193,8 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean add(ProductionPlanDto productionPlanDto) {
-        productionPlanDto.setDataSourceType(PRODUCTION_FORECAST.getCode());
+        productionPlanDto.setDataSourceType(DataSourceTypeEnum.MANUAL.getCode());
+        productionPlanDto.setStatus(0);
         productionPlanMapper.insert(productionPlanDto);
         return true;
     }
@@ -199,8 +203,8 @@
     @Transactional(rollbackFor = Exception.class)
     public boolean update(ProductionPlanDto productionPlanDto) {
         // 宸蹭笅鍙戠姸鎬侊紝涓嶈兘缂栬緫
-        if (productionPlanDto.getStatus() == 1) {
-            throw new BaseException("宸蹭笅鍙戠姸鎬侊紝涓嶈兘缂栬緫");
+        if (productionPlanDto.getStatus() != 0) {
+            throw new BaseException("宸蹭笅鍙戞垨閮ㄥ垎涓嬪彂鐘舵�侊紝涓嶈兘缂栬緫");
         }
         // 鏌ヨ鏄惁鏈夊叧鑱旇鍗�
         boolean hasProductOrderPlan = productOrderPlanMapper.selectList(Wrappers.<ProductOrderPlan>lambdaQuery().eq(ProductOrderPlan::getProductionPlanId, productionPlanDto.getId())).stream().anyMatch(p -> p.getProductOrderId() != null);
@@ -219,8 +223,8 @@
     @Transactional(rollbackFor = Exception.class)
     public boolean delete(List<Long> ids) {
         // 濡傛灉瀛樺湪宸蹭笅鍙戠殑璁″垝锛屽垯涓嶈兘鍒犻櫎
-        if (productionPlanMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery().in(ProductionPlan::getId, ids).eq(ProductionPlan::getStatus, 1)).stream().anyMatch(p -> p.getStatus() == 1)) {
-            throw new BaseException("鍒犻櫎澶辫触锛屽瓨鍦ㄥ凡涓嬪彂鐨勮鍒�");
+        if (productionPlanMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery().in(ProductionPlan::getId, ids)).stream().anyMatch(p -> p.getStatus() == 1 || p.getStatus() == 2)) {
+            throw new BaseException("鍒犻櫎澶辫触锛屽瓨鍦ㄥ凡涓嬪彂鎴栭儴鍒嗕笅鍙戠殑璁″垝");
         }
         // 濡傛灉鏈夊叧鑱旇鍗曪紝鍒欎笉鑳藉垹闄�
         if (productOrderPlanMapper.selectList(Wrappers.<ProductOrderPlan>lambdaQuery().in(ProductOrderPlan::getProductionPlanId, ids)).stream().anyMatch(p -> p.getProductOrderId() != null)) {
@@ -236,138 +240,46 @@
     @Transactional(rollbackFor = Exception.class)
     public void syncProdData(Integer dataSyncType) {
         if (!syncLock.tryLock()) {
-            log.warn("鍚屾姝e湪杩涜涓紝鏈 {} 鍚屾璇锋眰琚烦杩�", dataSyncType == 1 ? "鎵嬪姩" : "瀹氭椂浠诲姟");
+            log.warn("鍚屾姝e湪杩涜涓紝鏈 {} 鍚屾璇锋眰琚烦杩�", dataSyncType == 1 ? "鎵嬪姩鍚屾" : "瀹氭椂浠诲姟鍚屾");
             return;
         }
 
         try {
-            //  鑾峰彇 AccessToken
-            String accessToken = getAccessToken();
-            if (StringUtils.isEmpty(accessToken)) {
+            JSONArray searchConditions = new JSONArray();
+            JSONObject condition = new JSONObject();
+            condition.put("key", "processApprovedResult");
+            JSONArray valueArray = new JSONArray();
+            valueArray.add("agree");
+
+            condition.put("value", valueArray);
+            condition.put("type", "ARRAY");
+            condition.put("operator", "in");
+            condition.put("componentName", "SelectField");
+            searchConditions.add(condition);
+
+            String searchFieldJson = searchConditions.toJSONString();
+
+            JSONArray dataArr = AliDingUtils.getFormDataList(aliDingConfig, aliDingConfig.getProducePlanFormUuid(), searchFieldJson, this, ProductionPlan::getFormModifiedTime);
+
+            if (dataArr.isEmpty()) {
+                log.info("娌℃湁鏇村鏂版暟鎹渶瑕佸悓姝�");
                 return;
             }
 
-            //  鑾峰彇鏈湴鏈�鍚庡悓姝ユ椂闂�
-            LocalDateTime lastSyncTime = getLastSyncTime();
-            log.info("寮�濮嬪閲忓悓姝ワ紝鏈湴鏈�鍚庝慨鏀规椂闂�: {}", lastSyncTime);
-
-            int pageNumber = 1;
-            int pageSize = 50;
-            boolean hasMore = true;
-            int totalSynced = 0;
-
-            while (hasMore) {
-                //  鏌ヨ鍙傛暟
-                JSONObject searchParam = buildSearchParam(lastSyncTime, pageNumber, pageSize);
-
-                //  璋冪敤瀹滄惌鎺ュ彛鎷夊彇鏁版嵁
-                String dataRes = HttpUtils.sendPostJson(
-                        aliDingConfig.getSearchFormDataUrl(),
-                        searchParam.toJSONString(),
-                        StandardCharsets.UTF_8.name(),
-                        null,
-                        accessToken
-                );
-
-                if (StringUtils.isEmpty(dataRes)) {
-                    log.warn("绗� {} 椤垫媺鍙栨暟鎹负绌�", pageNumber);
-                    break;
-                }
-
-                JSONObject resultObj = JSON.parseObject(dataRes);
-                JSONArray dataArr = resultObj.getJSONArray("data");
-                Integer totalCount = resultObj.getInteger("totalCount");
-
-                if (dataArr == null || dataArr.isEmpty()) {
-                    log.info("娌℃湁鏇村鏂版暟鎹渶瑕佸悓姝�");
-                    break;
-                }
-
-                //  瑙f瀽骞朵繚瀛樻暟鎹�
-                List<ProductionPlan> list = parseProductionPlans(dataArr, dataSyncType, totalCount);
-                if (!list.isEmpty()) {
-                    //  澶勭悊鏇存柊鎴栨柊澧�
-                    int affected = processSaveOrUpdate(list);
-                    totalSynced += affected;
-                }
-
-                //  鍒ゆ柇鏄惁杩樻湁涓嬩竴椤�
-                hasMore = (pageNumber * pageSize) < totalCount;
-                pageNumber++;
-
-                log.info("姝e湪鍚屾绗� {} 椤碉紝褰撳墠宸插悓姝� {}/{}", pageNumber - 1, totalSynced, totalCount);
+            //  瑙f瀽骞朵繚瀛樻暟鎹�
+            List<ProductionPlan> list = parseProductionPlans(dataArr, dataSyncType, dataArr.size());
+            if (!list.isEmpty()) {
+                //  澶勭悊鏇存柊鎴栨柊澧�
+                int affected = processSaveOrUpdate(list);
+                log.info("鏁版嵁鍚屾瀹屾垚锛屽叡鍚屾 {} 鏉℃暟鎹�", affected);
             }
 
-            log.info("鏁版嵁鍚屾瀹屾垚锛屽叡鍚屾 {} 鏉℃暟鎹�", totalSynced);
         } catch (Exception e) {
             log.error("鍚屾鐢熶骇璁″垝寮傚父", e);
         } finally {
             // 閲婃斁閿�
             syncLock.unlock();
         }
-    }
-
-    private String getAccessToken() {
-        String params = "appkey=" + aliDingConfig.getAppKey()
-                + "&appsecret=" + aliDingConfig.getAppSecret();
-        String tokenRes = HttpUtils.sendGet(aliDingConfig.getAccessTokenUrl(), params);
-        JSONObject tokenObj = JSON.parseObject(tokenRes);
-        String accessToken = tokenObj.getString("access_token");
-        if (StringUtils.isEmpty(accessToken)) {
-            log.error("鑾峰彇閽夐拤AccessToken澶辫触: {}", tokenRes);
-        }
-        return accessToken;
-    }
-
-    private LocalDateTime getLastSyncTime() {
-        // 鏌ヨ鏈湴鏁版嵁搴撲腑 formModifiedTime 鏈�澶х殑璁板綍
-        LambdaQueryWrapper<ProductionPlan> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.orderByDesc(ProductionPlan::getFormModifiedTime).last("LIMIT 1");
-        ProductionPlan lastRecord = this.getOne(queryWrapper);
-        return lastRecord != null ? lastRecord.getFormModifiedTime() : null;
-    }
-
-    private JSONObject buildSearchParam(LocalDateTime lastSyncTime, int pageNumber, int pageSize) {
-        JSONObject searchParam = new JSONObject();
-        searchParam.put("appType", aliDingConfig.getAppType());
-        searchParam.put("systemToken", aliDingConfig.getSystemToken());
-        searchParam.put("userId", aliDingConfig.getUserId());
-        searchParam.put("formUuid", aliDingConfig.getProducePlanFormUuid());
-        searchParam.put("currentPage", pageNumber);
-        searchParam.put("pageSize", pageSize);
-
-        JSONArray searchConditions = new JSONArray();
-        JSONObject condition = new JSONObject();
-        condition.put("key", "processApprovedResult");
-        JSONArray valueArray = new JSONArray();
-        valueArray.add("agree");
-
-        condition.put("value", valueArray);
-        condition.put("type", "ARRAY");
-        condition.put("operator", "in");
-        condition.put("componentName", "SelectField");
-        searchConditions.add(condition);
-
-        searchParam.put("searchFieldJson", searchConditions.toJSONString());
-
-        // 榛樿鎸変慨鏀规椂闂村崌搴忔帓搴忥紝纭繚鍒嗛〉鎷夊彇鏁版嵁鐨勮繛缁��
-        // "+" 琛ㄧず鍗囧簭锛�"gmt_modified" 鏄畼鏂瑰唴缃瓧娈�
-        searchParam.put("orderConfigJson", "{\"gmt_modified\":\"+\"}");
-
-        // 璁剧疆淇敼鏃堕棿绛涢�夊尯闂� (鏍煎紡蹇呴』涓簓yyy-MM-dd HH:mm:ss)
-        if (lastSyncTime != null) {
-            // 璧峰鏃堕棿锛氫笂娆″悓姝ュ埌鐨勬渶鍚庝竴鏉℃暟鎹殑淇敼鏃堕棿
-            String startTime = lastSyncTime.plusSeconds(1).atZone(ZoneId.systemDefault())
-                    .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
-            searchParam.put("modifiedFromTimeGMT", startTime);
-        }
-
-        // 鎴鏃堕棿锛氬綋鍓嶆椂闂达紝纭繚鑾峰彇鏈�鏂扮殑宸蹭慨鏀�/宸叉柊澧炴暟鎹�
-        String endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
-                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
-        searchParam.put("modifiedToTimeGMT", endTime);
-
-        return searchParam;
     }
 
     private List<ProductionPlan> parseProductionPlans(JSONArray dataArr, Integer dataSyncType, Integer totalCount) {
@@ -451,10 +363,9 @@
                     plan.setModifierName(modifyUser.getJSONObject("userName").getString("nameInChinese"));
                 }
 
-                plan.setFormCreatedTime(parseUtcTime(item.getString("createdTimeGMT")));
-                plan.setFormModifiedTime(parseUtcTime(item.getString("modifiedTimeGMT")));
-                plan.setDataSyncType(dataSyncType);
-                plan.setDataSourceType(1);
+                plan.setFormCreatedTime(AliDingUtils.parseUtcTime(item.getString("createdTimeGMT")));
+                plan.setFormModifiedTime(AliDingUtils.parseUtcTime(item.getString("modifiedTimeGMT")));
+                plan.setDataSourceType(DataSourceTypeEnum.DING_TALK.getCode());
                 plan.setCreateTime(now);
                 plan.setUpdateTime(now);
                 plan.setTotalCount(totalCount);
@@ -509,19 +420,6 @@
         return affected;
     }
 
-    private LocalDateTime parseUtcTime(String utcString) {
-        if (StringUtils.isEmpty(utcString)) {
-            return null;
-        }
-        try {
-            OffsetDateTime odt = OffsetDateTime.parse(utcString);
-            return odt.toLocalDateTime();
-        } catch (DateTimeParseException ex) {
-            log.warn("瑙f瀽鏃堕棿 {} 澶辫触: {}", utcString, ex.getMessage());
-            return null;
-        }
-    }
-
     @Override
     public List<ProductionPlanSummaryDto> summaryByProductType(ProductionPlanSummaryDto query) {
         return baseMapper.selectSummaryByProductType(query);
@@ -553,8 +451,7 @@
             entity.setAssignedQuantity(BigDecimal.ZERO);
             entity.setCreateTime(LocalDateTime.now());
             entity.setUpdateTime(LocalDateTime.now());
-            entity.setDataSourceType(2);
-            entity.setDataSyncType(1);
+            entity.setDataSourceType(DataSourceTypeEnum.DING_TALK.getCode());
 
             // 鏍规嵁鐗╂枡缂栫爜濉厖鍏宠仈ID
             if (StringUtils.isNotEmpty(dto.getMaterialCode())) {

--
Gitblit v1.9.3