ExcelData.java
15.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
package com.bsth.service.schedule.timetable;
import com.bsth.entity.Line;
import com.bsth.entity.LsStationRoute;
import com.bsth.entity.schedule.GuideboardInfo;
import com.bsth.service.LineService;
import com.bsth.service.StationRouteService;
import com.bsth.service.schedule.GuideboardInfoService;
import com.bsth.service.schedule.exception.ScheduleException;
import com.bsth.service.schedule.utils.DataToolsFile;
import com.bsth.service.schedule.utils.DataToolsFileType;
import com.bsth.service.schedule.utils.PoiUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.util.CollectionUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 时刻表Excel数据类。
*/
public class ExcelData {
/** 导入后的Excel文件对象 */
private DataToolsFile dataToolsFile;
/** 数据sheet名 */
private String sheetName;
/** 线路 */
private Line line;
/** 线路路由列表(包含上下行的起终点站) */
private List<LsStationRoute> lsStationRouteList;
/** 路牌列表 */
private List<GuideboardInfo> guideboardInfoList;
/** excel数据格式 */
private ExcelFormatType excelFormatType;
public DataToolsFile getDataToolsFile() {
return dataToolsFile;
}
public void setDataToolsFile(DataToolsFile dataToolsFile) {
this.dataToolsFile = dataToolsFile;
}
public String getSheetName() {
return sheetName;
}
public void setSheetName(String sheetName) {
this.sheetName = sheetName;
}
public Line getLine() {
return line;
}
public void setLine(Line line) {
this.line = line;
}
public List<LsStationRoute> getLsStationRouteList() {
return lsStationRouteList;
}
public void setLsStationRouteList(List<LsStationRoute> lsStationRouteList) {
this.lsStationRouteList = lsStationRouteList;
}
public List<GuideboardInfo> getGuideboardInfoList() {
return guideboardInfoList;
}
public void setGuideboardInfoList(List<GuideboardInfo> guideboardInfoList) {
this.guideboardInfoList = guideboardInfoList;
}
public ExcelFormatType getExcelFormatType() {
return excelFormatType;
}
public void setExcelFormatType(ExcelFormatType excelFormatType) {
this.excelFormatType = excelFormatType;
}
/**
* 构造函数
* @param filePath 文件路径
* @param sheetName sheet名字
* @param lineId 线路Id
* @param lineService LineServiceImpl
* @param guideboardInfoService GuideboardInfoServiceImpl
* @param lineRouteVersion 线路路由版本
* @param stationRouteService StationRouteServiceImpl
* @param excelFormatType excel数据格式
* @throws ScheduleException
* @see com.bsth.service.impl.LineServiceImpl
* @see com.bsth.service.schedule.impl.GuideboardInfoServiceImpl
* @see com.bsth.service.impl.StationRouteServiceImpl
*/
public ExcelData(
String filePath, String sheetName,
Integer lineId, LineService lineService, GuideboardInfoService guideboardInfoService,
Integer lineRouteVersion, StationRouteService stationRouteService,
ExcelFormatType excelFormatType) throws ScheduleException {
// 获取Excel文件对象,类型
File file = new File(filePath);
this.dataToolsFile = new DataToolsFile();
this.dataToolsFile.setFile(file);
if (DataToolsFileType.XLS.isThisType(file)) {
this.dataToolsFile.setFileType(DataToolsFileType.XLS);
} else if (DataToolsFileType.XLSX.isThisType(file)) {
this.dataToolsFile.setFileType(DataToolsFileType.XLSX);
} else {
throw new ScheduleException("不是xls xlsx文件!");
}
// sheet名字
this.sheetName = sheetName;
// 线路
this.line = lineService.findById(lineId);
// 线路路由列表
Map<String, Object> p1 = new HashMap<>();
p1.put("line.id_eq", lineId);
p1.put("stationMark_in", "B,E"); // 起点站
p1.put("destroy_eq", 0); // 未撤销
p1.put("versions_eq", lineRouteVersion); // 带线路版本
lsStationRouteList = (List<LsStationRoute>) stationRouteService.list_ls(p1);
// 路牌列表
p1.clear();
p1.put("xl.id_eq", lineId);
p1.put("isCancel_eq", false);
guideboardInfoList = guideboardInfoService.list(p1);
// excel数据格式
this.excelFormatType = excelFormatType;
}
/**
* 验证路由名字。
* @param colNum 列标号(从0开始)
* @param routeName 路由名字
* @throws ScheduleException
*/
private void validateRouteName(int colNum, String routeName) throws ScheduleException {
List<LsStationRoute> lsStationRoutes = new ArrayList<>();
for (LsStationRoute lsStationRoute : this.lsStationRouteList) {
if ("B".equals(lsStationRoute.getStationMark()) && routeName.equals(lsStationRoute.getStationName())) {
lsStationRoutes.add(lsStationRoute);
}
}
if (CollectionUtils.isEmpty(lsStationRoutes)) {
throw new ScheduleException(String.format("第1行,第%d列数据%s在%s站点路由中不是起点站",
colNum + 1, routeName, this.line.getName()));
} else if (lsStationRoutes.size() > 1) {
throw new ScheduleException(String.format("第1行,第%d列数据%s在%s站点路由中上下行都是起点站",
colNum + 1, routeName, this.line.getName()));
} else if (StringUtils.isEmpty(lsStationRoutes.get(0).getStationCode())) {
throw new ScheduleException(String.format("第1行,第%d列数据%s在%s站点路由中没有站点编码",
colNum + 1, routeName, this.line.getName()));
}
}
/**
* 验证路牌名字。
* @param colNum excelSheet 列index
* @param guideboardName 路牌名字
* @throws ScheduleException
*/
private void validateGuideboardName(int colNum, String guideboardName) throws ScheduleException {
List<GuideboardInfo> guideboardInfos = new ArrayList<>();
for (GuideboardInfo guideboardInfo : this.guideboardInfoList) {
if (guideboardName.equals(guideboardInfo.getLpName())) {
guideboardInfos.add(guideboardInfo);
}
}
if (CollectionUtils.isEmpty(guideboardInfos)) {
throw new ScheduleException(String.format("第%d行,第1列的路牌在%s中不存在",
colNum + 1, this.line.getName()));
} else if (guideboardInfos.size() > 1) {
throw new ScheduleException(String.format("第%d行,第1列的路牌在%s中重复",
colNum + 1, this.line.getName()));
}
}
/**
* 验证发车时间内容。
* @param rowNum excelSheet 行index
* @param colNum excelSheet 列index
* @param fcsjContent 发车时间内容
* @throws ScheduleException
*/
private void validateFcsjContent(int rowNum, int colNum, String fcsjContent) throws ScheduleException {
if (this.excelFormatType == ExcelFormatType.Normal) {
// 班次时间验证,正则表达式,格式hh:mm或者hhmm
String rex1 = "^([01]?[0-9]|2[0-3]):[0-5][0-9]$"; // hh:mm格式
String rex2 = "^([01]?[0-9]|2[0-3]),[0-5][0-9]$"; // hh,mm格式
String rex3 = "^([01]?[0-9]|2[0-3])[0-5][0-9]$"; // hhmm格式
Pattern p1 = Pattern.compile(rex1);
Pattern p2 = Pattern.compile(rex2);
Pattern p3 = Pattern.compile(rex3);
Matcher m1 = p1.matcher(fcsjContent.trim());
Matcher m2 = p2.matcher(fcsjContent.trim());
Matcher m3 = p3.matcher(fcsjContent.trim());
if ((!m1.matches()) && (!m2.matches()) && (!m3.matches())) {
throw new ScheduleException(String.format("第%d行,第%d列的发车时间格式不正确,格式应为hh:mm或hh,mm或hhmm",
rowNum + 1, colNum + 1));
}
} else if (this.excelFormatType == ExcelFormatType.Dynamic) {
String rex1 = "^(\u62a5|\u51fa)?([01]?[0-9]|2[0-3]):[0-5][0-9]$"; // (报|出)hh:mm格式
String rex2 = "^([01]?[0-9]|2[0-3]):[0-5][0-9](X|\u203b)?$"; // hh:mm(X|※)格式
Pattern p1 = Pattern.compile(rex1);
Pattern p2 = Pattern.compile(rex2);
Matcher m1 = p1.matcher(fcsjContent.trim());
Matcher m2 = p2.matcher(fcsjContent.trim());
if ((!m1.matches()) && (!m2.matches())) {
throw new ScheduleException(String.format(
"第%d行,第%d列的发车时间格式不正确,格式应为(报|出)hh:mm或hh:mm(X|※)",
rowNum + 1, colNum + 1));
}
} else {
throw new ScheduleException("未知ExcelFormatType");
}
}
private List<Integer> calcuExcelSheetColIndexList(Workbook workbook) throws ScheduleException {
// 有些列不需要参与计算,找出参与计算的列
List<Integer> colIndexList = new ArrayList<>();
Sheet sheet = workbook.getSheet(this.sheetName);
Row firstRow = sheet.getRow(0); // 获取第一行数据列
for (int i = 0; i < firstRow.getLastCellNum(); i++) {
if (excelFormatType == ExcelFormatType.Normal) {
colIndexList.add(i);
} else if (excelFormatType == ExcelFormatType.Dynamic) {
String cell_con = StringUtils.trimToEmpty(
PoiUtils.getStringValueFromCell(firstRow.getCell(i))); // trimToEmpty
if ("报到".equals(cell_con) ||
"到场".equals(cell_con) ||
"离场".equals(cell_con) ||
"总公里".equals(cell_con) ||
"营业公里".equals(cell_con) ||
"空驶公里".equals(cell_con) ||
"总工时".equals(cell_con) ||
"营业工时".equals(cell_con) ||
"营运班次".equals(cell_con)) {
// 这些列全部祛除
continue;
} else {
colIndexList.add(i);
}
} else {
throw new ScheduleException("未知excelFormatType");
}
}
return colIndexList;
}
/**
* 验证老系统ExcelSheet格式。
* @throws ScheduleException
*/
public void validateExcelSheet() throws ScheduleException {
try {
Workbook workbook = this.dataToolsFile.getFileType().getWorkBook(
this.dataToolsFile.getFile()
);
Sheet sheet = workbook.getSheet(this.sheetName);
int rowNums = sheet.getLastRowNum() + 1; // 基于0 base的,长度加1
int colNums = sheet.getRow(0).getLastCellNum(); // 不需要加1,就是长度
if (rowNums == 0 || colNums == 0) { // 工作区是否为空
throw new Exception(String.format("%s 工作区没有数据!", this.sheetName));
} else {
if (rowNums <= 1 || rowNums <= 1) {
throw new Exception(String.format("工作区至少包含2行2列的数据"));
} else {
List<Integer> colIndexList = this.calcuExcelSheetColIndexList(workbook);
Row firstRow = sheet.getRow(0); // 获取第一行数据列
for (int n = 0; n < colIndexList.size(); n++) {
int i = colIndexList.get(n);
String cell_con = StringUtils.trimToEmpty(
PoiUtils.getStringValueFromCell(firstRow.getCell(i))); // trimToEmpty
if (StringUtils.isEmpty(cell_con)) {
throw new Exception(String.format("第1行,第%d列数据不能为空", i + 1));
} else {
// 正则表达式去除右侧数字
// cell_con = cell_con.replaceAll("[\\d+]", "");
// cell_con = cell_con.replaceAll("(\\d+)$", "");
// 如果站名中有类似->{数字},使用正则表达式过滤掉
cell_con = cell_con.replaceAll("(->\\d+)", "");
if (i == 0) { // 第一列必须是路牌2个字
if (!"路牌".equals(cell_con.trim())) {
throw new Exception("第1行,第1列数据必须是路牌2个字");
}
} else { // 排除出场,进场,其余内容到站点路由里查询,以各个方向的起点站为查询依据
if ((!"出场".equals(cell_con.trim())) &&
(!"进场".equals(cell_con.trim()))) {
validateRouteName(i, cell_con.trim()); // 验证路由名字
}
}
}
}
// 验证路牌内容
Map<String, Integer> gbindexmap = new HashMap<>(); // 记录每个路牌在第几行
for (int i = 1; i < rowNums; i++) { // 从第2行开始验证数据
Cell cell = sheet.getRow(i).getCell(0); // 获取第1列
String bcell_con = StringUtils.trimToEmpty(
PoiUtils.getStringValueFromCell(cell)); // trimToEmpty
if (StringUtils.isEmpty(bcell_con)) {
throw new Exception(String.format("第%d行,第1列路牌无数据", i + 1));
} else if (gbindexmap.get(bcell_con.trim()) != null) {
throw new Exception(String.format("第%d行,第1列的路牌数据与第%d行,第1列数据重复",
i + 1,
gbindexmap.get(bcell_con.trim())));
} else {
validateGuideboardName(i, bcell_con.trim()); // 验证路牌名字
gbindexmap.put(bcell_con.trim(), i + 1);
}
}
// 验证发车时间格式
for (int i = 1; i < rowNums; i++) { // 从第2行开始验证数据
Row row = sheet.getRow(i);
for (int n = 1; n < colIndexList.size(); n++) { // 从第2列开始
int j = colIndexList.get(n);
String bcell_con = StringUtils.trimToEmpty(
PoiUtils.getStringValueFromCell(
row.getCell(j))).replaceAll("\\s*", ""); // trimToEmpty
if (StringUtils.isNotEmpty(bcell_con)) {
validateFcsjContent(i, j, bcell_con);
}
}
}
}
}
} catch (Exception exp) {
exp.printStackTrace();
throw new ScheduleException(exp.getMessage());
}
}
}