自动化立体仓库 - WMS系统
#
lty
18 小时以前 8c2dbd922448b88b1c28b5e87c26a31b882c28ac
src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
@@ -20,9 +20,11 @@
import com.zy.common.utils.NodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import com.alibaba.fastjson.JSON;
@@ -32,8 +34,10 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * Created by vincent on 2022/4/9
@@ -70,6 +74,27 @@
    private ApiLogService apiLogService;
    @Autowired
    private PlatformTransactionManager transactionManager;
    @Value("${mes-wk.base-url.test:http://192.168.0.41}")
    private String mesWkBaseUrlTest;
    @Value("${mes-wk.base-url.prod:http://192.168.0.42}")
    private String mesWkBaseUrlProd;
    @Value("${mes-wk.auth.ent-code:lfd}")
    private String mesWkEntCode;
    @Value("${mes-wk.auth.username:TL002}")
    private String mesWkUsername;
    @Value("${mes-wk.auth.password-md5:e10adc3949ba59abbe56e057f20f883e}")
    private String mesWkPasswordMd5;
    @Value("${mes-wk.auth.token-valid-minutes:30}")
    private Integer mesWkTokenValidMinutes;
    private final Map<String, MesWkTokenInfo> mesWkTokenCache = new ConcurrentHashMap<>();
    @Override
    @Transactional
    public void pakinOrderCreate(OpenOrderPakinParam param) {
@@ -677,7 +702,7 @@
    // 模拟调用仓库1接口
    private String syncToWarehouse1(ErpMat mat) {
        log.info("Calling Warehouse 1 API for mat: {}", mat.getMatNr());
        return callMatSyncApi(mat, "http://localhost:8081");
        return callMatSyncApi(mat, "http://localhost:8888");
    }
    // 模拟调用仓库2接口
@@ -752,9 +777,97 @@
            return "接口调用异常:" + e.getMessage();
        }
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void processOneOrder(OrderDto dto) {
        if (dto == null) {
            throw new CoolException("订单参数不完整");
        }
        ErpOrder order = new ErpOrder();
        order.setOrderNo(dto.getOrderNo());
        order.setOrderType(dto.getOrderType());
        order.setWkType(dto.getWkType());
        order.setBusinessTime(dto.getBusinessTime());
        order.setCreateTime(dto.getCreateTime());
        order.setWarehouseId(dto.getWareHouseId());
        order.setOperateType(dto.getOperateType());
        order.setSendStatus(dto.getSendStatus() != null ? dto.getSendStatus() : 0);
        List<ErpOrderDetl> detls = dto.getOrderItems();
        if (Cools.isEmpty(order.getOrderNo()) || order.getOrderType() == null
                || Cools.isEmpty(order.getWkType()) || order.getBusinessTime() == null
                || order.getCreateTime() == null || Cools.isEmpty(order.getWarehouseId())) {
            throw new CoolException("订单参数不完整:" +
                    "orderNo=" + order.getOrderNo() +
                    ",orderType=" + order.getOrderType() +
                    ",wkType=" + order.getWkType() +
                    ",warehouseId=" + order.getWarehouseId());
        }
        if (Cools.isEmpty(detls)) {
            throw new CoolException("订单明细为空:" + order.getOrderNo());
        }
        for (ErpOrderDetl d : detls) {
            if (d == null || Cools.isEmpty(d.getLineId())
                    || Cools.isEmpty(d.getMatNr())
                    || d.getAnfme() == null) {
                String lineId = d != null ? d.getLineId() : null;
                String matnr = d != null ? d.getMatNr() : null;
                throw new CoolException("明细参数不完整:" +
                        "orderNo=" + order.getOrderNo() +
                        ",lineId=" + lineId +
                        ",matnr=" + matnr);
            }
        }
        if (!erpOrderService.insert(order)) {
            throw new CoolException("订单插入失败:" + order.getOrderNo());
        }
        if (order.getId() == null) {
            throw new CoolException("订单插入失败,未返回orderId:" + order.getOrderNo());
        }
        for (ErpOrderDetl detl : detls) {
            detl.setOrderId(order.getId());
            if (!erpOrderDetlService.insert(detl)) {
                throw new CoolException("订单明细插入失败:" +
                        "orderNo=" + order.getOrderNo() +
                        ",lineId=" + detl.getLineId());
            }
        }
        String syncError = null;
        String whId = order.getWarehouseId();
        Integer orderType = order.getOrderType();
        if (!Cools.isEmpty(whId)) {
            if ("WH01".equals(whId)) {
                syncError = syncOrderToWarehouse1(order, detls, orderType);
            } else if ("WH2".equals(whId)) {
                syncError = syncOrderToWarehouse2(order, detls, orderType);
            } else if ("WH3".equals(whId)) {
                syncError = syncOrderToWarehouse3(order, detls, orderType);
            } else {
                throw new CoolException("未找到对应仓库编号:" +
                        "orderNo=" + order.getOrderNo() +
                        ",warehouseId=" + whId);
            }
        }
        if (syncError != null) {
            throw new CoolException("外部接口同步失败:" +
                    "orderNo=" + order.getOrderNo() +
                    ",warehouseId=" + whId +
                    ",原因=" + syncError);
        }
    }
    @Override
    public Map<String, Object> addErpOrder(List<OrderDto> orders) {
        if (Cools.isEmpty(orders)) {
            Map<String, Object> res = new HashMap<>();
            res.put("result", "FAIL");
@@ -765,84 +878,27 @@
            return res;
        }
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        List<String> successOrders = new ArrayList<>();
        List<Map<String, Object>> failOrders = new ArrayList<>();
        for (OrderDto dto : orders) {
            String orderNo = dto != null ? dto.getOrderNo() : null;
            try {
                transactionTemplate.execute(status -> {
                    if (dto == null) {
                        throw new CoolException("订单参数不完整");
                    }
                    ErpOrder order = new ErpOrder();
                    order.setOrderNo(dto.getOrderNo());
                    order.setOrderType(dto.getOrderType());
                    order.setWkType(dto.getWkType());
                    order.setBusinessTime(dto.getBusinessTime());
                    order.setCreateTime(dto.getCreateTime());
                    order.setWarehouseId(dto.getWareHouseId());
                    order.setOperateType(dto.getOperateType());
                    order.setSendStatus(dto.getSendStatus() != null ? dto.getSendStatus() : 0);
                    List<ErpOrderDetl> detls = dto.getOrderItems();
                    if (Cools.isEmpty(order.getOrderNo()) || order.getOrderType() == null
                            || Cools.isEmpty(order.getWkType()) || order.getBusinessTime() == null || order.getCreateTime() == null
                            || Cools.isEmpty(order.getWarehouseId())) {
                        throw new CoolException("订单参数不完整");
                    }
                    if (Cools.isEmpty(detls)) {
                        throw new CoolException("订单明细为空");
                    }
                    String syncError = null;
                    String whId = order.getWarehouseId();
                    Integer orderType = order.getOrderType();
                    if (!Cools.isEmpty(whId)) {
                        if ("WH01".equals(whId)) {
                            syncError = syncOrderToWarehouse1(order, detls, orderType);
                        } else if ("WH2".equals(whId)) {
                            syncError = syncOrderToWarehouse2(order, detls, orderType);
                        } else if ("WH3".equals(whId)) {
                            syncError = syncOrderToWarehouse3(order, detls, orderType);
                        } else {
                            throw new CoolException("未找到对应仓库编号");
                        }
                    }
                    if (syncError != null) {
                        throw new CoolException(syncError);
                    }
                    if (!erpOrderService.insert(order)) {
                        throw new CoolException("订单插入失败");
                    }
                    if (order.getId() == null) {
                        throw new CoolException("订单插入失败,未返回orderId");
                    }
                    for (ErpOrderDetl detl : detls) {
                        if (detl == null || Cools.isEmpty(detl.getLineId()) || Cools.isEmpty(detl.getMatNr())
                                || Cools.isEmpty(detl.getMakTx()) || detl.getAnfme() == null) {
                            throw new CoolException("明细参数不完整");
                        }
                        detl.setOrderId(order.getId());
                        if (!erpOrderDetlService.insert(detl)) {
                            throw new CoolException("订单明细插入失败");
                        }
                    }
                    return null;
                });
                processOneOrder(dto);
                successOrders.add(orderNo);
            } catch (Exception e) {
                Map<String, Object> fail = new HashMap<>();
                String whId = dto != null ? dto.getWareHouseId() : null;
                Integer orderType = dto != null ? dto.getOrderType() : null;
                fail.put("orderNo", orderNo);
                fail.put("warehouseId", whId);
                fail.put("orderType", orderType);
                fail.put("msg", e.getMessage());
                fail.put("exception", e.getClass().getSimpleName());
                Throwable cause = e.getCause();
                if (cause != null && cause != e) {
                    fail.put("cause", cause.getClass().getSimpleName() + ":" + cause.getMessage());
                }
                failOrders.add(fail);
            }
        }
@@ -853,6 +909,7 @@
        res.put("successCount", successOrders.size());
        res.put("failCount", failOrders.size());
        res.put("result", failOrders.isEmpty() ? "SUCCESS" : (successOrders.isEmpty() ? "FAIL" : "PART_SUCCESS"));
        return res;
    }
