🐛 完善港口码头/铁路导入失败原因与软删除恢复,并修复运单导入列表筛选

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-10 16:20:21 +08:00
parent a4287d8b15
commit 68fede2caf
7 changed files with 260 additions and 56 deletions
@@ -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;
@@ -45,6 +45,11 @@ public interface RailwayStationMapper extends BaseMapper<RailwayStation> {
*/
RailwayStation selectByCodeIncludingDeleted(@Param("code") String code);
/**
* 按 TMIS 国标编码查询铁路车站(包含逻辑删除记录,用于导入恢复)。
*/
RailwayStation selectByTmisCodeIncludingDeleted(@Param("tmisCode") String tmisCode);
/**
* 恢复逻辑删除铁路车站。
*/
@@ -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">
@@ -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) {
@@ -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;
}
@@ -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;
@@ -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());