1
6 天以前 d2c478f727ebf427e1490277420c841f8f884f56
lsh#
4个文件已修改
316 ■■■■■ 已修改文件
asrs-schedule/src/main/java/com/vincent/rsf/schedule/api/service/ReportMsgService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
asrs-schedule/src/main/java/com/vincent/rsf/schedule/api/service/impl/ReportMsgServiceImpl.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
asrs-schedule/src/main/java/com/vincent/rsf/schedule/schedules/AsnOrderLogSchedule.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java 157 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
asrs-schedule/src/main/java/com/vincent/rsf/schedule/api/service/ReportMsgService.java
@@ -1,5 +1,6 @@
package com.vincent.rsf.schedule.api.service;
import com.alibaba.fastjson.JSONObject;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.schedule.api.controller.erp.params.ReportParams;
import com.vincent.rsf.schedule.api.entity.dto.PoItemsDto;
@@ -19,6 +20,8 @@
    R uploadReportOrders(ReportParams params);
    R uploadReportOrdersNew(JSONObject orderData);
    R uploadCheckOrder(ReportParams params);
}
asrs-schedule/src/main/java/com/vincent/rsf/schedule/api/service/impl/ReportMsgServiceImpl.java
@@ -222,6 +222,45 @@
    }
    /**
     * 上报已完成订单(新链路,Order格式)
     * @author Ryan
     * @date 2025/10/28
     * @param orderData Order格式的JSON数据
     * @return com.vincent.rsf.framework.common.R
     */
    @Override
    public R uploadReportOrdersNew(JSONObject orderData) {
        if (Objects.isNull(orderData)) {
            throw new CoolException("参数不能为空!!");
        }
        String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.REPORT_ORDERS_NEW;
        log.info("上报已完成订单(新链路):{}, 请求参数: {}", rcsUrl, orderData.toJSONString());
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("api-version", "v2.0");
        HttpEntity httpEntity = new HttpEntity(orderData, headers);
        ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class);
        log.info("上报已完成订单(新链路),返回结果: {}", exchange);
        if (Objects.isNull(exchange.getBody())) {
            throw new CoolException("修改失败!!");
        } else {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.coercionConfigDefaults().setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
            try {
                CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class);
                if (result.getCode() == 200) {
                    return R.ok().add(result.getData());
                } else {
                    return R.ok(result.getMsg()).add(result.getData());
                }
            } catch (JsonProcessingException e) {
                throw new CoolException(e.getMessage());
            }
        }
    }
    /**
     * 盘点库存数据修改
     * @param params
     * @return
asrs-schedule/src/main/java/com/vincent/rsf/schedule/schedules/AsnOrderLogSchedule.java
@@ -5,10 +5,8 @@
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.schedule.api.controller.erp.params.ReportDataParam;
import com.vincent.rsf.schedule.api.controller.erp.params.ReportParams;
import com.alibaba.fastjson.JSONObject;
import com.vincent.rsf.schedule.api.service.ReportMsgService;
import com.vincent.rsf.schedule.common.utils.FieldsUtils;
import com.vincent.rsf.schedule.manager.entity.*;
import com.vincent.rsf.schedule.manager.enums.*;
import com.vincent.rsf.schedule.manager.service.*;
@@ -63,7 +61,7 @@
     * @description 删除已完成订单加入Log表
     * @time 2025/3/19 19:09
     */
