作者 朱兆平

FHL分单报文解析并校验

1 FROM java:8u111 1 FROM java:8u111
2 VOLUME /tmp 2 VOLUME /tmp
  3 +
  4 +ARG PINPOINT_VERSION
  5 +ARG AGENT_ID
  6 +ARG APP_NAME
  7 +ENV JAVA_OPTS="-javaagent:/pinpoint-agent/pinpoint-bootstrap-${PINPOINT_VERSION}.jar -Dpinpoint.agentId=${AGENT_ID} -Dpinpoint.applicationName=${APP_NAME} -Dspring.profiles.active=${SPRING_PROFILES}"
  8 +
3 ADD *.jar app.jar 9 ADD *.jar app.jar
4 EXPOSE 10001 10 EXPOSE 10001
5 RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 11 RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
6 RUN echo "Asia/Shanghai" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata 12 RUN echo "Asia/Shanghai" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata
7 COPY xsd /xsd 13 COPY xsd /xsd
8 -ENTRYPOINT ["java","-jar","/app.jar"] 14 +#ENTRYPOINT ["java","$JAVA_OPTS","-jar","/app.jar"]
  15 +ENTRYPOINT java $JAVA_OPTS -jar /app.jar
9 # Ubuntu 时区 16 # Ubuntu 时区
10 17
  1 +package com.tianbo.analysis.controller;
  2 +
  3 +
  4 +
  5 +import com.tianbo.analysis.model.Originmanifestsecondary;
  6 +import com.tianbo.analysis.model.ResultJson;
  7 +import com.tianbo.analysis.service.FHLResolve;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.web.bind.annotation.PostMapping;
  11 +import org.springframework.web.bind.annotation.RequestMapping;
  12 +import org.springframework.web.bind.annotation.RequestParam;
  13 +import org.springframework.web.bind.annotation.RestController;
  14 +
  15 +@RestController
  16 +@RequestMapping("/fhl")
  17 +@Slf4j
  18 +public class FHLController {
  19 +
  20 + @Autowired
  21 + FHLResolve resolve;
  22 +
  23 + @PostMapping("resolve")
  24 + public ResultJson resolve(@RequestParam String ffm){
  25 + Originmanifestsecondary fhlInfo = new Originmanifestsecondary();
  26 + fhlInfo.text = ffm;
  27 + boolean result = resolve.resolve(fhlInfo);
  28 + return result? new ResultJson("200","success"):new ResultJson("400","解析失败");
  29 + }
  30 +}
