啊鑫
2025-06-05 e47d11017af9eff6581591d5d73c1e55676b0955
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
package com.gs.xky.service;
 
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gs.xky.config.ApiResponse;
import com.gs.xky.config.DataAcquisitionConfiguration;
import com.gs.xky.config.PurchaseParam;
import com.gs.xky.config.XkyCommonParam;
import com.gs.xky.entity.MesRohInData;
import com.gs.xky.entity.PurchaseOrderCompare;
import com.gs.xky.entity.PurchaseOrderDetail;
import com.gs.xky.mapper.PurchaseOrderCompareMapper;
import com.gs.xky.mapper.PurchaseOrderDetailMapper;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.io.IOException;
import java.util.Date;
import java.util.List;
 
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class PurchaseService {
    private static final Logger log = LoggerFactory.getLogger(PurchaseService.class);
 
    private final ApiService apiService;
    private final MesRohInDataService mesRohInDataService;
    private final PurchaseOrderDetailService purchaseOrderDetailService;
    private final PurchaseOrderCompareMapper purchaseOrderCompareMapper;
    private final PurchaseOrderDetailMapper purchaseOrderDetailMapper;
 
    /**
     * 同步采购订单明细数据
     * 从第三方接口获取采购订单明细数据并保存到本地数据库
     * 注意:该接口有以下限制:
     * 1. 访问频率不能低于2小时
     * 2. 一次请求中时间范围不能大于24小时
     *
     * @throws IOException 接口调用异常
     */
    public void syncPurchaseOrderDetails() throws IOException {
        long currentTimeMillis = System.currentTimeMillis();
        // 限制请求时间范围为24小时
        long startDate = currentTimeMillis - (24 * 60 * 60 * 1000L);
 
        // 先删除已有数据,避免重复
        purchaseOrderDetailMapper.deleteByPrimaryKey();
        purchaseOrderCompareMapper.deleteByPrimaryKey();
 
        XkyCommonParam<PurchaseParam> param = XkyCommonParam.GetInit();
        PurchaseParam bodyParam = new PurchaseParam();
        bodyParam.setStartTime(startDate);
        bodyParam.setEndTime(currentTimeMillis);
        bodyParam.setErpCode(DataAcquisitionConfiguration.TEST_ERP_CODE);
        // 查询所有状态的订单
//        bodyParam.setOrderStatusList(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14});
        bodyParam.setPurchaseTypeList(new int[]{1});
 
        param.setBody(bodyParam);
 
        log.info("【syncPurchaseOrderDetails】开始同步采购订单数据,时间范围:{} 至 {}", startDate, currentTimeMillis);
 
        // 调用采购订单明细接口
        ApiResponse<PurchaseOrderDetail> response = apiService.sendListRequest(
                param,
                PurchaseOrderDetail.class,
                "https://openapi.xiekeyun.com/purchase/report/list.json"
        );
 
        List<PurchaseOrderDetail> orderDetails = response.getDataList();
        if (CollUtil.isEmpty(orderDetails)) {
            log.info("【syncPurchaseOrderDetails】返回列表为空,跳过处理");
            return;
        }
 
        log.info("【syncPurchaseOrderDetails】获取到{}条采购订单数据", orderDetails.size());
 
        // 分批处理数据,减少内存占用
        int batchSize = 100; // 每批处理100条数据
        int totalSize = orderDetails.size();
        int batchCount = (totalSize + batchSize - 1) / batchSize; // 向上取整计算批次数
 
        for (int i = 0; i < batchCount; i++) {
            int fromIndex = i * batchSize;
            int toIndex = Math.min((i + 1) * batchSize, totalSize);
 
            log.info("【syncPurchaseOrderDetails】处理第{}批数据,范围:{}-{}", i + 1, fromIndex, toIndex);
 
            // 获取当前批次的数据
            List<PurchaseOrderDetail> batchDetails = orderDetails.subList(fromIndex, toIndex);
 
            // 处理当前批次的数据
            processBatch(batchDetails);
 
            // 手动触发GC,释放内存(谨慎使用,仅在内存紧张时考虑)
            // System.gc();
        }
    }
 
    /**
     * 批量处理采购订单明细数据
     *
     * @param batchDetails 当前批次的采购订单明细数据
     */
    private void processBatch(List<PurchaseOrderDetail> batchDetails) {
        batchDetails.forEach(detail -> {
            try {
                // 根据有效标志和订单状态处理不同的业务逻辑
                if (detail.getValidFlag() != null && detail.getValidFlag() == 0) {
                    log.info("【syncPurchaseOrderDetails】无效订单,跳过处理: {}", detail.getPoErpNo());
                    return;
                }
 
                // 获取项次前面的编号部分
                String linePrefix = extractLinePrefix(detail.getLineNo());
                log.info("【syncPurchaseOrderDetails】项次前缀: {}", linePrefix);
 
                // 查询ERP系统中对应的采购订单数据
                LambdaQueryWrapper<MesRohInData> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(MesRohInData::getBillNo, detail.getPoErpNo())
                        .eq(MesRohInData::getOrderLineId, linePrefix);
 
                // 获取ERP数据
                MesRohInData erpData = mesRohInDataService.getOne(wrapper, false);
 
                if (erpData == null) {
                    log.info("【syncPurchaseOrderDetails】未找到对应的ERP数据,订单号: {}, 项次: {}",
                            detail.getPoErpNo(), linePrefix);
 
                    // 创建一个包含"暂无数据"值的比对记录
                    savePurchaseOrderCompare(detail, null);
                } else {
                    // 记录比对结果
                    savePurchaseOrderCompare(detail, erpData);
                }
 
                // 保存SRM采购订单明细
                savePurchaseOrderDetail(detail);
 
                // 帮助GC回收不再使用的对象
                wrapper = null;
                erpData = null;
            } catch (Exception e) {
                log.error("【syncPurchaseOrderDetails 处理异常】订单号: {}, 项次: {}, 异常: {}",
                        detail.getPoErpNo(), detail.getLineNo(), e.getMessage(), e);
                throw new RuntimeException(e);
            }
        });
    }
 
    /**
     * 从lineNo中提取前缀部分
     * 例如:从"10-1"中提取出"10"
     *
     * @param lineNo 项次编号,格式如"10-1"
     * @return 项次前缀,如"10"
     */
    private String extractLinePrefix(String lineNo) {
        if (lineNo == null || lineNo.isEmpty()) {
            return "";
        }
 
        // 使用"-"分割字符串
        String[] parts = lineNo.split("-");
        if (parts.length > 0) {
            return parts[0]; // 返回第一部分
        }
 
        // 如果没有"-",则返回原字符串
        return lineNo;
    }
 
    /**
     * 保存ERP与SRM采购订单数据比对结果
     *
     * @param detail  SRM采购订单明细
     * @param erpData ERP采购订单数据(可能为null)
     */
    private void savePurchaseOrderCompare(PurchaseOrderDetail detail, MesRohInData erpData) {
        String linePrefix = extractLinePrefix(detail.getLineNo());
 
        // 查询是否已存在比对记录
        LambdaQueryWrapper<PurchaseOrderCompare> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PurchaseOrderCompare::getBillNo, detail.getPoErpNo())
                .eq(PurchaseOrderCompare::getLineNo, detail.getLineNo());
 
        PurchaseOrderCompare compareData = purchaseOrderCompareMapper.selectOne(wrapper);
 
        if (compareData == null) {
            // 创建新的比对记录
            compareData = new PurchaseOrderCompare();
            compareData.setBillNo(detail.getPoErpNo());
            compareData.setOrderLineId(linePrefix);
            compareData.setLineNo(detail.getLineNo());
            compareData.setProductCode(detail.getProductCode());
            compareData.setProductName(detail.getProductName());
            compareData.setCreateTime(new Date());
        }
 
        // 计算SRM待收数量
        Integer srmPurchaseQty = detail.getTotalAnswerQty();
        Integer srmReceivedQty = detail.getPoWaitDeliveryQty() + detail.getReturnWaitDeliveryQty();
        //poWaitDeliveryQty + returnWaitDeliveryQty
        Integer srmWaitReceiveQty = srmPurchaseQty - srmReceivedQty;
 
        // 设置SRM数据
        compareData.setSrmPurchaseQty(srmPurchaseQty);
        compareData.setSrmReceivedQty(srmWaitReceiveQty);
        compareData.setSrmWaitReceiveQty(srmReceivedQty);
 
        // 设置ERP数据和差异
        if (erpData == null) {
            // ERP系统中没有数据,设置为0
            compareData.setErpPurchaseQty(0);
            compareData.setErpReceivedQty(0);
            compareData.setErpWaitReceiveQty(0);
            compareData.setDiffFlag(1); // 有差异
            compareData.setDiffQty(srmWaitReceiveQty); // 差异数量为SRM待收数量
        } else {
            // 计算ERP待收数量
            Long erpPurchaseQty = erpData.getPurchaseQty();
            Long erpReceivedQty = erpData.getTotalReceivedQty();
            Long erpWaitReceiveQty = erpPurchaseQty - erpReceivedQty;
 
            // 计算差异
            Long diffQty = srmWaitReceiveQty.longValue() - erpWaitReceiveQty;
 
//            Integer diffFlag = (diffQty > 0) ? 1 : 0;
            Integer diffFlag = srmWaitReceiveQty.longValue() != erpWaitReceiveQty ? 1 : 0;
 
            // 设置ERP数据
            compareData.setErpPurchaseQty(erpPurchaseQty.intValue());
            compareData.setErpReceivedQty(erpReceivedQty.intValue());
            compareData.setErpWaitReceiveQty(erpWaitReceiveQty.intValue());
            compareData.setDiffFlag(diffFlag);
            compareData.setDiffQty(diffQty.intValue());
        }
 
        compareData.setUpdateTime(new Date());
 
        // 保存或更新比对记录
        boolean result;
        if (compareData.getId() == null) {
            result = purchaseOrderCompareMapper.insert(compareData) > 0;
            if (result) {
                log.info("【savePurchaseOrderCompare】新增数据比对记录: 订单号:{}, 项次:{}, 差异标识:{}, 差异数量:{}",
                        detail.getPoErpNo(), detail.getLineNo(), compareData.getDiffFlag(), compareData.getDiffQty());
            } else {
                log.error("【savePurchaseOrderCompare】新增数据比对记录失败: 订单号:{}, 项次:{}",
                        detail.getPoErpNo(), detail.getLineNo());
            }
        } else {
            result = purchaseOrderCompareMapper.updateById(compareData) > 0;
            if (result) {
                log.info("【savePurchaseOrderCompare】更新数据比对记录: 订单号:{}, 项次:{}, 差异标识:{}, 差异数量:{}",
                        detail.getPoErpNo(), detail.getLineNo(), compareData.getDiffFlag(), compareData.getDiffQty());
            } else {
                log.error("【savePurchaseOrderCompare】更新数据比对记录失败: 订单号:{}, 项次:{}",
                        detail.getPoErpNo(), detail.getLineNo());
            }
        }
    }
 
    /**
     * 保存或更新采购订单明细
     *
     * @param detail 采购订单明细数据
     */
    private void savePurchaseOrderDetail(PurchaseOrderDetail detail) {
        log.info("【savePurchaseOrderDetail】保存采购订单明细: {}, 项次: {}", detail.getPoErpNo(), detail.getLineNo());
 
        // 保存采购订单明细
        boolean result = purchaseOrderDetailService.save(detail);
        if (result) {
            log.info("【savePurchaseOrderDetail】保存采购订单明细成功: {}, 项次: {}", detail.getPoErpNo(), detail.getLineNo());
        } else {
            log.error("【savePurchaseOrderDetail】保存采购订单明细失败: {}, 项次: {}", detail.getPoErpNo(), detail.getLineNo());
        }
    }
}