...
|
...
|
@@ -2,9 +2,13 @@ |
|
|
import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
|
|
|
import com.tianbo.analysis.NmmsAdminApplication;
|
|
|
import com.tianbo.analysis.exception.FFMResolveException;
|
|
|
import com.tianbo.analysis.model.FFMInfo;
|
|
|
import com.tianbo.analysis.model.ResultJson;
|
|
|
import com.tianbo.analysis.service.FFMResolve;
|
|
|
import com.tianbo.util.Date.DateUtil;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.junit.platform.commons.util.StringUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.boot.test.context.SpringBootTest;
|
|
|
|
|
|
import javax.validation.constraints.NotNull;
|
...
|
...
|
@@ -20,6 +24,9 @@ import java.util.regex.Pattern; |
|
|
@SpringBootTest(classes = NmmsAdminApplication.class)
|
|
|
@Slf4j
|
|
|
public class FFMTest {
|
|
|
|
|
|
@Autowired
|
|
|
FFMResolve ffmResolve;
|
|
|
private String ffm = "ZCZC\n" +
|
|
|
"QD SELKTCR SELKTCR ICNKO5X \n" +
|
|
|
".CGOFD1E 101732\n" +
|
...
|
...
|
@@ -66,352 +73,14 @@ public class FFMTest { |
|
|
"\n" +
|
|
|
"=\n" +
|
|
|
"NNNN";
|
|
|
private String flightNo;
|
|
|
private Date flightDate;
|
|
|
private String order;
|
|
|
private String originStation;
|
|
|
private String destinationStation="UNKONW";
|
|
|
private String planeNo;
|
|
|
private int currentLine = 0;
|
|
|
private static final String KEY_WORD_3 = "FFM,ULD,OSI,COR,OCI,DEG,DIM";
|
|
|
private static final String KEY_WORD_4 = "CONT,LAST";
|
|
|
private List<String> lineList = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
@org.junit.jupiter.api.Test
|
|
|
public void send(){
|
|
|
try{
|
|
|
|
|
|
if (StringUtils.isNotBlank(ffm)){
|
|
|
/**
|
|
|
* 检查报文是否包含尾部标识
|
|
|
*/
|
|
|
|
|
|
if(ffm.contains("CONT")|| ffm.contains("LAST")){
|
|
|
BufferedReader reader = new BufferedReader(new StringReader(ffm));
|
|
|
for ( String lineStr = reader.readLine();
|
|
|
lineStr != null;
|
|
|
lineStr = reader.readLine()){
|
|
|
// log.info("已读取行{}-{}",i,lineStr);
|
|
|
lineList.add(lineStr);
|
|
|
}
|
|
|
}else {
|
|
|
throw new FFMResolveException("报文尾部缺少文件结束标识LAST或者CONT");
|
|
|
}
|
|
|
|
|
|
|
|
|
resolve();
|
|
|
}
|
|
|
}catch (IOException ignored){
|
|
|
log.error(ignored.toString());
|
|
|
ignored.printStackTrace();
|
|
|
}catch (FFMResolveException ex){
|
|
|
log.error(ex.toString());
|
|
|
ex.printStackTrace();
|
|
|
}catch (ParseException e){
|
|
|
log.error("航班日期解析错误");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 寻找FFM节点并解析航班信息以及航班第一目的站信息
|
|
|
* 找到FFM节点后,开始根据currentLine的值逐步一行一行解析
|
|
|
*/
|
|
|
public void resolve() throws FFMResolveException,ParseException{
|
|
|
if (!lineList.isEmpty()){
|
|
|
int keyword_i = 0;
|
|
|
for (int i = 0; i < lineList.size(); i++) {
|
|
|
//根据行关键字走相应的解析逻辑
|
|
|
String line = lineList.get(i);
|
|
|
log.info("1.开始处理行[{}]-[{}]",i,line);
|
|
|
String keyword = keyword(line);
|
|
|
if (!"NOT_KEYWORD".equals(keyword)){
|
|
|
log.info("2.行[{}]包含关键字,开始处理",i);
|
|
|
if ("FFM".equals(keyword)){
|
|
|
currentLine = i;
|
|
|
//ffm版本
|
|
|
String ver = line.split("/")[1];
|
|
|
log.info("报文版本{}",ver.trim());
|
|
|
|
|
|
//处理航班信息
|
|
|
currentLine++;
|
|
|
flight(lineList.get(currentLine).trim());
|
|
|
|
|
|
/**
|
|
|
* 取第一目的站
|
|
|
* 这里注意,有的错误报文的航班节点后面没带或者忘了带目的站节点,
|
|
|
* 所以这里需要加个校验
|
|
|
*/
|
|
|
currentLine++;
|
|
|
destination(lineList.get(currentLine).trim());
|
|
|
currentLine++;
|
|
|
}else if ("CONT".equals(keyword) || "LAST".equals(keyword) ){
|
|
|
keyword_i++;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|
|
|
if (keyword_i==0){
|
|
|
throw new FFMResolveException("报文尾部缺少文件结束标识LAST或者CONT");
|
|
|
}
|
|
|
resolve_ULD_Waybill();
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 判断是否有散舱
|
|
|
* 有BUP散舱先处理散舱,没有则进入ULD及ULD货物解析阶段
|
|
|
* 假设默认航班有散舱,将散舱的ULD号默认为 "BUP"
|
|
|
* 设置当前的ULD 为 散舱ULD "BUP"
|
|
|
* 那么散舱下的运单直到解析到真正的板箱号后,uld变成真正的货物板箱
|
|
|
*/
|
|
|
public void resolve_ULD_Waybill() throws FFMResolveException{
|
|
|
log.info("5. 开始解析舱单列表");
|
|
|
String uld = "BUP";
|
|
|
for (int i = currentLine; i < lineList.size(); i++) {
|
|
|
String line = lineList.get(i);
|
|
|
log.info("5.1 开始解析行[{}]-[{}]",i,line);
|
|
|
|
|
|
//行尾部结束标识检查
|
|
|
String keyword = keyword(line);
|
|
|
if ("CONT".equals(keyword) || "LAST".equals(keyword)){
|
|
|
log.info("[END] 已解析到文件结束标识[{}],解析完毕",keyword);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 多航班目的站解析
|
|
|
*/
|
|
|
//校验正则1,取前三位验证是否是机场代码
|
|
|
String pattern_f = "^[A-Z]{3}$|^[A-Z]{3}/\\d{2}[A-Z]{3}\\d{4}";
|
|
|
// 创建 Pattern 对象
|
|
|
Pattern r_f = Pattern.compile(pattern_f);
|
|
|
// 现在创建 matcher 对象
|
|
|
Matcher mF = r_f.matcher(line);
|
|
|
if (mF.find()){
|
|
|
log.info("5-[FLIGHT] 适配到航班其他目的站");
|
|
|
String flightDes = line.substring(0,3);
|
|
|
if (!KEY_WORD_3.contains(flightDes)){
|
|
|
destinationStation = flightDes;
|
|
|
log.info("5-[FLIGHT] 新航班目的站为[{}]",destinationStation);
|
|
|
}
|
|
|
}
|
|
|
if ("UNKONW".equals(destinationStation)){
|
|
|
throw new FFMResolveException("航班目的站节点校验不通过.");
|
|
|
}
|
|
|
|
|
|
if ("ULD".equals(keyword)){
|
|
|
log.info("5-[ULD] 此行为ULD信息行");
|
|
|
//板箱格式适配
|
|
|
String patternULD = "^ULD/([A-Z]{3}\\S{5}\\S{2})";
|
|
|
Pattern rULD = Pattern.compile(patternULD);
|
|
|
// 现在创建 matcher 对象
|
|
|
Matcher mULD = rULD.matcher(line);
|
|
|
if (mULD.find()){
|
|
|
log.info("5-[ULD] 查到分组{}",mULD.groupCount());
|
|
|
// for (int j1 = 1; j1 < mULD.groupCount()+1; j1++) {
|
|
|
// log.info("5-[ULD] 分组{}内容为:{}",j1,mULD.group(j1));
|
|
|
// }
|
|
|
uld= mULD.group(1);
|
|
|
log.info("5-[ULD] 当前ULD已变更为{}",uld);
|
|
|
|
|
|
}else {
|
|
|
throw new FFMResolveException("ULD节点格式错误,无法适配正则(^ULD/([A-Z]{3})(\\S{5})(\\S{2})");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//运单格式适配
|
|
|
String pattern = "^(\\d{3}-\\d{8})([A-Z]{3})([A-Z]{3})/(T|P|S|M)(\\d+)(K)([0-9\\.]+)(MC)?([0-9\\.]+)?(T)?([0-9]+)?/(\\S+)";
|
|
|
// 创建 Pattern 对象
|
|
|
Pattern r = Pattern.compile(pattern);
|
|
|
// 现在创建 matcher 对象
|
|
|
Matcher m = r.matcher(line);
|
|
|
if(m.find()){
|
|
|
log.info("5-[AWB] 行[{}]属于运单行,开始解析,当前板箱号为:{}",i,uld);
|
|
|
log.info("5-[AWB] 查到分组{}",m.groupCount());
|
|
|
String waybillNo = m.group(1);
|
|
|
String waybillOrigin = m.group(2);
|
|
|
String waybillDes = m.group(3);
|
|
|
String waybillSplit = m.group(4);
|
|
|
String waybillPiece = m.group(5);
|
|
|
String waybillWeight = m.group(7);
|
|
|
String waybillVolume = m.group(9);
|
|
|
String waybillTotalPiece = m.group(11);
|
|
|
String waybillGoodsDes = m.group(12);
|
|
|
|
|
|
// for (int j = 1; j < m.groupCount()+1; j++) {
|
|
|
// log.info("5-[AWB] 分组{}内容为:{}",j,m.group(j));
|
|
|
// }
|
|
|
|
|
|
if(model7Check(waybillNo)){
|
|
|
log.info("5-[AWB] 运单-({})模七校验通过",waybillNo);
|
|
|
}else {
|
|
|
log.error("{}运单模七校验不通过",waybillNo);
|
|
|
throw new FFMResolveException(waybillNo+"运单模七校验不通过");
|
|
|
}
|
|
|
|
|
|
log.info("5-[AWB-INFO] 运单信息:报文序号:{} 所属航班:{}/{} 航班起始站/目的站:{}/{} 所属板箱:{} 运单号:{} 起始站/目的站:{}/{} 分批标识:{} 分批件重:{}/{} 体积:{} 总件数:{} 货物描述:{}",
|
|
|
order,
|
|
|
flightNo,
|
|
|
flightDate,
|
|
|
originStation,
|
|
|
destinationStation,
|
|
|
uld,
|
|
|
waybillNo,
|
|
|
waybillOrigin,
|
|
|
waybillDes,
|
|
|
waybillSplit,
|
|
|
waybillPiece,
|
|
|
waybillWeight,
|
|
|
waybillVolume,
|
|
|
waybillTotalPiece,
|
|
|
waybillGoodsDes);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 关键字识别
|
|
|
* @param text 每行的内容
|
|
|
* @return 识别为关键字的返回关键字,未被识别为关键字的返回NOT_KEYWORD;
|
|
|
* 返回的关键字 三字码关键字 机场代码关键字 文件结尾关键字 CONT,LAST
|
|
|
*/
|
|
|
public String keyword(@NotNull String text){
|
|
|
//取每行前三位
|
|
|
if (StringUtils.isNotBlank(text) && text.length()>3){
|
|
|
String s_3 = text.substring(0,3);
|
|
|
String s_4 = text.substring(0,4);
|
|
|
if(KEY_WORD_3.contains(s_3)){
|
|
|
return s_3;
|
|
|
}
|
|
|
if(KEY_WORD_4.contains(s_4)){
|
|
|
return s_4;
|
|
|
}
|
|
|
}else if(text.length()==3){
|
|
|
log.info("{}很大可能是机场目的站",text);
|
|
|
}
|
|
|
|
|
|
return "NOT_KEYWORD";
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 解析航班第一目的站
|
|
|
*
|
|
|
*/
|
|
|
public void destination(String text) throws FFMResolveException{
|
|
|
log.info("4.开始校验处理航班目的站节点信息");
|
|
|
if (text.length()>=3){
|
|
|
//校验是否空货机
|
|
|
String pattern_nil = "[A-Z]{3}/NIL";
|
|
|
Pattern r_nil = Pattern.compile(pattern_nil);
|
|
|
// 现在创建 matcher 对象
|
|
|
Matcher m_nil = r_nil.matcher(text);
|
|
|
if (m_nil.find()){
|
|
|
log.info("4.1 航班目的站货物为空");
|
|
|
throw new FFMResolveException("航班第一目的站货物为空,解析结束");
|
|
|
}
|
|
|
|
|
|
//校验是否是目的站节点
|
|
|
//校验正则1,取前三位验证是否是机场代码
|
|
|
String pattern = "^[A-Z]{3}$|^[A-Z]{3}/\\d{2}[A-Z]{3}\\d{4}";
|
|
|
// 创建 Pattern 对象
|
|
|
Pattern r = Pattern.compile(pattern);
|
|
|
// 现在创建 matcher 对象
|
|
|
Matcher m = r.matcher(text);
|
|
|
if (m.find()){
|
|
|
log.info("4.1 航班目的站节点校验通过");
|
|
|
|
|
|
destinationStation = text.substring(0,3);
|
|
|
log.info("4.2 航班目的站一为[{}]",destinationStation);
|
|
|
}else {
|
|
|
log.error("4.1[ERROR] !!航班目的站节点校验不通过!!");
|
|
|
throw new FFMResolveException("航班目的站节点校验不通过.");
|
|
|
}
|
|
|
}else {
|
|
|
throw new FFMResolveException("航班目的站节点长度不对.");
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
public void flight(String text) throws FFMResolveException, ParseException {
|
|
|
log.info("3.开始处理航班信息");
|
|
|
String[] flight = text.split("/");
|
|
|
if (flight.length>3){
|
|
|
order = flight[0];
|
|
|
log.info("3.1 报文序号为{}",order);
|
|
|
flightNo = flight[1];
|
|
|
log.info("3.2 航班号为[{}]",flightNo);
|
|
|
|
|
|
|
|
|
//航班日期取出来的格式为10SEP或者10SEP1022,带时间的注意
|
|
|
String flightDateStr = flight[2];
|
|
|
log.info("3.3 航班日期信息为[{}]",flightDate);
|
|
|
String flightDateDay = flightDateStr.substring(0,2);
|
|
|
String flightDateMonth = flightDateStr.substring(2,5);
|
|
|
|
|
|
|
|
|
/**
|
|
|
* FFM跨年问题
|
|
|
* FFM报文中的月份为12月,当前服务器时间月份为1月,视为跨年.
|
|
|
* 跨年问题需要设置FFM报文的航班年份为当前年份-1
|
|
|
*/
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
//当前年份
|
|
|
int yearNow = cal.get(Calendar.YEAR);
|
|
|
int monthNow = cal.get(Calendar.MONTH);
|
|
|
|
|
|
if (monthNow == 1 && "DEC".equals(flightDateMonth)){
|
|
|
log.info("3.3.1 航班日期跨年");
|
|
|
yearNow = yearNow-1;
|
|
|
}
|
|
|
|
|
|
flightDateStr = flightDateStr+yearNow;
|
|
|
flightDate = DateUtil.dateFormatFlight(flightDateStr);
|
|
|
|
|
|
|
|
|
originStation = flight[3];
|
|
|
log.info("3.4 航班起始站为[{}]",originStation);
|
|
|
if (flight.length>4){
|
|
|
planeNo = flight[4];
|
|
|
log.info("3.4 航班飞机号为[{}]",planeNo);
|
|
|
}
|
|
|
}else {
|
|
|
log.error("航班信息节点不正确");
|
|
|
throw new FFMResolveException("航班信息节点不正确");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 模七校验
|
|
|
* @param waybillNo
|
|
|
* @return
|
|
|
*/
|
|
|
public boolean model7Check(String waybillNo){
|
|
|
String pattern = "\\d{3}-\\d{8}";
|
|
|
// 创建 Pattern 对象
|
|
|
Pattern r = Pattern.compile(pattern);
|
|
|
// 现在创建 matcher 对象
|
|
|
Matcher m = r.matcher(waybillNo);
|
|
|
if (m.find()){
|
|
|
String num = waybillNo.split("-")[1];
|
|
|
String num_7 = num.substring(0,7);
|
|
|
String num_end = num.substring(7,8);
|
|
|
if (Integer.parseInt(num_7)% 7 == Integer.parseInt(num_end)){
|
|
|
return true;
|
|
|
}else {
|
|
|
log.error("{}模七不通过",waybillNo);
|
|
|
}
|
|
|
|
|
|
}else {
|
|
|
log.error("{}运单格式不正确",waybillNo);
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
FFMInfo ffmInfo = new FFMInfo();
|
|
|
ffmInfo.text = ffm;
|
|
|
ResultJson result = ffmResolve.resolve(ffmInfo);
|
|
|
return;
|
|
|
}
|
|
|
} |
...
|
...
|
|