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()); } } }