|
|
@@ -1,17 +1,54 @@
|
|
|
package com.dgtly.system.service.impl;
|
|
|
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.dgtly.common.utils.StringUtils;
|
|
|
+import com.dgtly.common.utils.http.HttpUtils;
|
|
|
+import com.dgtly.system.domain.ApplicationContextProvider;
|
|
|
import com.dgtly.system.mapper.HanaSalesOrderMapper;
|
|
|
import com.dgtly.system.service.HanaSalesOrderService;
|
|
|
+import com.dgtly.system.util.HttpUtil;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.retry.support.RetryTemplate;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
-import java.util.List;
|
|
|
+import java.text.DateFormat;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+import static org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute;
|
|
|
|
|
|
@Service
|
|
|
public class HanaSalesOrderServiceImpl implements HanaSalesOrderService {
|
|
|
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(HanaSalesOrderServiceImpl.class);
|
|
|
+
|
|
|
+ /** 批量删除每批最大条数,避免 IN 语句过长 */
|
|
|
+ private static final int BATCH_SIZE = 500;
|
|
|
+
|
|
|
+ private String sapResult;
|
|
|
+
|
|
|
+ /** 通知 SAP 取消要货的 ESB 接口名称(固定值) */
|
|
|
+ private static final String ESB_SERVICE_NAME = "ZSD_SSB_DELIV_INFO_IF";
|
|
|
+
|
|
|
+ /** 取消要货时传给 SAP 的 TRANSPORT 字段值 */
|
|
|
+ private static final String CANCEL_TRANSPORT = "交货冻结";
|
|
|
+
|
|
|
@Autowired
|
|
|
private HanaSalesOrderMapper hanaSalesOrderMapper;
|
|
|
+
|
|
|
+ /** Spring Retry 重试模板 */
|
|
|
+ @Autowired
|
|
|
+ private RetryTemplate retryTemplate;
|
|
|
+
|
|
|
+ /** ESB 通用接口地址(从配置文件读取) */
|
|
|
+ @Value("${spring.esb.esbOrderCancelUrl}")
|
|
|
+ private String esbOrderCancelUrl;
|
|
|
+
|
|
|
/**
|
|
|
* @description: 未过信的经销商订单的要货记录id(做逻辑删除,重新要货)
|
|
|
* @param: []
|
|
|
@@ -26,4 +63,177 @@ public class HanaSalesOrderServiceImpl implements HanaSalesOrderService {
|
|
|
hanaSalesOrderMapper.deleteLogic(orderBaseList);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ public String getSapResult() {
|
|
|
+ return sapResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @description: 交货冻结取货定时任务
|
|
|
+ * 查询已冻结经销商(不在白名单中)且订单状态满足条件、尚未完全发货的要货记录,
|
|
|
+ * 执行逻辑删除(is_delete = '1')以取消要货,并记录操作日志。
|
|
|
+ * 取消后逐条调用 ESB 接口 ZSD_SSB_DELIV_INFO_IF 通知 SAP,TRANSPORT 字段传"交货冻结"。
|
|
|
+ * @return: 已取消的要货记录详情列表(含 customerCode/orderNumber/confirmBy),供调用方做企微通知
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<Map<String, Object>> cancelFreezeCustomerOrder() {
|
|
|
+ log.info("【交货冻结取货】定时任务开始执行");
|
|
|
+
|
|
|
+ // 先查详情,用于日志记录和企微通知(含 customerCode/orderNumber/confirmBy)
|
|
|
+ List<Map<String, Object>> detailList = hanaSalesOrderMapper.selectFreezeCustomerOrderDetailList();
|
|
|
+ if (detailList == null || detailList.isEmpty()) {
|
|
|
+ log.info("【交货冻结取货】未查询到需要取消的要货记录,任务结束");
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取 id 列表
|
|
|
+ List<String> orderIdList = detailList.stream()
|
|
|
+ .map(m -> String.valueOf(m.get("id")))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ log.info("【交货冻结取货】共查询到需要取消的要货记录 {} 条,开始分批执行逻辑删除", orderIdList.size());
|
|
|
+ int total = orderIdList.size();
|
|
|
+ int deleted = 0;
|
|
|
+ for (int i = 0; i < total; i += BATCH_SIZE) {
|
|
|
+ List<String> batch = orderIdList.subList(i, Math.min(i + BATCH_SIZE, total));
|
|
|
+ int rows = hanaSalesOrderMapper.cancelFreezeCustomerOrderLogic(batch);
|
|
|
+ deleted += rows;
|
|
|
+ log.info("【交货冻结取货】已处理批次 {}/{}, 本批影响行数: {}",
|
|
|
+ (i / BATCH_SIZE + 1), (int) Math.ceil((double) total / BATCH_SIZE), rows);
|
|
|
+ }
|
|
|
+ log.info("【交货冻结取货】逻辑删除完成,共取消要货记录 {} 条", deleted);
|
|
|
+
|
|
|
+ // ---- 操作日志:按经销商分组输出结构化日志 ----
|
|
|
+ String cancelTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
|
|
|
+ Map<String, List<String>> customerOrderMap = new LinkedHashMap<>();
|
|
|
+ Map<String, Set<String>> customerConfirmByMap = new LinkedHashMap<>();
|
|
|
+ for (Map<String, Object> detail : detailList) {
|
|
|
+ String customerCode = String.valueOf(detail.get("customerCode"));
|
|
|
+ String orderNumber = String.valueOf(detail.get("orderNumber"));
|
|
|
+ String confirmBy = detail.get("confirmBy") != null ? String.valueOf(detail.get("confirmBy")) : "";
|
|
|
+ customerOrderMap.computeIfAbsent(customerCode, k -> new ArrayList<>()).add(orderNumber);
|
|
|
+ customerConfirmByMap.computeIfAbsent(customerCode, k -> new LinkedHashSet<>()).add(confirmBy);
|
|
|
+ }
|
|
|
+ for (Map.Entry<String, List<String>> entry : customerOrderMap.entrySet()) {
|
|
|
+ String customerCode = entry.getKey();
|
|
|
+ List<String> orderNumbers = entry.getValue();
|
|
|
+ Set<String> confirmBys = customerConfirmByMap.get(customerCode);
|
|
|
+ log.info("【交货冻结取货-操作日志】 取消时间={}, 经销商编码={}, 要货人={}, 取消订单号={}",
|
|
|
+ cancelTime, customerCode,
|
|
|
+ String.join(",", confirmBys),
|
|
|
+ String.join(",", orderNumbers));
|
|
|
+ }
|
|
|
+
|
|
|
+ // ---- 逐条通知 SAP:调用 ESB 接口 ZSD_SSB_DELIV_INFO_IF ----
|
|
|
+ log.info("【交货冻结取货】开始通知 SAP,共 {} 条,ESB接口={}", detailList.size(), ESB_SERVICE_NAME);
|
|
|
+ int esbSuccess = 0;
|
|
|
+ int esbFail = 0;
|
|
|
+ Set<String> orderNumberSet = new HashSet<>();
|
|
|
+ for (Map<String, Object> detail : detailList) {
|
|
|
+ String orderNumber = String.valueOf(detail.get("orderNumber"));
|
|
|
+ try {
|
|
|
+ //调用sap不重复发
|
|
|
+ if (orderNumberSet.add(orderNumber)){
|
|
|
+ notifySapCancelOrder(orderNumber);
|
|
|
+ esbSuccess++;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ esbFail++;
|
|
|
+ log.error("【交货冻结取货】ESB 通知 SAP 失败,订单号={}", orderNumber, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("【交货冻结取货】ESB 通知 SAP 完成,成功={} 条,失败={} 条", esbSuccess, esbFail);
|
|
|
+
|
|
|
+ return detailList;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 调用 ESB 接口 ZSD_SSB_DELIV_INFO_IF 通知 SAP 取消指定要货订单。
|
|
|
+ * TRANSPORT 字段固定传"交货冻结"。
|
|
|
+ *
|
|
|
+ * @param orderNumber 要取消的订单号(sales_order_base.order_number)
|
|
|
+ */
|
|
|
+ private void notifySapCancelOrder(String orderNumber) {
|
|
|
+ String uuidStr = UUID.randomUUID().toString();
|
|
|
+
|
|
|
+ // 请求头
|
|
|
+ Map<String, String> headers = new HashMap<>(8);
|
|
|
+ headers.put("Content-Type", "application/json");
|
|
|
+ headers.put("requestId", uuidStr);
|
|
|
+ headers.put("trackId", uuidStr);
|
|
|
+ headers.put("sourceSystem", "SSB");
|
|
|
+ headers.put("serviceName", "S_SSB_ERP_ZsdSsbDelivInfoIf_S");
|
|
|
+
|
|
|
+ List<Map<String, String>> params =new ArrayList<>();
|
|
|
+ Map<String, String> maps = new HashMap<>(7);
|
|
|
+ maps.put("VBELN", orderNumber);
|
|
|
+ maps.put("POSNR", "000000");
|
|
|
+ maps.put("KWMENG_2", "0");
|
|
|
+ maps.put("KWMENG_1", "0");
|
|
|
+ maps.put("ZYZH", "system");
|
|
|
+ maps.put("DATE", format(new Date(), "yyyyMMdd"));
|
|
|
+ maps.put("TIME", format(new Date(), "HHmmss"));
|
|
|
+ maps.put("TRANSPORT", CANCEL_TRANSPORT);
|
|
|
+ params.add( maps);
|
|
|
+ Map<String, List<Map<String, String>>> item = new HashMap<>(1);
|
|
|
+ item.put("item", params);
|
|
|
+ Map<String, Map<String, List<Map<String, String>>>> itData = new HashMap<>(1);
|
|
|
+ itData.put("IT_DATA", item);
|
|
|
+ Map<String, Map<String, Map<String, List<Map<String, String>>>>> zsdSsbDelivInfoIf = new HashMap<>(1);
|
|
|
+ zsdSsbDelivInfoIf.put("ZSD_SSB_DELIV_INFO_IF", itData);
|
|
|
+
|
|
|
+ log.info("【交货冻结取货】ESB 通知 SAP,订单号={},请求报文={}", orderNumber, params);
|
|
|
+ boolean execute = false;
|
|
|
+ try {
|
|
|
+ execute = retry("http://esb-test.nipponpaint.com.cn/esb/erp/api", headers, zsdSsbDelivInfoIf);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[{}] retry error, headers is [{}], params is [{}]", "http://esb-test.nipponpaint.com.cn/esb/erp/api", JSON.toJSONString(headers), JSON.toJSONString(zsdSsbDelivInfoIf), e);
|
|
|
+ }
|
|
|
+ log.info("【交货冻结取货】ESB 响应,订单号={},响应={}", orderNumber, execute);
|
|
|
+
|
|
|
+// // 解析响应,非成功时抛出异常由调用方记录
|
|
|
+// JSONObject jsonResult = JSONObject.parseObject(result);
|
|
|
+// if (jsonResult != null && jsonResult.containsKey("code")) {
|
|
|
+// Object code = jsonResult.get("code");
|
|
|
+// if (!"0".equals(String.valueOf(code)) && !"200".equals(String.valueOf(code))) {
|
|
|
+// throw new RuntimeException("ESB 返回非成功状态,code=" + code
|
|
|
+// + ",msg=" + jsonResult.getString("msg"));
|
|
|
+// }
|
|
|
+// }
|
|
|
+ }
|
|
|
+ public static String format(Date date, String format) {
|
|
|
+ if (null != date && !StringUtils.isEmpty(format)) {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat(format);
|
|
|
+ return sdf.format(date);
|
|
|
+ } else {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ public boolean retry(String url, Map<String, String> headers, Map<String, Map<String, Map<String, List<Map<String, String>>>>> params) throws Exception {
|
|
|
+ return retryTemplate.execute(
|
|
|
+ context -> execute(url, headers, params),
|
|
|
+ context -> {
|
|
|
+ log.error("[{}] final request retry fail , headers is [{}], params is [{}]", url, JSON.toJSONString(headers), JSON.toJSONString(params), context.getLastThrowable());
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ private boolean execute(String url, Map<String, String> headers, Map<String, Map<String, Map<String, List<Map<String, String>>>>> params) throws Exception {
|
|
|
+ String returnString = HttpUtil.executePostBodyJsonParamRetry(url, headers, params);
|
|
|
+ log.info("SendOrderExpectedTimeToSapTask headers = [{}], params = [{}], returnString = [{}]", headers, params, returnString);
|
|
|
+ this.sapResult = returnString;
|
|
|
+ JSONObject jsonObject = JSON.parseObject(returnString);
|
|
|
+ JSONObject zsdSsbDelivInfoIfResponse = jsonObject.getJSONObject("ZSD_SSB_DELIV_INFO_IFResponse");
|
|
|
+ String code = zsdSsbDelivInfoIfResponse.getString("EV_FLAG");
|
|
|
+ return org.apache.commons.lang3.StringUtils.equals("S", code);
|
|
|
+ }
|
|
|
+
|
|
|
+// public static void main(String[] args) {
|
|
|
+// notifySapCancelOrder("1231111111");
|
|
|
+// }
|
|
|
+
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
+
|