作者 朱兆平

新舱单辅助管理-ffm报文解析其他部分完成

@@ -11,6 +11,7 @@ import java.util.regex.Pattern; @@ -11,6 +11,7 @@ import java.util.regex.Pattern;
11 11
12 import com.tianbo.analysis.exception.FFMResolveException; 12 import com.tianbo.analysis.exception.FFMResolveException;
13 import com.tianbo.util.Date.DateUtil; 13 import com.tianbo.util.Date.DateUtil;
  14 +import javassist.compiler.ast.Keyword;
14 import lombok.Data; 15 import lombok.Data;
15 import lombok.extern.slf4j.Slf4j; 16 import lombok.extern.slf4j.Slf4j;
16 import org.apache.commons.lang.StringUtils; 17 import org.apache.commons.lang.StringUtils;
@@ -81,6 +82,30 @@ public class FFMInfo implements Serializable { @@ -81,6 +82,30 @@ public class FFMInfo implements Serializable {
81 private static final String KEY_WORD_4 = "CONT,LAST"; 82 private static final String KEY_WORD_4 = "CONT,LAST";
82 private static final long serialVersionUID = 1L; 83 private static final long serialVersionUID = 1L;
83 84
  85 + /**
  86 + * 报文解析一级缓存
  87 + * 记录上一行解析的是ULD还是运单信息行或者其他运单信息节点;
  88 + * 初始化解析缓存
  89 + */
  90 + Map<String,String> temp = new HashMap<String,String>(){
  91 + {
  92 + put("keyword", "unknow");
  93 + put("ULD", "unknow");
  94 + put("AWB", "unknow");
  95 +
  96 + //记录上一行是否是运单信息节点
  97 + put("ISAWB", "no");
  98 + }
  99 + };
  100 +
  101 + //运单信息节点二级缓存
  102 + Map<String,String> tempAWB = new HashMap<String,String>(){
  103 + {
  104 + put("keyword", "unknow");
  105 + }
  106 + };
  107 +
  108 +
84 public FFMInfo(){ 109 public FFMInfo(){
85 110
86 } 111 }
@@ -213,6 +238,7 @@ public class FFMInfo implements Serializable { @@ -213,6 +238,7 @@ public class FFMInfo implements Serializable {
213 currentLine++; 238 currentLine++;
214 wayBillParseStartLine = currentLine; 239 wayBillParseStartLine = currentLine;
215 }else if ("CONT".equals(keyword) || "LAST".equals(keyword) ){ 240 }else if ("CONT".equals(keyword) || "LAST".equals(keyword) ){
  241 + islast = keyword;
216 keyword_i++; 242 keyword_i++;
217 } 243 }
218 244
@@ -318,6 +344,29 @@ public class FFMInfo implements Serializable { @@ -318,6 +344,29 @@ public class FFMInfo implements Serializable {
318 } 344 }
319 345
320 /** 346 /**
  347 + * 多航班目的站解析
  348 + */
  349 + private void multiDestinationParse(String line) throws FFMResolveException {
  350 +
  351 + //校验正则1,取前三位验证是否是机场代码
  352 + String pattern_f = "^[A-Z]{3}$|^[A-Z]{3}/\\d{2}[A-Z]{3}\\d{4}";
  353 + // 创建 Pattern 对象
  354 + Pattern r_f = Pattern.compile(pattern_f);
  355 + // 现在创建 matcher 对象
  356 + Matcher mF = r_f.matcher(line);
  357 + if (mF.find()){
  358 + log.debug("5-[FLIGHT] 适配到航班其他目的站");
  359 + String flightDes = line.substring(0,3);
  360 + if (!KEY_WORD_3.contains(flightDes)){
  361 + destinationstation = flightDes;
  362 + log.info("5-[FLIGHT] 新航班目的站为[{}]",destinationstation);
  363 + }
  364 + }
  365 + if (StringUtils.isEmpty(destinationstation)){
  366 + throw new FFMResolveException("航班目的站节点校验不通过.");
  367 + }
  368 + }
  369 + /**
321 * 判断是否有散舱 370 * 判断是否有散舱
322 * 有BUP散舱先处理散舱,没有则进入ULD及ULD货物解析阶段 371 * 有BUP散舱先处理散舱,没有则进入ULD及ULD货物解析阶段
323 * 假设默认航班有散舱,将散舱的ULD号默认为 "BUP" 372 * 假设默认航班有散舱,将散舱的ULD号默认为 "BUP"
@@ -328,32 +377,46 @@ public class FFMInfo implements Serializable { @@ -328,32 +377,46 @@ public class FFMInfo implements Serializable {
328 public List<FFMInfo> resolve_ULD_Waybill() throws FFMResolveException{ 377 public List<FFMInfo> resolve_ULD_Waybill() throws FFMResolveException{
329 log.info("[BILL] 开始解析舱单列表"); 378 log.info("[BILL] 开始解析舱单列表");
330 String uld = "BUP"; 379 String uld = "BUP";
  380 +
331 List<FFMInfo> ffmInfoList = new ArrayList<>(); 381 List<FFMInfo> ffmInfoList = new ArrayList<>();
332 - //默认非国际转运 382 +
333 if (this.wayBillParseStartLine ==0){ 383 if (this.wayBillParseStartLine ==0){
334 return ffmInfoList; 384 return ffmInfoList;
335 } 385 }
336 386
337 for (int i = this.wayBillParseStartLine; i < lineList.size(); i++) { 387 for (int i = this.wayBillParseStartLine; i < lineList.size(); i++) {
338 - wayBillParseStartLine++;  
339 String line = lineList.get(i); 388 String line = lineList.get(i);
340 log.debug("[BILL] 开始解析行[{}]-[{}]",i,line); 389 log.debug("[BILL] 开始解析行[{}]-[{}]",i,line);
341 - String customStatus = "-1";  
342 //行尾部结束标识检查 390 //行尾部结束标识检查
343 String keyword = keyword(line); 391 String keyword = keyword(line);
344 if ("CONT".equals(keyword) || "LAST".equals(keyword)){ 392 if ("CONT".equals(keyword) || "LAST".equals(keyword)){
345 log.info("[END] 已解析到文件结束标识[{}],解析完毕",keyword); 393 log.info("[END] 已解析到文件结束标识[{}],解析完毕",keyword);
346 - islast = keyword;  
347 break; 394 break;
348 } 395 }
349 396
  397 +
  398 + //判定前面阶段解析的是运单还是uld
  399 + String tempKey = temp.get("keyword");
  400 + log.info("[BILL] tempKey = {}",tempKey);
  401 + if ("AWB".equals(tempKey)){
  402 + log.info("[BILL] {},进入处理解析运单其他信息阶段.",line);
  403 + waybillOtherInfoParse(line,temp.get("AWB"));
  404 + }
  405 +
350 multiDestinationParse(line); 406 multiDestinationParse(line);
351 407
352 if ("ULD".equals(keyword)){ 408 if ("ULD".equals(keyword)){
353 uld = ULDParse(line); 409 uld = ULDParse(line);
  410 + temp.put("keyword","ULD");
  411 + temp.put("ULD",uld);
  412 + temp.put("ISAWB","no");
  413 + continue;
354 } 414 }
355 FFMInfo ffmInfoParsed = WayBillParse(line,uld); 415 FFMInfo ffmInfoParsed = WayBillParse(line,uld);
356 if (ffmInfoParsed!=null){ 416 if (ffmInfoParsed!=null){
  417 + temp.put("keyword","AWB");
  418 + temp.put("AWB",ffmInfoParsed.getWaybillnomaster());
  419 + temp.put("ISAWB","yes");
357 ffmInfoList.add(ffmInfoParsed); 420 ffmInfoList.add(ffmInfoParsed);
358 } 421 }
359 422
@@ -363,29 +426,7 @@ public class FFMInfo implements Serializable { @@ -363,29 +426,7 @@ public class FFMInfo implements Serializable {
363 return ffmInfoList; 426 return ffmInfoList;
364 427
365 } 428 }
366 - /**  
367 - * 多航班目的站解析  
368 - */  
369 - private void multiDestinationParse(String line) throws FFMResolveException {  
370 429
371 - //校验正则1,取前三位验证是否是机场代码  
372 - String pattern_f = "^[A-Z]{3}$|^[A-Z]{3}/\\d{2}[A-Z]{3}\\d{4}";  
373 - // 创建 Pattern 对象  
374 - Pattern r_f = Pattern.compile(pattern_f);  
375 - // 现在创建 matcher 对象  
376 - Matcher mF = r_f.matcher(line);  
377 - if (mF.find()){  
378 - log.debug("5-[FLIGHT] 适配到航班其他目的站");  
379 - String flightDes = line.substring(0,3);  
380 - if (!KEY_WORD_3.contains(flightDes)){  
381 - destinationstation = flightDes;  
382 - log.info("5-[FLIGHT] 新航班目的站为[{}]",destinationstation);  
383 - }  
384 - }  
385 - if (StringUtils.isEmpty(destinationstation)){  
386 - throw new FFMResolveException("航班目的站节点校验不通过.");  
387 - }  
388 - }  
389 430
390 /** 431 /**
391 * 解析ULD板箱号部分 432 * 解析ULD板箱号部分
@@ -453,8 +494,8 @@ public class FFMInfo implements Serializable { @@ -453,8 +494,8 @@ public class FFMInfo implements Serializable {
453 log.error("{}运单模七校验不通过",waybillNo); 494 log.error("{}运单模七校验不通过",waybillNo);
454 throw new FFMResolveException(waybillNo+"运单模七校验不通过"); 495 throw new FFMResolveException(waybillNo+"运单模七校验不通过");
455 } 496 }
456 - log.debug("运单的下一行为{}",lineList.get(wayBillParseStartLine));  
457 497
  498 + if ("CONT".equals(islast) || "LAST".equals(islast)){
458 log.info("[AWB-INFO] 运单信息:报文序号:{} 所属航班:{}/{} 航班起始站/目的站:{}/{} 所属板箱:{} 运单号:{} 起始站/目的站:{}/{} 分批标识:{} 分批件重:{}/{} 体积:{} 密度:{} 总件数:{} 货物描述:{}", 499 log.info("[AWB-INFO] 运单信息:报文序号:{} 所属航班:{}/{} 航班起始站/目的站:{}/{} 所属板箱:{} 运单号:{} 起始站/目的站:{}/{} 分批标识:{} 分批件重:{}/{} 体积:{} 密度:{} 总件数:{} 货物描述:{}",
459 order, 500 order,
460 flightno, 501 flightno,
@@ -472,6 +513,10 @@ public class FFMInfo implements Serializable { @@ -472,6 +513,10 @@ public class FFMInfo implements Serializable {
472 waybillDensity, 513 waybillDensity,
473 waybillTotalPiece, 514 waybillTotalPiece,
474 waybillGoodsDes); 515 waybillGoodsDes);
  516 + }else {
  517 + throw new FFMResolveException("报文缺少LAST标识");
  518 + }
  519 +
475 520
476 return new FFMInfo( 521 return new FFMInfo(
477 UUID.randomUUID().toString(), 522 UUID.randomUUID().toString(),
@@ -498,6 +543,232 @@ public class FFMInfo implements Serializable { @@ -498,6 +543,232 @@ public class FFMInfo implements Serializable {
498 } 543 }
499 return null; 544 return null;
500 } 545 }
  546 +
  547 + public void waybillOtherInfoParse(String line,String waybill) throws FFMResolveException {
  548 + String keywordPattern = "^OSI|^COR|^OCI|^DEG|^DIM|^/";
  549 + Pattern r = Pattern.compile(keywordPattern);
  550 + Matcher m = r.matcher(line);
  551 +
  552 +
  553 + //获取缓存
  554 + String lastIsAwb = temp.get("ISAWB");
  555 + //具备关键字
  556 + if (m.find()){
  557 + log.debug("[AWB-OTHER] {}-运单其他信息适配成功,为{}",waybill,line);
  558 +
  559 + /**
  560 + * 特殊操作代码获取及校验
  561 + * 此行的开头是/符号并且此行的上一行为运单信息
  562 + * 如果运单没有特殊操作代码 这里要处理下
  563 + * 运单信息行的下一行 不是后续航程和特殊操作这种带/的节点,就是以关键字开头的节点.
  564 + */
  565 + if (line.startsWith("/")){
  566 +
  567 + if (waybillMoveInfo(line)){
  568 + temp.put("ISAWB","no");
  569 + return;
  570 + };
  571 + if(waybillTransportPriority(line)){
  572 + temp.put("ISAWB","no");
  573 + return;
  574 + }
  575 + if("yes".equals(lastIsAwb)){
  576 + waybillSPH(line);
  577 + }else {
  578 + // 以关键字开头的下一行是以/开头,从这里取关键字处理
  579 +// 第一次的时候keyword 适配不上 第二次时有标识了
  580 + String keyword = tempAWB.get("keyword");
  581 + log.debug("[AWB-OTHER] 二级缓存keyword = {}",keyword);
  582 + Matcher matcherKeyword = r.matcher(keyword);
  583 +
  584 + //有keyword时 start以keyword为准,行开头标识
  585 + if(matcherKeyword.find()){
  586 + otherInfoParse(keyword+line);
  587 + }
  588 + }
  589 + }else {
  590 + otherInfoParse(line);
  591 + }
  592 + /**
  593 + * 进来这个方法的,处理完毕后行,都不再是运单行
  594 + * 解析完毕,缓存重置
  595 + */
  596 +
  597 + temp.put("ISAWB","no");
  598 + }
  599 +
  600 + }
  601 +
  602 +
  603 + private void otherInfoParse(String line) throws FFMResolveException {
  604 + String start = line.substring(0,3);
  605 + //第一次时 其他信息开始标识
  606 + switch (start){
  607 + case "COR":
  608 + COR_Parse(line);
  609 + break;
  610 + case "OSI":
  611 + OSI_Parse(line);
  612 + //有换行节点的写入这个
  613 + tempAWB.put("keyword",start);
  614 + break;
  615 + case "OCI":
  616 + OCI_Parse(line);
  617 + tempAWB.put("keyword",start);
  618 + break;
  619 + case "DEG":
  620 + break;
  621 + case "DIM":
  622 + break;
  623 + default:
  624 + break;
  625 + }
  626 + }
  627 +
  628 + /**
  629 + * 运单特殊操作代码
  630 + * @param line
  631 + */
  632 + private boolean waybillSPH(String line) throws FFMResolveException {
  633 + String keyword = "^/([A-Z]{3})$|^(/[A-Z]{3})+";
  634 + // 创建 Pattern 对象
  635 + Pattern r = Pattern.compile(keyword);
  636 + // 现在创建 matcher 对象
  637 + Matcher m = r.matcher(line.trim());
  638 + if (m.find()){
  639 + String[] specialCodes = line.split("/");
  640 + for (int i = 1; i < specialCodes.length; i++) {
  641 + String specialCode = specialCodes[i];
  642 + if (specialCode.trim().length()==3){
  643 + log.debug("[AWB-OTHER-SPH] 运单特殊操作代码为{}",specialCode);
  644 + }else {
  645 + throw new FFMResolveException(specialCode+"特殊操作代码格式校验错误");
  646 + }
  647 + }
  648 + return true;
  649 + }else {
  650 + return false;
  651 + }
  652 + }
  653 + /**
  654 + * @param line 报文行
  655 + * 运单节点后续航程
  656 + * /TAOXH6142/17SEP
  657 + */
  658 + private boolean waybillMoveInfo(String line) throws FFMResolveException {
  659 + String keyword = "^/(\\w{3})(\\w{2})(\\d{3})(\\w{0,2})/(\\d{2})(\\w{3})";
  660 + // 创建 Pattern 对象
  661 + Pattern r = Pattern.compile(keyword);
  662 + // 现在创建 matcher 对象
  663 + Matcher m = r.matcher(line);
  664 + if (m.find()){
  665 + String airport = m.group(1);
  666 + String carrier = m.group(2);
  667 + String flightno = carrier+m.group(3)+m.group(4);
  668 + String flightDate = m.group(5)+m.group(6);
  669 + log.debug("[AWB-OTHER-MOV] 运单后续航程信息为{}/{}/{}/{}",airport,carrier,flightno,flightDate);
  670 + return true;
  671 + }else {
  672 + return false;
  673 + }
  674 + }
  675 +
  676 + /**
  677 + * 运输优先级
  678 + * @param line
  679 + */
  680 + private boolean waybillTransportPriority(String line){
  681 + String keyword = "^/([A-Z])$";
  682 + // 创建 Pattern 对象
  683 + Pattern r = Pattern.compile(keyword);
  684 + // 现在创建 matcher 对象
  685 + Matcher m = r.matcher(line.trim());
  686 + if (m.find()){
  687 + String value = m.group(1);
  688 + log.debug("[AWB-OTHER-TP] 运单运输优先级为{}",value);
  689 + return true;
  690 + }else {
  691 + return false;
  692 + }
  693 + }
  694 + /**
  695 + * OSI Other Service Infomation 其他服务信息
  696 + * @param line 报文行
  697 + * @return
  698 + * @throws FFMResolveException
  699 + */
  700 + public String OSI_Parse(String line) throws FFMResolveException {
  701 + String keyword = "^(OSI)?/(\\S+)";
  702 + // 创建 Pattern 对象
  703 + Pattern r = Pattern.compile(keyword);
  704 + // 现在创建 matcher 对象
  705 + Matcher m = r.matcher(line);
  706 + if (m.find()){
  707 + String value = line.split("/")[1];
  708 + log.debug("[AWB-OTHER-OSI] 运单OSI其他服务信息为{}",value);
  709 + return value;
  710 + }else {
  711 + throw new FFMResolveException("COR海关原产地格式校验错误");
  712 + }
  713 + }
  714 +
  715 + /**
  716 + * @param line 报文行
  717 + * COR/海关原产地节点解析
  718 + * @return
  719 + */
  720 + public String COR_Parse(String line) throws FFMResolveException {
  721 + String keyword = "^(COR)?/([A-Z]{0,2})";
  722 + // 创建 Pattern 对象
  723 + Pattern r = Pattern.compile(keyword);
  724 + // 现在创建 matcher 对象
  725 + Matcher m = r.matcher(line);
  726 + if (m.find()){
  727 + String value = m.group(2);
  728 + log.debug("[AWB-OTHER-COR] 运单COR海关原产地信息为{}",value);
  729 + return value;
  730 + }else {
  731 + throw new FFMResolveException("COR海关原产地格式校验错误");
  732 + }
  733 +
  734 + }
  735 +
  736 + /**
  737 + * Other Customs, Security and Regulatory Control Information
  738 + * 其他海关信息解析
  739 + * @param line
  740 + * @return
  741 + * @throws FFMResolveException
  742 + */
  743 + public boolean OCI_Parse(String line) throws FFMResolveException{
  744 + String keyword = "^(OCI)?/(\\w{2})?/(\\w{3})?/(\\w{0,2})?/(\\S+)";
  745 + // 创建 Pattern 对象
  746 + Pattern r = Pattern.compile(keyword);
  747 + // 现在创建 matcher 对象
  748 + Matcher m = r.matcher(line);
  749 + if (m.find()){
  750 +
  751 + String countryCode = line.split("/")[1];
  752 + //信息标识
  753 + String infoCode = line.split("/")[2];
  754 + //海关信息标识
  755 + String customsCode =line.split("/")[3];
  756 + //补充海关信息
  757 + String value = line.split("/")[4];
  758 + log.debug("[AWB-OTHER-OCI] 运单OCI海关其他信息,国家代码:{} , 信息标识:{}, 海关信息标识:{}, 补充海关信息:{} ",
  759 + countryCode,
  760 + infoCode,
  761 + customsCode,
  762 + value);
  763 + if (value.length()>35){
  764 + throw new FFMResolveException(value+"-OCI海关其他信息长度超过35,解析错误");
  765 + }
  766 + return true;
  767 + }else {
  768 + throw new FFMResolveException("OCI海关其他信息格式校验错误");
  769 + }
  770 +
  771 + }
501 /** 772 /**
502 * 关键字识别 773 * 关键字识别
503 * @param text 每行的内容 774 * @param text 每行的内容
@@ -46,10 +46,10 @@ public class FFMResolveImpl implements FFMResolve { @@ -46,10 +46,10 @@ public class FFMResolveImpl implements FFMResolve {
46 ffm.setCustomsstatus("002"); 46 ffm.setCustomsstatus("002");
47 log.info("运单{}为国际转运货物",ffm.getWaybillnomaster()); 47 log.info("运单{}为国际转运货物",ffm.getWaybillnomaster());
48 } 48 }
49 -// int result = ffmInfoDao.insertSelective(ffm);  
50 -// if (result <=0){  
51 -// log.error("{}入库失败",ffm.getWaybillnomaster());  
52 -// } 49 + int result = ffmInfoDao.insertSelective(ffm);
  50 + if (result <=0){
  51 + log.error("{}入库失败",ffm.getWaybillnomaster());
  52 + }
53 //todo:重复报文入库逻辑将以删除旧报文插入新报文为准 53 //todo:重复报文入库逻辑将以删除旧报文插入新报文为准
54 } 54 }
55 return true; 55 return true;