|
@@ -2,9 +2,13 @@ |
|
@@ -2,9 +2,13 @@ |
2
|
import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
|
2
|
import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
|
3
|
import com.tianbo.analysis.NmmsAdminApplication;
|
3
|
import com.tianbo.analysis.NmmsAdminApplication;
|
4
|
import com.tianbo.analysis.exception.FFMResolveException;
|
4
|
import com.tianbo.analysis.exception.FFMResolveException;
|
|
|
5
|
+import com.tianbo.analysis.model.FFMInfo;
|
|
|
6
|
+import com.tianbo.analysis.model.ResultJson;
|
|
|
7
|
+import com.tianbo.analysis.service.FFMResolve;
|
5
|
import com.tianbo.util.Date.DateUtil;
|
8
|
import com.tianbo.util.Date.DateUtil;
|
6
|
import lombok.extern.slf4j.Slf4j;
|
9
|
import lombok.extern.slf4j.Slf4j;
|
7
|
import org.junit.platform.commons.util.StringUtils;
|
10
|
import org.junit.platform.commons.util.StringUtils;
|
|
|
11
|
+import org.springframework.beans.factory.annotation.Autowired;
|
8
|
import org.springframework.boot.test.context.SpringBootTest;
|
12
|
import org.springframework.boot.test.context.SpringBootTest;
|
9
|
|
13
|
|
10
|
import javax.validation.constraints.NotNull;
|
14
|
import javax.validation.constraints.NotNull;
|
|
@@ -20,6 +24,9 @@ import java.util.regex.Pattern; |
|
@@ -20,6 +24,9 @@ import java.util.regex.Pattern; |
20
|
@SpringBootTest(classes = NmmsAdminApplication.class)
|
24
|
@SpringBootTest(classes = NmmsAdminApplication.class)
|
21
|
@Slf4j
|
25
|
@Slf4j
|
22
|
public class FFMTest {
|
26
|
public class FFMTest {
|
|
|
27
|
+
|
|
|
28
|
+ @Autowired
|
|
|
29
|
+ FFMResolve ffmResolve;
|
23
|
private String ffm = "ZCZC\n" +
|
30
|
private String ffm = "ZCZC\n" +
|
24
|
"QD SELKTCR SELKTCR ICNKO5X \n" +
|
31
|
"QD SELKTCR SELKTCR ICNKO5X \n" +
|
25
|
".CGOFD1E 101732\n" +
|
32
|
".CGOFD1E 101732\n" +
|
|
@@ -66,352 +73,14 @@ public class FFMTest { |
|
@@ -66,352 +73,14 @@ public class FFMTest { |
66
|
"\n" +
|
73
|
"\n" +
|
67
|
"=\n" +
|
74
|
"=\n" +
|
68
|
"NNNN";
|
75
|
"NNNN";
|
69
|
- private String flightNo;
|
|
|
70
|
- private Date flightDate;
|
|
|
71
|
- private String order;
|
|
|
72
|
- private String originStation;
|
|
|
73
|
- private String destinationStation="UNKONW";
|
|
|
74
|
- private String planeNo;
|
|
|
75
|
- private int currentLine = 0;
|
|
|
76
|
- private static final String KEY_WORD_3 = "FFM,ULD,OSI,COR,OCI,DEG,DIM";
|
|
|
77
|
- private static final String KEY_WORD_4 = "CONT,LAST";
|
|
|
78
|
- private List<String> lineList = new ArrayList<>();
|
|
|
79
|
|
76
|
|
80
|
|
77
|
|
81
|
|
78
|
|
82
|
@org.junit.jupiter.api.Test
|
79
|
@org.junit.jupiter.api.Test
|
83
|
public void send(){
|
80
|
public void send(){
|
84
|
- try{
|
|
|
85
|
-
|
|
|
86
|
- if (StringUtils.isNotBlank(ffm)){
|
|
|
87
|
- /**
|
|
|
88
|
- * 检查报文是否包含尾部标识
|
|
|
89
|
- */
|
|
|
90
|
-
|
|
|
91
|
- if(ffm.contains("CONT")|| ffm.contains("LAST")){
|
|
|
92
|
- BufferedReader reader = new BufferedReader(new StringReader(ffm));
|
|
|
93
|
- for ( String lineStr = reader.readLine();
|
|
|
94
|
- lineStr != null;
|
|
|
95
|
- lineStr = reader.readLine()){
|
|
|
96
|
-// log.info("已读取行{}-{}",i,lineStr);
|
|
|
97
|
- lineList.add(lineStr);
|
|
|
98
|
- }
|
|
|
99
|
- }else {
|
|
|
100
|
- throw new FFMResolveException("报文尾部缺少文件结束标识LAST或者CONT");
|
|
|
101
|
- }
|
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
- resolve();
|
|
|
105
|
- }
|
|
|
106
|
- }catch (IOException ignored){
|
|
|
107
|
- log.error(ignored.toString());
|
|
|
108
|
- ignored.printStackTrace();
|
|
|
109
|
- }catch (FFMResolveException ex){
|
|
|
110
|
- log.error(ex.toString());
|
|
|
111
|
- ex.printStackTrace();
|
|
|
112
|
- }catch (ParseException e){
|
|
|
113
|
- log.error("航班日期解析错误");
|
|
|
114
|
- }
|
|
|
115
|
- }
|
|
|
116
|
-
|
|
|
117
|
- /**
|
|
|
118
|
- * 寻找FFM节点并解析航班信息以及航班第一目的站信息
|
|
|
119
|
- * 找到FFM节点后,开始根据currentLine的值逐步一行一行解析
|
|
|
120
|
- */
|
|
|
121
|
- public void resolve() throws FFMResolveException,ParseException{
|
|
|
122
|
- if (!lineList.isEmpty()){
|
|
|
123
|
- int keyword_i = 0;
|
|
|
124
|
- for (int i = 0; i < lineList.size(); i++) {
|
|
|
125
|
- //根据行关键字走相应的解析逻辑
|
|
|
126
|
- String line = lineList.get(i);
|
|
|
127
|
- log.info("1.开始处理行[{}]-[{}]",i,line);
|
|
|
128
|
- String keyword = keyword(line);
|
|
|
129
|
- if (!"NOT_KEYWORD".equals(keyword)){
|
|
|
130
|
- log.info("2.行[{}]包含关键字,开始处理",i);
|
|
|
131
|
- if ("FFM".equals(keyword)){
|
|
|
132
|
- currentLine = i;
|
|
|
133
|
- //ffm版本
|
|
|
134
|
- String ver = line.split("/")[1];
|
|
|
135
|
- log.info("报文版本{}",ver.trim());
|
|
|
136
|
-
|
|
|
137
|
- //处理航班信息
|
|
|
138
|
- currentLine++;
|
|
|
139
|
- flight(lineList.get(currentLine).trim());
|
|
|
140
|
-
|
|
|
141
|
- /**
|
|
|
142
|
- * 取第一目的站
|
|
|
143
|
- * 这里注意,有的错误报文的航班节点后面没带或者忘了带目的站节点,
|
|
|
144
|
- * 所以这里需要加个校验
|
|
|
145
|
- */
|
|
|
146
|
- currentLine++;
|
|
|
147
|
- destination(lineList.get(currentLine).trim());
|
|
|
148
|
- currentLine++;
|
|
|
149
|
- }else if ("CONT".equals(keyword) || "LAST".equals(keyword) ){
|
|
|
150
|
- keyword_i++;
|
|
|
151
|
- }
|
|
|
152
|
-
|
|
|
153
|
- }
|
|
|
154
|
- }
|
|
|
155
|
- if (keyword_i==0){
|
|
|
156
|
- throw new FFMResolveException("报文尾部缺少文件结束标识LAST或者CONT");
|
|
|
157
|
- }
|
|
|
158
|
- resolve_ULD_Waybill();
|
|
|
159
|
- }
|
|
|
160
|
-
|
|
|
161
|
- }
|
|
|
162
|
-
|
|
|
163
|
- /**
|
|
|
164
|
- * 判断是否有散舱
|
|
|
165
|
- * 有BUP散舱先处理散舱,没有则进入ULD及ULD货物解析阶段
|
|
|
166
|
- * 假设默认航班有散舱,将散舱的ULD号默认为 "BUP"
|
|
|
167
|
- * 设置当前的ULD 为 散舱ULD "BUP"
|
|
|
168
|
- * 那么散舱下的运单直到解析到真正的板箱号后,uld变成真正的货物板箱
|
|
|
169
|
- */
|
|
|
170
|
- public void resolve_ULD_Waybill() throws FFMResolveException{
|
|
|
171
|
- log.info("5. 开始解析舱单列表");
|
|
|
172
|
- String uld = "BUP";
|
|
|
173
|
- for (int i = currentLine; i < lineList.size(); i++) {
|
|
|
174
|
- String line = lineList.get(i);
|
|
|
175
|
- log.info("5.1 开始解析行[{}]-[{}]",i,line);
|
|
|
176
|
-
|
|
|
177
|
- //行尾部结束标识检查
|
|
|
178
|
- String keyword = keyword(line);
|
|
|
179
|
- if ("CONT".equals(keyword) || "LAST".equals(keyword)){
|
|
|
180
|
- log.info("[END] 已解析到文件结束标识[{}],解析完毕",keyword);
|
|
|
181
|
- break;
|
|
|
182
|
- }
|
|
|
183
|
-
|
|
|
184
|
- /**
|
|
|
185
|
- * 多航班目的站解析
|
|
|
186
|
- */
|
|
|
187
|
- //校验正则1,取前三位验证是否是机场代码
|
|
|
188
|
- String pattern_f = "^[A-Z]{3}$|^[A-Z]{3}/\\d{2}[A-Z]{3}\\d{4}";
|
|
|
189
|
- // 创建 Pattern 对象
|
|
|
190
|
- Pattern r_f = Pattern.compile(pattern_f);
|
|
|
191
|
- // 现在创建 matcher 对象
|
|
|
192
|
- Matcher mF = r_f.matcher(line);
|
|
|
193
|
- if (mF.find()){
|
|
|
194
|
- log.info("5-[FLIGHT] 适配到航班其他目的站");
|
|
|
195
|
- String flightDes = line.substring(0,3);
|
|
|
196
|
- if (!KEY_WORD_3.contains(flightDes)){
|
|
|
197
|
- destinationStation = flightDes;
|
|
|
198
|
- log.info("5-[FLIGHT] 新航班目的站为[{}]",destinationStation);
|
|
|
199
|
- }
|
|
|
200
|
- }
|
|
|
201
|
- if ("UNKONW".equals(destinationStation)){
|
|
|
202
|
- throw new FFMResolveException("航班目的站节点校验不通过.");
|
|
|
203
|
- }
|
|
|
204
|
-
|
|
|
205
|
- if ("ULD".equals(keyword)){
|
|
|
206
|
- log.info("5-[ULD] 此行为ULD信息行");
|
|
|
207
|
- //板箱格式适配
|
|
|
208
|
- String patternULD = "^ULD/([A-Z]{3}\\S{5}\\S{2})";
|
|
|
209
|
- Pattern rULD = Pattern.compile(patternULD);
|
|
|
210
|
- // 现在创建 matcher 对象
|
|
|
211
|
- Matcher mULD = rULD.matcher(line);
|
|
|
212
|
- if (mULD.find()){
|
|
|
213
|
- log.info("5-[ULD] 查到分组{}",mULD.groupCount());
|
|
|
214
|
-// for (int j1 = 1; j1 < mULD.groupCount()+1; j1++) {
|
|
|
215
|
-// log.info("5-[ULD] 分组{}内容为:{}",j1,mULD.group(j1));
|
|
|
216
|
-// }
|
|
|
217
|
- uld= mULD.group(1);
|
|
|
218
|
- log.info("5-[ULD] 当前ULD已变更为{}",uld);
|
|
|
219
|
-
|
|
|
220
|
- }else {
|
|
|
221
|
- throw new FFMResolveException("ULD节点格式错误,无法适配正则(^ULD/([A-Z]{3})(\\S{5})(\\S{2})");
|
|
|
222
|
- }
|
|
|
223
|
- }
|
|
|
224
|
-
|
|
|
225
|
- //运单格式适配
|
|
|
226
|
- 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+)";
|
|
|
227
|
- // 创建 Pattern 对象
|
|
|
228
|
- Pattern r = Pattern.compile(pattern);
|
|
|
229
|
- // 现在创建 matcher 对象
|
|
|
230
|
- Matcher m = r.matcher(line);
|
|
|
231
|
- if(m.find()){
|
|
|
232
|
- log.info("5-[AWB] 行[{}]属于运单行,开始解析,当前板箱号为:{}",i,uld);
|
|
|
233
|
- log.info("5-[AWB] 查到分组{}",m.groupCount());
|
|
|
234
|
- String waybillNo = m.group(1);
|
|
|
235
|
- String waybillOrigin = m.group(2);
|
|
|
236
|
- String waybillDes = m.group(3);
|
|
|
237
|
- String waybillSplit = m.group(4);
|
|
|
238
|
- String waybillPiece = m.group(5);
|
|
|
239
|
- String waybillWeight = m.group(7);
|
|
|
240
|
- String waybillVolume = m.group(9);
|
|
|
241
|
- String waybillTotalPiece = m.group(11);
|
|
|
242
|
- String waybillGoodsDes = m.group(12);
|
|
|
243
|
-
|
|
|
244
|
-// for (int j = 1; j < m.groupCount()+1; j++) {
|
|
|
245
|
-// log.info("5-[AWB] 分组{}内容为:{}",j,m.group(j));
|
|
|
246
|
-// }
|
|
|
247
|
-
|
|
|
248
|
- if(model7Check(waybillNo)){
|
|
|
249
|
- log.info("5-[AWB] 运单-({})模七校验通过",waybillNo);
|
|
|
250
|
- }else {
|
|
|
251
|
- log.error("{}运单模七校验不通过",waybillNo);
|
|
|
252
|
- throw new FFMResolveException(waybillNo+"运单模七校验不通过");
|
|
|
253
|
- }
|
|
|
254
|
-
|
|
|
255
|
- log.info("5-[AWB-INFO] 运单信息:报文序号:{} 所属航班:{}/{} 航班起始站/目的站:{}/{} 所属板箱:{} 运单号:{} 起始站/目的站:{}/{} 分批标识:{} 分批件重:{}/{} 体积:{} 总件数:{} 货物描述:{}",
|
|
|
256
|
- order,
|
|
|
257
|
- flightNo,
|
|
|
258
|
- flightDate,
|
|
|
259
|
- originStation,
|
|
|
260
|
- destinationStation,
|
|
|
261
|
- uld,
|
|
|
262
|
- waybillNo,
|
|
|
263
|
- waybillOrigin,
|
|
|
264
|
- waybillDes,
|
|
|
265
|
- waybillSplit,
|
|
|
266
|
- waybillPiece,
|
|
|
267
|
- waybillWeight,
|
|
|
268
|
- waybillVolume,
|
|
|
269
|
- waybillTotalPiece,
|
|
|
270
|
- waybillGoodsDes);
|
|
|
271
|
- }
|
|
|
272
|
- }
|
|
|
273
|
-
|
|
|
274
|
- }
|
|
|
275
|
-
|
|
|
276
|
- /**
|
|
|
277
|
- * 关键字识别
|
|
|
278
|
- * @param text 每行的内容
|
|
|
279
|
- * @return 识别为关键字的返回关键字,未被识别为关键字的返回NOT_KEYWORD;
|
|
|
280
|
- * 返回的关键字 三字码关键字 机场代码关键字 文件结尾关键字 CONT,LAST
|
|
|
281
|
- */
|
|
|
282
|
- public String keyword(@NotNull String text){
|
|
|
283
|
- //取每行前三位
|
|
|
284
|
- if (StringUtils.isNotBlank(text) && text.length()>3){
|
|
|
285
|
- String s_3 = text.substring(0,3);
|
|
|
286
|
- String s_4 = text.substring(0,4);
|
|
|
287
|
- if(KEY_WORD_3.contains(s_3)){
|
|
|
288
|
- return s_3;
|
|
|
289
|
- }
|
|
|
290
|
- if(KEY_WORD_4.contains(s_4)){
|
|
|
291
|
- return s_4;
|
|
|
292
|
- }
|
|
|
293
|
- }else if(text.length()==3){
|
|
|
294
|
- log.info("{}很大可能是机场目的站",text);
|
|
|
295
|
- }
|
|
|
296
|
-
|
|
|
297
|
- return "NOT_KEYWORD";
|
|
|
298
|
- }
|
|
|
299
|
-
|
|
|
300
|
- /**
|
|
|
301
|
- * 解析航班第一目的站
|
|
|
302
|
- *
|
|
|
303
|
- */
|
|
|
304
|
- public void destination(String text) throws FFMResolveException{
|
|
|
305
|
- log.info("4.开始校验处理航班目的站节点信息");
|
|
|
306
|
- if (text.length()>=3){
|
|
|
307
|
- //校验是否空货机
|
|
|
308
|
- String pattern_nil = "[A-Z]{3}/NIL";
|
|
|
309
|
- Pattern r_nil = Pattern.compile(pattern_nil);
|
|
|
310
|
- // 现在创建 matcher 对象
|
|
|
311
|
- Matcher m_nil = r_nil.matcher(text);
|
|
|
312
|
- if (m_nil.find()){
|
|
|
313
|
- log.info("4.1 航班目的站货物为空");
|
|
|
314
|
- throw new FFMResolveException("航班第一目的站货物为空,解析结束");
|
|
|
315
|
- }
|
|
|
316
|
-
|
|
|
317
|
- //校验是否是目的站节点
|
|
|
318
|
- //校验正则1,取前三位验证是否是机场代码
|
|
|
319
|
- String pattern = "^[A-Z]{3}$|^[A-Z]{3}/\\d{2}[A-Z]{3}\\d{4}";
|
|
|
320
|
- // 创建 Pattern 对象
|
|
|
321
|
- Pattern r = Pattern.compile(pattern);
|
|
|
322
|
- // 现在创建 matcher 对象
|
|
|
323
|
- Matcher m = r.matcher(text);
|
|
|
324
|
- if (m.find()){
|
|
|
325
|
- log.info("4.1 航班目的站节点校验通过");
|
|
|
326
|
-
|
|
|
327
|
- destinationStation = text.substring(0,3);
|
|
|
328
|
- log.info("4.2 航班目的站一为[{}]",destinationStation);
|
|
|
329
|
- }else {
|
|
|
330
|
- log.error("4.1[ERROR] !!航班目的站节点校验不通过!!");
|
|
|
331
|
- throw new FFMResolveException("航班目的站节点校验不通过.");
|
|
|
332
|
- }
|
|
|
333
|
- }else {
|
|
|
334
|
- throw new FFMResolveException("航班目的站节点长度不对.");
|
|
|
335
|
- }
|
|
|
336
|
-
|
|
|
337
|
-
|
|
|
338
|
- }
|
|
|
339
|
-
|
|
|
340
|
-
|
|
|
341
|
- public void flight(String text) throws FFMResolveException, ParseException {
|
|
|
342
|
- log.info("3.开始处理航班信息");
|
|
|
343
|
- String[] flight = text.split("/");
|
|
|
344
|
- if (flight.length>3){
|
|
|
345
|
- order = flight[0];
|
|
|
346
|
- log.info("3.1 报文序号为{}",order);
|
|
|
347
|
- flightNo = flight[1];
|
|
|
348
|
- log.info("3.2 航班号为[{}]",flightNo);
|
|
|
349
|
-
|
|
|
350
|
-
|
|
|
351
|
- //航班日期取出来的格式为10SEP或者10SEP1022,带时间的注意
|
|
|
352
|
- String flightDateStr = flight[2];
|
|
|
353
|
- log.info("3.3 航班日期信息为[{}]",flightDate);
|
|
|
354
|
- String flightDateDay = flightDateStr.substring(0,2);
|
|
|
355
|
- String flightDateMonth = flightDateStr.substring(2,5);
|
|
|
356
|
-
|
|
|
357
|
-
|
|
|
358
|
- /**
|
|
|
359
|
- * FFM跨年问题
|
|
|
360
|
- * FFM报文中的月份为12月,当前服务器时间月份为1月,视为跨年.
|
|
|
361
|
- * 跨年问题需要设置FFM报文的航班年份为当前年份-1
|
|
|
362
|
- */
|
|
|
363
|
- Calendar cal = Calendar.getInstance();
|
|
|
364
|
- //当前年份
|
|
|
365
|
- int yearNow = cal.get(Calendar.YEAR);
|
|
|
366
|
- int monthNow = cal.get(Calendar.MONTH);
|
|
|
367
|
-
|
|
|
368
|
- if (monthNow == 1 && "DEC".equals(flightDateMonth)){
|
|
|
369
|
- log.info("3.3.1 航班日期跨年");
|
|
|
370
|
- yearNow = yearNow-1;
|
|
|
371
|
- }
|
|
|
372
|
-
|
|
|
373
|
- flightDateStr = flightDateStr+yearNow;
|
|
|
374
|
- flightDate = DateUtil.dateFormatFlight(flightDateStr);
|
|
|
375
|
-
|
|
|
376
|
-
|
|
|
377
|
- originStation = flight[3];
|
|
|
378
|
- log.info("3.4 航班起始站为[{}]",originStation);
|
|
|
379
|
- if (flight.length>4){
|
|
|
380
|
- planeNo = flight[4];
|
|
|
381
|
- log.info("3.4 航班飞机号为[{}]",planeNo);
|
|
|
382
|
- }
|
|
|
383
|
- }else {
|
|
|
384
|
- log.error("航班信息节点不正确");
|
|
|
385
|
- throw new FFMResolveException("航班信息节点不正确");
|
|
|
386
|
- }
|
|
|
387
|
- }
|
|
|
388
|
-
|
|
|
389
|
-
|
|
|
390
|
- /**
|
|
|
391
|
- * 模七校验
|
|
|
392
|
- * @param waybillNo
|
|
|
393
|
- * @return
|
|
|
394
|
- */
|
|
|
395
|
- public boolean model7Check(String waybillNo){
|
|
|
396
|
- String pattern = "\\d{3}-\\d{8}";
|
|
|
397
|
- // 创建 Pattern 对象
|
|
|
398
|
- Pattern r = Pattern.compile(pattern);
|
|
|
399
|
- // 现在创建 matcher 对象
|
|
|
400
|
- Matcher m = r.matcher(waybillNo);
|
|
|
401
|
- if (m.find()){
|
|
|
402
|
- String num = waybillNo.split("-")[1];
|
|
|
403
|
- String num_7 = num.substring(0,7);
|
|
|
404
|
- String num_end = num.substring(7,8);
|
|
|
405
|
- if (Integer.parseInt(num_7)% 7 == Integer.parseInt(num_end)){
|
|
|
406
|
- return true;
|
|
|
407
|
- }else {
|
|
|
408
|
- log.error("{}模七不通过",waybillNo);
|
|
|
409
|
- }
|
|
|
410
|
-
|
|
|
411
|
- }else {
|
|
|
412
|
- log.error("{}运单格式不正确",waybillNo);
|
|
|
413
|
- }
|
|
|
414
|
-
|
|
|
415
|
- return false;
|
81
|
+ FFMInfo ffmInfo = new FFMInfo();
|
|
|
82
|
+ ffmInfo.text = ffm;
|
|
|
83
|
+ ResultJson result = ffmResolve.resolve(ffmInfo);
|
|
|
84
|
+ return;
|
416
|
}
|
85
|
}
|
417
|
} |
86
|
} |