1
2 天以前 db06b944e7886832d20b8e3ae62b2cb70bcba30f
lsh#订单完结
8个文件已修改
236 ■■■■■ 已修改文件
asrs-schedule/src/main/java/com/vincent/rsf/schedule/api/entity/constant/RcsConstant.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
asrs-schedule/src/main/java/com/vincent/rsf/schedule/api/service/ReportMsgService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
asrs-schedule/src/main/java/com/vincent/rsf/schedule/api/service/impl/ReportMsgServiceImpl.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
asrs-schedule/src/main/java/com/vincent/rsf/schedule/schedules/AsnOrderLogSchedule.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/OpenApiOrder.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/ErpReportService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
asrs-schedule/src/main/java/com/vincent/rsf/schedule/api/entity/constant/RcsConstant.java
@@ -12,6 +12,9 @@
    public static String REPORT_ORDERS = "/rsf-open-api/erp/report/order";
    public static String REPORT_ORDERS_NEW = "/rsf-open-api/erp/report/order/new";
    //触发ERP上报
    public static String REPORT_ORDERS_ERP = "/rsf-open-api/erp/report/order/erp";
    public static String REPORT_TASKS = "/rsf-open-api/mes/reportTaskExecute";
    //盘点库存修改
asrs-schedule/src/main/java/com/vincent/rsf/schedule/api/service/ReportMsgService.java
@@ -22,6 +22,8 @@
    R uploadReportOrdersNew(JSONObject orderData);
    R triggerErpReport();
    R uploadCheckOrder(ReportParams params);
}
asrs-schedule/src/main/java/com/vincent/rsf/schedule/api/service/impl/ReportMsgServiceImpl.java
@@ -261,6 +261,39 @@
    }
    /**
     * 触发rsf-open-api上报ERP
     * @return com.vincent.rsf.framework.common.R
     */
    @Override
    public R triggerErpReport() {
        String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.REPORT_ORDERS_ERP;
        log.info("触发ERP上报:{}", rcsUrl);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("api-version", "v2.0");
        HttpEntity httpEntity = new HttpEntity(headers);
        try {
            ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class);
            log.info("触发ERP上报,返回结果: {}", exchange);
            if (Objects.isNull(exchange.getBody())) {
                return R.ok("无返回结果");
            }
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.coercionConfigDefaults().setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
            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 (Exception e) {
            log.error("触发ERP上报失败", e);
            return R.ok("触发ERP上报失败:" + e.getMessage());
        }
    }
    /**
     * 盘点库存数据修改
     * @param params
     * @return
asrs-schedule/src/main/java/com/vincent/rsf/schedule/schedules/AsnOrderLogSchedule.java
@@ -108,6 +108,21 @@
    /**
     * @author Ryan
     * @date 2025/10/28
     * @description: 定时触发rsf-open-api上报ERP
     * @version 1.0
     */
    @Scheduled(cron = "0/30 * * * * ?  ")
    public void reportToErp() {
        try {
            reportMsgService.triggerErpReport();
        } catch (Exception e) {
            log.error("触发ERP上报失败", e);
        }
    }
    /**
     * @author Ryan
     * @date 2025/10/28
     * @description: 上传已完成订单至ERP平台(新链路,Order格式)
     * @version 1.0
     */
