解决工作问题---跟上游进行接口联调
1.以TCP方式进行通信
自己手写一个服务端:
public static void main(String[] args) { try { ServerSocket socket = new ServerSocket(8801); System.out.println("socket套接字创建成功,等待连接"); while (true){ final Socket con = socket.accept(); System.out.println("有客户端接入"); new Thread(new Runnable() { @Override public void run() { //获取输入流,用来读取数据 InputStream in; try { in = con.getInputStream(); int len; byte[] data = new byte[2048]; len = in.read(data); System.out.println("读到消息:" + new String(data,0,len)); OutputStream out = con.getOutputStream(); byte[] a = xml.getBytes(StandardCharsets.UTF_8); out.write(a); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } catch (IOException e) { e.printStackTrace(); } }
以XML格式进行对接则需要构建XML
@XmlRootElement(name="root") @XmlType(propOrder = {"header","event","body"}) public class Root { private Header header; private Event event; private Body body; @XmlElement public Header getHeader() { return header; } public void setHeader(Header header) { this.header = header; } @XmlElement public Event getEvent() { return event; } public void setEvent(Event event) { this.event = event; } @XmlElement public Body getBody() { return body; } public void setBody(Body body) { this.body = body; } }
@XmlType(propOrder = {"eventId","eventName","version","eventSwitch","contentType","location"}) //按你指定的顺序展示 public class Header { private String eventId = "0"; private String eventName = "partProcessed"; private String version = "1.0"; private String eventSwitch; private String contentType = "3"; private Location location; @XmlAttribute public String getEventId() { return eventId; } public void setEventId(String eventId) { this.eventId = eventId; } @XmlAttribute public String getEventName() { return eventName; } public void setEventName(String eventName) { this.eventName = eventName; } @XmlAttribute public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } @XmlAttribute public String getEventSwitch() { return eventSwitch; } public void setEventSwitch(String eventSwitch) { this.eventSwitch = eventSwitch; } @XmlAttribute public String getContentType() { return contentType; } public void setContentType(String contentType) { this.contentType = contentType; } @XmlElement public Location getLocation() { return location; } public void setLocation(Location location) { this.location = location; } }
@XmlType(propOrder = {"lineNo","statNo","statIdx","fuNo","workPos","toolPos","application","processName","processNo"}) public class Location { private String lineNo = "5109"; private String statNo = "175"; private String statIdx = "1"; private String fuNo = "1"; private String workPos = "1"; private String toolPos = "1"; private String application = "IPC"; private String processName =""; private String processNo="51091175"; @XmlAttribute public String getLineNo() { return lineNo; } public void setLineNo(String lineNo) { this.lineNo = lineNo; } @XmlAttribute public String getStatNo() { return statNo; } public void setStatNo(String statNo) { this.statNo = statNo; } @XmlAttribute public String getStatIdx() { return statIdx; } public void setStatIdx(String statIdx) { this.statIdx = statIdx; } @XmlAttribute public String getFuNo() { return fuNo; } public void setFuNo(String fuNo) { this.fuNo = fuNo; } @XmlAttribute public String getWorkPos() { return workPos; } public void setWorkPos(String workPos) { this.workPos = workPos; } @XmlAttribute public String getToolPos() { return toolPos; } public void setToolPos(String toolPos) { this.toolPos = toolPos; } @XmlAttribute public String getApplication() { return application; } public void setApplication(String application) { this.application = application; } @XmlAttribute public String getProcessName() { return processName; } public void setProcessName(String processName) { this.processName = processName; } @XmlAttribute public String getProcessNo() { return processNo; } public void setProcessNo(String processNo) { this.processNo = processNo; } }
public class Event { private PartProcessed partProcessed; @XmlElement public PartProcessed getPartProcessed() { return partProcessed; } public void setPartProcessed(PartProcessed partProcessed) { this.partProcessed = partProcessed; } }
@XmlType(propOrder = {"identifier"}) public class PartProcessed { private String identifier; @XmlAttribute public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier = identifier; } }
public class Body { private Structs structs; @XmlElement public Structs getStructs() { return structs; } public void setStructs(Structs structs) { this.structs = structs; } }
public class Structs { private ResHead resHead; @XmlElement public ResHead getResHead() { return resHead; } public void setResHead(ResHead resHead) { this.resHead = resHead; } }
@XmlType(propOrder = {"nioBits","result","typeNo","typeVar","workingCode"}) public class ResHead { private String nioBits = "0"; private String result = "1"; private String typeNo = "1111111111"; private String typeVar = "0001"; private String workingCode = "0"; @XmlAttribute public String getNioBits() { return nioBits; } public void setNioBits(String nioBits) { this.nioBits = nioBits; } @XmlAttribute public String getResult() { return result; } public void setResult(String result) { this.result = result; } @XmlAttribute public String getTypeNo() { return typeNo; } public void setTypeNo(String typeNo) { this.typeNo = typeNo; } @XmlAttribute public String getTypeVar() { return typeVar; } public void setTypeVar(String typeVar) { this.typeVar = typeVar; } @XmlAttribute public String getWorkingCode() { return workingCode; } public void setWorkingCode(String workingCode) { this.workingCode = workingCode; } }
public Integer backToLibrary(String binCode,String eventSwitch) { //构建XML Root root = new Root(); Header header = new Header(); Location location = new Location(); header.setLocation(location); header.setEventSwitch(eventSwitch); Event event = new Event(); PartProcessed partProcessed = new PartProcessed(); partProcessed.setIdentifier(binCode); event.setPartProcessed(partProcessed); Body body = new Body(); Structs structs = new Structs(); ResHead resHead = new ResHead(); structs.setResHead(resHead); body.setStructs(structs); root.setHeader(header); root.setEvent(event); root.setBody(body); JAXBContext context; Integer code = null; try { context = JAXBContext.newInstance(Root.class); Marshaller mar = context.createMarshaller(); mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); mar.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); StringWriter writer = new StringWriter(); mar.marshal(root, writer); log.info("XML: {}",writer.toString()); //将拼接好的xml发送给上游服务端,获取回复报文 String result = returnResult(writer.toString()); //解析XML,转成JSON拿到数量 code = returnCode(result); } catch (JAXBException e) { e.printStackTrace(); } return code; }
调用接口后需要解析上游返回的XML来进行业务处理
public Integer returnCode(String xml) { Integer code = null; try { JSONObject js = XML.toJSONObject(xml); log.info("JSON数据 : {}",js); Object root = js.get("root"); JSONObject rootValue = (JSONObject) root; Object event = rootValue.get("event"); JSONObject eventValue = (JSONObject) event; Object result = eventValue.get("result"); JSONObject resultValue = (JSONObject) result; Object returnCode = resultValue.get("returnCode"); code = (Integer) returnCode; } catch (JSONException e) { e.printStackTrace(); } return code; }
建立通信连接
public String returnResult(String xml) { String result = ""; Socket client = null; OutputStream out = null; DataOutputStream output = null; InputStream in = null; DataInputStream din = null; try { client = new Socket(host, port); //获取数据发送通道 out = client.getOutputStream(); output = new DataOutputStream(out); SendMessage(xml,output); //获取数据接收通道 in = client.getInputStream(); din = new DataInputStream(in); int len; byte[] data = new byte[2048]; len = din.read(data); result = new String(data,0,len); log.info("获得服务端返回的数据:" + new String(data,0,len)); } catch (IOException e) { e.printStackTrace(); }finally { //释放资源 try { if(din!=null){ din.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(in!=null){ in.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(output !=null){ output.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(out!=null){ out.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(client!=null){ client.close(); } } catch (IOException e) { e.printStackTrace(); } } return result; }
对发送的报文进行十六进制转译
public static void SendMessage(String message, DataOutputStream netS) throws IOException{ //把发送过来的信息转化为一个字符数组1 byte[] bSendContentOra = message.getBytes(); //字符串的长度 int iLenSendContent = message.length(); //报文长度+4的总长度转化为字节数组 byte[] bSendContentHead = getBytes(iLenSendContent + 4); byte[] bSendContent = new byte[iLenSendContent + 4]; //一个新的字符数组3,大小为字符串长度+4 for (int i = 0; i < 4; i++) { //这个新的数组3的前四项等于数组2的倒置 bSendContent[i] = bSendContentHead[4 - i - 1]; } for (int i = 0; i < message.length(); i++) { //这个新的数组3的第四项之后等于字符数组的每一项 bSendContent[i + 4] = bSendContentOra[i]; } netS.write(bSendContent, 0,bSendContent.length); } public static byte[] getBytes(int data){ byte[] bytes = new byte[4]; bytes[0] = (byte) (data & 0xff); bytes[1] = (byte) ((data & 0xff00) >> 8); bytes[2] = (byte) ((data & 0xff0000) >> 16); bytes[3] = (byte) ((data & 0xff000000) >> 24); return bytes; }
2.以WebService方式通信
public interface WipClient { @RequestLine("POST") @Headers(value = {"Content-Type: application/x-www-form-urlencoded;charset=UTF-8"}) String send(URI uri,@RequestBody String body); @RequestLine("POST") @Headers(value = {"Content-Type: application/x-www-form-urlencoded;charset=UTF-8"}) String moveInSendReturnIn(URI uri, @RequestBody String body); @RequestLine("POST") @Headers(value = {"Content-Type: application/x-www-form-urlencoded;charset=UTF-8"}) String moveOutSend(URI uri,@RequestBody String body); @RequestLine("POST") @Headers(value = {"Content-Type: application/x-www-form-urlencoded;charset=UTF-8"}) String moveInSendProductionIn(URI uri,@RequestBody String body); }
需要在Feign注册
@Configuration public class FeignConfig {
@Bean
public WipClient wipClient(Decoder decoder, Encoder encoder) {
return Feign.builder().encoder(encoder).decoder(decoder)
.target(Target.EmptyTarget.create(WipClient.class));
}
}
需要自己写一个mock用来自测
@RestController @RequestMapping(value = "") @Slf4j public class wipController {
@Value("${wip.flag1}")
private Boolean flag1;
@Value("${wip.flag2}")
private Boolean flag2;
@Value("${wip.flag3}")
private Boolean flag3;
@PostMapping("/test111")
public String moveInSendReturnIn(@RequestBody String body){
log.info("上架returnIn回传内容" + body);
if (flag1){
return b;
}else {
return a;
}
}
@PostMapping("/test222")
public String moveOutSend(@RequestBody String body){
log.info("出库回传内容" + body);
if (flag2){
return b;
}else {
return a;
}
}
@PostMapping("/test333")
public String moveInSendProductionIn(@RequestBody String body){
log.info("上架productionIN回传内容" + body);
if (flag3){
return b;
}else {
return a;
}
}
}
需要在Nacos里配置好
wip: customer_url: http://172.20.8.120:10000 stockSync: /test1 moveInSendReturnIn: /test111 moveOutSend: /test222 moveInSendProductionIn: /test333 api: moveInCallbackApi1: WIP_PCBAPUTAWAY_IWMS moveInCallbackApi2: WIP_PCBABACK_IWMS moveOutCallbackApi: WIP_PCBAPICKOUT_IWMS flag1: true flag2: true flag3: true mes: # 出库回传标识 eventSwitch1: -1 # 回库标识 eventSwitch2: 2 # 上游服务端host host: 192.168.230.130 # 上游服务端port port: 8801
解析上游返回的数据
private String xml2JSON(String xml){ cn.hutool.json.JSONObject jsonObject = XML.toJSONObject(xml); Object returnMsg = jsonObject.get("string"); cn.hutool.json.JSONObject returnMsgValue = (cn.hutool.json.JSONObject) returnMsg; String o = (String) returnMsgValue.get("content"); Gson gson = new Gson(); Map<String, Object> map = new HashMap<String, Object>(); map = gson.fromJson(o, map.getClass()); return (String) map.get("MESSAGE_TYPE"); }
XXL-Job定时查询数据进行回传
/** * 定时查询出库单状态,如果有完成且Wip为未回传的出库单,则回传给客户系统,按订单,封箱码,明细回传都是针对一个订单 */ @XxlJob("moveOutOrderCallBackByOrderIdWipScheduler") public void moveOutOrderCallBackByOrderIdWip() { log.info("moveOutOrderCallBackByOrderId,当前时间" + LocalDateTime.now()); CommonResponse<List<KsSaleOrderHeaderDTO>> moveOutOrderFinishedAndNotCallBack = moveOutApi.getMoveOutPreAllocatedFinishedAndNotCallBackWip(); List<KsSaleOrderHeaderDTO> moveOutList = moveOutOrderFinishedAndNotCallBack.getData(); moveOutList = moveOutList.stream().filter(moveOut -> moveOut.getStatus() == KsSaleOrderStatusEnum.FULL.getCode()).collect(Collectors.toList()); log.info("出库预分配完成的订单信息{}", JSON.toJSONString(moveOutList)); if (moveOutOrderFinishedAndNotCallBack.isSuccess() && !CollectionUtils.isEmpty(moveOutList)) { for (KsSaleOrderHeaderDTO ksSaleOrderHeaderDTO : moveOutList) { CommonResponse<List<KsSaleOrderDetailPreAllocatedRecordDTO>> listCommonResponse = moveOutApi.queryByHeaderId(ksSaleOrderHeaderDTO.getId()); if (listCommonResponse.isSuccess()) { List<KsSaleOrderDetailPreAllocatedRecordDTO> preAllocatedRecordDTOS = listCommonResponse.getData(); for (KsSaleOrderDetailPreAllocatedRecordDTO preAllocatedRecordDTO : preAllocatedRecordDTOS) { if (OrderCallbackStatus.NOT_CALLBACK.getCode() == preAllocatedRecordDTO.getWipCallbackStatus() || OrderCallbackStatus.CALLBACK_ERROR.getCode() == preAllocatedRecordDTO.getWipCallbackStatus()) { WipMoveOutDTO wipMoveOutDTO = moveOutNewWipDTO(ksSaleOrderHeaderDTO,preAllocatedRecordDTO); try { String bodyStr = wipClient.moveOutSend(new URI(uri + moveOutSend),"Param=" + JsonUtils.obj2String(wipMoveOutDTO)); log.info("callbackWIP返回结果:{}", bodyStr); String messageType = xml2JSON(bodyStr); if ("SUCCESS".equalsIgnoreCase(messageType)) { String topic = KafkaTopicConstants.wms_core_move_out_order_wip; JSONObject jsonObject = moveOutSuccessNewJsonObjectWip(preAllocatedRecordDTO); log.info("回传-将结果发MQ给Core {} {}", kv(LogKey.TOPIC, topic), JSONObject.toJSONString(jsonObject)); kafkaSender.send(topic, jsonObject.toJSONString()); } else { String topic = KafkaTopicConstants.wms_core_move_out_order_wip; JSONObject jsonObject = moveOutErrorNewJsonObjectWip(ksSaleOrderHeaderDTO, preAllocatedRecordDTO, wipMoveOutDTO); log.info("回传-将结果发MQ给Core {} {}", kv(LogKey.TOPIC, topic), JSONObject.toJSONString(jsonObject)); kafkaSender.send(topic, jsonObject.toJSONString()); } } catch (Exception e) { log.error(e.getMessage()); throw new ApiException("WIP出库回传错误!"); } } } } } } }
3.以HTTP方式通信
手写一个HTTP请求
@Slf4j @RequiredArgsConstructor @Component public class SealClient { @Value("${callback.url}") private String uri; @Value("${callback.sealBox}") private String sealBox; public String sealBox(List<SealBinCallBackDTO> sealBinCallBackDTOs){ HashMap<String, String> headers = new HashMap<>(); headers.put("Content-Type","application/json"); String body = HttpUtil.createPost(uri + sealBox) .addHeaders(headers) .body(JSON.toJSONString(sealBinCallBackDTOs)) .execute() .body(); return body; } }
调用接口进行封箱回传
@Transactional(transactionManager = "mysqlTransactionManager", rollbackFor = Exception.class) public synchronized void callbackSealBox(String outerBoxCode) { List<SealBinCallBackDTO> sealBinCallBackDTOs = createSealBinCallBackDTO(outerBoxCode); log.info("调用上游接口入参: {}",JSON.toJSONString(sealBinCallBackDTOs)); String response = sealClient.sealBox(sealBinCallBackDTOs); log.info("上游系统返回: {}",response); JSONObject jsonObject = JSON.parseObject(response); Object result = jsonObject.get("result"); Map<String, Object> requestBody = JSON.parseObject(String.valueOf(result)); if (requestBody.get("code").equals(0)){ log.info("成功了"); LambdaUpdateWrapper<KsSaleOrderContainerSku> wrapper = new LambdaUpdateWrapper<KsSaleOrderContainerSku>() .set(KsSaleOrderContainerSku::getCallbackStatus, KsSaleOrderSnCallbackStatus.UPLOADED.getValue()) .eq(KsSaleOrderContainerSku::getOuterBoxCode, outerBoxCode); ksSaleOrderContainerSkuMapper.update(null,wrapper); }else { LambdaUpdateWrapper<KsSaleOrderContainerSku> wrapper = new LambdaUpdateWrapper<KsSaleOrderContainerSku>() .set(KsSaleOrderContainerSku::getCallbackStatus, KsSaleOrderSnCallbackStatus.UPLOAD_FAILED.getValue()) .eq(KsSaleOrderContainerSku::getOuterBoxCode, outerBoxCode); ksSaleOrderContainerSkuMapper.update(null,wrapper); } }
手写一个MOCK服务端
@RestController @RequestMapping(value = "") @Slf4j public class MockController { @Value("${callback.mock}") private boolean isMock; @PostMapping("/test1") public String sealBox(@RequestBody String body){ log.info("外箱码封箱接口内容:" + body); if (isMock) { return " {\n" + " \"jsonrpc\": \"2.0\",\n" + " \"result\": {\n" + " \"code\": 0,\n" + " \"data\": {},\n" + " \"message\": \"ok\",\n" + " \"timestamp\": \"\",\n" + " \"traceid\": \"\"\n" + " }\n" + "}"; } return " {\n" + " \"jsonrpc\": \"2.0\",\n" + " \"result\": {\n" + " \"code\": 500,\n" + " \"data\": {},\n" + " \"message\": \"ok\",\n" + " \"timestamp\": \"\",\n" + " \"traceid\": \"\"\n" + " }\n" + "}"; } }