lbq
2 天以前 1f2ab3faafd2c0e06d4aceb4bf5a815235a32608
实现mes和erp推送数据功能,mes已初测,erp待测试
8个文件已添加
6个文件已修改
833 ■■■■■ 已修改文件
rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/PlatformProperties.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/MESController.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/PhyzMesConstant.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/ErpReportParams.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MesReportList.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MesReportOne.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Pallet.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Station.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/TaskResult.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/ErpReportService.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/MesReportService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/MesReportServiceImpl.java 296 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/PlatformProperties.java
@@ -1,6 +1,7 @@
package com.vincent.rsf.openApi.config;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@@ -63,6 +64,9 @@
        /**rcs调用端口**/
        private String port;
        public String getErpUrl() {
            return host + (StringUtils.isBlank(port) ? "" : ":" + port + "/");
        }
    }
@@ -95,5 +99,23 @@
    }
    @Data
    @Configuration
    @ConfigurationProperties(prefix = "platform.mes")
    public class MesApi {
        /**
         * mes调用路径
         */
        private String host;
        /**mes调用端口**/
        private String port;
        public String getMesUrl() {
            return host + (StringUtils.isBlank(port) ? "" : ":" + port + "/");
        }
    }
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java
@@ -6,6 +6,7 @@
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.openApi.entity.dto.CommonResponse;
import com.vincent.rsf.openApi.entity.phyz.*;
import com.vincent.rsf.openApi.service.phyz.ErpReportService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -15,6 +16,7 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
@@ -25,6 +27,10 @@
@Api("银座新工厂(五期)ERP接口")
@Slf4j
public class ERPController {
    @Resource
    private ErpReportService erpReportService;
    @ApiOperation("仓库信息同步")
    @PostMapping("/wareHouse/sync")
@@ -281,4 +287,28 @@
        return new JSONArray();
    }
    // region 测试推送功能
    @ApiOperation("登录")
    @PostMapping("/loginBySign")
    public CommonResponse loginBySign(@RequestBody Object objParams) {
        try {
            erpReportService.loginBySign();
        } catch (Exception e) {
            log.error("erp, loginBySign", e);
        }
        return CommonResponse.ok();
    }
    @ApiOperation("入/出库任务回调")
    @PostMapping("/reportInOrOutBound")
    public CommonResponse reportInOrOutBound(@RequestBody Object objParams) {
        try {
            erpReportService.reportInOrOutBound(objParams);
        } catch (Exception e) {
            log.error("erp, reportInOrOutBound", e);
        }
        return CommonResponse.ok();
    }
    // endregion
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/MESController.java
@@ -5,23 +5,31 @@
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.openApi.entity.dto.CommonResponse;
import com.vincent.rsf.openApi.entity.phyz.*;
import com.vincent.rsf.openApi.service.phyz.MesReportService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import static com.vincent.rsf.openApi.controller.AuthController.SIMULATED_DATA_ENABLE;
import static com.vincent.rsf.openApi.controller.phyz.ERPController.paramsFormat;
@Slf4j
@RestController
@RequestMapping("/mes")
@Api("银座新工厂(五期)MES接口")
public class MESController {
    @Resource
    private MesReportService mesReportService;
    @ApiOperation("备料通知")
    @PostMapping("/callMaterial")
@@ -91,4 +99,39 @@
        return CommonResponse.ok();
    }
    // region 测试推送功能
    @ApiOperation("托盘信息同步")
    @PostMapping("/syncPalletInfo")
    public CommonResponse syncPalletInfo(@RequestBody Object objParams) {
        try {
            return mesReportService.syncPalletInfo(objParams);
        } catch (Exception e) {
            log.error("mes, syncPalletInfo", e);
        }
        return CommonResponse.ok();
    }
    @ApiOperation("站点信息同步")
    @PostMapping("/syncStationInfo")
    public CommonResponse syncStationInfo(@RequestBody Object objParams) {
        try {
            return mesReportService.syncStationInfo(objParams);
        } catch (Exception e) {
            log.error("mes, syncStationInfo", e);
        }
        return CommonResponse.ok();
    }
    @ApiOperation("AGV任务回调")
    @PostMapping("/reportTaskExecute")
    public CommonResponse reportTaskExecute(@RequestBody Object objParams) {
        try {
            return mesReportService.reportTaskExecute(objParams);
        } catch (Exception e) {
            log.error("mes, reportTaskExecute", e);
        }
        return CommonResponse.ok();
    }
    // endregion
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/PhyzMesConstant.java
New file
@@ -0,0 +1,20 @@
package com.vincent.rsf.openApi.entity.constant;
public class PhyzMesConstant {
    // 获取token
    public static final String TOKEN = "/sit-auth/OAuth/Token";
    // token登录用户
    public static final String TOKEN_USER = "thirdparty";
    // token登录密码
    public static final String TOKEN_PASSWORD = "thirdparty";
    // 托盘信息同步
    public static final String PALLET_SYN = "/sit-svc/GINZA/Application/BasicDataApp/odata/API_PalletSyncCmd";
    // 站点信息同步
    public static final String STATION_SYN = "/sit-svc/GINZA/Application/LogisticsApp/odata/API_ConnPortSyncCmd";
    // 任务上报
    public static final String TASK_EXECUTE_REPORT = "/sit-svc/GINZA/Application/LogisticsApp/odata/API_PalletStatus";
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/ErpReportParams.java
New file
@@ -0,0 +1,105 @@
package com.vincent.rsf.openApi.entity.phyz;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(value = "ErpReportParams", description = "Erp入/出库上报")
public class ErpReportParams {
//    // 表单id
//    @JsonProperty("formid")
//    private String formid = "ke3d5adc0bbe64eceafd5891400adf40e";
//
//    // 表单数据包
//    @JsonProperty("Model")
//    private List<Object> Model;
    // 日期
    @JsonProperty("FDate")
    private String FDate;
    // 单据类型,
    // "A":生产领料单
    //“B”:生产补料单
    //“C”:生产退料单
    //“D”生产入库单
    @JsonProperty("F_OHDL_BillnoType")
    private String F_OHDL_BillnoType;
    // 库存组织{"FNumber": "100"}
    @JsonProperty("FStockOrgId")
    private Object FStockOrgId;
    // 生产组织{"FNumber": "200"}
    @JsonProperty("FPrdOrgId")
    private Object FPrdOrgId;
    // 生产计划号
    @JsonProperty("F_OHDL_ProPlan")
    private String F_OHDL_ProPlan;
    // 单据体明细数据包
    @JsonProperty("FEntity")
    private List<FEntityItem> FEntity;
    // 单据体明细
    public static class FEntityItem {
        // 物料信息{"FNumber": ""}
        @JsonProperty("FMaterialId")
        private Object FMaterialId;
        // 计量单位信息{"FNumber": "PCS"}
        @JsonProperty("FUnitID")
        private Object FUnitID;
        // 申请数量
        @JsonProperty("FAppQty")
        private Double FAppQty;
        // 实际数量
        @JsonProperty("FActualQty")
        private Double FActualQty;
        // 仓库信息{"FNumber": ""}
        @JsonProperty("FStockId")
        private Object FStockId;
        // 车间{"FNumber": ""}
        @JsonProperty("F_OHDL_BworkShop")
        private Object F_OHDL_BworkShop;
        // 托盘
        @JsonProperty("F_OHDL_Pallet")
        private String F_OHDL_Pallet;
        // 接驳点位
        @JsonProperty("F_OHDL_ConPoint")
        private String F_OHDL_ConPoint;
        // 计划跟踪号
        @JsonProperty("F_OHDL_PlanNo")
        private String F_OHDL_PlanNo;
        // 生产订单编号
        @JsonProperty("F_OHDL_MONo")
        private String F_OHDL_MONo;
        // 生产订单ID
        @JsonProperty("F_OHDL_MOId")
        private String F_OHDL_MOId;
        // 生产订单行号
        @JsonProperty("F_OHDL_MOLine")
        private String F_OHDL_MOLine;
        // 生产订单行ID
        @JsonProperty("F_OHDL_MOLineId")
        private String F_OHDL_MOLineId;
    }
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MesReportList.java
New file
@@ -0,0 +1,54 @@
package com.vincent.rsf.openApi.entity.phyz;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
 * 托盘信息同步请求
 * @author System
 * @date 2026-01-10
 */
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@Accessors(chain = true)
@ApiModel(value = "MesReportList", description = "信息同步")
public class MesReportList<T> {
    /**
     * 命令对象,包含托盘数据列表
     */
    @NotNull(message = "command不能为空")
    @Valid
    @JsonProperty("command")
    @ApiModelProperty(value = "命令对象", required = true)
    private CommandData<T> command;
    /**
     * 命令数据对象
     */
    @Data
    @JsonIgnoreProperties(ignoreUnknown = true)
    @Accessors(chain = true)
    @ApiModel(value = "CommandData", description = "命令数据")
    public static class CommandData<T> {
        /**
         * 数据列表
         */
        @NotNull(message = "DataList不能为空")
        @Valid
        @JsonProperty("DataList")
        @JSONField(name = "DataList")
        @ApiModelProperty(value = "数据列表", required = true)
        private List<T> dataList;
    }
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MesReportOne.java
New file
@@ -0,0 +1,32 @@
package com.vincent.rsf.openApi.entity.phyz;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
/**
 * 托盘信息同步请求
 * @author System
 * @date 2026-01-10
 */
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@Accessors(chain = true)
@ApiModel(value = "MesReportOne", description = "信息同步请求")
public class MesReportOne<T> {
    /**
     * 命令对象
     */
    @NotNull(message = "command不能为空")
    @Valid
    @JsonProperty("command")
    @ApiModelProperty(value = "命令对象", required = true)
    private T command;
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Pallet.java
@@ -1,5 +1,6 @@
package com.vincent.rsf.openApi.entity.phyz;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
@@ -17,20 +18,26 @@
    // 托盘条码
    @NotNull
    @JsonProperty("BarCode")
    @JSONField(name = "BarCode")
    private String barCode;
    // 托盘编码
    @JsonProperty("PalletCode")
    @JSONField(name = "PalletCode")
    private String palletCode;
    // 托盘名称
    @JsonProperty("PalletName")
    @JSONField(name = "PalletName")
    private String palletName;
    // 托盘类型编码
    @JsonProperty("PalletTypeCode")
    @JSONField(name = "PalletTypeCode")
    private String palletTypeCode;
    // 托盘类型
    @JsonProperty("PalletTypeName")
    @JSONField(name = "palletTypeName")
    private String palletTypeName;
    // 创建人
    @JsonProperty("CreatedBy")
    @JSONField(name = "CreatedBy")
    private String createdBy;
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Station.java
@@ -1,5 +1,6 @@
package com.vincent.rsf.openApi.entity.phyz;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
@@ -17,23 +18,30 @@
    // 接驳口编码
    @NotNull
    @JsonProperty("ConnPortCode")
    @JSONField(name = "ConnPortCode")
    private String connPortCode;
    // 接驳口名称
    @JsonProperty("ConnPortName")
    @JSONField(name = "ConnPortName")
    private String connPortName;
    // 车间编码
    @JsonProperty("WorkshopCode")
    @JSONField(name = "WorkshopCode")
    private String workshopCode;
    // 车间
    @JsonProperty("WorkshopName")
    @JSONField(name = "WorkshopName")
    private String workshopName;
    // 仓库编码
    @JsonProperty("ProductionLineCode")
    @JSONField(name = "ProductionLineCode")
    private String productionLineCode;
    // 仓库
    @JsonProperty("ProductionLineName")
    @JSONField(name = "ProductionLineName")
    private String productionLineName;
    // 创建人
    @JsonProperty("CreatedBy")
    @JSONField(name = "CreatedBy")
    private String createdBy;
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/TaskResult.java
@@ -1,5 +1,6 @@
package com.vincent.rsf.openApi.entity.phyz;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
@@ -17,8 +18,10 @@
    // 任务号,唯一标识
    @NotNull
    @JsonProperty("TaskNo")
    @JSONField(name = "TaskNo")
    private String taskNo;
    // 状态
    // 状态 //1搬离 2完成 9取消
    @JsonProperty("Status")
    private String status;
    @JSONField(name = "Status")
    private Integer status;
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/ErpReportService.java
New file
@@ -0,0 +1,13 @@
package com.vincent.rsf.openApi.service.phyz;
import com.vincent.rsf.openApi.entity.dto.CommonResponse;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
public interface ErpReportService {
    void loginBySign() throws UnsupportedEncodingException, NoSuchAlgorithmException;
    CommonResponse reportInOrOutBound(Object params);
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/MesReportService.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.openApi.service.phyz;
import com.vincent.rsf.openApi.entity.dto.CommonResponse;
public interface MesReportService {
    CommonResponse syncPalletInfo(Object params);
    CommonResponse syncStationInfo(Object params);
    CommonResponse reportTaskExecute(Object params);
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/ErpReportServiceImpl.java
New file
@@ -0,0 +1,184 @@
package com.vincent.rsf.openApi.service.phyz.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.openApi.config.PlatformProperties;
import com.vincent.rsf.openApi.entity.dto.CommonResponse;
import com.vincent.rsf.openApi.entity.phyz.ErpReportParams;
import com.vincent.rsf.openApi.service.phyz.ErpReportService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Objects;
@Slf4j
@Service
public class ErpReportServiceImpl implements ErpReportService {
    private static String ERP_REPORT_URL;
    @Resource
    private PlatformProperties.ErpApi erpApi;
    @Resource
    private RestTemplate restTemplate;
    @PostConstruct
    public void init() {
        ERP_REPORT_URL = erpApi.getErpUrl();
    }
    // 登录参数依次为账套ID、用户名、应用ID、时间戳、签名信息、语言ID
    public void loginBySign() throws UnsupportedEncodingException, NoSuchAlgorithmException {
        String url = ERP_REPORT_URL + "/Kingdee.BOS.WebApi.ServicesStub.AuthService.LoginBySign.common.kdsvc";
        JSONObject params = new JSONObject();
        params.put("parameters", loginParams());
        JSONObject result = postRequest(url, params, false);
    }
    // 入/出库任务完成上报
    public CommonResponse reportInOrOutBound(Object params) {
        if (Objects.isNull(params)) {
            throw new CoolException("入/出库任务信息参数不能为空!!");
        }
        // TODO:参数转换
        ErpReportParams erpReportParams = new ErpReportParams();
        erpReportParams = (ErpReportParams) params;
        String erpUrl = ERP_REPORT_URL + "/Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Save.common.kdsvc";
        log.info("Erp入/出库任务完成上报: {}, 请求参数: {}", erpUrl, JSONObject.toJSONString(erpReportParams));
        try {
            JSONObject jsonObject = postRequest(erpUrl, erpReportParams, true);
            boolean sendSuccess = jsonObject.getJSONObject("Result").getJSONObject("ResponseStatus").getBoolean("IsSuccess");
            // TODO:转换后返回
            if (sendSuccess) {
                return CommonResponse.ok();
            } else {
                JSONArray errors = jsonObject.getJSONObject("Result").getJSONObject("ResponseStatus").getJSONArray("Errors");
                String errorMsg = "";
                for (int i = 0; i < errors.size(); i++) {
                    errorMsg += errors.getJSONObject(i).getString("Message") + " ";
                }
                return CommonResponse.error(errorMsg);
            }
        } catch (Exception e) {
            log.error("Erp入/出库任务上报响应失败", e);
            throw new CoolException("Erp解析响应失败:" + e.getMessage());
        }
    }
    // 其他入/出库主动上报
    // 盘点结果上报
    private Object[] loginParams() throws UnsupportedEncodingException, NoSuchAlgorithmException {
        //时间戳
        long timestamp = System.currentTimeMillis() / 1000;
        //数据中心ID
        String dbId = "69368c1051a322";
        //用户名称
        String userName = "楼坚伟";
        //第三方系统应用Id
        String appId = "330678_w2eBwdkp0oCW2XxuW16D4w/NRhTaTPKp";
        //第三方系统应用秘钥
        String appSecret = "31c9e5da6472456193e0c8a7dd2160d9";
        //将账套ID、用户名、应用ID、应用秘钥、时间戳 放到数组里面
        String[] arr = new String[] { dbId, userName, appId, appSecret, String.valueOf(timestamp) };
        //生成签名信息
        String sign = getSha256(arr);
        return new Object[] { dbId, userName, appId, String.valueOf(timestamp), sign,  2052};
    }
    public static String getSha256(String[] input) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchAlgorithmException {
        Arrays.sort(input);
        //SHA1加密的话改成MessageDigest.getInstance("SHA-1");
        MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
        for (String str : input) {
            sha256.update(str.getBytes("UTF-8"));
        }
        byte[] hashBytes = sha256.digest();
        StringBuilder hashString = new StringBuilder();
        for (byte b : hashBytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hashString.append('0');
            }
            hashString.append(hex);
        }
        return hashString.toString();
    }
    /**
     * 通用HTTP POST请求方法
     *
     * @param url 请求URL
     * @param params 请求参数
     * @param needToken 是否需要token认证
     * @return 响应结果
     */
    public JSONObject postRequest(String url, Object params, boolean needToken) {
        if (StringUtils.isBlank(url)) {
            throw new CoolException("请求URL不能为空!!");
        }
        if (Objects.isNull(params)) {
            throw new CoolException("请求参数不能为空!!");
        }
        log.info("Erp POST请求: {}, 请求参数: {}", url, JSONObject.toJSONString(params));
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json;charset=utf-8");
        if (needToken) {
//            String token = getToken();
//            headers.add("Authorization", "Bearer " + token);
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(params, headers);
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
        log.info("Erp POST请求响应结果: {}", exchange);
        if (Objects.isNull(exchange.getBody())) {
            throw new CoolException("请求失败!!");
        }
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.coercionConfigDefaults().setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
            return objectMapper.readValue(exchange.getBody(), JSONObject.class);
        } catch (JsonProcessingException e) {
            log.error("Erp解析响应失败", e);
            throw new CoolException("Erp解析响应失败:" + e.getMessage());
        }
    }
}
rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/phyz/impl/MesReportServiceImpl.java
New file
@@ -0,0 +1,296 @@
package com.vincent.rsf.openApi.service.phyz.impl;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.openApi.config.PlatformProperties;
import com.vincent.rsf.openApi.entity.constant.PhyzMesConstant;
import com.vincent.rsf.openApi.entity.dto.CommonResponse;
import com.vincent.rsf.openApi.entity.phyz.*;
import com.vincent.rsf.openApi.service.phyz.MesReportService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.http.client.ClientHttpResponse;
import java.util.Objects;
@Service
@Slf4j
public class MesReportServiceImpl implements MesReportService {
    private static String MES_REPORT_URL;
    @Resource
    private PlatformProperties.MesApi mesApi;
    @Resource
    private RestTemplate restTemplate;
    @PostConstruct
    public void init() {
        MES_REPORT_URL = mesApi.getMesUrl();
    }
    /**
     * 获取token
     *
     * @return token字符串
     */
    private String getToken() {
        String mesUrl = MES_REPORT_URL + PhyzMesConstant.TOKEN;
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.add("Content-Type", "application/x-www-form-urlencoded");
            // 准备表单数据
            MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
            map.add("grant_type", "password");
            map.add("username", PhyzMesConstant.TOKEN_USER);
            map.add("password", PhyzMesConstant.TOKEN_PASSWORD);
            map.add("client_id", "global");
            map.add("client_secret", "cs");
            HttpEntity<Object> httpEntity = new HttpEntity<>(map, headers);
            ResponseEntity<String> exchange = restTemplate.exchange(mesUrl, HttpMethod.POST, httpEntity, String.class);
            if (Objects.isNull(exchange.getBody())) {
                throw new CoolException("请求失败!!");
            }
            try {
                ObjectMapper objectMapper = new ObjectMapper();
                objectMapper.coercionConfigDefaults().setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
                JSONObject jsonObject = objectMapper.readValue(exchange.getBody(), JSONObject.class);
                return jsonObject.getString("access_token");
            } catch (JsonProcessingException e) {
                log.error("Mes解析响应失败", e);
                throw new CoolException("Mes解析响应失败:" + e.getMessage());
            }
        } catch (Exception e) {
            log.error("Mes解析token响应失败", e);
            return StringUtils.EMPTY;
        }
    }
    /**
     * 托盘信息同步
     *
     * @param params 托盘信息参数
     * @return 响应结果
     */
    public CommonResponse syncPalletInfo(Object params) {
        if (Objects.isNull(params)) {
            throw new CoolException("托盘信息参数不能为空!!");
        }
        // TODO: 参数转换
        List<Pallet> pallets = new ArrayList<>();
        // 将传入的 params 转换为 Pallet 对象列表
        if (params instanceof List) {
            for (Object obj : (List<?>) params) {
                if (obj instanceof Map) {
                    Map<String, Object> map = (Map<String, Object>) obj;
                    Pallet pallet = new Pallet();
                    pallet.setBarCode((String) map.get("BarCode"));
                    pallet.setPalletCode((String) map.get("PalletCode"));
                    pallet.setPalletName((String) map.get("PalletName"));
                    pallet.setPalletTypeCode((String) map.get("PalletTypeCode"));
                    pallet.setPalletTypeName((String) map.get("PalletTypeName"));
                    pallet.setCreatedBy((String) map.get("CreatedBy"));
                    pallets.add(pallet);
                } else if (obj instanceof Pallet) {
                    pallets.add((Pallet) obj);
                }
            }
        }
        MesReportList<Pallet> reportList = new MesReportList<>();
        reportList.setCommand(new MesReportList.CommandData<Pallet>().setDataList(pallets));
        String mesUrl = MES_REPORT_URL + PhyzMesConstant.PALLET_SYN;
        log.info("Mes托盘信息同步: {}, 请求参数: {}", mesUrl, JSONObject.toJSONString(reportList));
        try {
            JSONObject jsonObject = postRequest(mesUrl, reportList, getToken());
            boolean sendSuccess = jsonObject.getBoolean("Succeeded");
            if (sendSuccess) {
                return CommonResponse.ok();
            } else {
                // TODO:失败后记录重发
                return CommonResponse.error(jsonObject.getJSONObject("Error").getString("ErrorMessage"));
            }
        } catch (Exception e) {
            log.error("Mes解析托盘信息同步响应失败", e);
            throw new CoolException("Mes解析响应失败:" + e.getMessage());
        }
    }
    /**
     * 接驳口信息同步(站点信息同步)
     *
     * @param params 站点信息参数
     * @return 响应结果
     */
    public CommonResponse syncStationInfo(Object params) {
        if (Objects.isNull(params)) {
            throw new CoolException("站点信息参数不能为空!!");
        }
        // TODO:参数转换
        List<Station> stations = new ArrayList<>();
        if (params instanceof List) {
            for (Object obj : (List<?>) params) {
                if (obj instanceof Map) {
                    Map<String, Object> map = (Map<String, Object>) obj;
                    Station station = new Station();
                    station.setConnPortCode((String) map.get("ConnPortCode"));
                    station.setConnPortName((String) map.get("ConnPortName"));
                    station.setWorkshopCode((String) map.get("WorkshopCode"));
                    station.setWorkshopName((String) map.get("WorkshopName"));
                    station.setProductionLineCode((String) map.get("ProductionLineCode"));
                    station.setProductionLineName((String) map.get("ProductionLineName"));
                    station.setCreatedBy((String) map.get("CreatedBy"));
                    stations.add(station);
                } else if (obj instanceof Station) {
                    stations.add((Station) obj);
                }
            }
        }
        MesReportList<Station> reportList = new MesReportList<>();
        reportList.setCommand(new MesReportList.CommandData<Station>().setDataList(stations));
        String mesUrl = MES_REPORT_URL + PhyzMesConstant.STATION_SYN;
        log.info("Mes站点信息同步: {}, 请求参数: {}", mesUrl, JSONObject.toJSONString(reportList));
        try {
            JSONObject jsonObject = postRequest(mesUrl, reportList, getToken());
            boolean sendSuccess = jsonObject.getBoolean("Succeeded");
            if (sendSuccess) {
                return CommonResponse.ok();
            } else {
                // TODO:失败后记录重发
                return CommonResponse.error(jsonObject.getJSONObject("Error").getString("ErrorMessage"));
            }
        } catch (Exception e) {
            log.error("Mes解析站点信息同步响应失败", e);
            throw new CoolException("Mes解析响应失败:" + e.getMessage());
        }
    }
    /**
     * 托盘任务回调(任务执行上报)
     *
     * @param params 任务执行参数
     * @return 响应结果
     */
    public CommonResponse reportTaskExecute(Object params) {
        if (Objects.isNull(params)) {
            throw new CoolException("任务执行参数不能为空!!");
        }
        // TODO:参数转换
        TaskResult taskResult = new TaskResult();
        if (params instanceof TaskResult) {
            TaskResult task = (TaskResult) params;
            taskResult.setTaskNo(task.getTaskNo());
            taskResult.setStatus(task.getStatus());
        } else if (params instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) params;
            taskResult.setTaskNo((String) map.get("TaskNo"));
            taskResult.setStatus((Integer) map.get("Status"));
        }
        MesReportOne<TaskResult> reportOne = new MesReportOne<>();
        reportOne.setCommand(taskResult);
        String mesUrl = MES_REPORT_URL + PhyzMesConstant.TASK_EXECUTE_REPORT;
        log.info("Mes任务执行上报: {}, 请求参数: {}", mesUrl, JSONObject.toJSONString(reportOne));
        try {
            JSONObject jsonObject = postRequest(mesUrl, reportOne, getToken());
            boolean sendSuccess = jsonObject.getBoolean("Succeeded");
            if (sendSuccess) {
                return CommonResponse.ok();
            } else {
                // TODO:失败后记录重发
                return CommonResponse.error(jsonObject.getJSONObject("Error").getString("ErrorMessage"));
            }
        } catch (Exception e) {
            log.error("Mes解析任务执行上报响应失败", e);
            throw new CoolException("Mes解析响应失败:" + e.getMessage());
        }
    }
    /**
     * 通用HTTP POST请求方法
     *
     * @param url 请求URL
     * @param params 请求参数
     * @param token token
     * @return 响应结果
     */
    public JSONObject postRequest(String url, Object params, String token) {
        if (StringUtils.isBlank(url)) {
            throw new CoolException("请求URL不能为空!!");
        }
        if (Objects.isNull(params)) {
            throw new CoolException("请求参数不能为空!!");
        }
        log.info("Mes POST请求: {}, 请求参数: {}", url, JSONObject.toJSONString(params));
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json;charset=utf-8");    //
        headers.add("Authorization", "Bearer " + token);
        HttpEntity<Object> httpEntity = new HttpEntity<>(params, headers);
        try {
            // 创建自定义的ResponseErrorHandler来处理非200状态码
            RestTemplate customRestTemplate = new RestTemplate();
            customRestTemplate.setRequestFactory(restTemplate.getRequestFactory());
            // 设置错误处理器,不抛出异常,而是返回响应体
            customRestTemplate.setErrorHandler(new org.springframework.web.client.DefaultResponseErrorHandler() {
                @Override
                public boolean hasError(ClientHttpResponse response) throws IOException {
                    // 不管状态码如何,都不视为错误
                    return false;
                }
            });
            ResponseEntity<String> exchange = customRestTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
            log.info("Mes POST请求响应结果: {}", exchange);
            if (Objects.isNull(exchange.getBody()) || exchange.getBody().isEmpty()) {
                throw new CoolException("请求失败!!");
            }
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.coercionConfigDefaults().setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
            return objectMapper.readValue(exchange.getBody(), JSONObject.class);
        } catch (Exception e) { //JsonProcessingException
            log.error("Mes解析响应失败", e);
            throw new CoolException("Mes解析响应失败:" + e.getMessage());
        }
    }
}