@@ -163,7 +178,7 @@
            try {
                R reported = reportMsgService.uploadReportOrdersNew(orderData);
                if (reported.get("code").equals("200")) {
                if (reported.get("code").equals(200)) {
                    order.setNtyStatus(OrderReportStatus.ORDER_REPORT_STATUS_ALL.val);
                    asnOrderService.updateById(order);
                }
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java
@@ -195,6 +195,12 @@
        return erpReportService.reportOrderNew(objParams);
    }
    @ApiOperation("订单ERP上报")
    @PostMapping("/report/order/erp")
    public CommonResponse reportOrdersToErp() {
        return erpReportService.reportOrdersToErp();
    }
    @ApiOperation("入/出库任务通知单取消")
    @PostMapping("/order/cancel")
    public CommonResponse cancelOrder(@RequestBody Object objParams) {
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/OpenApiOrder.java
@@ -42,6 +42,9 @@
    @TableField("exce_status")
    private Integer exceStatus;
    @TableField("nty_status")
    private Integer ntyStatus;
    private Integer status;
    @TableField("business_time")
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/ErpReportService.java
@@ -22,4 +22,6 @@
    CommonResponse reportInOrOutBound(Object params);
    CommonResponse reportOrderNew(Object params);
    CommonResponse reportOrdersToErp();
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java
@@ -14,7 +14,6 @@
import com.vincent.rsf.openApi.entity.app.OpenApiOrder;
import com.vincent.rsf.openApi.entity.app.OpenApiOrderItem;
import com.vincent.rsf.openApi.entity.app.OpenApiOrderItemMap;
import com.vincent.rsf.openApi.entity.app.OpenApiOrderReportEvent;
import com.vincent.rsf.openApi.entity.constant.WmsConstant;
import com.vincent.rsf.openApi.entity.dto.CommonResponse;
import com.vincent.rsf.openApi.entity.phyz.*;
@@ -23,7 +22,6 @@
import com.vincent.rsf.openApi.mapper.OpenApiOrderItemMapMapper;
import com.vincent.rsf.openApi.mapper.OpenApiOrderItemMapper;
import com.vincent.rsf.openApi.mapper.OpenApiOrderMapper;
import com.vincent.rsf.openApi.mapper.OpenApiOrderReportEventMapper;
import com.vincent.rsf.openApi.service.WmsErpService;
import com.vincent.rsf.openApi.service.phyz.ErpReportService;
import com.vincent.rsf.openApi.utils.ParamsMapUtils;
@@ -83,8 +81,6 @@
    private OpenApiOrderItemMapper openApiOrderItemMapper;
    @Resource
    private OpenApiOrderItemMapMapper openApiOrderItemMapMapper;
    @Resource
    private OpenApiOrderReportEventMapper openApiOrderReportEventMapper;
    private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
@@ -442,22 +438,10 @@
        }
        JSONObject root = JSONObject.parseObject(JSON.toJSONString(params));
        String eventId = pickString(root, "eventId", "EventId");
        String taskNo = pickString(root, "taskNo", "TaskNo");
        String reportNo = pickString(root, "reportNo", "ReportNo");
        if (StringUtils.isBlank(eventId) && StringUtils.isBlank(taskNo) && StringUtils.isBlank(reportNo)) {
            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, orderNo, root.toJSONString());
        // 解析明细:优先取 orderItems(Order格式),兼容 Data/data(ReportParams格式)
        JSONArray dataArray = pickArray(root, "orderItems", "orderItems");
@@ -478,17 +462,6 @@
            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;
        List<String> errors = new ArrayList<>();
        Set<String> affectedOrderNos = new HashSet<>();
@@ -496,8 +469,6 @@
        for (int i = 0; i < mappedDataArray.size(); i++) {
            JSONObject row = mappedDataArray.getJSONObject(i);
            // 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");
@@ -523,28 +494,9 @@
            refreshOrderFinishStatus(no);
        }
        // 组装ReportParams上报ERP(一次性整单上报)
        try {
            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={}", orderNo);
            } else {
                String msg = Objects.isNull(erpResp) ? "ERP响应为空" : erpResp.getMsg();
                errors.add("上报ERP失败:" + msg);
            }
        } catch (Exception e) {
            log.error("上报ERP异常", e);
            errors.add("上报ERP异常:" + e.getMessage());
        }
        if (!errors.isEmpty()) {
            reportEvent.setStatus(2);
            openApiOrderReportEventMapper.updateById(reportEvent);
            return CommonResponse.error("处理完成,但存在异常:" + String.join(" | ", errors));
        }
        reportEvent.setStatus(1);
        openApiOrderReportEventMapper.updateById(reportEvent);
        Map<String, Object> result = new HashMap<>();
        result.put("allocatedCount", allocateCount);