//    @Scheduled(cron = "0/35 * * * * ?  ")
    @Scheduled(cron = "0/1 * * * * ?  ")
    @Transactional(rollbackFor = Exception.class)
    public void InStockToLog() {
        List<WkOrder> wkOrders = asnOrderService.list(new LambdaQueryWrapper<WkOrder>()
@@ -110,86 +108,67 @@
    /**
     * @author Ryan
     * @date 2025/10/28
     * @description: 上传已完成订单至ERP平台
     * @description: 上传已完成订单至ERP平台(新链路,Order格式)
     * @version 1.0
     */
    public void reportOrders(WkOrder order) {
            ReportParams params = new ReportParams();
            List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId()));
            if (orderItems.isEmpty()) {
                throw new CoolException("订据明细不存在!!");
            }
            List<ReportDataParam> reportData = new ArrayList<>();
            orderItems.forEach(orderItem -> {
                ReportDataParam param = new ReportDataParam();
                User user = userService.getById(order.getUpdateBy());
                String nickName = null;
                if (!Objects.isNull(user)) {
                    nickName = user.getNickname();
            // 构建Order格式的JSON数据,与下发/add接口字段保持一致
            JSONObject orderData = new JSONObject(true);
            // 幂等键:orderNo + reportOnce 确保每次上报唯一
            int seq = order.getReportOnce() == null ? 0 : order.getReportOnce();
            orderData.put("eventId", order.getCode() + "_REPORT_" + seq);
            orderData.put("orderNo", order.getCode());
            orderData.put("poCode", order.getPoCode());
            orderData.put("type", order.getType());
            orderData.put("wkType", order.getWkType());
            orderData.put("createTime", order.getCreateTime() != null ? order.getCreateTime().getTime() / 1000 : null);
            orderData.put("memo", order.getMemo());
            User user = userService.getById(order.getUpdateBy());
            String nickName = null;
            if (!Objects.isNull(user)) {
                nickName = user.getNickname();
            }
            orderData.put("editUser", nickName);
            orderData.put("editDate", order.getUpdateTime() != null ? order.getUpdateTime().getTime() / 1000 : null);
            // 构建明细列表,字段与OrderItem一致
            List<JSONObject> items = new ArrayList<>();
            for (WkOrderItem orderItem : orderItems) {
                JSONObject item = new JSONObject(true);
                item.put("lineId", orderItem.getPlatItemId());
                item.put("planNo", orderItem.getPlatWorkCode());
                item.put("matNr", orderItem.getMatnrCode());
                item.put("makTx", orderItem.getMaktx());
                item.put("anfme", orderItem.getAnfme());
                item.put("batch", orderItem.getBatch());
                item.put("unit", orderItem.getStockUnit());
                item.put("spec", orderItem.getSpec());
                item.put("model", orderItem.getModel());
                item.put("memo", orderItem.getMemo());
                // 完成数量:入库用qty,出库用workQty
                if (order.getType().equals(OrderType.ORDER_IN.type)) {
                    item.put("doneQty", orderItem.getQty());
                } else {
                    item.put("doneQty", orderItem.getWorkQty());
                }
                items.add(item);
            }
            orderData.put("orderItems", items);
                Map<String, String> fields = FieldsUtils.getFields(orderItem.getFieldsIndex());
                //设置通用参数
                param.setWMSNO(order.getCode())
                        .setPONO(order.getPoCode())
                        .setOrderNO(order.getPoCode())
                        .setOrderDate(order.getCreateTime())
                        .setItemCode(orderItem.getMatnrCode())
                        .setEditUser(nickName)
                        .setEditDate(order.getUpdateTime())
//                        .setGoodsNO(fields.get("crushNo"))
                        .setMemoDtl(order.getMemo());
                if (order.getWkType().equals(OrderWorkType.ORDER_WORK_TYPE_DONE_IN.type)) {
                    //采购入库单
                    params.setOrderType("PO_Instock").setAction("Update");
                    //获取指定查询字段CrushNo 票号
                    param.setInQty(orderItem.getQty());
                } else if (order.getWkType().equals(OrderWorkType.ORDER_WORK_TYPE_PURCHASE_RETURN.type)) {
                    //采购退货
                    params.setOrderType("PR_Outstock").setAction("Update");
                    param.setOutQty(orderItem.getQty());
                } else if (order.getWkType().equals(OrderWorkType.ORDER_WORK_TYPE_PROD.type)) {
                    //生产入库单
//                params.setOrderType("WO_Outstock").setAction("Update");
                } else if (order.getWkType().equals(OrderWorkType.ORDER_WORK_TYPE_PURCHASE.type)) {
                    //生产退料
                    params.setOrderType("WR_Instock").setAction("Update");
                    param.setInQty(orderItem.getQty());
                } else if (order.getWkType().equals(OrderWorkType.ORDER_WORK_TYPE_RETURN_ORDER.type)) {
                    //生产领料
                    params.setOrderType("WO_Outstock").setAction("Update");
                    param.setOutQty(orderItem.getQty());
                } else if (order.getWkType().equals(OrderWorkType.ORDER_WORK_TYPE_PROD_ADDITION.type)) {
                    //生产补料
                    params.setOrderType("WR_Instock_BL").setAction("Update");
                    param.setOutQty(orderItem.getQty());
                } else if (order.getWkType().equals(OrderWorkType.ORDER_WORK_TYPE_FIX_OUT.type)) {
                    params.setOrderType("WO_Outstock_WR").setAction("Update");
                    param.setOutQty(orderItem.getQty());
                } else if (order.getWkType().equals(OrderWorkType.ORDER_WORK_TYPE_OTHER_IN.type)) {
                    //其它入库单/手动入库单
                    params.setOrderType("In_Instock").setAction("Update");
                    param.setInQty(orderItem.getQty());
                } else if (order.getWkType().equals(OrderWorkType.ORDER_WORK_TYPE_OTHER.type)) {
                    //其它出库单/手动出库单
                    params.setOrderType("Io_Outstock").setAction("Update");
                    param.setOutQty(orderItem.getQty());
                }
                reportData.add(param);
            });
            params.setData(reportData);
            try {
                R reported = reportMsgService.uploadReportOrders(params);
                R reported = reportMsgService.uploadReportOrdersNew(orderData);
                if (reported.get("code").equals("200")) {
                    order.setNtyStatus(OrderReportStatus.ORDER_REPORT_STATUS_ALL.val);
                    asnOrderService.updateById(order);
                }
            } catch (Exception e) {
                log.error("<UNK>", e);
                log.error("上报订单失败", e);
            }
    }
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java
@@ -449,21 +449,44 @@
            throw new CoolException("幂等键不能为空(eventId/taskNo/reportNo)");
        }
        String orderNo = pickString(root, "orderNo", "OrderNo", "WMSNO", "wmsno");
        if (StringUtils.isBlank(orderNo)) {
            return CommonResponse.error("orderNo不能为空");
        }
        if (existsReportEvent(eventId, taskNo, reportNo)) {
            return CommonResponse.ok("重复回传已忽略");
        }
        OpenApiOrderReportEvent reportEvent = saveReportEvent(eventId, taskNo, reportNo, pickString(root, "orderNo", "WMSNO"), root.toJSONString());
        OpenApiOrderReportEvent reportEvent = saveReportEvent(eventId, taskNo, reportNo, orderNo, root.toJSONString());
        JSONArray dataArray = pickArray(root, "Data", "data");
        // 解析明细:优先取 orderItems(Order格式),兼容 Data/data(ReportParams格式)
        JSONArray dataArray = pickArray(root, "orderItems", "orderItems");
        if (Objects.isNull(dataArray) || dataArray.isEmpty()) {
            dataArray = pickArray(root, "Data", "data");
        }
        if (Objects.isNull(dataArray) || dataArray.isEmpty()) {
            return CommonResponse.error("回传明细为空");
        }
        // 对回传数据做反映射(WMS字段名 → ERP字段名)
        // 对回传数据做反映射(WMS字段名 → ERP字段名),兼容旧格式
        JSONObject mappedRoot = ParamsMapUtils.reverseApiMaps("erp", "orderId", root);
        JSONArray mappedDataArray = pickArray(mappedRoot, "Data", "data");
        JSONArray mappedDataArray = pickArray(mappedRoot, "orderItems", "orderItems");
        if (Objects.isNull(mappedDataArray) || mappedDataArray.isEmpty()) {
            mappedDataArray = pickArray(mappedRoot, "Data", "data");
        }
        if (Objects.isNull(mappedDataArray) || mappedDataArray.isEmpty()) {
            mappedDataArray = dataArray;
        }
        // 主单级信息(Order格式上报时携带)
        String orderType = pickString(root, "type", "Type");
        String wkType = pickString(root, "wkType", "WkType");
        String poCode = pickString(root, "poCode", "PoCode", "PONO");
        String editUser = pickString(root, "editUser", "EditUser");
        Date editDate = null;
        Object editDateObj = root.get("editDate");
        if (editDateObj instanceof Number) {
            editDate = toDate(((Number) editDateObj).longValue());
        }
        int allocateCount = 0;