@@ -864,8 +921,8 @@
        List<String> whIds = new ArrayList<>();
        if (Cools.isEmpty(wareHouseId)) {
            whIds.add("WH01");
            whIds.add("WH02");
            whIds.add("WH03");
//            whIds.add("WH02");
//            whIds.add("WH03");
        } else {
            whIds.add(wareHouseId);
        }
@@ -894,8 +951,8 @@
        List<String> whIds = new ArrayList<>();
        if (Cools.isEmpty(wareHouseId)) {
            whIds.add("WH01");
            whIds.add("WH02");
            whIds.add("WH03");
//            whIds.add("WH02");
//            whIds.add("WH03");
        } else {
            whIds.add(wareHouseId);
        }
@@ -916,13 +973,79 @@
        return result;
    }
    @Override
    public Map<String, Object> orderReport(OrderReportParam param) throws IOException {
        if (param == null) {
            throw new CoolException("参数不能为空");
        }
        if (Cools.isEmpty(param.getWkType())) {
            throw new CoolException("参数[wkType]不能为空");
        }
        if (Cools.isEmpty(param.getOrderDetls())) {
            throw new CoolException("参数[orderDetls]不能为空");
        }
        String baseUrl = resolveMesWkBaseUrl(param);
        MesWkBizType bizType = resolveMesWkBizType(param.getWkType());
        String token = getMesWkToken(baseUrl);
        Map<String, Object> headers = new HashMap<>();
        headers.put("Authorization", token);
        String path;
        String bizDesc;
        Map<String, Object> payload;
        if (bizType == MesWkBizType.PURCHASE_IN_STOCK) {
            path = "/purchaseInStock/lkPurchaseInStockAdd";
            bizDesc = "立库采购入库";
            payload = buildPurchaseInStockPayload(param.getOrderDetls());
        } else if (bizType == MesWkBizType.PRODUCT_ISSUE) {
            path = "/productIssue/lkProductIssueAdd";
            bizDesc = "立库生产发料";
            payload = buildProductBizPayload(param.getOrderDetls(), "productIssueDtls", "productIssueBoms");
        } else if (bizType == MesWkBizType.PRODUCT_FEED) {
            path = "/productFeed/lkProductFeedAdd";
            bizDesc = "立库生产补料";
            payload = buildProductBizPayload(param.getOrderDetls(), "productFeedDtls", "productFeedBoms");
        } else if (bizType == MesWkBizType.PRODUCT_RETURN) {
            path = "/productReturn/lkProductReturnAdd";
            bizDesc = "立库生产退料";
            payload = buildProductBizPayload(param.getOrderDetls(), "productReturnDtls", "productReturnBoms");
        } else {
            throw new CoolException("不支持的wkType:" + param.getWkType());
        }
        try {
            Map<String, Object> res = callMesWkApi(baseUrl, path, headers, payload, bizDesc);
            Map<String, Object> out = new HashMap<>();
            out.put("wkType", param.getWkType());
            out.put("baseUrl", baseUrl);
            out.put("path", path);
            out.put("result", res);
            return out;
        } catch (MesWkAuthException e) {
            mesWkTokenCache.remove(baseUrl);
            String newToken = getMesWkToken(baseUrl);
            headers.put("Authorization", newToken);
            Map<String, Object> res = callMesWkApi(baseUrl, path, headers, payload, bizDesc);
            Map<String, Object> out = new HashMap<>();
            out.put("wkType", param.getWkType());
            out.put("baseUrl", baseUrl);
            out.put("path", path);
            out.put("result", res);
            out.put("tokenRefreshed", true);
            return out;
        }
    }
    private String getWarehouseBaseUrl(String wareHouseId) {
        if (Cools.isEmpty(wareHouseId)) {
            return null;
        }
        String wh = wareHouseId.trim();
        if ("WH01".equalsIgnoreCase(wh) || "WH1".equalsIgnoreCase(wh)) {
            return "http://localhost:8081";
            return "http://localhost:8888";
        }
        if ("WH02".equalsIgnoreCase(wh) || "WH2".equalsIgnoreCase(wh)) {
            return "http://localhost:8080";
@@ -934,48 +1057,313 @@
    }
    private List<Map<String, Object>> callMesInventoryApi(String baseUrl, String path, Map<String, Object> payload) throws IOException {
        String json = JSON.toJSONString(payload == null ? new HashMap<>() : payload);
        Map<String, Object> headers = new HashMap<>();
        headers.put("appkey", "ea1f0459efc02a79f046f982767939ae");
        String url = baseUrl + path;
        String requestJson = JSON.toJSONString(payload == null ? new HashMap<>() : payload);
        try {
            Map<String, Object> headers = new HashMap<>();
            headers.put("appkey", "ea1f0459efc02a79f046f982767939ae");
            String response = new HttpHandler.Builder()
                    .setHeaders(headers)
                    .setUri(baseUrl)
                    .setPath(path)
                    .setJson(requestJson)
                    .build()
                    .doPost();
            // ===== 无响应 =====
            if (Cools.isEmpty(response)) {
                callApiLogSave(null, url, "接口无响应", false);
                throw new CoolException("接口无响应");
            }
            JSONObject jsonResponse = JSON.parseObject(response);
            if (jsonResponse == null) {
                callApiLogSave(null, url, "接口响应格式错误", false);
                throw new CoolException("接口响应格式错误");
            }
            Integer code = jsonResponse.getInteger("code");
            if (code != null && (code == 0 || code == 200)) {
                // ✅ 成功日志
                callApiDetlLogSave("库存明细查询", url, requestJson, response, true);                Object data = jsonResponse.get("data");
                if (data == null) {
                    return new ArrayList<>();
                }
                if (data instanceof List) {
                    return JSON.parseObject(JSON.toJSONString(data), List.class);
                }
                List<Map<String, Object>> list = new ArrayList<>();
                Map<String, Object> one = JSON.parseObject(JSON.toJSONString(data), Map.class);
                if (one != null) {
                    list.add(one);
                }
                return list;
            } else {
                String msg = jsonResponse.getString("msg");
                String err = !Cools.isEmpty(msg) ? msg : "未知错误,code=" + code;
                // ❌ 失败日志
                callApiDetlLogSave("库存明细查询", url, requestJson, response, false);
                throw new CoolException(err);
            }
        } catch (Exception e) {
            log.error("callMesInventoryApi error", e);
            // ❌ 异常日志
            callApiDetlLogSave("库存明细查询", url, requestJson, e.getMessage(), false);            throw e;
        }
    }
    private String resolveMesWkBaseUrl(OrderReportParam param) {
        String raw = param != null ? param.getWarehouseId() : null;
        if (!Cools.isEmpty(raw)) {
            String v = raw.trim();
            if (v.startsWith("http://") || v.startsWith("https://")) {
                return v;
            }
            if (v.matches("^\\d{1,3}(\\.\\d{1,3}){3}(:\\d+)?$")) {
                return "http://" + v;
            }
        }
        String wkType = param != null ? param.getWkType() : null;
        String norm = wkType == null ? "" : wkType.trim().toLowerCase();
        if (norm.contains("test") || norm.contains("41") || norm.contains("测试")) {
            return normalizeHttpBaseUrl(mesWkBaseUrlTest);
        }
        return normalizeHttpBaseUrl(mesWkBaseUrlProd);
    }
    private String normalizeHttpBaseUrl(String baseUrl) {
        if (Cools.isEmpty(baseUrl)) {
            return null;
        }
        String v = baseUrl.trim();
        if (v.startsWith("http://") || v.startsWith("https://")) {
            return v;
        }
        return "http://" + v;
    }
    private MesWkBizType resolveMesWkBizType(String wkType) {
        String v = wkType == null ? "" : wkType.trim().toLowerCase();
        if (v.contains("purchase") || v.contains("purch") || v.contains("cg") || v.contains("采购") || v.equals("1")) {
            return MesWkBizType.PURCHASE_IN_STOCK;
        }
        if (v.contains("issue") || v.contains("fl") || v.contains("发料") || v.equals("2")) {
            return MesWkBizType.PRODUCT_ISSUE;
        }
        if (v.contains("feed") || v.contains("bl") || v.contains("补料") || v.equals("3")) {
            return MesWkBizType.PRODUCT_FEED;
        }
        if (v.contains("return") || v.contains("tl") || v.contains("退料") || v.equals("4")) {
            return MesWkBizType.PRODUCT_RETURN;
        }
        throw new CoolException("无法识别wkType:" + wkType);
    }
    private synchronized String getMesWkToken(String baseUrl) throws IOException {
        if (Cools.isEmpty(baseUrl)) {
            throw new CoolException("服务器地址未配置");
        }
        long now = System.currentTimeMillis();
        MesWkTokenInfo cached = mesWkTokenCache.get(baseUrl);
        if (cached != null && !Cools.isEmpty(cached.token) && now < cached.expireAt) {
            return cached.token;
        }
        String path = "/acco/login";
        Map<String, Object> payload = new HashMap<>();
        payload.put("entCode", mesWkEntCode);
        payload.put("username", mesWkUsername);
        payload.put("password", mesWkPasswordMd5);
        String requestJson = JSON.toJSONString(payload);
        String url = baseUrl + path;
        String response = new HttpHandler.Builder()
                .setHeaders(headers)
                .setUri(baseUrl)
                .setPath(path)
                .setJson(json)
                .setJson(requestJson)
                .build()
                .doPost();
        if (Cools.isEmpty(response)) {
            callApiDetlLogSave("立库接口登录", url, requestJson, "接口无响应", false);
            throw new CoolException("立库接口登录无响应");
        }
        JSONObject json = JSON.parseObject(response);
        if (json == null) {
            callApiDetlLogSave("立库接口登录", url, requestJson, response, false);
            throw new CoolException("立库接口登录响应格式错误");
        }
        Integer code = json.getInteger("code");
        if (code == null || (code != 200 && code != 0)) {
            callApiDetlLogSave("立库接口登录", url, requestJson, response, false);
            throw new CoolException("立库接口登录失败:" + response);
        }
        String token = json.getString("data");
        if (Cools.isEmpty(token)) {
            callApiDetlLogSave("立库接口登录", url, requestJson, response, false);
            throw new CoolException("立库接口登录未返回token");
        }
        callApiDetlLogSave("立库接口登录", url, requestJson, response, true);
        long expireAt = now + (mesWkTokenValidMinutes == null ? 30 : mesWkTokenValidMinutes) * 60L * 1000L;
        mesWkTokenCache.put(baseUrl, new MesWkTokenInfo(token, expireAt));
        return token;
    }
    private Map<String, Object> callMesWkApi(String baseUrl, String path, Map<String, Object> headers, Map<String, Object> payload, String bizDesc) throws IOException {
        String url = baseUrl + path;
        String requestJson = JSON.toJSONString(payload == null ? new HashMap<>() : payload);
        String response;
        try {
            response = new HttpHandler.Builder()
                    .setUri(baseUrl)
                    .setPath(path)
                    .setHeaders(headers)
                    .setJson(requestJson)
                    .build()
                    .doPost();
        } catch (Exception e) {
            callApiDetlLogSave(bizDesc, url, requestJson, e.getMessage(), false);
            throw e;
        }
        if (Cools.isEmpty(response)) {
            callApiDetlLogSave(bizDesc, url, requestJson, "接口无响应", false);
            throw new CoolException("接口无响应");
        }
        JSONObject jsonResponse = JSON.parseObject(response);
        if (jsonResponse == null) {
        JSONObject json = JSON.parseObject(response);
        if (json == null) {
            callApiDetlLogSave(bizDesc, url, requestJson, response, false);
            throw new CoolException("接口响应格式错误");
        }
        Integer code = jsonResponse.getInteger("code");
        if (code != null && (code == 0 || code == 200)) {
            Object data = jsonResponse.get("data");
            if (data == null) {
                return new ArrayList<>();
            }
            if (data instanceof List) {
                return JSON.parseObject(JSON.toJSONString(data), List.class);
            }
            List<Map<String, Object>> list = new ArrayList<>();
            Map<String, Object> one = JSON.parseObject(JSON.toJSONString(data), Map.class);
            if (one != null) {
                list.add(one);
            }
            return list;
        Integer code = json.getInteger("code");
        if (code != null && (code == 200 || code == 0)) {
            callApiDetlLogSave(bizDesc, url, requestJson, response, true);
            return JSON.parseObject(response, Map.class);
        }
        String msg = jsonResponse.getString("msg");
        throw new CoolException(!Cools.isEmpty(msg) ? msg : "未知错误,code=" + code);
        String err = json.getString("error");
        String msg = json.getString("msg");
        String text = !Cools.isEmpty(err) ? err : (!Cools.isEmpty(msg) ? msg : "未知错误");
        callApiDetlLogSave(bizDesc, url, requestJson, response, false);
        if (text.toLowerCase().contains("token") || text.toLowerCase().contains("unauthor")) {
            throw new MesWkAuthException(text);
        }
        throw new CoolException(text);
    }
    private Map<String, Object> buildPurchaseInStockPayload(List<OrderDetl> orderDetls) {
        List<Map<String, Object>> dtls = new ArrayList<>();
        for (OrderDetl d : orderDetls) {
            if (d == null) {
                continue;
            }
            if (Cools.isEmpty(d.getBatch())) {
                throw new CoolException("orderDetls.batch不能为空");
            }
            if (d.getAnfme() == null) {
                throw new CoolException("orderDetls.anfme不能为空");
            }
            Map<String, Object> one = new HashMap<>();
            one.put("batchNum", d.getBatch());
            one.put("quantity", d.getAnfme());
            dtls.add(one);
        }
        Map<String, Object> payload = new HashMap<>();
        payload.put("purchaseInStockDtls", dtls);
        return payload;
    }
    private Map<String, Object> buildProductBizPayload(List<OrderDetl> orderDetls, String dtlsKey, String bomsKey) {
        Map<String, List<OrderDetl>> bySku = new LinkedHashMap<>();
        for (OrderDetl d : orderDetls) {
            if (d == null) {
                continue;
            }
            if (Cools.isEmpty(d.getSku())) {
                throw new CoolException("orderDetls.sku不能为空");
            }
            bySku.computeIfAbsent(d.getSku(), k -> new ArrayList<>()).add(d);
        }
        List<Map<String, Object>> dtls = new ArrayList<>();
        for (Map.Entry<String, List<OrderDetl>> entry : bySku.entrySet()) {
            String sku = entry.getKey();
            List<OrderDetl> list = entry.getValue();
            List<Map<String, Object>> boms = new ArrayList<>();
            for (OrderDetl d : list) {
                if (Cools.isEmpty(d.getBatch())) {
                    throw new CoolException("orderDetls.batch不能为空");
                }
                if (d.getAnfme() == null) {
                    throw new CoolException("orderDetls.anfme不能为空");
                }
                Map<String, Object> bom = new HashMap<>();
                bom.put("batchNum", d.getBatch());
                bom.put("quantity", d.getAnfme());
                boms.add(bom);
            }
            Map<String, Object> one = new HashMap<>();
            one.put("planSpNo", sku);
            one.put(bomsKey, boms);
            dtls.add(one);
        }
        Map<String, Object> payload = new HashMap<>();
        payload.put(dtlsKey, dtls);
        return payload;
    }
    private enum MesWkBizType {
        PURCHASE_IN_STOCK,
        PRODUCT_ISSUE,
        PRODUCT_FEED,
        PRODUCT_RETURN
    }
    private static class MesWkTokenInfo {
        private final String token;
        private final long expireAt;
        private MesWkTokenInfo(String token, long expireAt) {
            this.token = token;
            this.expireAt = expireAt;
        }
    }
    private static class MesWkAuthException extends RuntimeException {
        private MesWkAuthException(String message) {
            super(message);
        }
    }
    // 模拟调用仓库1接口
    private String syncOrderToWarehouse1(ErpOrder order, List<ErpOrderDetl> details, Integer orderType) {
        log.info("Calling Warehouse 1 API for order: {}", order.getOrderNo());
        return callOrderSyncApi(order, details, orderType, "http://localhost:8081");
        return callOrderSyncApi(order, details, orderType, "http://localhost:8888");
    }
    // 模拟调用仓库2接口
@@ -991,66 +1379,66 @@
    }
    private String callOrderSyncApi(ErpOrder order, List<ErpOrderDetl> details, Integer orderType, String baseUrl) {
        String path = null;
        String json = null;
        String url = null;
        try {
            // 根据 orderType 判断接口路径
            // 假设 1=出库,2=入库(请根据实际业务调整,这里按用户描述逻辑实现)
            // 用户描述:若是入库单则调用order/pakin/default/v1,若是出库order/pakout/default/v1
            // 假设 orderType 2 为入库,1 或 3 为出库(需确认 3 调拨单属于哪类,暂归为出库或不处理)
            String path;
            String json;
            // ===== 构造请求 =====
            if (orderType == 2) {
                // 入库单
                path = "/lfdwms/open/asrs/order/pakin/default/v1";
                OpenOrderPakinParam param = new OpenOrderPakinParam();
                param.setOrderNo(order.getOrderNo());
                // 这里可能需要映射 wkType 到 orderType 字符串,或者直接用
                param.setOrderType(order.getWkType());
                param.setOrderType(order.getWkType());
                param.setOrderTime(DateUtils.convert(new Date(order.getBusinessTime())));
                List<DetlDto> detlDtos = new ArrayList<>();
                if (!Cools.isEmpty(details)) {
                    for (ErpOrderDetl d : details) {
                        DetlDto dto = new DetlDto();
                        dto.setMatnr(d.getMatNr());
                        // 如果有 batch 字段则设置,OpenOrderPakinParam 的 DetlDto 可能需要确认字段
                        dto.setBatch(d.getBatch());
                        dto.setAnfme(d.getAnfme() != null ? d.getAnfme().doubleValue() : 0.0);
                        // 其他字段映射...
                        detlDtos.add(dto);
                    }
                for (ErpOrderDetl d : details) {
                    DetlDto dto = new DetlDto();
                    dto.setMatnr(d.getMatNr());
                    dto.setBatch(d.getBatch());
                    dto.setAnfme(d.getAnfme() != null ? d.getAnfme().doubleValue() : 0.0);
                    detlDtos.add(dto);
                }
                param.setOrderDetails(detlDtos);
                json = JSON.toJSONString(param);
            } else if (orderType == 1 || orderType == 3) {
                // 出库单
                path = "/lfdwms/open/asrs/order/pakout/default/v1";
                OpenOrderPakoutParam param = new OpenOrderPakoutParam();
                param.setOrderNo(order.getOrderNo());
                param.setOrderType(order.getWkType());
                param.setOrderTime(DateUtils.convert(new Date(order.getBusinessTime())));
                param.setLgort("5006"); // 必填校验要求
                param.setLgort("5006");
                List<DetlDto> detlDtos = new ArrayList<>();
                if (!Cools.isEmpty(details)) {
                    for (ErpOrderDetl d : details) {
                        DetlDto dto = new DetlDto();
                        dto.setMatnr(d.getMatNr());
                        dto.setBatch(d.getBatch());
                        dto.setAnfme(d.getAnfme() != null ? d.getAnfme().doubleValue() : 0.0);
                        detlDtos.add(dto);
                    }
                for (ErpOrderDetl d : details) {
                    DetlDto dto = new DetlDto();
                    dto.setMatnr(d.getMatNr());
                    dto.setBatch(d.getBatch());
                    dto.setAnfme(d.getAnfme() != null ? d.getAnfme().doubleValue() : 0.0);
                    detlDtos.add(dto);
                }
                param.setOrderDetails(detlDtos);
                json = JSON.toJSONString(param);
            } else {
                return "未知的订单类型:" + orderType;
            }
            url = baseUrl + path;
            // ===== 调接口 =====
            Map<String, Object> headers = new HashMap<>();
            headers.put("appkey", "ea1f0459efc02a79f046f982767939ae");
            String response = new HttpHandler.Builder()
                    .setHeaders(headers)
                    .setUri(baseUrl)
@@ -1058,25 +1446,46 @@
                    .setJson(json)
                    .build()
                    .doPost();
            // ===== 判空 =====
            if (Cools.isEmpty(response)) {
                callOrderLogSave(order.getOrderNo(), url, json, "接口无响应", false);
                return "接口无响应";
            }
            JSONObject jsonResponse = JSON.parseObject(response);
            if (jsonResponse == null) {
                callOrderLogSave(order.getOrderNo(), url, json, "响应格式错误", false);
                return "接口响应格式错误";
            }
            Integer code = jsonResponse.getInteger("code");
            if (code != null && (code == 0 || code == 200)) {
                // ✅ 成功日志
                callOrderLogSave(order.getOrderNo(), url, json, response, true);
                return null;
            } else {
                return jsonResponse.getString("msg") != null ? jsonResponse.getString("msg") : "未知错误,code=" + code;
                String msg = jsonResponse.getString("msg");
                String err = msg != null ? msg : "未知错误 code=" + code;
                // ❌ 失败日志
                callOrderLogSave(order.getOrderNo(), url, json, response, false);
                return err;
            }
        } catch (Exception e) {
            log.error("Call order sync api error", e);
            // ❌ 异常日志
            callOrderLogSave(order.getOrderNo(), url, json, e.getMessage(), false);
            return "接口调用异常:" + e.getMessage();
        }
    }
@@ -1092,4 +1501,27 @@
                bool
        );
    }
    public void callOrderLogSave(String orderNo, String url, String request, String response, Boolean success) {
        apiLogService.save(
                "订单新增接口",
                url,
                request,
                null,
                "订单号:" + orderNo,
                response,
                success
        );
    }
    public void callApiDetlLogSave(String bizDesc, String url, String request, String response, Boolean success) {
        apiLogService.save(
                bizDesc,
                url,
                request,
                null,
                null,
                response,
                success
        );
    }
}