@@ -552,39 +504,88 @@
    }
    /**
     * 根据Order格式数据组装ReportParams,参照旧链路wmsErpService.reportOrders格式上报ERP
     * 查询已完成但未上报ERP的订单,组装ReportParams上报ERP
     * 由asrs-schedule定时任务触发
     */
    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);
    @Override
    public CommonResponse reportOrdersToErp() {
        List<OpenApiOrder> pendingOrders = openApiOrderMapper.selectList(new LambdaQueryWrapper<OpenApiOrder>()
                .eq(OpenApiOrder::getExceStatus, 2)
                .eq(OpenApiOrder::getNtyStatus, 0)
                .last("limit 50"));
        if (pendingOrders.isEmpty()) {
            return CommonResponse.ok("无待上报ERP的订单");
        }
        List<String> errors = new ArrayList<>();
        int successCount = 0;
        for (OpenApiOrder order : pendingOrders) {
            try {
                List<OpenApiOrderItem> items = openApiOrderItemMapper.selectList(new LambdaQueryWrapper<OpenApiOrderItem>()
                        .eq(OpenApiOrderItem::getOrderId, order.getId()));
                if (items.isEmpty()) {
                    log.warn("订单无明细,跳过上报ERP,orderNo={}", order.getCode());
                    continue;
                }
                ReportParams erpParams = buildReportParams(order, items);
                CommonResponse erpResp = wmsErpService.reportOrders(erpParams);
                if (Objects.nonNull(erpResp) && Objects.equals(erpResp.getCode(), 200)) {
                    order.setNtyStatus(1);
                    openApiOrderMapper.updateById(order);
                    successCount++;
                    log.info("上报ERP成功,orderNo={}", order.getCode());
                } else {
                    String msg = Objects.isNull(erpResp) ? "ERP响应为空" : erpResp.getMsg();
                    log.warn("上报ERP失败,orderNo={},msg={}", order.getCode(), msg);
                    errors.add(order.getCode() + ":" + msg);
                }
            } catch (Exception e) {
                log.error("上报ERP异常,orderNo={}", order.getCode(), e);
                errors.add(order.getCode() + ":" + e.getMessage());
            }
        }
        if (!errors.isEmpty()) {
            Map<String, Object> result = new HashMap<>();
            result.put("successCount", successCount);
            result.put("totalCount", pendingOrders.size());
            return CommonResponse.error("部分订单上报ERP失败:" + String.join(" | ", errors));
        }
        Map<String, Object> result = new HashMap<>();
        result.put("successCount", successCount);
        result.put("totalCount", pendingOrders.size());
        return CommonResponse.ok(result);
    }
    /**
     * 从OpenApiOrder+OpenApiOrderItem组装ReportParams
     */
    private ReportParams buildReportParams(OpenApiOrder order, List<OpenApiOrderItem> items) {
        String erpOrderType = resolveErpOrderType(order.getType(), order.getWkType());
        List<ReportDataParam> reportDataList = new ArrayList<>();
        for (int i = 0; i < dataArray.size(); i++) {
            JSONObject row = dataArray.getJSONObject(i);
            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");
        for (OpenApiOrderItem item : items) {
            BigDecimal qty = defaultQty(item.getQty());
            if (qty.compareTo(ZERO) <= 0) {
                continue;
            }
            ReportDataParam dataParam = new ReportDataParam()
                    .setWMSNO(orderNo)
                    .setPONO(poCode)
                    .setOrderNO(orderNo)
                    .setGoodsNO(batch)
                    .setItemCode(matnrCode)
                    .setEditUser(editUser)
                    .setEditDate(editDate)
                    .setMemoDtl(pickString(row, "memo", "MemoDtl"));
                    .setWMSNO(order.getCode())
                    .setPONO(order.getPoCode())
                    .setOrderNO(order.getCode())
                    .setGoodsNO(item.getBatch())
                    .setItemCode(item.getMatnrCode())
                    .setEditUser("schedule")
                    .setEditDate(new Date())
                    .setMemoDtl(item.getMemo());
            // 入库类型设InQty,出库类型设OutQty
            if ("in".equalsIgnoreCase(type)) {
                if (doneQty.compareTo(ZERO) > 0) {
                    dataParam.setInQty(doneQty.doubleValue());
                }
            if ("in".equalsIgnoreCase(order.getType())) {
                dataParam.setInQty(qty.doubleValue());
            } else {
                if (doneQty.compareTo(ZERO) > 0) {
                    dataParam.setOutQty(doneQty.doubleValue());
                }
                dataParam.setOutQty(qty.doubleValue());
            }
            reportDataList.add(dataParam);
@@ -633,6 +634,7 @@
                .setQty(ZERO)
                .setWorkQty(ZERO)
                .setExceStatus(0)
                .setNtyStatus(0)
                .setStatus(1)
                .setBusinessTime(businessTime)
                .setOrderInternalCode(order.getOrderInternalCode())
@@ -794,26 +796,6 @@
        }
        merged.setOrderItems(mergedItems);
        return merged;
    }
    private boolean existsReportEvent(String eventId, String taskNo, String reportNo) {
        return openApiOrderReportEventMapper.selectCount(new LambdaQueryWrapper<OpenApiOrderReportEvent>()
                .eq(OpenApiOrderReportEvent::getEventId, defaultString(eventId))
                .eq(OpenApiOrderReportEvent::getTaskNo, defaultString(taskNo))
                .eq(OpenApiOrderReportEvent::getReportNo, defaultString(reportNo))
                .eq(OpenApiOrderReportEvent::getStatus, 1)) > 0;
    }
    private OpenApiOrderReportEvent saveReportEvent(String eventId, String taskNo, String reportNo, String orderCode, String payload) {
        OpenApiOrderReportEvent event = new OpenApiOrderReportEvent()
                .setEventId(defaultString(eventId))
                .setTaskNo(defaultString(taskNo))
                .setReportNo(defaultString(reportNo))
                .setOrderCode(orderCode)
                .setPayload(payload)
                .setStatus(0);
        openApiOrderReportEventMapper.insert(event);
        return event;
    }
    private int allocateMergedDoneQty(String orderNo, String matnrCode, String batch, String lineId, BigDecimal mergedDoneQty, Map<String, OpenApiOrder> orderCache) {