@@ -473,43 +496,45 @@
        for (int i = 0; i < mappedDataArray.size(); i++) {
            JSONObject row = mappedDataArray.getJSONObject(i);
            String orderNo = pickString(row, "WMSNO", "wmsNo", "orderNo", "OrderNo");
            String matnrCode = pickString(row, "ItemCode", "itemCode", "matNr", "MatNr");
            String batch = pickString(row, "Batch", "batch", "GoodsNO", "goodsNo");
            BigDecimal doneQty = pickDecimal(row, "qty", "doneQty", "InQty", "OutQty");
            if (StringUtils.isBlank(orderNo) || StringUtils.isBlank(matnrCode) || doneQty.compareTo(ZERO) <= 0) {
                errors.add("第" + (i + 1) + "行缺少必要字段(WMSNO/ItemCode/数量)");
            // Order格式:matNr/anfme/batch/doneQty/lineId
            // ReportParams格式兼容:ItemCode/InQty/OutQty/GoodsNO
            String matnrCode = pickString(row, "matNr", "MatNr", "ItemCode", "itemCode");
            String batch = pickString(row, "batch", "Batch", "GoodsNO", "goodsNo");
            String lineId = pickString(row, "lineId", "LineId", "sourceLineId");
            BigDecimal doneQty = pickDecimal(row, "doneQty", "anfme", "qty", "InQty", "inQty", "OutQty", "outQty", "pdqty", "PDQty");
            if (StringUtils.isBlank(matnrCode) || doneQty.compareTo(ZERO) <= 0) {
                errors.add("第" + (i + 1) + "行缺少必要字段(matNr/数量)");
                continue;
            }
            doneQty = doneQty.setScale(2, RoundingMode.HALF_UP);
            try {
                int singleAllocated = allocateMergedDoneQty(orderNo, matnrCode, batch, doneQty, orderCache);
                int singleAllocated = allocateMergedDoneQty(orderNo, matnrCode, batch, lineId, doneQty, orderCache);
                allocateCount += singleAllocated;
                affectedOrderNos.add(orderNo);
            } catch (Exception e) {
                log.error("处理回传失败,orderNo={}, matnr={}, batch={}", orderNo, matnrCode, batch, e);
                log.error("处理回传失败,orderNo={}, matnr={}, batch={}, lineId={}", orderNo, matnrCode, batch, lineId, e);
                errors.add("第" + (i + 1) + "行处理失败:" + e.getMessage());
            }
        }
        // 循环结束后,每个受影响订单只刷新一次状态
        for (String orderNo : affectedOrderNos) {
            refreshOrderFinishStatus(orderNo);
        for (String no : affectedOrderNos) {
            refreshOrderFinishStatus(no);
        }
        // 反映射后组装ReportParams,参照旧链路上报ERP(一次性整单上报)
        // 组装ReportParams上报ERP(一次性整单上报)
        try {
            ReportParams erpReportParams = buildReportParamsFromMappedData(mappedRoot, mappedDataArray);
            ReportParams erpReportParams = buildReportParamsFromOrderData(orderNo, poCode, orderType, wkType, editUser, editDate, mappedDataArray);
            CommonResponse erpResp = wmsErpService.reportOrders(erpReportParams);
            if (Objects.nonNull(erpResp) && Objects.equals(erpResp.getCode(), 200)) {
                log.info("反映射上报ERP成功,orderNo={}", pickString(root, "orderNo", "WMSNO"));
                log.info("上报ERP成功,orderNo={}", orderNo);
            } else {
                String msg = Objects.isNull(erpResp) ? "ERP响应为空" : erpResp.getMsg();
                errors.add("上报ERP失败:" + msg);
            }
        } catch (Exception e) {
            log.error("反映射上报ERP异常", e);
            log.error("上报ERP异常", e);
            errors.add("上报ERP异常:" + e.getMessage());
        }
@@ -527,39 +552,70 @@
    }
    /**
     * 根据反映射后的数据组装ReportParams,参照旧链路wmsErpService.reportOrders格式上报ERP
     * 根据Order格式数据组装ReportParams,参照旧链路wmsErpService.reportOrders格式上报ERP
     */
    private ReportParams buildReportParamsFromMappedData(JSONObject root, JSONArray dataArray) {
        String orderType = pickString(root, "OrderType", "orderType", "type", "wkType");
    private ReportParams buildReportParamsFromOrderData(String orderNo, String poCode, String type, String wkType,
                                                         String editUser, Date editDate, JSONArray dataArray) {
        // 根据wkType/type确定ERP上报的OrderType
        String erpOrderType = resolveErpOrderType(type, wkType);
        List<ReportDataParam> reportDataList = new ArrayList<>();
        for (int i = 0; i < dataArray.size(); i++) {
            JSONObject row = dataArray.getJSONObject(i);
            ReportDataParam dataParam = new ReportDataParam()
                    .setWMSNO(pickString(row, "WMSNO", "wmsNo", "orderNo", "OrderNo"))
                    .setPONO(pickString(row, "PONO", "poNo"))
                    .setOrderNO(pickString(row, "OrderNO", "orderNo"))
                    .setGoodsNO(pickString(row, "GoodsNO", "goodsNo", "batch", "Batch"))
                    .setItemCode(pickString(row, "ItemCode", "itemCode", "matNr", "MatNr"))
                    .setEditUser(pickString(row, "EditUser", "editUser"))
                    .setMemoDtl(pickString(row, "MemoDtl", "memoDtl", "memo"));
            String matnrCode = pickString(row, "matNr", "MatNr", "ItemCode", "itemCode");
            String batch = pickString(row, "batch", "Batch", "GoodsNO", "goodsNo");
            BigDecimal doneQty = pickDecimal(row, "doneQty", "anfme", "qty", "InQty", "inQty", "OutQty", "outQty");
            BigDecimal inQty = pickDecimal(row, "InQty", "inQty");
            BigDecimal outQty = pickDecimal(row, "OutQty", "outQty");
            if (Objects.nonNull(inQty) && inQty.compareTo(ZERO) > 0) {
                dataParam.setInQty(inQty.doubleValue());
            }
            if (Objects.nonNull(outQty) && outQty.compareTo(ZERO) > 0) {
                dataParam.setOutQty(outQty.doubleValue());
            ReportDataParam dataParam = new ReportDataParam()
                    .setWMSNO(orderNo)
                    .setPONO(poCode)
                    .setOrderNO(orderNo)
                    .setGoodsNO(batch)
                    .setItemCode(matnrCode)
                    .setEditUser(editUser)
                    .setEditDate(editDate)
                    .setMemoDtl(pickString(row, "memo", "MemoDtl"));
            // 入库类型设InQty,出库类型设OutQty
            if ("in".equalsIgnoreCase(type)) {
                if (doneQty.compareTo(ZERO) > 0) {
                    dataParam.setInQty(doneQty.doubleValue());
                }
            } else {
                if (doneQty.compareTo(ZERO) > 0) {
                    dataParam.setOutQty(doneQty.doubleValue());
                }
            }
            reportDataList.add(dataParam);
        }
        return new ReportParams()
                .setOrderType(orderType)
                .setOrderType(erpOrderType)
                .setAction("Update")
                .setData(reportDataList);
    }
    /**
     * 根据type和wkType解析ERP上报的订单类型
     */
    private String resolveErpOrderType(String type, String wkType) {
        if (StringUtils.isBlank(wkType)) {
            return "in".equalsIgnoreCase(type) ? "PO_Instock" : "WO_Outstock";
        }
        switch (wkType) {
            case "PUR_ReceiveBill": return "PO_Instock";
            case "PUR_MRB": return "PR_Outstock";
            case "PRD_ReturnMtrl": return "WR_Instock";
            case "PRD_PickMtrl": return "WO_Outstock";
            case "PRD_FeedMtrl": return "WR_Instock_BL";
            case "PRD_INSTOCK":
            case "PRD_MORPT": return "WO_Outstock";
            case "STK_MISCELLANEOUS": return "In_Instock";
            case "STK_MisDelivery": return "Io_Outstock";
            default:
                return "in".equalsIgnoreCase(type) ? "PO_Instock" : "WO_Outstock";
        }
    }
    private OpenApiOrder saveOpenApiOrder(Order order) {
@@ -760,7 +816,7 @@
        return event;
    }
    private int allocateMergedDoneQty(String orderNo, String matnrCode, String batch, BigDecimal mergedDoneQty, Map<String, OpenApiOrder> orderCache) {
    private int allocateMergedDoneQty(String orderNo, String matnrCode, String batch, String lineId, BigDecimal mergedDoneQty, Map<String, OpenApiOrder> orderCache) {
        OpenApiOrder order = orderCache.computeIfAbsent(orderNo, k ->
                openApiOrderMapper.selectOne(new LambdaQueryWrapper<OpenApiOrder>()
                        .eq(OpenApiOrder::getCode, k)
@@ -771,13 +827,32 @@
        LambdaQueryWrapper<OpenApiOrderItemMap> queryWrapper = new LambdaQueryWrapper<OpenApiOrderItemMap>()
                .eq(OpenApiOrderItemMap::getOrderId, order.getId())
                .eq(OpenApiOrderItemMap::getMergeMatnrCode, defaultString(matnrCode))
                .orderByAsc(OpenApiOrderItemMap::getSeqNo)
                .orderByAsc(OpenApiOrderItemMap::getId);
        if (StringUtils.isNotBlank(batch)) {
            queryWrapper.eq(OpenApiOrderItemMap::getMergeBatch, batch);
        // 优先用lineId精确匹配原始行
        if (StringUtils.isNotBlank(lineId)) {
            queryWrapper.eq(OpenApiOrderItemMap::getSourceLineId, lineId);
        } else {
            queryWrapper.eq(OpenApiOrderItemMap::getMergeMatnrCode, defaultString(matnrCode));
            if (StringUtils.isNotBlank(batch)) {
                queryWrapper.eq(OpenApiOrderItemMap::getMergeBatch, batch);
            }
        }
        List<OpenApiOrderItemMap> mappingRows = openApiOrderItemMapMapper.selectList(queryWrapper);
        if (mappingRows.isEmpty() && StringUtils.isNotBlank(lineId)) {
            // lineId匹配不到时,回退到matNr+batch匹配
            queryWrapper = new LambdaQueryWrapper<OpenApiOrderItemMap>()
                    .eq(OpenApiOrderItemMap::getOrderId, order.getId())
                    .eq(OpenApiOrderItemMap::getMergeMatnrCode, defaultString(matnrCode))
                    .orderByAsc(OpenApiOrderItemMap::getSeqNo)
                    .orderByAsc(OpenApiOrderItemMap::getId);
            if (StringUtils.isNotBlank(batch)) {
                queryWrapper.eq(OpenApiOrderItemMap::getMergeBatch, batch);
            }
            mappingRows = openApiOrderItemMapMapper.selectList(queryWrapper);
        }
        if (mappingRows.isEmpty()) {
            throw new CoolException("未找到映射关系,订单:" + orderNo + ",物料:" + matnrCode);
        }