🐛 完善港口码头/铁路导入失败原因与软删除恢复,并修复运单导入列表筛选
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+6
@@ -32,6 +32,12 @@ public class WaybillImportBatchRequest {
|
||||
private Long planId;
|
||||
private String planName;
|
||||
private String remark;
|
||||
/** 列表筛选:创建人 */
|
||||
private Long createUser;
|
||||
/** 列表筛选:创建时间起(yyyy-MM-dd HH:mm:ss) */
|
||||
private String createTimeStart;
|
||||
/** 列表筛选:创建时间止(yyyy-MM-dd HH:mm:ss) */
|
||||
private String createTimeEnd;
|
||||
private List<Map<String, Object>> rows;
|
||||
/** 分批确认导入时,是否追加到已有批次(首批为 false,后续批为 true)。 */
|
||||
private Boolean append;
|
||||
|
||||
+5
@@ -45,6 +45,11 @@ public interface RailwayStationMapper extends BaseMapper<RailwayStation> {
|
||||
*/
|
||||
RailwayStation selectByCodeIncludingDeleted(@Param("code") String code);
|
||||
|
||||
/**
|
||||
* 按 TMIS 国标编码查询铁路车站(包含逻辑删除记录,用于导入恢复)。
|
||||
*/
|
||||
RailwayStation selectByTmisCodeIncludingDeleted(@Param("tmisCode") String tmisCode);
|
||||
|
||||
/**
|
||||
* 恢复逻辑删除铁路车站。
|
||||
*/
|
||||
|
||||
+8
@@ -36,6 +36,14 @@
|
||||
SELECT *
|
||||
FROM blade_railway_station
|
||||
WHERE code = #{code}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<select id="selectByTmisCodeIncludingDeleted" resultType="org.springblade.system.pojo.entity.RailwayStation">
|
||||
SELECT *
|
||||
FROM blade_railway_station
|
||||
WHERE tmis_code = #{tmisCode}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<update id="restoreById">
|
||||
|
||||
+195
-52
@@ -29,7 +29,8 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
@@ -44,6 +45,7 @@ import org.springblade.system.pojo.entity.Region;
|
||||
import org.springblade.system.pojo.vo.PortTerminalVO;
|
||||
import org.springblade.system.service.IPortTerminalService;
|
||||
import org.springblade.system.service.IRegionService;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -66,8 +68,9 @@ import java.util.regex.Pattern;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper, PortTerminal> implements IPortTerminalService {
|
||||
|
||||
private static final String CATEGORY_PORT = "港口";
|
||||
@@ -76,8 +79,12 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
private static final String SOURCE_INITIAL_OLD = "初始导入";
|
||||
private static final String SOURCE_BATCH = "批量导入";
|
||||
private static final String SOURCE_MANUAL = "手工导入";
|
||||
private static final String SOURCE_MANUAL_OLD = "手动录入";
|
||||
private static final int STATUS_ENABLED = 1;
|
||||
private static final int STATUS_DISABLED = 2;
|
||||
private static final int PROVINCE_REGION_LEVEL = 1;
|
||||
private static final int CITY_REGION_LEVEL = 2;
|
||||
private static final int DISTRICT_REGION_LEVEL = 3;
|
||||
private static final int CODE_MAX_LENGTH = 30;
|
||||
private static final int NAME_MAX_LENGTH = 100;
|
||||
private static final int REGION_MAX_LENGTH = 50;
|
||||
@@ -102,6 +109,7 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submit(PortTerminal portTerminal) {
|
||||
prepare(portTerminal, SOURCE_MANUAL);
|
||||
prepareSubmitTarget(portTerminal);
|
||||
validate(portTerminal);
|
||||
return saveOrUpdate(portTerminal);
|
||||
}
|
||||
@@ -206,7 +214,9 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
? PORT_CODE_PATTERN.matcher(code).matches()
|
||||
: TERMINAL_CODE_PATTERN.matcher(code).matches();
|
||||
if (!codeValid) {
|
||||
String message = CATEGORY_PORT.equals(category) ? "港口编码为5位大写字母" : "码头编码格式为港口编码-码头标识";
|
||||
String message = CATEGORY_PORT.equals(category)
|
||||
? "港口编码为5位大写字母,当前为「" + code + "」"
|
||||
: "码头编码格式为港口编码-码头标识,当前为「" + code + "」";
|
||||
failureList.add(buildFailure(data, index, message));
|
||||
continue;
|
||||
}
|
||||
@@ -239,8 +249,11 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
portTerminal.setDataSource(SOURCE_BATCH);
|
||||
portTerminal.setStatus(STATUS_ENABLED);
|
||||
prepare(portTerminal, SOURCE_BATCH);
|
||||
prepareSubmitTarget(portTerminal);
|
||||
validate(portTerminal);
|
||||
save(portTerminal);
|
||||
if (!saveOrUpdate(portTerminal)) {
|
||||
throw new ServiceException("港口码头保存失败");
|
||||
}
|
||||
batchPortMap.put(portTerminal.getCode(), portTerminal);
|
||||
} catch (Exception exception) {
|
||||
failureList.add(buildFailure(data, index, resolveMessage(exception)));
|
||||
@@ -265,8 +278,11 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
portTerminal.setDataSource(SOURCE_BATCH);
|
||||
portTerminal.setStatus(STATUS_ENABLED);
|
||||
prepareTerminal(portTerminal, batchPortMap);
|
||||
prepareSubmitTarget(portTerminal);
|
||||
validate(portTerminal);
|
||||
save(portTerminal);
|
||||
if (!saveOrUpdate(portTerminal)) {
|
||||
throw new ServiceException("港口码头保存失败");
|
||||
}
|
||||
} catch (ParentPortNotFoundException exception) {
|
||||
// 上级港口就声明在本文件里,只是那一行自己校验失败(已被标红)。
|
||||
// 此时码头行本身没有问题,不再连带报错,避免用户看到"两行都错"的假象。
|
||||
@@ -308,6 +324,12 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
if (Func.isNotEmpty(parent.getCountry())) {
|
||||
portTerminal.setCountry(parent.getCountry());
|
||||
}
|
||||
if (Func.isNotEmpty(parent.getProvinceCode())) {
|
||||
portTerminal.setProvinceCode(parent.getProvinceCode());
|
||||
}
|
||||
if (Func.isNotEmpty(parent.getProvinceName())) {
|
||||
portTerminal.setProvinceName(parent.getProvinceName());
|
||||
}
|
||||
if (Func.isNotEmpty(parent.getCity())) {
|
||||
portTerminal.setCity(parent.getCity());
|
||||
}
|
||||
@@ -341,10 +363,59 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析异常信息,非业务异常统一提示导入失败。
|
||||
* 解析异常信息:优先取业务异常,其次解包数据访问异常,避免失败明细只剩「导入失败」。
|
||||
*/
|
||||
private String resolveMessage(Exception exception) {
|
||||
return exception instanceof ServiceException ? exception.getMessage() : "导入失败";
|
||||
ServiceException serviceException = findCause(exception, ServiceException.class);
|
||||
if (serviceException != null && Func.isNotEmpty(serviceException.getMessage())
|
||||
&& !(serviceException instanceof ImportFailureException)) {
|
||||
return serviceException.getMessage();
|
||||
}
|
||||
DataAccessException dataAccessException = findCause(exception, DataAccessException.class);
|
||||
if (dataAccessException != null) {
|
||||
Throwable root = rootCause(dataAccessException);
|
||||
String rootMessage = root.getMessage();
|
||||
log.warn("港口码头导入数据访问异常", exception);
|
||||
if (Func.isNotEmpty(rootMessage)) {
|
||||
if (rootMessage.contains("Duplicate") || rootMessage.contains("duplicate") || rootMessage.contains("uk_port_terminal_code")) {
|
||||
return "该编码已存在";
|
||||
}
|
||||
return "数据保存失败:" + shortenMessage(rootMessage);
|
||||
}
|
||||
return "数据保存失败";
|
||||
}
|
||||
Throwable root = rootCause(exception);
|
||||
String rootMessage = root.getMessage();
|
||||
log.warn("港口码头导入非业务异常, type={}", root.getClass().getName(), exception);
|
||||
if (Func.isNotEmpty(rootMessage)) {
|
||||
return shortenMessage(rootMessage);
|
||||
}
|
||||
return "导入失败:" + root.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private <T extends Throwable> T findCause(Throwable throwable, Class<T> type) {
|
||||
for (Throwable current = throwable; current != null; current = current.getCause()) {
|
||||
if (type.isInstance(current)) {
|
||||
return type.cast(current);
|
||||
}
|
||||
if (current.getCause() == current) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Throwable rootCause(Throwable throwable) {
|
||||
Throwable root = throwable;
|
||||
while (root.getCause() != null && root.getCause() != root) {
|
||||
root = root.getCause();
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
private String shortenMessage(String message) {
|
||||
String value = trimToEmpty(message);
|
||||
return value.length() > 200 ? value.substring(0, 200) + "..." : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -370,6 +441,26 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
return validRange(value, min, max) ? value.setScale(6, RoundingMode.HALF_UP) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码已存在且为软删除时:取消软删除并复用原主键,后续按更新落库;
|
||||
* 编码存在且未删除时仍报「该编码已存在」。
|
||||
*/
|
||||
private void prepareSubmitTarget(PortTerminal portTerminal) {
|
||||
if (Func.isNotEmpty(portTerminal.getId())) {
|
||||
return;
|
||||
}
|
||||
PortTerminal existingPortTerminal = baseMapper.selectByCodeIncludingDeleted(portTerminal.getCode());
|
||||
if (existingPortTerminal == null) {
|
||||
return;
|
||||
}
|
||||
if (!Objects.equals(existingPortTerminal.getIsDeleted(), 1)) {
|
||||
throw new ServiceException("该编码已存在");
|
||||
}
|
||||
baseMapper.restoreById(existingPortTerminal.getId());
|
||||
portTerminal.setId(existingPortTerminal.getId());
|
||||
portTerminal.setIsDeleted(0);
|
||||
}
|
||||
|
||||
private void prepare(PortTerminal portTerminal, String defaultDataSource) {
|
||||
prepare(portTerminal, defaultDataSource, false);
|
||||
}
|
||||
@@ -386,6 +477,8 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
portTerminal.setCategory(trimToEmpty(portTerminal.getCategory()));
|
||||
portTerminal.setName(trimToEmpty(portTerminal.getName()));
|
||||
portTerminal.setCountry(trimToEmpty(portTerminal.getCountry()));
|
||||
portTerminal.setProvinceCode(trimToNull(portTerminal.getProvinceCode()));
|
||||
portTerminal.setProvinceName(trimToNull(portTerminal.getProvinceName()));
|
||||
portTerminal.setCity(trimToEmpty(portTerminal.getCity()));
|
||||
portTerminal.setDistrictCode(trimToNull(portTerminal.getDistrictCode()));
|
||||
portTerminal.setDistrictName(trimToNull(portTerminal.getDistrictName()));
|
||||
@@ -431,68 +524,99 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
if (!CATEGORY_PORT.equals(parent.getCategory())) {
|
||||
throw new ServiceException("上级港口类型不正确");
|
||||
}
|
||||
portTerminal.setParentId(parent.getId());
|
||||
portTerminal.setParentCode(parent.getCode());
|
||||
portTerminal.setParentName(parent.getName());
|
||||
// 父港口区域信息为空时保留码头自身填写的值,避免历史数据(区县为空)导致码头无法导入。
|
||||
if (Func.isNotEmpty(parent.getCountry())) {
|
||||
portTerminal.setCountry(parent.getCountry());
|
||||
}
|
||||
if (Func.isNotEmpty(parent.getCity())) {
|
||||
portTerminal.setCity(parent.getCity());
|
||||
}
|
||||
if (Func.isNotEmpty(parent.getDistrictCode())) {
|
||||
portTerminal.setDistrictCode(parent.getDistrictCode());
|
||||
portTerminal.setRegionCode(parent.getDistrictCode());
|
||||
} else if (Func.isNotEmpty(portTerminal.getRegionCode())) {
|
||||
applyParent(portTerminal, parent);
|
||||
if (Func.isEmpty(portTerminal.getDistrictCode()) && Func.isNotEmpty(portTerminal.getRegionCode())) {
|
||||
portTerminal.setDistrictCode(portTerminal.getRegionCode());
|
||||
}
|
||||
if (Func.isNotEmpty(parent.getDistrictName())) {
|
||||
portTerminal.setDistrictName(parent.getDistrictName());
|
||||
}
|
||||
}
|
||||
|
||||
private void fillRegion(PortTerminal portTerminal) {
|
||||
Region province = null;
|
||||
if (Func.isNotEmpty(portTerminal.getProvinceCode())) {
|
||||
province = regionService.getById(portTerminal.getProvinceCode());
|
||||
}
|
||||
if (Func.isEmpty(province) && Func.isNotEmpty(portTerminal.getProvinceName())) {
|
||||
province = findRegionByName(portTerminal.getProvinceName(), PROVINCE_REGION_LEVEL, null);
|
||||
}
|
||||
if (Func.isEmpty(province)) {
|
||||
throw new ServiceException("请选择所属省份");
|
||||
}
|
||||
|
||||
Region city = null;
|
||||
if (Func.isNotEmpty(portTerminal.getCity())) {
|
||||
city = findRegionByName(portTerminal.getCity(), CITY_REGION_LEVEL, province.getCode());
|
||||
}
|
||||
if (Func.isEmpty(city)) {
|
||||
throw new ServiceException("请选择城市");
|
||||
}
|
||||
if (!Objects.equals(city.getParentCode(), province.getCode())) {
|
||||
throw new ServiceException("所属城市与所属省份不匹配");
|
||||
}
|
||||
|
||||
Region district = null;
|
||||
if (Func.isNotEmpty(portTerminal.getDistrictCode())) {
|
||||
district = regionService.getById(portTerminal.getDistrictCode());
|
||||
}
|
||||
if (Func.isEmpty(district) && Func.isNotEmpty(portTerminal.getDistrictName())) {
|
||||
if (Func.isNotEmpty(portTerminal.getCity())) {
|
||||
List<Region> cityList = regionService.list(Wrappers.<Region>lambdaQuery()
|
||||
.eq(Region::getName, portTerminal.getCity())
|
||||
.eq(Region::getRegionLevel, 2));
|
||||
for (Region city : cityList) {
|
||||
district = regionService.getOne(Wrappers.<Region>lambdaQuery()
|
||||
.eq(Region::getParentCode, city.getCode())
|
||||
.eq(Region::getName, portTerminal.getDistrictName()), false);
|
||||
if (Func.isNotEmpty(district)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Func.isEmpty(district)) {
|
||||
district = regionService.getOne(Wrappers.<Region>lambdaQuery()
|
||||
.eq(Region::getName, portTerminal.getDistrictName())
|
||||
.eq(Region::getRegionLevel, 3), false);
|
||||
}
|
||||
district = findRegionByName(portTerminal.getDistrictName(), DISTRICT_REGION_LEVEL, city.getCode());
|
||||
}
|
||||
if (Func.isEmpty(district)) {
|
||||
throw new ServiceException("请选择区县");
|
||||
}
|
||||
Region city = regionService.getById(district.getParentCode());
|
||||
if (Func.isEmpty(city)) {
|
||||
throw new ServiceException("区县所属城市不存在");
|
||||
}
|
||||
if (Func.isNotEmpty(portTerminal.getCity()) && !Objects.equals(portTerminal.getCity(), city.getName())) {
|
||||
throw new ServiceException("区县与城市不匹配");
|
||||
if (!Objects.equals(district.getParentCode(), city.getCode())) {
|
||||
throw new ServiceException("所属区县与所属城市不匹配");
|
||||
}
|
||||
|
||||
portTerminal.setProvinceCode(province.getCode());
|
||||
portTerminal.setProvinceName(province.getName());
|
||||
portTerminal.setCity(city.getName());
|
||||
portTerminal.setDistrictCode(district.getCode());
|
||||
portTerminal.setDistrictName(district.getName());
|
||||
portTerminal.setRegionCode(district.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按名称查找区划,兼容「福建/福建省」「泉州/泉州市」等简写。
|
||||
*/
|
||||
private Region findRegionByName(String name, int regionLevel, String parentCode) {
|
||||
String normalized = trimToEmpty(name);
|
||||
if (Func.isEmpty(normalized)) {
|
||||
return null;
|
||||
}
|
||||
for (String candidate : regionNameCandidates(normalized)) {
|
||||
LambdaQueryWrapper<Region> queryWrapper = Wrappers.<Region>lambdaQuery()
|
||||
.eq(Region::getRegionLevel, regionLevel)
|
||||
.eq(Region::getName, candidate);
|
||||
if (Func.isNotEmpty(parentCode)) {
|
||||
queryWrapper.eq(Region::getParentCode, parentCode);
|
||||
}
|
||||
Region region = regionService.getOne(queryWrapper, false);
|
||||
if (Func.isNotEmpty(region)) {
|
||||
return region;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<String> regionNameCandidates(String name) {
|
||||
List<String> candidates = new ArrayList<>();
|
||||
candidates.add(name);
|
||||
if (name.endsWith("省") || name.endsWith("市") || name.endsWith("区") || name.endsWith("县") || name.endsWith("州")) {
|
||||
String shortName = name.substring(0, name.length() - 1);
|
||||
if (Func.isNotEmpty(shortName) && !candidates.contains(shortName)) {
|
||||
candidates.add(shortName);
|
||||
}
|
||||
} else {
|
||||
for (String suffix : List.of("省", "市", "区", "县", "州")) {
|
||||
String fullName = name + suffix;
|
||||
if (!candidates.contains(fullName)) {
|
||||
candidates.add(fullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private void validate(PortTerminal portTerminal) {
|
||||
if (!CATEGORY_PORT.equals(portTerminal.getCategory()) && !CATEGORY_TERMINAL.equals(portTerminal.getCategory())) {
|
||||
throw new ServiceException("类型只能为港口或码头");
|
||||
@@ -506,16 +630,17 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
validateLength(portTerminal.getCode(), CODE_MAX_LENGTH, "编码不能超过30字");
|
||||
validateLength(portTerminal.getName(), NAME_MAX_LENGTH, "港口/码头名称不能超过100字");
|
||||
validateLength(portTerminal.getCountry(), REGION_MAX_LENGTH, "国家不能超过50字");
|
||||
validateLength(portTerminal.getProvinceName(), REGION_MAX_LENGTH, "所属省份不能超过50字");
|
||||
validateLength(portTerminal.getCity(), REGION_MAX_LENGTH, "城市不能超过50字");
|
||||
validateLength(portTerminal.getDistrictName(), REGION_MAX_LENGTH, "区县不能超过50字");
|
||||
validateLength(portTerminal.getRegionCode(), REGION_CODE_MAX_LENGTH, "行政区划编码不能超过32字");
|
||||
validateLength(portTerminal.getDetailAddress(), DETAIL_ADDRESS_MAX_LENGTH, "详细地址不能超过255字");
|
||||
validateLength(portTerminal.getRemark(), REMARK_MAX_LENGTH, "备注不能超过200个字");
|
||||
if (CATEGORY_PORT.equals(portTerminal.getCategory()) && !PORT_CODE_PATTERN.matcher(portTerminal.getCode()).matches()) {
|
||||
throw new ServiceException("港口编码为5位大写字母");
|
||||
throw new ServiceException("港口编码为5位大写字母,当前为「" + portTerminal.getCode() + "」");
|
||||
}
|
||||
if (CATEGORY_TERMINAL.equals(portTerminal.getCategory()) && !TERMINAL_CODE_PATTERN.matcher(portTerminal.getCode()).matches()) {
|
||||
throw new ServiceException("码头编码格式为港口编码-码头标识");
|
||||
throw new ServiceException("码头编码格式为港口编码-码头标识,当前为「" + portTerminal.getCode() + "」");
|
||||
}
|
||||
if (CATEGORY_TERMINAL.equals(portTerminal.getCategory()) && !portTerminal.getCode().startsWith(portTerminal.getParentCode() + "-")) {
|
||||
throw new ServiceException("码头编码必须以所属港口编码开头");
|
||||
@@ -523,12 +648,24 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
if (Func.isEmpty(portTerminal.getCountry())) {
|
||||
throw new ServiceException("国家不能为空");
|
||||
}
|
||||
if (Func.isEmpty(portTerminal.getProvinceCode()) && Func.isEmpty(portTerminal.getProvinceName())) {
|
||||
throw new ServiceException("所属省份不能为空");
|
||||
}
|
||||
if (Func.isEmpty(portTerminal.getCity())) {
|
||||
throw new ServiceException("城市不能为空");
|
||||
}
|
||||
if (Func.isEmpty(portTerminal.getDistrictCode())) {
|
||||
throw new ServiceException("区县不能为空");
|
||||
}
|
||||
if (Func.isEmpty(portTerminal.getDetailAddress())) {
|
||||
throw new ServiceException("详细地址不能为空");
|
||||
}
|
||||
if (Func.isEmpty(portTerminal.getLongitude())) {
|
||||
throw new ServiceException("经度不能为空");
|
||||
}
|
||||
if (Func.isEmpty(portTerminal.getLatitude())) {
|
||||
throw new ServiceException("纬度不能为空");
|
||||
}
|
||||
validateDataSource(portTerminal.getDataSource());
|
||||
validateStatus(portTerminal.getStatus());
|
||||
validateRange(portTerminal.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE, "经度范围为 -180 到 180");
|
||||
@@ -593,7 +730,13 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
|
||||
private String normalizeDataSource(String dataSource) {
|
||||
String value = trimToEmpty(dataSource);
|
||||
return SOURCE_INITIAL_OLD.equals(value) ? SOURCE_INITIAL : value;
|
||||
if (SOURCE_INITIAL_OLD.equals(value)) {
|
||||
return SOURCE_INITIAL;
|
||||
}
|
||||
if (SOURCE_MANUAL_OLD.equals(value)) {
|
||||
return SOURCE_MANUAL;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private void validateEnabledTerminal(Long parentId) {
|
||||
@@ -611,7 +754,7 @@ public class PortTerminalServiceImpl extends BaseServiceImpl<PortTerminalMapper,
|
||||
}
|
||||
|
||||
private String trimToEmpty(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
return value == null ? "" : value.replace("\uFEFF", "").strip();
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
|
||||
+11
-2
@@ -102,6 +102,7 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submit(RailwayStation railwayStation) {
|
||||
prepare(railwayStation, SOURCE_MANUAL);
|
||||
prepareSubmitTarget(railwayStation);
|
||||
validate(railwayStation);
|
||||
return saveOrUpdate(railwayStation);
|
||||
}
|
||||
@@ -170,7 +171,8 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
throw new org.springblade.common.excel.ImportFailureException(data);
|
||||
}
|
||||
for (RailwayStation railwayStation : railwayStationList) {
|
||||
prepareImportTarget(railwayStation);
|
||||
// 编码/TMIS 命中逻辑删除记录时复用原主键,恢复后更新,避免唯一索引冲突。
|
||||
prepareSubmitTarget(railwayStation);
|
||||
if (!saveOrUpdate(railwayStation)) {
|
||||
throw new ServiceException("铁路车站保存失败");
|
||||
}
|
||||
@@ -178,11 +180,18 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
return errorList;
|
||||
}
|
||||
|
||||
private void prepareImportTarget(RailwayStation railwayStation) {
|
||||
/**
|
||||
* 编码或 TMIS 已存在且为软删除时:取消软删除并复用原主键,后续按更新落库;
|
||||
* 存在且未删除时仍报「该编码已存在」。
|
||||
*/
|
||||
private void prepareSubmitTarget(RailwayStation railwayStation) {
|
||||
if (Func.isNotEmpty(railwayStation.getId())) {
|
||||
return;
|
||||
}
|
||||
RailwayStation existingRailwayStation = baseMapper.selectByCodeIncludingDeleted(railwayStation.getCode());
|
||||
if (existingRailwayStation == null && Func.isNotEmpty(railwayStation.getTmisCode())) {
|
||||
existingRailwayStation = baseMapper.selectByTmisCodeIncludingDeleted(railwayStation.getTmisCode());
|
||||
}
|
||||
if (existingRailwayStation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
+26
-2
@@ -23,6 +23,7 @@
|
||||
package org.springblade.transport.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import cn.idev.excel.write.handler.SheetWriteHandler;
|
||||
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
|
||||
@@ -43,6 +44,7 @@ import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.transport.excel.WaybillExcel;
|
||||
import org.springblade.transport.excel.WaybillImportBatchExcel;
|
||||
import org.springblade.transport.pojo.entity.ContractManage;
|
||||
@@ -50,6 +52,7 @@ import org.springblade.transport.pojo.entity.CustomerArchive;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springblade.transport.pojo.entity.TransportPlan;
|
||||
import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.pojo.entity.WaybillImportBatch;
|
||||
import org.springblade.transport.pojo.dto.WaybillImportBatchRequest;
|
||||
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
||||
import org.springblade.transport.pojo.vo.LoadingManageVO;
|
||||
@@ -77,8 +80,11 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 运单管理 控制器
|
||||
@@ -159,10 +165,22 @@ public class WaybillController extends BladeController {
|
||||
.map(contract -> option(contract.getId(), "contractName", contract.getContractName(), "projectId", contract.getProjectId()))
|
||||
.toList());
|
||||
options.put("carriers", customerArchiveService.list().stream()
|
||||
.filter(customer -> "承运商".equals(customer.getCustomerType()) && Integer.valueOf(1).equals(customer.getStatus()))
|
||||
.filter(customer -> isCarrierCustomer(customer) && Integer.valueOf(1).equals(customer.getStatus()))
|
||||
.map(customer -> option(customer.getId(), "name", customer.getFullName(), null, null))
|
||||
.toList());
|
||||
options.put("creators", new ArrayList<>());
|
||||
// 创建人选项取已有导入批次的创建人,保证筛选项与列表数据可对应。
|
||||
Set<Long> creatorIds = new LinkedHashSet<>();
|
||||
waybillImportBatchService.list(Wrappers.<WaybillImportBatch>lambdaQuery()
|
||||
.select(WaybillImportBatch::getCreateUser)
|
||||
.eq(WaybillImportBatch::getIsDeleted, 0)
|
||||
.isNotNull(WaybillImportBatch::getCreateUser))
|
||||
.stream()
|
||||
.map(WaybillImportBatch::getCreateUser)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(creatorIds::add);
|
||||
options.put("creators", creatorIds.stream()
|
||||
.map(userId -> option(userId, "name", UserCache.getUserRealName(userId), null, null))
|
||||
.toList());
|
||||
options.put("plans", transportPlanService.list().stream()
|
||||
.map(plan -> option(plan.getId(), "planName", plan.getPlanName(), "projectId", plan.getProjectId()))
|
||||
.toList());
|
||||
@@ -372,6 +390,12 @@ public class WaybillController extends BladeController {
|
||||
return option;
|
||||
}
|
||||
|
||||
/** 客商类型支持多选(逗号分隔),包含「承运商」即视为承运商。 */
|
||||
private boolean isCarrierCustomer(CustomerArchive customer) {
|
||||
String type = Func.toStr(customer.getCustomerType()).replace(",", ",");
|
||||
return Func.toStrList(type).stream().anyMatch("承运商"::equals);
|
||||
}
|
||||
|
||||
private static final class TextColumnStyleHandler implements SheetWriteHandler {
|
||||
|
||||
private final int[] columnIndexes;
|
||||
|
||||
+9
@@ -344,7 +344,16 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl<WaybillImport
|
||||
.like(Func.isNotEmpty(request.getBatchNo()), WaybillImportBatch::getBatchNo, request.getBatchNo())
|
||||
.apply(Func.isNotEmpty(request.getCarrierId()), "FIND_IN_SET({0}, carrier_ids)", request.getCarrierId())
|
||||
.eq(Func.isNotEmpty(request.getCarrierName()), WaybillImportBatch::getCarrierName, request.getCarrierName())
|
||||
.eq(Func.isNotEmpty(request.getCreateUser()), WaybillImportBatch::getCreateUser, request.getCreateUser())
|
||||
.orderByDesc(WaybillImportBatch::getCreateTime);
|
||||
if (Func.isNotEmpty(request.getCreateTimeStart())) {
|
||||
queryWrapper.ge(WaybillImportBatch::getCreateTime,
|
||||
DateUtil.parse(request.getCreateTimeStart(), DateUtil.PATTERN_DATETIME));
|
||||
}
|
||||
if (Func.isNotEmpty(request.getCreateTimeEnd())) {
|
||||
queryWrapper.le(WaybillImportBatch::getCreateTime,
|
||||
DateUtil.parse(request.getCreateTimeEnd(), DateUtil.PATTERN_DATETIME));
|
||||
}
|
||||
IPage<WaybillImportBatch> entityPage = page(page, queryWrapper);
|
||||
List<WaybillImportBatchVO> records = entityPage.getRecords().stream().map(this::toVO).toList();
|
||||
Page<WaybillImportBatchVO> resultPage = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
|
||||
|
||||
Reference in New Issue
Block a user