This commit is contained in:
2026-08-24 23:28:30 +08:00
parent 71114f73dc
commit 4698ca34fb
20 changed files with 1146 additions and 57 deletions
@@ -100,7 +100,7 @@ public class AirportMasterExcel implements Serializable {
@ExcelProperty("备注")
private String remark;
@ExcelProperty
@ExcelIgnore
private String errorMessage;
}
@@ -53,13 +53,13 @@ public class PortTerminalExcel implements Serializable {
@ExcelIgnore
private Long id;
@ExcelProperty("编码")
@ExcelProperty("*编码")
private String code;
@ExcelProperty("港口/码头名称")
@ExcelProperty("*港口/码头名称")
private String name;
@ExcelProperty("类型")
@ExcelProperty("*类型")
private String category;
@ExcelProperty("上级港口")
@@ -68,13 +68,13 @@ public class PortTerminalExcel implements Serializable {
@ExcelProperty("上级港口编码")
private String parentCode;
@ExcelProperty("国家")
@ExcelProperty("*国家")
private String country;
@ExcelProperty("城市")
@ExcelProperty("*城市")
private String city;
@ExcelProperty("区县")
@ExcelProperty("*区县")
private String districtName;
@ExcelProperty("行政区划编码")
@@ -83,24 +83,24 @@ public class PortTerminalExcel implements Serializable {
@ExcelProperty("详细地址")
private String detailAddress;
@ExcelProperty("经度")
@ExcelProperty("*经度")
@NumberFormat("0.000000")
private BigDecimal longitude;
@ExcelProperty("纬度")
@ExcelProperty("*纬度")
@NumberFormat("0.000000")
private BigDecimal latitude;
@ExcelProperty("数据来源")
@ExcelIgnore
private String dataSource;
@ExcelProperty("启停状态")
@ExcelIgnore
private String statusName;
@ExcelProperty("备注")
private String remark;
@ExcelProperty
@ExcelIgnore
private String errorMessage;
}
@@ -96,7 +96,7 @@ public class RailwayStationExcel implements Serializable {
@ExcelProperty("备注")
private String remark;
@ExcelProperty
@ExcelIgnore
private String errorMessage;
}
@@ -43,12 +43,15 @@ import org.springblade.system.service.IAirportMasterService;
import org.springblade.system.service.IRegionService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
@@ -124,24 +127,184 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
throw new ServiceException("导入数据不能为空");
}
List<AirportMasterExcel> errorList = new ArrayList<>();
List<AirportMaster> airportMasterList = new ArrayList<>();
Map<String, Integer> iataCodeCountMap = buildImportValueCountMap(data.stream()
.map(excel -> trimToEmpty(excel.getIataCode()).toUpperCase(Locale.ROOT))
.toList());
Map<String, Integer> icaoCodeCountMap = buildImportValueCountMap(data.stream()
.map(excel -> trimToEmpty(excel.getIcaoCode()).toUpperCase(Locale.ROOT))
.toList());
for (int index = 0; index < data.size(); index++) {
AirportMasterExcel excel = data.get(index);
AirportMaster airportMaster = Objects.requireNonNull(BeanUtil.copyProperties(excel, AirportMaster.class));
airportMaster.setDataSource(SOURCE_BATCH);
airportMaster.setStatus(STATUS_ENABLED);
normalizeImportAirportMaster(airportMaster);
List<String> validationErrors = validateImportAirportMaster(airportMaster, iataCodeCountMap, icaoCodeCountMap);
if (Func.isNotEmpty(validationErrors)) {
excel.setErrorMessage(formatImportErrorMessage(index + 2, validationErrors));
errorList.add(excel);
continue;
}
try {
AirportMaster airportMaster = Objects.requireNonNull(BeanUtil.copyProperties(excel, AirportMaster.class));
airportMaster.setDataSource(SOURCE_BATCH);
airportMaster.setStatus(STATUS_ENABLED);
prepare(airportMaster, SOURCE_BATCH);
validate(airportMaster);
save(airportMaster);
airportMasterList.add(airportMaster);
} catch (Exception exception) {
String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败";
excel.setErrorMessage("" + (index + 2) + "行:" + message);
excel.setErrorMessage(formatImportErrorMessage(index + 2, List.of(message)));
errorList.add(excel);
}
}
if (Func.isNotEmpty(errorList)) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return errorList;
}
for (AirportMaster airportMaster : airportMasterList) {
if (!save(airportMaster)) {
throw new ServiceException("空港机场保存失败");
}
}
return errorList;
}
private Map<String, Integer> buildImportValueCountMap(List<String> values) {
Map<String, Integer> valueCountMap = new HashMap<>();
for (String value : values) {
if (Func.isNotEmpty(value)) {
valueCountMap.merge(value, 1, Integer::sum);
}
}
return valueCountMap;
}
private void normalizeImportAirportMaster(AirportMaster airportMaster) {
airportMaster.setIataCode(trimToEmpty(airportMaster.getIataCode()).toUpperCase(Locale.ROOT));
airportMaster.setCode(CODE_PREFIX + airportMaster.getIataCode());
airportMaster.setIcaoCode(trimToEmpty(airportMaster.getIcaoCode()).toUpperCase(Locale.ROOT));
airportMaster.setName(trimToEmpty(airportMaster.getName()));
airportMaster.setShortName(trimToNull(airportMaster.getShortName()));
airportMaster.setProvinceCode(trimToNull(airportMaster.getProvinceCode()));
airportMaster.setProvinceName(trimToNull(airportMaster.getProvinceName()));
airportMaster.setCityCode(trimToNull(airportMaster.getCityCode()));
airportMaster.setCityName(trimToNull(airportMaster.getCityName()));
airportMaster.setDistrictCode(trimToNull(airportMaster.getDistrictCode()));
airportMaster.setDistrictName(trimToNull(airportMaster.getDistrictName()));
airportMaster.setRegionCode(trimToNull(airportMaster.getRegionCode()));
if (Func.isEmpty(airportMaster.getDistrictCode()) && Func.isNotEmpty(airportMaster.getRegionCode())) {
airportMaster.setDistrictCode(airportMaster.getRegionCode());
}
airportMaster.setDetailAddress(trimToNull(airportMaster.getDetailAddress()));
airportMaster.setRemark(trimToNull(airportMaster.getRemark()));
}
private List<String> validateImportAirportMaster(AirportMaster airportMaster,
Map<String, Integer> iataCodeCountMap, Map<String, Integer> icaoCodeCountMap) {
List<String> validationErrors = new ArrayList<>();
if (Func.isEmpty(airportMaster.getIataCode())) {
addValidationError(validationErrors, "IATA编码不能为空");
} else {
if (!IATA_CODE_PATTERN.matcher(airportMaster.getIataCode()).matches()) {
addValidationError(validationErrors, "IATA编码为3位大写字母");
}
if (iataCodeCountMap.getOrDefault(airportMaster.getIataCode(), 0) > 1) {
addValidationError(validationErrors, "IATA编码在本次导入中重复");
}
validateImportUnique(AirportMaster::getIataCode, airportMaster.getIataCode(), "该IATA编码已存在", validationErrors);
validateImportUnique(AirportMaster::getCode, airportMaster.getCode(), "该编码已存在", validationErrors);
}
if (Func.isEmpty(airportMaster.getIcaoCode())) {
addValidationError(validationErrors, "ICAO代码不能为空");
} else {
if (!ICAO_CODE_PATTERN.matcher(airportMaster.getIcaoCode()).matches()) {
addValidationError(validationErrors, "ICAO代码为4位大写字母");
}
if (icaoCodeCountMap.getOrDefault(airportMaster.getIcaoCode(), 0) > 1) {
addValidationError(validationErrors, "ICAO代码在本次导入中重复");
}
validateImportUnique(AirportMaster::getIcaoCode, airportMaster.getIcaoCode(), "该ICAO代码已存在", validationErrors);
}
if (Func.isEmpty(airportMaster.getName())) {
addValidationError(validationErrors, "机场标准名称不能为空");
}
validateImportLength(airportMaster.getCode(), CODE_MAX_LENGTH, "编码不能超过20字", validationErrors);
validateImportLength(airportMaster.getName(), NAME_MAX_LENGTH, "机场标准名称不能超过100字", validationErrors);
validateImportLength(airportMaster.getShortName(), SHORT_NAME_MAX_LENGTH, "机场简称不能超过100字", validationErrors);
validateImportLength(airportMaster.getProvinceName(), REGION_NAME_MAX_LENGTH, "所属省份不能超过128字", validationErrors);
validateImportLength(airportMaster.getCityName(), REGION_NAME_MAX_LENGTH, "所属城市不能超过128字", validationErrors);
validateImportLength(airportMaster.getDistrictName(), REGION_NAME_MAX_LENGTH, "所属区县不能超过128字", validationErrors);
validateImportLength(airportMaster.getRegionCode(), REGION_CODE_MAX_LENGTH, "行政区划编号不能超过32字", validationErrors);
validateImportLength(airportMaster.getDetailAddress(), DETAIL_ADDRESS_MAX_LENGTH, "详细地址不能超过255字", validationErrors);
validateImportLength(airportMaster.getRemark(), REMARK_MAX_LENGTH, "备注不能超过200字", validationErrors);
if (Func.isEmpty(airportMaster.getLongitude())) {
addValidationError(validationErrors, "经度不能为空");
} else if (!validRange(airportMaster.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE)) {
addValidationError(validationErrors, "经度范围为 -180 到 180");
}
if (Func.isEmpty(airportMaster.getLatitude())) {
addValidationError(validationErrors, "纬度不能为空");
} else if (!validRange(airportMaster.getLatitude(), MIN_LATITUDE, MAX_LATITUDE)) {
addValidationError(validationErrors, "纬度范围为 -90 到 90");
}
validateImportAirportRegion(airportMaster, validationErrors);
return validationErrors;
}
private void validateImportAirportRegion(AirportMaster airportMaster, List<String> validationErrors) {
boolean provinceMissing = Func.isEmpty(airportMaster.getProvinceCode()) && Func.isEmpty(airportMaster.getProvinceName());
boolean cityMissing = Func.isEmpty(airportMaster.getCityCode()) && Func.isEmpty(airportMaster.getCityName());
boolean districtMissing = Func.isEmpty(airportMaster.getDistrictCode()) && Func.isEmpty(airportMaster.getDistrictName());
if (provinceMissing) {
addValidationError(validationErrors, "所属省份不能为空");
}
if (cityMissing) {
addValidationError(validationErrors, "所属城市不能为空");
}
if (districtMissing) {
addValidationError(validationErrors, "所属区县不能为空");
}
if (provinceMissing || cityMissing || districtMissing) {
return;
}
try {
fillRegion(airportMaster);
} catch (ServiceException exception) {
addValidationError(validationErrors, exception.getMessage());
}
}
private void validateImportUnique(com.baomidou.mybatisplus.core.toolkit.support.SFunction<AirportMaster, ?> column,
String value, String message, List<String> validationErrors) {
if (count(Wrappers.<AirportMaster>lambdaQuery()
.eq(column, value)
.eq(AirportMaster::getIsDeleted, 0)) > 0L) {
addValidationError(validationErrors, message);
}
}
private void validateImportLength(String value, int maxLength, String message, List<String> validationErrors) {
if (Func.isNotEmpty(value) && value.length() > maxLength) {
addValidationError(validationErrors, message);
}
}
private void addValidationError(List<String> validationErrors, String message) {
if (Func.isNotEmpty(message) && !validationErrors.contains(message)) {
validationErrors.add(message);
}
}
private String formatImportErrorMessage(int rowNumber, List<String> validationErrors) {
StringBuilder errorMessage = new StringBuilder("").append(rowNumber).append("行:");
for (int index = 0; index < validationErrors.size(); index++) {
if (index > 0) {
errorMessage.append(System.lineSeparator());
}
errorMessage.append(index + 1).append(". ").append(validationErrors.get(index));
}
return errorMessage.toString();
}
@Override
public List<AirportMasterExcel> exportAirportMaster(Wrapper<AirportMaster> queryWrapper) {
List<AirportMaster> airportMasterList = list(queryWrapper);
@@ -43,12 +43,15 @@ import org.springblade.system.service.IPortTerminalService;
import org.springblade.system.service.IRegionService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
@@ -134,24 +137,187 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
throw new ServiceException("导入数据不能为空");
}
List<PortTerminalExcel> errorList = new ArrayList<>();
List<PortTerminal> portTerminalList = new ArrayList<>();
Map<String, Integer> codeCountMap = buildImportCodeCountMap(data);
for (int index = 0; index < data.size(); index++) {
PortTerminalExcel excel = data.get(index);
PortTerminal portTerminal = Objects.requireNonNull(BeanUtil.copyProperties(excel, PortTerminal.class));
portTerminal.setDataSource(SOURCE_BATCH);
portTerminal.setStatus(STATUS_ENABLED);
normalizeImportPortTerminal(portTerminal);
List<String> validationErrors = validateImportPortTerminal(portTerminal, codeCountMap);
if (Func.isNotEmpty(validationErrors)) {
excel.setErrorMessage(formatImportErrorMessage(index + 2, validationErrors));
errorList.add(excel);
continue;
}
try {
PortTerminal portTerminal = Objects.requireNonNull(BeanUtil.copyProperties(excel, PortTerminal.class));
portTerminal.setDataSource(SOURCE_BATCH);
portTerminal.setStatus(STATUS_ENABLED);
prepare(portTerminal, SOURCE_BATCH);
validate(portTerminal);
save(portTerminal);
portTerminalList.add(portTerminal);
} catch (Exception exception) {
String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败";
excel.setErrorMessage("" + (index + 2) + "行:" + message);
excel.setErrorMessage(formatImportErrorMessage(index + 2, List.of(message)));
errorList.add(excel);
}
}
if (Func.isNotEmpty(errorList)) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return errorList;
}
for (PortTerminal portTerminal : portTerminalList) {
if (!save(portTerminal)) {
throw new ServiceException("港口码头保存失败");
}
}
return errorList;
}
private String formatImportErrorMessage(int rowNumber, List<String> validationErrors) {
StringBuilder errorMessage = new StringBuilder("").append(rowNumber).append("行:");
for (int index = 0; index < validationErrors.size(); index++) {
if (index > 0) {
errorMessage.append(System.lineSeparator());
}
errorMessage.append(index + 1).append(". ").append(validationErrors.get(index));
}
return errorMessage.toString();
}
private Map<String, Integer> buildImportCodeCountMap(List<PortTerminalExcel> data) {
Map<String, Integer> codeCountMap = new HashMap<>();
for (PortTerminalExcel excel : data) {
String code = trimToEmpty(excel.getCode()).toUpperCase(Locale.ROOT);
if (Func.isNotEmpty(code)) {
codeCountMap.merge(code, 1, Integer::sum);
}
}
return codeCountMap;
}
private void normalizeImportPortTerminal(PortTerminal portTerminal) {
portTerminal.setCode(trimToEmpty(portTerminal.getCode()).toUpperCase(Locale.ROOT));
portTerminal.setCategory(trimToEmpty(portTerminal.getCategory()));
portTerminal.setName(trimToEmpty(portTerminal.getName()));
portTerminal.setParentCode(trimToNull(portTerminal.getParentCode()));
if (Func.isNotEmpty(portTerminal.getParentCode())) {
portTerminal.setParentCode(portTerminal.getParentCode().toUpperCase(Locale.ROOT));
}
portTerminal.setParentName(trimToNull(portTerminal.getParentName()));
portTerminal.setCountry(trimToEmpty(portTerminal.getCountry()));
portTerminal.setCity(trimToEmpty(portTerminal.getCity()));
portTerminal.setDistrictCode(trimToNull(portTerminal.getDistrictCode()));
portTerminal.setDistrictName(trimToNull(portTerminal.getDistrictName()));
portTerminal.setRegionCode(trimToNull(portTerminal.getRegionCode()));
if (Func.isEmpty(portTerminal.getDistrictCode()) && Func.isNotEmpty(portTerminal.getRegionCode())) {
portTerminal.setDistrictCode(portTerminal.getRegionCode());
}
portTerminal.setDetailAddress(trimToNull(portTerminal.getDetailAddress()));
portTerminal.setRemark(trimToNull(portTerminal.getRemark()));
}
private List<String> validateImportPortTerminal(PortTerminal portTerminal, Map<String, Integer> codeCountMap) {
List<String> validationErrors = new ArrayList<>();
if (!CATEGORY_PORT.equals(portTerminal.getCategory()) && !CATEGORY_TERMINAL.equals(portTerminal.getCategory())) {
addValidationError(validationErrors, "类型只能为港口或码头");
}
if (Func.isEmpty(portTerminal.getCode())) {
addValidationError(validationErrors, "编码不能为空");
} else {
validateImportLength(portTerminal.getCode(), CODE_MAX_LENGTH, "编码不能超过30字", validationErrors);
if (CATEGORY_PORT.equals(portTerminal.getCategory()) && !PORT_CODE_PATTERN.matcher(portTerminal.getCode()).matches()) {
addValidationError(validationErrors, "港口编码为5位大写字母");
}
if (CATEGORY_TERMINAL.equals(portTerminal.getCategory()) && !TERMINAL_CODE_PATTERN.matcher(portTerminal.getCode()).matches()) {
addValidationError(validationErrors, "码头编码格式为港口编码-码头标识");
}
if (codeCountMap.getOrDefault(portTerminal.getCode(), 0) > 1) {
addValidationError(validationErrors, "编码在本次导入中重复");
}
if (count(Wrappers.<PortTerminal>lambdaQuery()
.eq(PortTerminal::getCode, portTerminal.getCode())
.eq(PortTerminal::getIsDeleted, 0)) > 0L) {
addValidationError(validationErrors, "该编码已存在");
}
}
if (Func.isEmpty(portTerminal.getName())) {
addValidationError(validationErrors, "港口/码头名称不能为空");
}
validateImportLength(portTerminal.getName(), NAME_MAX_LENGTH, "港口/码头名称不能超过100字", validationErrors);
validateImportLength(portTerminal.getCountry(), REGION_MAX_LENGTH, "国家不能超过50字", validationErrors);
validateImportLength(portTerminal.getCity(), REGION_MAX_LENGTH, "城市不能超过50字", validationErrors);
validateImportLength(portTerminal.getDistrictName(), REGION_MAX_LENGTH, "区县不能超过50字", validationErrors);
validateImportLength(portTerminal.getRegionCode(), REGION_CODE_MAX_LENGTH, "行政区划编码不能超过32字", validationErrors);
validateImportLength(portTerminal.getDetailAddress(), DETAIL_ADDRESS_MAX_LENGTH, "详细地址不能超过255字", validationErrors);
validateImportLength(portTerminal.getRemark(), REMARK_MAX_LENGTH, "备注不能超过200个字", validationErrors);
if (Func.isEmpty(portTerminal.getLongitude())) {
addValidationError(validationErrors, "经度不能为空");
} else if (!validRange(portTerminal.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE)) {
addValidationError(validationErrors, "经度范围为 -180 到 180");
}
if (Func.isEmpty(portTerminal.getLatitude())) {
addValidationError(validationErrors, "纬度不能为空");
} else if (!validRange(portTerminal.getLatitude(), MIN_LATITUDE, MAX_LATITUDE)) {
addValidationError(validationErrors, "纬度范围为 -90 到 90");
}
if (CATEGORY_PORT.equals(portTerminal.getCategory())) {
validateImportPortRegion(portTerminal, validationErrors);
}
if (CATEGORY_TERMINAL.equals(portTerminal.getCategory())) {
validateImportParentPort(portTerminal, validationErrors);
}
return validationErrors;
}
private void validateImportPortRegion(PortTerminal portTerminal, List<String> validationErrors) {
if (Func.isEmpty(portTerminal.getCountry())) {
addValidationError(validationErrors, "国家不能为空");
}
if (Func.isEmpty(portTerminal.getCity())) {
addValidationError(validationErrors, "城市不能为空");
}
if (Func.isEmpty(portTerminal.getDistrictCode()) && Func.isEmpty(portTerminal.getDistrictName())) {
addValidationError(validationErrors, "区县不能为空");
return;
}
try {
fillRegion(portTerminal);
} catch (ServiceException exception) {
addValidationError(validationErrors, exception.getMessage());
}
}
private void validateImportParentPort(PortTerminal portTerminal, List<String> validationErrors) {
if (Func.isEmpty(portTerminal.getParentCode())) {
addValidationError(validationErrors, "上级港口编码不能为空");
return;
}
PortTerminal parentPort = getOne(Wrappers.<PortTerminal>lambdaQuery()
.eq(PortTerminal::getCode, portTerminal.getParentCode())
.eq(PortTerminal::getCategory, CATEGORY_PORT)
.eq(PortTerminal::getIsDeleted, 0), false);
if (Func.isEmpty(parentPort)) {
addValidationError(validationErrors, "上级港口编码对应的港口不存在");
} else if (Func.isNotEmpty(portTerminal.getParentName()) && !Objects.equals(portTerminal.getParentName(), parentPort.getName())) {
addValidationError(validationErrors, "上级港口与上级港口编码不匹配");
}
if (Func.isNotEmpty(portTerminal.getCode()) && !portTerminal.getCode().startsWith(portTerminal.getParentCode() + "-")) {
addValidationError(validationErrors, "码头编码必须以上级港口编码开头");
}
}
private void validateImportLength(String value, int maxLength, String message, List<String> validationErrors) {
if (Func.isNotEmpty(value) && value.length() > maxLength) {
addValidationError(validationErrors, message);
}
}
private void addValidationError(List<String> validationErrors, String message) {
if (Func.isNotEmpty(message) && !validationErrors.contains(message)) {
validationErrors.add(message);
}
}
@Override
public List<PortTerminalExcel> exportPortTerminal(Wrapper<PortTerminal> queryWrapper) {
List<PortTerminal> portTerminalList = list(queryWrapper);
@@ -43,12 +43,15 @@ import org.springblade.system.service.IRailwayStationService;
import org.springblade.system.service.IRegionService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
@@ -127,26 +130,196 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
throw new ServiceException("导入数据不能为空");
}
List<RailwayStationExcel> errorList = new ArrayList<>();
List<RailwayStation> railwayStationList = new ArrayList<>();
Map<String, Integer> tmisCodeCountMap = buildImportValueCountMap(data.stream()
.map(excel -> trimToEmpty(excel.getTmisCode()))
.toList());
Map<String, Integer> telegraphCodeCountMap = buildImportValueCountMap(data.stream()
.map(excel -> trimToEmpty(excel.getTelegraphCode()).toUpperCase(Locale.ROOT))
.toList());
for (int index = 0; index < data.size(); index++) {
RailwayStationExcel excel = data.get(index);
RailwayStation railwayStation = Objects.requireNonNull(BeanUtil.copyProperties(excel, RailwayStation.class));
List<String> validationErrors = new ArrayList<>();
railwayStation.setLongitude(parseImportCoordinate(excel.getLongitude(), "经度", validationErrors));
railwayStation.setLatitude(parseImportCoordinate(excel.getLatitude(), "纬度", validationErrors));
railwayStation.setDataSource(SOURCE_BATCH);
railwayStation.setStatus(STATUS_ENABLED);
normalizeImportRailwayStation(railwayStation);
validationErrors.addAll(validateImportRailwayStation(railwayStation, tmisCodeCountMap, telegraphCodeCountMap));
if (Func.isNotEmpty(validationErrors)) {
excel.setErrorMessage(formatImportErrorMessage(index + 2, validationErrors));
errorList.add(excel);
continue;
}
try {
RailwayStation railwayStation = Objects.requireNonNull(BeanUtil.copyProperties(excel, RailwayStation.class));
railwayStation.setLongitude(parseCoordinate(excel.getLongitude(), "经度"));
railwayStation.setLatitude(parseCoordinate(excel.getLatitude(), "纬度"));
railwayStation.setDataSource(SOURCE_BATCH);
railwayStation.setStatus(STATUS_ENABLED);
prepare(railwayStation, SOURCE_BATCH);
validate(railwayStation);
save(railwayStation);
railwayStationList.add(railwayStation);
} catch (Exception exception) {
String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败";
excel.setErrorMessage("" + (index + 2) + "行:" + message);
excel.setErrorMessage(formatImportErrorMessage(index + 2, List.of(message)));
errorList.add(excel);
}
}
if (Func.isNotEmpty(errorList)) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return errorList;
}
for (RailwayStation railwayStation : railwayStationList) {
if (!save(railwayStation)) {
throw new ServiceException("铁路车站保存失败");
}
}
return errorList;
}
private Map<String, Integer> buildImportValueCountMap(List<String> values) {
Map<String, Integer> valueCountMap = new HashMap<>();
for (String value : values) {
if (Func.isNotEmpty(value)) {
valueCountMap.merge(value, 1, Integer::sum);
}
}
return valueCountMap;
}
private void normalizeImportRailwayStation(RailwayStation railwayStation) {
railwayStation.setTmisCode(trimToEmpty(railwayStation.getTmisCode()));
railwayStation.setCode(CODE_PREFIX + railwayStation.getTmisCode());
railwayStation.setTelegraphCode(trimToEmpty(railwayStation.getTelegraphCode()).toUpperCase(Locale.ROOT));
railwayStation.setName(trimToEmpty(railwayStation.getName()));
railwayStation.setProvinceCode(trimToNull(railwayStation.getProvinceCode()));
railwayStation.setProvinceName(trimToNull(railwayStation.getProvinceName()));
railwayStation.setCityCode(trimToNull(railwayStation.getCityCode()));
railwayStation.setCityName(trimToNull(railwayStation.getCityName()));
railwayStation.setDistrictCode(trimToNull(railwayStation.getDistrictCode()));
railwayStation.setDistrictName(trimToNull(railwayStation.getDistrictName()));
railwayStation.setRegionCode(trimToNull(railwayStation.getRegionCode()));
if (Func.isEmpty(railwayStation.getDistrictCode()) && Func.isNotEmpty(railwayStation.getRegionCode())) {
railwayStation.setDistrictCode(railwayStation.getRegionCode());
}
railwayStation.setDetailAddress(trimToNull(railwayStation.getDetailAddress()));
railwayStation.setRemark(trimToNull(railwayStation.getRemark()));
}
private BigDecimal parseImportCoordinate(String value, String name, List<String> validationErrors) {
String trimValue = trimToEmpty(value);
if (trimValue.isEmpty()) {
return null;
}
try {
return new BigDecimal(trimValue);
} catch (NumberFormatException exception) {
addValidationError(validationErrors, name + "范围不正确");
return null;
}
}
private List<String> validateImportRailwayStation(RailwayStation railwayStation,
Map<String, Integer> tmisCodeCountMap, Map<String, Integer> telegraphCodeCountMap) {
List<String> validationErrors = new ArrayList<>();
if (Func.isEmpty(railwayStation.getTmisCode())) {
addValidationError(validationErrors, "TMIS国标编码不能为空");
} else {
if (!TMIS_CODE_PATTERN.matcher(railwayStation.getTmisCode()).matches()) {
addValidationError(validationErrors, "TMIS国标编码为5位数字");
}
if (tmisCodeCountMap.getOrDefault(railwayStation.getTmisCode(), 0) > 1) {
addValidationError(validationErrors, "TMIS国标编码在本次导入中重复");
}
validateImportUnique(RailwayStation::getTmisCode, railwayStation.getTmisCode(), "该TMIS国标编码已存在", validationErrors);
validateImportUnique(RailwayStation::getCode, railwayStation.getCode(), "该编码已存在", validationErrors);
}
if (Func.isEmpty(railwayStation.getTelegraphCode())) {
addValidationError(validationErrors, "电报码不能为空");
} else {
if (!TELEGRAPH_CODE_PATTERN.matcher(railwayStation.getTelegraphCode()).matches()) {
addValidationError(validationErrors, "电报码为3位大写字母");
}
if (telegraphCodeCountMap.getOrDefault(railwayStation.getTelegraphCode(), 0) > 1) {
addValidationError(validationErrors, "电报码在本次导入中重复");
}
validateImportUnique(RailwayStation::getTelegraphCode, railwayStation.getTelegraphCode(), "电报码已存在", validationErrors);
}
if (Func.isEmpty(railwayStation.getName())) {
addValidationError(validationErrors, "车站名称不能为空");
}
validateImportLength(railwayStation.getCode(), CODE_MAX_LENGTH, "编码不能超过20字", validationErrors);
validateImportLength(railwayStation.getName(), NAME_MAX_LENGTH, "车站名称不能超过50字", validationErrors);
validateImportLength(railwayStation.getProvinceName(), REGION_NAME_MAX_LENGTH, "所属省份不能超过128字", validationErrors);
validateImportLength(railwayStation.getCityName(), REGION_NAME_MAX_LENGTH, "所属城市不能超过128字", validationErrors);
validateImportLength(railwayStation.getDistrictName(), REGION_NAME_MAX_LENGTH, "所属区县不能超过128字", validationErrors);
validateImportLength(railwayStation.getRegionCode(), REGION_CODE_MAX_LENGTH, "行政区划编号不能超过32字", validationErrors);
validateImportLength(railwayStation.getDetailAddress(), DETAIL_ADDRESS_MAX_LENGTH, "详细地址不能超过255字", validationErrors);
validateImportLength(railwayStation.getRemark(), REMARK_MAX_LENGTH, "备注不能超过200字", validationErrors);
validateImportCoordinate(railwayStation.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE, "经度", validationErrors);
validateImportCoordinate(railwayStation.getLatitude(), MIN_LATITUDE, MAX_LATITUDE, "纬度", validationErrors);
validateImportRailwayRegion(railwayStation, validationErrors);
return validationErrors;
}
private void validateImportRailwayRegion(RailwayStation railwayStation, List<String> validationErrors) {
boolean provinceMissing = Func.isEmpty(railwayStation.getProvinceCode()) && Func.isEmpty(railwayStation.getProvinceName());
boolean cityMissing = Func.isEmpty(railwayStation.getCityCode()) && Func.isEmpty(railwayStation.getCityName());
boolean districtMissing = Func.isEmpty(railwayStation.getDistrictCode()) && Func.isEmpty(railwayStation.getDistrictName());
if (provinceMissing) {
addValidationError(validationErrors, "所属省份不能为空");
}
if (cityMissing) {
addValidationError(validationErrors, "所属城市不能为空");
}
if (districtMissing) {
addValidationError(validationErrors, "所属区县不能为空");
}
if (provinceMissing || cityMissing || districtMissing) {
return;
}
try {
fillRegion(railwayStation);
} catch (ServiceException exception) {
addValidationError(validationErrors, exception.getMessage());
}
}
private void validateImportCoordinate(BigDecimal value, BigDecimal min, BigDecimal max, String name, List<String> validationErrors) {
if (Func.isNotEmpty(value) && !validRange(value, min, max)) {
addValidationError(validationErrors, name + "范围不正确");
}
}
private void validateImportUnique(com.baomidou.mybatisplus.core.toolkit.support.SFunction<RailwayStation, ?> column,
String value, String message, List<String> validationErrors) {
if (count(Wrappers.<RailwayStation>lambdaQuery()
.eq(column, value)
.eq(RailwayStation::getIsDeleted, 0)) > 0L) {
addValidationError(validationErrors, message);
}
}
private void validateImportLength(String value, int maxLength, String message, List<String> validationErrors) {
if (Func.isNotEmpty(value) && value.length() > maxLength) {
addValidationError(validationErrors, message);
}
}
private void addValidationError(List<String> validationErrors, String message) {
if (Func.isNotEmpty(message) && !validationErrors.contains(message)) {
validationErrors.add(message);
}
}
private String formatImportErrorMessage(int rowNumber, List<String> validationErrors) {
StringBuilder errorMessage = new StringBuilder("").append(rowNumber).append("行:");
for (int index = 0; index < validationErrors.size(); index++) {
if (index > 0) {
errorMessage.append(System.lineSeparator());
}
errorMessage.append(index + 1).append(". ").append(validationErrors.get(index));
}
return errorMessage.toString();
}
@Override
public List<RailwayStationExcel> exportRailwayStation(Wrapper<RailwayStation> queryWrapper) {
List<RailwayStation> railwayStationList = list(queryWrapper);