@@ -17,6 +17,7 @@ public class SendPlanController { @@ -17,6 +17,7 @@ public class SendPlanController {
17 @Resource 17 @Resource
18 SENDPLANMapper sendplanMapper; 18 SENDPLANMapper sendplanMapper;
19 19
  20 + @ReSubmitCheck
20 @UserPermissionCheck 21 @UserPermissionCheck
21 @PostMapping("add") 22 @PostMapping("add")
22 public ResultJson add(@RequestBody SENDPLAN sendplan,@CookieValue("username") String username,@CookieValue("userid") String userid){ 23 public ResultJson add(@RequestBody SENDPLAN sendplan,@CookieValue("username") String username,@CookieValue("userid") String userid){
1 package com.tianbo.analysis.model; 1 package com.tianbo.analysis.model;
2 2
3 3
  4 +import com.tianbo.analysis.exception.FFMResolveException;
  5 +import com.tianbo.analysis.tools.WaybillTools;
4 import com.tianbo.util.Date.DateUtil; 6 import com.tianbo.util.Date.DateUtil;
5 import lombok.Data; 7 import lombok.Data;
6 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
7 import org.apache.commons.lang.StringUtils; 9 import org.apache.commons.lang.StringUtils;
8 10
9 -import java.util.Date;  
10 -import java.util.List; 11 +import javax.validation.constraints.NotNull;
  12 +import java.io.BufferedReader;
  13 +import java.io.IOException;
  14 +import java.io.StringReader;
  15 +import java.util.*;
  16 +import java.util.regex.Matcher;
  17 +import java.util.regex.Pattern;
11 18
12 @Data 19 @Data
13 @Slf4j 20 @Slf4j
@@ -78,6 +85,15 @@ public class Originmanifestsecondary { @@ -78,6 +85,15 @@ public class Originmanifestsecondary {
78 85
79 private ORIGINMANIFESTMASTER master; 86 private ORIGINMANIFESTMASTER master;
80 87
  88 + public String text;
  89 + public List<String> lineList;
  90 +
  91 + private static final String KEY_WORD = "FHL,MBI,HBS,TXT,HTS,SHP,CNE,NAM,ADR,LOC,CVD,OSI,COR,OCI,DEG,DIM";
  92 +
  93 + private String TEMP_KEY_WORD="";
  94 +
  95 + private int CURRENT_LINE=0;
  96 +
81 public String getAutoid() { 97 public String getAutoid() {
82 return autoid; 98 return autoid;
83 } 99 }
@@ -343,4 +359,634 @@ public class Originmanifestsecondary { @@ -343,4 +359,634 @@ public class Originmanifestsecondary {
343 this.waybillnosecondary = waybillnosecondary; 359 this.waybillnosecondary = waybillnosecondary;
344 this.originmanifestmasterautoid = originmanifestmasterautoid; 360 this.originmanifestmasterautoid = originmanifestmasterautoid;
345 } 361 }
  362 +
  363 +
  364 + /**
  365 + * 校验报文结尾标识
  366 + * 将报文字符窜转换成行数组
  367 + * @return
  368 + * @throws IOException
  369 + * @throws FFMResolveException
  370 + */
  371 + public boolean textToStringList() throws IOException, FFMResolveException {
  372 + if (StringUtils.isNotBlank(text) && text.contains("FHL/")) {
  373 + lineList = new ArrayList<>();
  374 +
  375 + BufferedReader reader = new BufferedReader(new StringReader(text));
  376 + log.info(this.toString());
  377 + for (String lineStr = reader.readLine();
  378 + lineStr != null;
  379 + lineStr = reader.readLine()) {
  380 + if (StringUtils.isNotEmpty(lineStr)){
  381 + lineList.add(lineStr);
  382 + }
  383 + }
  384 + return true;
  385 + }
  386 + log.error("[FHL]-{}->报文不属于分单报",text);
  387 + return false;
  388 + }
  389 +
  390 + /**
  391 + * 开始解析
  392 + * * Cargo-IMP Messages may only contain the following approved characters:
  393 + * * the letters A to Z (upper case only)
  394 + * * the numerals 0 to 9
  395 + * * the special characters / -. Space < =
  396 + */
  397 + public boolean startParse() throws FFMResolveException {
  398 + if (!lineList.isEmpty()){
  399 + int keyword_i = 0;
  400 + for (int i = 0; i < lineList.size(); i++) {
  401 +
  402 + String pattern = "[^A-Z0-9/\\.\\-<=\\s]+";
  403 + // 创建 Pattern 对象
  404 + Pattern r = Pattern.compile(pattern);
  405 + // 现在创建 matcher 对象
  406 + Matcher m = r.matcher(text);
  407 +
  408 + if(m.find()){
  409 + log.error("[FHL] 行[{}]中包含非允许特殊字符,报文中只允许包含-[{}]等特殊字符",i," / -. Space < =");
  410 + throw new FFMResolveException("[FHL] 报文中包含特殊字符,报文中只允许包含 / -. Space < = 等特殊字符");
  411 + }
  412 +
  413 + //根据行关键字走相应的解析逻辑
  414 + String line = lineList.get(i);
  415 + log.debug("[TEXT] 开始处理行[{}]-[{}]",i,line);
  416 + String keyword = keyword(line);
  417 + CURRENT_LINE = i;
  418 + if (!"NOT_KEYWORD".equals(keyword)){
  419 + TEMP_KEY_WORD = "";
  420 + keywordParse(keyword,line,i);
  421 + }else{
  422 + log.debug("[[TEXT-LINE-{}]当前行不属于关键字解析行,根据缓存关键字解析下行信息",i);
  423 + keywordNextLineParse(line,i);
  424 + }
  425 +
  426 + }
  427 + }
  428 + return false;
  429 + }
  430 +
  431 + /**
  432 + * 根据关键字适配解析方法
  433 + * @param keyword 关键字
  434 + * @param line 当前行内容
  435 + * @param current 当前行数
  436 + */
  437 + public void keywordParse(String keyword,String line,int current) throws FFMResolveException {
  438 + log.debug("[TEXT] 行[{}]包含关键字{},开始处理",current,keyword);
  439 + switch (keyword){
  440 + case "FHL":
  441 + fhlInfoParse(line);
  442 + break;
  443 + case "MBI":
  444 + mbiInfoParse(line);
  445 + break;
  446 + case "HBS":
  447 + hbsInfoParse(line);
  448 + break;
  449 + case "TXT":
  450 + textInfoParse(line);
  451 + break;
  452 + case "HTS":
  453 + htsInfoParse(line);
  454 + break;
  455 + case "SHP":
  456 + shipperHasSplitParse(line,"SHP");
  457 + break;
  458 + case "CNE":
  459 + shipperHasSplitParse(line,"CNE");
  460 + break;
  461 + case "OCI":
  462 + ociInfoParse(line);
  463 + break;
  464 + case "CVD":
  465 + cvdParse(line);
  466 + break;
  467 + default:
  468 + break;
  469 + }
  470 +
  471 + }
  472 +
  473 + /**
  474 + * 根据关键字适配解析方法
  475 + * @param line 当前行内容
  476 + * @param current 当前行数
  477 + */
  478 + public void keywordNextLineParse(String line,int current) throws FFMResolveException {
  479 + log.info("行[{}]当前临时关键字-[{}]",current,TEMP_KEY_WORD);
  480 + switch (TEMP_KEY_WORD){
  481 + case "HBS-SHC":
  482 + hbsOtherInfoParse(line);
  483 + break;
  484 + case "TXT":
  485 + textInfoParse(line);
  486 + break;
  487 + case "HTS":
  488 + htsInfoParse(line);
  489 + break;
  490 + case "CNE-NAM":
  491 + case "SHP-NAM":
  492 + //SHP下的第一行地址行
  493 + shipperNamV2Parse(line);
  494 + break;
  495 + case "CNE-ADR":
  496 + case "SHP-ADR":
  497 + //SHP下的第一行地址行
  498 + shipperAdrV1Parse(line);
  499 + break;
  500 + case "CNE-LOC":
  501 + case "SHP-LOC":
  502 + //SHP下的第一行地址行
  503 + shipperLocV1Parse(line);
  504 + break;
  505 + case "CNE-CON":
  506 + case "SHP-CON":
  507 + //SHP下的第一行地址行
  508 + shipperContactV1Parse(line);
  509 + break;
  510 + case "OCI":
  511 + ociInfoParse(line);
  512 + break;
  513 + default:
  514 + break;
  515 + }
  516 + }
  517 +
  518 + /**
  519 + * 关键字识别
  520 + * @param text 每行的内容
  521 + * @return 识别为关键字的返回关键字,未被识别为关键字的返回NOT_KEYWORD;
  522 + * 返回的关键字 三字码关键字 机场代码关键字 文件结尾关键字 CONT,LAST
  523 + */
  524 + public String keyword(@NotNull String text){
  525 + //取每行前三位
  526 + if (StringUtils.isNotBlank(text) && text.length()>3){
  527 + String s_3 = text.substring(0,3);
  528 + if(KEY_WORD.contains(s_3)){
  529 + return s_3;
  530 + }
  531 + }
  532 + return "NOT_KEYWORD";
  533 + }
  534 +
  535 + /**
  536 + * FHL信息解析
  537 + * @param verlLine FHL-报文版本行信息解析
  538 + */
  539 + public boolean fhlInfoParse(String verlLine) throws FFMResolveException {
  540 + log.debug("[TEXT] 找到FHL开头节点");
  541 + //校验正则1,取前三位验证是否是机场代码
  542 + String pattern = "^FHL/\\d{1,3}";
  543 + // 创建 Pattern 对象
  544 + Pattern r = Pattern.compile(pattern);
  545 + // 现在创建 matcher 对象
  546 + Matcher m = r.matcher(verlLine);
  547 + //格式正则校验
  548 + if(m.find()){
  549 + String ver = verlLine.split("/")[1];
  550 + log.info("[VER] 报文版本{}",ver.trim());
  551 + return true;
  552 + }
  553 + throw new FFMResolveException("[FLIGHT] FHL版本信息错误");
  554 + }
  555 +
  556 + /**
  557 + * 主单行信息解析
  558 + * @param mbiLine MBI-主单行信息解析
  559 + */
  560 + public boolean mbiInfoParse(String mbiLine) throws FFMResolveException {
  561 + log.debug("[TEXT] 找到MBI主单开头节点");
  562 + //校验正则1,取前三位验证是否是机场代码
  563 + String pattern = "^MBI/(\\d{3}-\\d{8})([A-Z]{3})([A-Z]{3})/T(\\d{1,4})[A-Z0-9]([0-9\\.]{1,7})";
  564 + // 创建 Pattern 对象
  565 + Pattern r = Pattern.compile(pattern);
  566 + // 现在创建 matcher 对象
  567 + Matcher m = r.matcher(mbiLine);
  568 + //格式正则校验
  569 + if(m.find()){
  570 + waybillnomaster = m.group(1);
  571 +
  572 + //主单模七校验
  573 + if(WaybillTools.model7Check(waybillnomaster)){
  574 + log.debug("[AWB] 运单-({})模七校验通过",waybillnomaster);
  575 + }else {
  576 + log.error("{}运单模七校验不通过",waybillnomaster);
  577 + throw new FFMResolveException(waybillnomaster+"运单模七校验不通过");
  578 + }
  579 +
  580 + String masterOringStation = m.group(2);
  581 + String masterDestinationStation = m.group(3);
  582 + String masterTotalPiece = m.group(4);
  583 + String masterTotalWeight = m.group(5);
  584 + return true;
  585 + }
  586 + throw new FFMResolveException("[MBI] MBI主单节点信息错误");
  587 + }
  588 +
  589 + /**
  590 + * 分单行信息解析,分单信息可以重复,要考虑重复解析如果报文中不存在收发货人的话,目前按单一分单信息解析
  591 + * @param fhlLine HBS-分单行信息解析,
  592 + */
  593 + public boolean hbsInfoParse(String fhlLine) throws FFMResolveException {
  594 +
  595 + log.debug("[TEXT] 找到HBS分单开头节点");
  596 + //校验正则1,取前三位验证是否是机场代码
  597 + String pattern = "^HBS/([A-Z0-9]{1,12})/([A-Z]{3})([A-Z]{3})/(\\d{1,4})/[A-Z0-9]([0-9\\.]{1,7})/(\\d{1,5})?/(.{1,15})";
  598 + // 创建 Pattern 对象
  599 + Pattern r = Pattern.compile(pattern);
  600 + // 现在创建 matcher 对象
  601 + Matcher m = r.matcher(fhlLine);
  602 + //格式正则校验
  603 + if(m.find()){
  604 + String strList[] = fhlLine.split("/");
  605 + waybillnosecondary = m.group(1);
  606 + originatingstationBill = m.group(2);
  607 + destinationstationBill = m.group(3);
  608 + piece = manifestpiece = m.group(4);
  609 + weight = manifestweight = m.group(5);
  610 + String SLAC = strList[5];
  611 + productname = strList[6];
  612 +
  613 + log.debug("[HBS] 分单信息节点解析完毕");
  614 + TEMP_KEY_WORD = "HBS-SHC";
  615 + return true;
  616 + }
  617 + throw new FFMResolveException("[HBS] HBS分单节点信息错误");
  618 + }
  619 +
  620 + /**
  621 + * 分单其他信息解析
  622 + * @param line HBS-分单其他行信息解析
  623 + */
  624 + public boolean hbsOtherInfoParse(String line) throws FFMResolveException {
  625 + if (line.startsWith("/") && "HBS-SHC".equals(TEMP_KEY_WORD)){
  626 + log.debug("适配到分单特货代码节点");
  627 +
  628 + //校验正则1,取前三位验证是否是机场代码
  629 + String pattern = "(/[A-Z]{3})+";
  630 + // 创建 Pattern 对象
  631 + Pattern r = Pattern.compile(pattern);
  632 + // 现在创建 matcher 对象
  633 + Matcher m = r.matcher(line);
  634 + if (m.find()){
  635 + String strList[] = line.split("/");
  636 + log.info("[HBS-SHC]-特货代码为-{}",line);
  637 + return true;
  638 + }
  639 +
  640 + throw new FFMResolveException("[HBS] HBS-分单特货代码节点信息错误");
  641 + }else{
  642 + return false;
  643 + }
  644 +
  645 +
  646 + }
  647 +
  648 + /**
  649 + * 分单备注描述信息解析
  650 + * @param line TXT
  651 + */
  652 + public boolean textInfoParse(String line) throws FFMResolveException {
  653 + log.debug("[TEXT] 找到TXT开头节点");
  654 + //校验正则1,取前三位验证是否是机场代码
  655 + String pattern = "(TXT)?/(.{1,65})";
  656 + // 创建 Pattern 对象
  657 + Pattern r = Pattern.compile(pattern);
  658 + // 现在创建 matcher 对象
  659 + Matcher m = r.matcher(line);
  660 + //格式正则校验
  661 + if(m.find()){
  662 + String strList[] = line.split("/");
  663 + String freeText = strList[1].trim();
  664 + log.info("[TXT]-{}",freeText);
  665 + TEMP_KEY_WORD = "TXT";
  666 + return true;
  667 + }
  668 + throw new FFMResolveException("[TXT] TXT节点信息错误,字数不能超过65");
  669 + }
  670 +
  671 + /**
  672 + * HTS国际协调关税表信息解析
  673 + * @param line HTS
  674 + */
  675 + public boolean htsInfoParse(String line) throws FFMResolveException {
  676 + log.debug("[TEXT] 找到HTS开头节点");
  677 + //校验正则1,取前三位验证是否是机场代码
  678 + String pattern = "^(HTS)?/[A-Z0-9]{6,18}";
  679 + // 创建 Pattern 对象
  680 + Pattern r = Pattern.compile(pattern);
  681 + // 现在创建 matcher 对象
  682 + Matcher m = r.matcher(line);
  683 + //格式正则校验
  684 + if(m.find()){
  685 + String strList[] = line.split("/");
  686 + String htsText = strList[1];
  687 + log.debug("[HTS] 信息为:{}",htsText);
  688 + TEMP_KEY_WORD = "HTS";
  689 + return true;
  690 + }
  691 + throw new FFMResolveException("[HTS] HTS国际协调关税表信息错误,字数为6-18位,最多9次");
  692 + }
  693 +
  694 + /**
  695 + * 收货人信息解析
  696 + * @param consigneLine CNE-收货人行信息解析
  697 + */
  698 + public void consigneeInfoParse(String consigneLine){
  699 +
  700 + }
  701 +
  702 + /**
  703 + * 发货人信息解析1版本包含分割符解析
  704 + * 节点有两种表现 一种是SHP标识后就换行发货人名称,一种是SHP/发货人名称
  705 + * @param line SHP-发货人行信息解析
  706 + */
  707 + public void shipperHasSplitParse(String line,String SHP_OR_CNE) throws FFMResolveException {
  708 + log.debug("[TEXT] 找到SHP|CNE分单开头节点");
  709 + //SHP/发货人名称 - 正则适配
  710 + String keyword = "^(SHP|CNE)(/.{1,35})?";
  711 + // 创建 Pattern 对象
  712 + Pattern r = Pattern.compile(keyword);
  713 + // 现在创建 matcher 对象
  714 + Matcher m = r.matcher(line);
  715 + if (m.find()){
  716 + String strList[] = line.split("/");
  717 + if (strList.length>1){
  718 + if("SHP".equals(SHP_OR_CNE)){
  719 + shippername = strList[1];
  720 + log.debug("[SHP] 发货人信息为:{}",shippername);
  721 + TEMP_KEY_WORD= "SHP-ADR";
  722 + }else{
  723 + consigneename = strList[1];
  724 + log.debug("[CNE] 收货人信息为:{}",consigneename);
  725 + TEMP_KEY_WORD= "CNE-ADR";
  726 + }
  727 +
  728 + }else {
  729 + if("SHP".equals(SHP_OR_CNE)){
  730 +
  731 + log.info("[SHP]主节点为分单数据标识节点,下面几行解析具体信息");
  732 + TEMP_KEY_WORD= "SHP-NAM";
  733 + }else {
  734 + log.info("[CNE] 主节点为分单数据标识节点,下面几行解析具体信息");
  735 + TEMP_KEY_WORD= "CNE-NAM";
  736 + }
  737 +
  738 + }
  739 + }else {
  740 + throw new FFMResolveException("[SHP|CNE] 收发货人信息格式校验错误");
  741 + }
  742 + }
  743 +
  744 + /**
  745 + * 发货人名称2版本解析
  746 + * @param line NAM-发货人行信息解析
  747 + */
  748 + public void shipperNamV2Parse(String line) throws FFMResolveException {
  749 + if ("SHP-NAM".equals(TEMP_KEY_WORD) || "CNE-NAM".equals(TEMP_KEY_WORD)){
  750 + log.debug("[TEXT] 找到收发货人名称节点");
  751 + //SHP/发货人名称 - 正则适配
  752 + String keyword = "^(NAM)?/.{1,35}";
  753 + // 创建 Pattern 对象
  754 + Pattern r = Pattern.compile(keyword);
  755 + // 现在创建 matcher 对象
  756 + Matcher m = r.matcher(line);
  757 + if (m.find()){
  758 + String strList[] = line.split("/");
  759 + if("SHP-NAM".equals(TEMP_KEY_WORD)){
  760 + shippername = strList[1];
  761 + log.debug("[SHP] 发货人名称为:{}",shippername);
  762 + TEMP_KEY_WORD= "SHP-ADR";
  763 + }else{
  764 + consigneename = strList[1];
  765 + log.debug("[CNE] 收货人地址为:{}",consigneename);
  766 + TEMP_KEY_WORD= "CNE-ADR";
  767 + }
  768 +
  769 + }else {
  770 + throw new FFMResolveException("[SHP|CNE] 收发货人名称格式校验错误");
  771 + }
  772 + }
  773 + }
  774 +
  775 + /**
  776 + * 发货人地址1版本解析
  777 + * @param line NAM-发货人行信息解析
  778 + */
  779 + public void shipperAdrV1Parse(String line) throws FFMResolveException {
  780 + if ("SHP-ADR".equals(TEMP_KEY_WORD)|| "CNE-ADR".equals(TEMP_KEY_WORD)){
  781 + log.debug("[TEXT] 找到发货人地址节点");
  782 + //SHP/发货人名称 - 正则适配
  783 + String keyword = "^(ADR)?/.{1,35}";
  784 + // 创建 Pattern 对象
  785 + Pattern r = Pattern.compile(keyword);
  786 + // 现在创建 matcher 对象
  787 + Matcher m = r.matcher(line);
  788 + if (m.find()){
  789 + String strList[] = line.split("/");
  790 + if("SHP-ADR".equals(TEMP_KEY_WORD)){
  791 + shipperaddress = strList[1];
  792 + log.debug("[SHP] 发货人地址为:{}",shipperaddress);
  793 + TEMP_KEY_WORD= "SHP-LOC";
  794 + }else {
  795 + consigneeaddress = strList[1];
  796 + log.debug("[CNE] 收货人地址为:{}",consigneeaddress);
  797 + TEMP_KEY_WORD= "CNE-LOC";
  798 + }
  799 +
  800 + }else {
  801 + throw new FFMResolveException("[SHP|CNE] 收发货人地址格式校验错误");
  802 + }
  803 + }
  804 + }
  805 +
  806 + /**
  807 + * 发货人城市省份1版本解析
  808 + * @param line LOC-发货人行信息解析
  809 + */
  810 + public void shipperLocV1Parse(String line) throws FFMResolveException {
  811 + if ("SHP-LOC".equals(TEMP_KEY_WORD) || "CNE-LOC".equals(TEMP_KEY_WORD)){
  812 + log.debug("[TEXT] 找到发货人城市省份节点");
  813 + //SHP/发货人名称 - 正则适配
  814 + String keyword = "^(LOC)?/.{0,17}(/.{0,9})?";
  815 + // 创建 Pattern 对象
  816 + Pattern r = Pattern.compile(keyword);
  817 + // 现在创建 matcher 对象
  818 + Matcher m = r.matcher(line);
  819 + if (m.find()){
  820 + String strList[] = line.split("/");
  821 + if("SHP-LOC".equals(TEMP_KEY_WORD)){
  822 + String cityName = strList[1];
  823 + log.debug("[SHP] 发货人城市为:{}",cityName);
  824 + if (strList.length>2){
  825 + log.debug("[SHP] 发货人省份信息为:{}",strList[2]);
  826 + }
  827 + TEMP_KEY_WORD= "SHP-CON";
  828 + }else{
  829 + String cityName = strList[1];
  830 + log.debug("[CNE] 收货人城市为:{}",cityName);
  831 + if (strList.length>2){
  832 + log.debug("[CNE] 收货人省份信息为:{}",strList[2]);
  833 + }
  834 + TEMP_KEY_WORD= "CNE-CON";
  835 + }
  836 +
  837 + }else {
  838 + throw new FFMResolveException("[SHP|CNE] 发货人地址格式校验错误");
  839 + }
  840 + }
  841 + }
  842 +
  843 + /**
  844 + * 发货人国家及联系信息1版本解析
  845 + * @param line LOC-发货人行信息解析
  846 + */
  847 + public void shipperContactV1Parse(String line) throws FFMResolveException {
  848 + if ("SHP-CON".equals(TEMP_KEY_WORD)||"CNE-CON".equals(TEMP_KEY_WORD)){
  849 + log.debug("[TEXT] 找到收发货人国家及联系信息节点");
  850 + //SHP/发货人名称 - 正则适配
  851 + String keyword = "^/[A-Z]{2}(/.{1,9})?((/[TEFXL]{2}/.{1,25})+)?";
  852 + // 创建 Pattern 对象
  853 + Pattern r = Pattern.compile(keyword);
  854 + // 现在创建 matcher 对象
  855 + Matcher m = r.matcher(line);
  856 + if (m.find()){
  857 + String strList[] = line.split("/");
  858 + if("SHP-CON".equals(TEMP_KEY_WORD)){
  859 + shipperCountrycode = strList[1];
  860 + log.debug("[SHP] 发货人国家为:{}",shipperCountrycode);
  861 + if (strList.length>3){
  862 + log.debug("[SHP] 发货人邮编信息为:{}",strList[2]);
  863 + if ("TE".equals(strList[3])){
  864 + shipperPhone = strList[4];
  865 + log.debug("[SHP] 发货人电话信息为:{}",shipperPhone);
  866 + }
  867 + if ("FX".equals(strList[3])){
  868 + shipperFax = strList[4];
  869 + log.debug("[SHP] 发货人传真信息为:{}",consigneeFax);
  870 + }
  871 + }
  872 + }else{
  873 + consigneeCountrycode = strList[1];
  874 + log.debug("[CNE] 收货人国家为:{}",consigneeCountrycode);
  875 + if (strList.length>3){
  876 + log.debug("[CNE] 收货人邮编信息为:{}",strList[2]);
  877 + if ("TE".equals(strList[3])){
  878 + consigneePhone = strList[4];
  879 + log.debug("[CNE] 收货人电话信息为:{}",consigneePhone);
  880 + }
  881 + if ("FX".equals(strList[3])){
  882 + consigneeFax = strList[4];
  883 + log.debug("[CNE] 收货人传真信息为:{}",consigneeFax);
  884 + }
  885 + }
  886 + }
  887 +
  888 + }else {
  889 + throw new FFMResolveException("[SHP|CNE] 收发货人国家及联系信息格式校验错误");
  890 + }
  891 + }
  892 + }
  893 +
  894 + /**
  895 + * OCI其他海关信息节点解析
  896 + * @param line OCI行
  897 + */
  898 + public void ociInfoParse(String line) throws FFMResolveException {
  899 + String keyword = "^(OCI)?/([A-Z]{2})?/([A-Z]{3})?/([A-Z]{0,2})?/(.{1,35})";
  900 + // 创建 Pattern 对象
  901 + Pattern r = Pattern.compile(keyword);
  902 + // 现在创建 matcher 对象
  903 + Matcher m = r.matcher(line);
  904 + if (m.find()){
  905 + //二位国家代码
  906 + String countryCode = line.split("/")[1];
  907 + //三位信息标识
  908 + String infoCode = line.split("/")[2];
  909 + //二位海关信息标识
  910 + String customsCode =line.split("/")[3];
  911 + //补充海关信息
  912 + String customMsg = line.split("/")[4];
  913 + log.debug("[AWB-OTHER-OCI] 运单OCI海关其他信息,国家代码:{} , 信息标识:{}, 海关信息标识:{}, 补充海关信息:{} ",
  914 + countryCode,
  915 + infoCode,
  916 + customsCode,
  917 + customMsg);
  918 + if (customMsg.length()>35){
  919 + throw new FFMResolveException(customMsg+"-OCI海关其他信息长度超过35,解析错误");
  920 + }
  921 + ociInfoLineParse(countryCode,infoCode,customsCode,customMsg);
  922 + TEMP_KEY_WORD= "OCI";
  923 + }else {
  924 + throw new FFMResolveException("OCI海关其他信息格式校验错误");
  925 + }
  926 + }
  927 +
  928 + /**
  929 + * OCI 信息标识节点解析与判定
  930 + * @param countryCode 国家代码儿子
  931 + * @param infoCode 信息代码识别码三字码
  932 + * @param customsCode 海关标识代码
  933 + * @param customMsg 具体标识信息
  934 + */
  935 + public boolean ociInfoLineParse(String countryCode,String infoCode,String customsCode,String customMsg){
  936 +
  937 + log.info("[OCI] - 国家代码[{}],信息代码三字码[{}],海关标识代码[{}],具体标识内容[{}]",countryCode,infoCode,customsCode,customMsg);
  938 + switch (customsCode){
  939 + //具体收货人姓名
  940 + case "CP":
  941 + case "KC":
  942 + case "ST":
  943 + specificConsigneename = customMsg;
  944 + log.info("收货具体联系人名称[{}]",customMsg);
  945 + break;
  946 + //具体收货人电话
  947 + case "CT":
  948 + case "U":
  949 + specificConsigneePhone = customMsg;
  950 + log.info("收货具体联系人电话[{}]",customMsg);
  951 + break;
  952 + //海关企业注册代码
  953 + case "T":
  954 + String enterpriseCode= customMsg;
  955 + if ("CNE".equals(infoCode)){
  956 + consigneeCode = customMsg;
  957 + log.info("收货人国家[{}]企业代码[{}]",countryCode,customMsg);
  958 + }else if ("SHP".equals(infoCode)){
  959 + shipperCode = customMsg;
  960 + log.info("发货人国家[{}]企业代码[{}]",countryCode,customMsg);
  961 + }
  962 + break;
  963 + default:
  964 + break;
  965 + }
  966 +
  967 + return true;
  968 + }
  969 +
  970 + /**
  971 + * CVD 计费声明节点解析
  972 + * @param line
  973 + */
  974 + public void cvdParse(String line){
  975 + String keyword = "^CVD/([A-Z]{3})/([PC]{2})/([0-9\\.]{1,12}|NVD)/(NCV|[0-9\\.]{1,12})/(XXX|[0-9\\.]{1,11})";
  976 + // 创建 Pattern 对象
  977 + Pattern r = Pattern.compile(keyword);
  978 + // 现在创建 matcher 对象
  979 + Matcher m = r.matcher(line);
  980 + if (m.find()){
  981 + String ISO_Currency_Code = m.group(1);
  982 + paymode = m.group(2);
  983 + String Declared_Value_for_Carriage = m.group(3);
  984 + String Declared_Value_for_Customs = m.group(4);
  985 + String Declared_Value_for_Insurance = m.group(5);
  986 + log.info("[CVD]-计费声明:货币代码[{}]-付费模式[{}]-运输申报价值[{}]-海关申报价值[{}]-保险申报价值[{}]",
  987 + ISO_Currency_Code,paymode,Declared_Value_for_Carriage,Declared_Value_for_Customs,Declared_Value_for_Insurance);
  988 + }
  989 + }
  990 +
  991 +
346 } 992 }
  1 +package com.tianbo.analysis.service;
  2 +
  3 +import com.tianbo.analysis.model.FFMInfo;
  4 +import com.tianbo.analysis.model.Originmanifestsecondary;
  5 +
  6 +public interface FHLResolve {
  7 + boolean resolve(Originmanifestsecondary var1);
  8 +}
  1 +package com.tianbo.analysis.service.imp;
  2 +
  3 +import com.tianbo.analysis.dao.OriginmanifestsecondaryMapper;
  4 +import com.tianbo.analysis.dao.TBBaseAirportInfoDao;
  5 +import com.tianbo.analysis.model.Originmanifestsecondary;
  6 +import com.tianbo.analysis.service.FHLResolve;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +import javax.annotation.Resource;
  10 +
  11 +@Service("nmmsV1FHLResolve")
  12 +public class FHLResolveImpl implements FHLResolve {
  13 +
  14 + @Resource
  15 + TBBaseAirportInfoDao airportInfoDao;
  16 +
  17 + @Resource
  18 + OriginmanifestsecondaryMapper mapper;
  19 +
  20 + @Override
  21 + public boolean resolve(Originmanifestsecondary fhl) {
  22 + try{
  23 + fhl.textToStringList();
  24 + fhl.startParse();
  25 + }catch (Exception e){
  26 + e.printStackTrace();
  27 + }
  28 + return false;
  29 + }
  30 +
  31 +
  32 +}
1 package com.tianbo.analysis.tools; 1 package com.tianbo.analysis.tools;
2 2
  3 +import lombok.extern.slf4j.Slf4j;
3 import org.apache.commons.lang.StringUtils; 4 import org.apache.commons.lang.StringUtils;
4 5
  6 +import java.util.regex.Matcher;
5 import java.util.regex.Pattern; 7 import java.util.regex.Pattern;
6 8
7 /** 9 /**
8 * @author mrz 10 * @author mrz
9 * 关于运单的工具类 11 * 关于运单的工具类
10 */ 12 */
  13 +@Slf4j
11 public class WaybillTools { 14 public class WaybillTools {
12 15
13 /** 16 /**
@@ -50,4 +53,32 @@ public class WaybillTools { @@ -50,4 +53,32 @@ public class WaybillTools {
50 } 53 }
51 return ""; 54 return "";
52 } 55 }
  56 +
  57 + /**
  58 + * 模七校验
  59 + * @param waybillNo
  60 + * @return
  61 + */
  62 + public static boolean model7Check(String waybillNo){
  63 + String pattern = "\\d{3}-\\d{8}";
  64 + // 创建 Pattern 对象
  65 + Pattern r = Pattern.compile(pattern);
  66 + // 现在创建 matcher 对象
  67 + Matcher m = r.matcher(waybillNo);
  68 + if (m.find()){
  69 + String num = waybillNo.split("-")[1];
  70 + String num_7 = num.substring(0,7);
  71 + String num_end = num.substring(7,8);
  72 + if (Integer.parseInt(num_7)% 7 == Integer.parseInt(num_end)){
  73 + return true;
  74 + }else {
  75 + log.error("{}模七不通过",waybillNo);
  76 + }
  77 +
  78 + }else {
  79 + log.error("{}运单格式不正确",waybillNo);
  80 + }
  81 +
  82 + return false;
  83 + }
53 } 84 }
  1 +
  2 +import com.tianbo.analysis.NmmsAdminApplication;
  3 +import com.tianbo.analysis.exception.FFMResolveException;
  4 +import com.tianbo.analysis.model.Originmanifestsecondary;
  5 +import org.apache.logging.log4j.LogManager;
  6 +import org.apache.logging.log4j.Logger;
  7 +import org.springframework.boot.test.context.SpringBootTest;
  8 +
  9 +import java.io.IOException;
  10 +import java.util.regex.Matcher;
  11 +import java.util.regex.Pattern;
  12 +
  13 +//@RunWith(SpringRunner.class)
  14 +@SpringBootTest(classes = NmmsAdminApplication.class)
  15 +public class FHLTest {
  16 +
  17 + private static Logger logger = LogManager.getLogger(FHLTest.class);
  18 +
  19 + private static String FHL = "ZCZC\n" +
  20 + "QK CGOFD1E \n" +
  21 + ".AUHFMEY 130837\n" +
  22 + "FHL/4 \n" +
  23 + "MBI/607-23719323PVGDWC/T15K1058 \n" +
  24 + "HBS/8KR6230/PVGDWC/1/K119/50/LAPTOP COMPUTER\n" +
  25 + "/ELI \n" +
  26 + "TXT/LAPTOP COMPUTERNO FREE TEXT DESCRIPTION OF GOODS SUPPLIED \n" +
  27 + "SHP/YCH LOGISTICS KUNSHAN CO. LTD. \n" +
  28 + "/NO 5 TAOYUAN ROAD KUNSHAN FREE TRAD \n" +
  29 + "/KUNSHAN/32 \n" +
  30 + "/CN/215300/FX/112321-0 \n" +
  31 + "CNE/MINDWARE FZ LLC \n" +
  32 + "/PLOT WF07 DUBAI LOGISTICS CITY \n" +
  33 + "/DUBAI WORLD CENTR/DU \n" +
  34 + "/AE/55609/TE/112321-0 \n" +
  35 + "CVD/USD/PP/NVD/NCV/XXX\n" +
  36 + "HTS/82089000 \n" +
  37 + "/84419010 \n"+
  38 + "OCI/KR/SHP/T/VAT NUMBER1058174316 \n" +
  39 + "/KR/SHP/KC/WILSON KIM \n" +
  40 + "/KR/SHP/U/821071510907 \n" +
  41 + "/CN/CNE/T/USCI91140100792221652P \n" +
  42 + "/CN/CNE/KC/AMY LI \n" +
  43 + "/CN/CNE/U/53214229437 \n" +
  44 + "/CN/NFY/T/USCI91140100792221652P \n" +
  45 + "/CN/NFY/KC/AMY LI \n" +
  46 + "/CN/NFY/U/03517198188 \n" +
  47 + "=\n" +
  48 + "NNNN";
  49 +
  50 + @org.junit.jupiter.api.Test
  51 + public void send() {
  52 + try{
  53 + Originmanifestsecondary fhl= new Originmanifestsecondary() ;
  54 + fhl.text = FHL;
  55 + fhl.textToStringList();
  56 + fhl.startParse();
  57 + }catch (Exception e){
  58 + e.printStackTrace();
  59 + }
  60 +
  61 +
  62 + }
  63 +}