Merge remote-tracking branch 'websoft/dev'
This commit is contained in:
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
*/
|
||||||
|
package org.springblade.transport.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** 运单批量导入行与已有运单的重复情况。 */
|
||||||
|
@Data
|
||||||
|
@Schema(description = "运单批量导入行与已有运单的重复情况")
|
||||||
|
public class WaybillImportDuplicateVO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "行号(从0开始,相对当前批次)")
|
||||||
|
private Integer rowIndex;
|
||||||
|
|
||||||
|
@Schema(description = "命中的已有运单号")
|
||||||
|
private List<String> waybillNos = new ArrayList<>();
|
||||||
|
}
|
||||||
+4
-3
@@ -135,9 +135,10 @@ public class VoucherManageController extends BladeController {
|
|||||||
@ApiOperationSupport(order = 7)
|
@ApiOperationSupport(order = 7)
|
||||||
@Operation(summary = "可关联运输批次")
|
@Operation(summary = "可关联运输批次")
|
||||||
public R<IPage<Map<String, Object>>> waybillBatches(Query query, @RequestParam(required = false) String batchNo,
|
public R<IPage<Map<String, Object>>> waybillBatches(Query query, @RequestParam(required = false) String batchNo,
|
||||||
@RequestParam(required = false) String createUser, @RequestParam(required = false) Integer waybillCount,
|
@RequestParam(required = false) Long projectId, @RequestParam(required = false) String createUser,
|
||||||
@RequestParam(required = false) String createTimeStart, @RequestParam(required = false) String createTimeEnd) {
|
@RequestParam(required = false) Integer waybillCount, @RequestParam(required = false) String createTimeStart,
|
||||||
return R.data(voucherManageService.selectableWaybillBatches(Condition.getPage(query), batchNo, createUser, waybillCount, createTimeStart, createTimeEnd));
|
@RequestParam(required = false) String createTimeEnd) {
|
||||||
|
return R.data(voucherManageService.selectableWaybillBatches(Condition.getPage(query), batchNo, projectId, createUser, waybillCount, createTimeStart, createTimeEnd));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/audit-pass")
|
@PostMapping("/audit-pass")
|
||||||
|
|||||||
+8
@@ -57,6 +57,7 @@ import org.springblade.transport.pojo.dto.WaybillImportBatchRequest;
|
|||||||
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
||||||
import org.springblade.transport.pojo.vo.LoadingManageVO;
|
import org.springblade.transport.pojo.vo.LoadingManageVO;
|
||||||
import org.springblade.transport.pojo.vo.WaybillImportBatchVO;
|
import org.springblade.transport.pojo.vo.WaybillImportBatchVO;
|
||||||
|
import org.springblade.transport.pojo.vo.WaybillImportDuplicateVO;
|
||||||
import org.springblade.transport.pojo.vo.WaybillImportValidateResultVO;
|
import org.springblade.transport.pojo.vo.WaybillImportValidateResultVO;
|
||||||
import org.springblade.transport.pojo.vo.WaybillLocateVO;
|
import org.springblade.transport.pojo.vo.WaybillLocateVO;
|
||||||
import org.springblade.transport.pojo.vo.WaybillTrackVO;
|
import org.springblade.transport.pojo.vo.WaybillTrackVO;
|
||||||
@@ -230,6 +231,13 @@ public class WaybillController extends BladeController {
|
|||||||
waybillImportBatchService.exportFailures(request, response);
|
waybillImportBatchService.exportFailures(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/import-batch/duplicates")
|
||||||
|
@ApiOperationSupport(order = 6)
|
||||||
|
@Operation(summary = "查询与已有运单重复的导入行")
|
||||||
|
public R<List<WaybillImportDuplicateVO>> checkImportDuplicates(@RequestBody WaybillImportBatchRequest request) {
|
||||||
|
return R.data(waybillImportBatchService.checkDuplicates(request));
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/import-batch/confirm")
|
@PostMapping("/import-batch/confirm")
|
||||||
@ApiOperationSupport(order = 7)
|
@ApiOperationSupport(order = 7)
|
||||||
@Operation(summary = "确认运单批量导入")
|
@Operation(summary = "确认运单批量导入")
|
||||||
|
|||||||
+3
-2
@@ -13,8 +13,9 @@ import org.apache.ibatis.annotations.Delete;
|
|||||||
public interface VoucherWaybillBatchMapper extends BaseMapper<VoucherWaybillBatch> {
|
public interface VoucherWaybillBatchMapper extends BaseMapper<VoucherWaybillBatch> {
|
||||||
@Delete("DELETE FROM blade_voucher_waybill_batch WHERE voucher_id = #{voucherId}")
|
@Delete("DELETE FROM blade_voucher_waybill_batch WHERE voucher_id = #{voucherId}")
|
||||||
int deletePhysicalByVoucherId(@Param("voucherId") Long voucherId);
|
int deletePhysicalByVoucherId(@Param("voucherId") Long voucherId);
|
||||||
IPage<Map<String, Object>> selectVoucherWaybillBatchPage(IPage<?> page, @Param("tenantId") String tenantId, @Param("batchNo") String batchNo, @Param("createUser") String createUser,
|
IPage<Map<String, Object>> selectVoucherWaybillBatchPage(IPage<?> page, @Param("tenantId") String tenantId, @Param("batchNo") String batchNo,
|
||||||
@Param("waybillCount") Integer waybillCount, @Param("createTimeStart") String createTimeStart, @Param("createTimeEnd") String createTimeEnd,
|
@Param("projectId") Long projectId, @Param("createUser") String createUser, @Param("waybillCount") Integer waybillCount,
|
||||||
|
@Param("createTimeStart") String createTimeStart, @Param("createTimeEnd") String createTimeEnd,
|
||||||
@Param("restrictCarrier") boolean restrictCarrier, @Param("carrierName") String carrierName);
|
@Param("restrictCarrier") boolean restrictCarrier, @Param("carrierName") String carrierName);
|
||||||
List<Map<String, Object>> selectWaybillBatchesByIds(@Param("tenantId") String tenantId, @Param("ids") List<Long> ids,
|
List<Map<String, Object>> selectWaybillBatchesByIds(@Param("tenantId") String tenantId, @Param("ids") List<Long> ids,
|
||||||
@Param("restrictCarrier") boolean restrictCarrier, @Param("carrierName") String carrierName);
|
@Param("restrictCarrier") boolean restrictCarrier, @Param("carrierName") String carrierName);
|
||||||
|
|||||||
+2
@@ -4,12 +4,14 @@
|
|||||||
<select id="selectVoucherWaybillBatchPage" resultType="java.util.LinkedHashMap">
|
<select id="selectVoucherWaybillBatchPage" resultType="java.util.LinkedHashMap">
|
||||||
SELECT MIN(w.id) AS id, MAX(w.create_time) AS createTime, MAX(w.create_user) AS createUser,
|
SELECT MIN(w.id) AS id, MAX(w.create_time) AS createTime, MAX(w.create_user) AS createUser,
|
||||||
MAX(u.real_name) AS createUserName, COUNT(*) AS waybillCount,
|
MAX(u.real_name) AS createUserName, COUNT(*) AS waybillCount,
|
||||||
|
CASE WHEN COUNT(DISTINCT COALESCE(w.project_id, -1)) > 1 THEN '多个项目' ELSE MAX(w.project_name) END AS projectName,
|
||||||
COALESCE(NULLIF(w.batch_no, ''), '未分批运单') AS batchNo
|
COALESCE(NULLIF(w.batch_no, ''), '未分批运单') AS batchNo
|
||||||
FROM blade_waybill w
|
FROM blade_waybill w
|
||||||
LEFT JOIN blade_user u ON u.id = w.create_user
|
LEFT JOIN blade_user u ON u.id = w.create_user
|
||||||
WHERE w.is_deleted = 0 AND w.tenant_id = #{tenantId}
|
WHERE w.is_deleted = 0 AND w.tenant_id = #{tenantId}
|
||||||
<if test="restrictCarrier">AND w.carrier_name = #{carrierName}</if>
|
<if test="restrictCarrier">AND w.carrier_name = #{carrierName}</if>
|
||||||
<if test="batchNo != null and batchNo != ''">AND w.batch_no LIKE CONCAT('%', #{batchNo}, '%')</if>
|
<if test="batchNo != null and batchNo != ''">AND w.batch_no LIKE CONCAT('%', #{batchNo}, '%')</if>
|
||||||
|
<if test="projectId != null">AND w.project_id = #{projectId}</if>
|
||||||
<if test="createUser != null and createUser != ''">AND w.create_user = #{createUser}</if>
|
<if test="createUser != null and createUser != ''">AND w.create_user = #{createUser}</if>
|
||||||
<if test="createTimeStart != null and createTimeStart != ''">AND w.create_time <![CDATA[>=]]> #{createTimeStart}</if>
|
<if test="createTimeStart != null and createTimeStart != ''">AND w.create_time <![CDATA[>=]]> #{createTimeStart}</if>
|
||||||
<if test="createTimeEnd != null and createTimeEnd != ''">AND w.create_time <![CDATA[<=]]> #{createTimeEnd}</if>
|
<if test="createTimeEnd != null and createTimeEnd != ''">AND w.create_time <![CDATA[<=]]> #{createTimeEnd}</if>
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ public interface IVoucherManageService extends BaseService<VoucherManage> {
|
|||||||
void processUploadedVoucher(Long voucherId);
|
void processUploadedVoucher(Long voucherId);
|
||||||
void reprocessUploadedVoucher(Long voucherId);
|
void reprocessUploadedVoucher(Long voucherId);
|
||||||
void removeVoucher(Long id);
|
void removeVoucher(Long id);
|
||||||
IPage<Map<String, Object>> selectableWaybillBatches(IPage<?> page, String batchNo, String createUser, Integer waybillCount, String createTimeStart, String createTimeEnd);
|
IPage<Map<String, Object>> selectableWaybillBatches(IPage<?> page, String batchNo, Long projectId, String createUser, Integer waybillCount, String createTimeStart, String createTimeEnd);
|
||||||
void auditPass(Long id);
|
void auditPass(Long id);
|
||||||
void auditReject(Long id, String rejectReason);
|
void auditReject(Long id, String rejectReason);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -7,8 +7,11 @@ import org.springblade.transport.pojo.dto.WaybillImportBatchRequest;
|
|||||||
import org.springblade.transport.pojo.entity.WaybillImportBatch;
|
import org.springblade.transport.pojo.entity.WaybillImportBatch;
|
||||||
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
||||||
import org.springblade.transport.pojo.vo.WaybillImportBatchVO;
|
import org.springblade.transport.pojo.vo.WaybillImportBatchVO;
|
||||||
|
import org.springblade.transport.pojo.vo.WaybillImportDuplicateVO;
|
||||||
import org.springblade.transport.pojo.vo.WaybillImportValidateResultVO;
|
import org.springblade.transport.pojo.vo.WaybillImportValidateResultVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/** 运单批次服务。 */
|
/** 运单批次服务。 */
|
||||||
public interface IWaybillImportBatchService extends BaseService<WaybillImportBatch> {
|
public interface IWaybillImportBatchService extends BaseService<WaybillImportBatch> {
|
||||||
WaybillImportBatch saveDraft(WaybillImportBatchRequest request);
|
WaybillImportBatch saveDraft(WaybillImportBatchRequest request);
|
||||||
@@ -18,4 +21,7 @@ public interface IWaybillImportBatchService extends BaseService<WaybillImportBat
|
|||||||
IPage<WaybillImportBatchVO> page(IPage<WaybillImportBatch> page, WaybillImportBatchRequest request);
|
IPage<WaybillImportBatchVO> page(IPage<WaybillImportBatch> page, WaybillImportBatchRequest request);
|
||||||
BusinessRemoveResultVO removeBatches(String ids);
|
BusinessRemoveResultVO removeBatches(String ids);
|
||||||
String nextBatchNo();
|
String nextBatchNo();
|
||||||
|
|
||||||
|
/** 逐行比对导入行与项目下已有运单,返回命中的运单号。 */
|
||||||
|
List<WaybillImportDuplicateVO> checkDuplicates(WaybillImportBatchRequest request);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1331,8 +1331,8 @@ public class VoucherManageServiceImpl extends BaseServiceImpl<VoucherManageMappe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<Map<String, Object>> selectableWaybillBatches(IPage<?> page, String batchNo, String createUser, Integer waybillCount, String createTimeStart, String createTimeEnd) {
|
public IPage<Map<String, Object>> selectableWaybillBatches(IPage<?> page, String batchNo, Long projectId, String createUser, Integer waybillCount, String createTimeStart, String createTimeEnd) {
|
||||||
return voucherWaybillBatchMapper.selectVoucherWaybillBatchPage(page, AuthUtil.getTenantId(), batchNo, createUser, waybillCount, createTimeStart, createTimeEnd,
|
return voucherWaybillBatchMapper.selectVoucherWaybillBatchPage(page, AuthUtil.getTenantId(), batchNo, projectId, createUser, waybillCount, createTimeStart, createTimeEnd,
|
||||||
!TransportBusinessSupport.isAdministrator(), currentCarrierName());
|
!TransportBusinessSupport.isAdministrator(), currentCarrierName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+91
@@ -12,6 +12,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springblade.common.excel.ImportFailureExcelUtil;
|
import org.springblade.common.excel.ImportFailureExcelUtil;
|
||||||
|
import org.springblade.common.excel.LenientDateParser;
|
||||||
import org.springblade.core.log.exception.ServiceException;
|
import org.springblade.core.log.exception.ServiceException;
|
||||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||||
import org.springblade.core.secure.utils.AuthUtil;
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
@@ -39,6 +40,7 @@ import org.springblade.transport.pojo.entity.Waybill;
|
|||||||
import org.springblade.transport.pojo.entity.WaybillImportBatch;
|
import org.springblade.transport.pojo.entity.WaybillImportBatch;
|
||||||
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
||||||
import org.springblade.transport.pojo.vo.WaybillImportBatchVO;
|
import org.springblade.transport.pojo.vo.WaybillImportBatchVO;
|
||||||
|
import org.springblade.transport.pojo.vo.WaybillImportDuplicateVO;
|
||||||
import org.springblade.transport.pojo.vo.WaybillImportRowErrorVO;
|
import org.springblade.transport.pojo.vo.WaybillImportRowErrorVO;
|
||||||
import org.springblade.transport.pojo.vo.WaybillImportValidateResultVO;
|
import org.springblade.transport.pojo.vo.WaybillImportValidateResultVO;
|
||||||
import org.springblade.transport.service.ICustomerArchiveService;
|
import org.springblade.transport.service.ICustomerArchiveService;
|
||||||
@@ -50,6 +52,7 @@ import org.springblade.transport.service.ITransportPlanService;
|
|||||||
import org.springblade.transport.service.IWaybillImportBatchService;
|
import org.springblade.transport.service.IWaybillImportBatchService;
|
||||||
import org.springblade.transport.service.IWaybillService;
|
import org.springblade.transport.service.IWaybillService;
|
||||||
import org.springblade.transport.support.TransportBusinessSupport;
|
import org.springblade.transport.support.TransportBusinessSupport;
|
||||||
|
import org.springblade.transport.support.VehiclePlateSupport;
|
||||||
import org.springblade.transport.support.WaybillProcessSupport;
|
import org.springblade.transport.support.WaybillProcessSupport;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -173,6 +176,94 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl<WaybillImport
|
|||||||
return toVO(batch);
|
return toVO(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<WaybillImportDuplicateVO> checkDuplicates(WaybillImportBatchRequest request) {
|
||||||
|
List<WaybillImportDuplicateVO> duplicates = new ArrayList<>();
|
||||||
|
if (request == null || Func.isEmpty(request.getRows()) || Func.isEmpty(request.getProjectId())) {
|
||||||
|
return duplicates;
|
||||||
|
}
|
||||||
|
List<Long> carrierIds = resolveCarrierIds(request);
|
||||||
|
String carrierName = resolveCarrierNames(carrierIds, request.getCarrierName());
|
||||||
|
LambdaQueryWrapper<Waybill> wrapper = Wrappers.<Waybill>lambdaQuery()
|
||||||
|
.eq(Waybill::getIsDeleted, 0)
|
||||||
|
.eq(Waybill::getProjectId, request.getProjectId());
|
||||||
|
if (Func.isNotEmpty(request.getId())) {
|
||||||
|
// 追加导入时,本批次已落库的运单不算历史运单。
|
||||||
|
wrapper.ne(Waybill::getImportBatchId, request.getId());
|
||||||
|
}
|
||||||
|
List<Waybill> candidates = waybillService.list(wrapper);
|
||||||
|
if (Func.isEmpty(candidates)) {
|
||||||
|
return duplicates;
|
||||||
|
}
|
||||||
|
List<Map<String, Object>> rows = request.getRows();
|
||||||
|
for (int index = 0; index < rows.size(); index++) {
|
||||||
|
List<String> waybillNos = new ArrayList<>();
|
||||||
|
for (Waybill waybill : candidates) {
|
||||||
|
if (!isDuplicateRow(rows.get(index), waybill, carrierIds, carrierName)) continue;
|
||||||
|
String waybillNo = waybill.getWaybillNo();
|
||||||
|
if (Func.isNotEmpty(waybillNo) && !waybillNos.contains(waybillNo)) {
|
||||||
|
waybillNos.add(waybillNo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (waybillNos.isEmpty()) continue;
|
||||||
|
WaybillImportDuplicateVO duplicate = new WaybillImportDuplicateVO();
|
||||||
|
duplicate.setRowIndex(index);
|
||||||
|
duplicate.setWaybillNos(waybillNos);
|
||||||
|
duplicates.add(duplicate);
|
||||||
|
}
|
||||||
|
return duplicates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判重口径为导入必填项逐一相同:承运商、车牌、运输方式、起止地址、货物名称与类型、
|
||||||
|
* 数量及单位、实际发货与实际完成时间。历史运单这些字段常为空,双方都空按相同处理。
|
||||||
|
*/
|
||||||
|
private boolean isDuplicateRow(Map<String, Object> row, Waybill waybill, List<Long> carrierIds, String carrierName) {
|
||||||
|
if (row == null || waybill == null) return false;
|
||||||
|
if (!carrierMatches(waybill, carrierIds, carrierName)) return false;
|
||||||
|
if (!samePlate(vehicleNoFromRow(row), waybill.getVehicleNo())) return false;
|
||||||
|
String transportType = resolveTransportTypeKey(
|
||||||
|
stringValue(row, "transportType", "运输类型", "*运输类型", "运输方式", "*运输方式"));
|
||||||
|
if (!sameText(transportType, waybill.getTransportType())) return false;
|
||||||
|
if (!sameText(stringValue(row, "departureAddress"), waybill.getDepartureAddress())) return false;
|
||||||
|
if (!sameText(stringValue(row, "arrivalAddress"), waybill.getArrivalAddress())) return false;
|
||||||
|
if (!sameText(stringValue(row, "cargoName"), waybill.getCargoName())) return false;
|
||||||
|
if (!sameText(stringValue(row, "cargoType"), waybill.getCargoType())) return false;
|
||||||
|
BigDecimal quantity = defaultQuantity(decimalValue(row, "quantity", "数量", "重量"));
|
||||||
|
if (quantity.compareTo(defaultQuantity(waybill.getQuantity())) != 0) return false;
|
||||||
|
String quantityUnit = stringValue(row, "quantityUnit", "数量单位");
|
||||||
|
if (!sameText(Func.isEmpty(quantityUnit) ? "吨" : quantityUnit, waybill.getQuantityUnit())) return false;
|
||||||
|
if (!Objects.equals(duplicateDate(row.get("actualStartDate")), waybill.getStartDate())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Objects.equals(duplicateDate(row.get("actualEndDate")), waybill.getEndDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 承运商优先按档案 ID 比对;历史手工运单只有名称,退化为名称比对。 */
|
||||||
|
private boolean carrierMatches(Waybill waybill, List<Long> carrierIds, String carrierName) {
|
||||||
|
if (Func.isNotEmpty(carrierIds) && waybill.getCarrierId() != null) {
|
||||||
|
return carrierIds.contains(waybill.getCarrierId());
|
||||||
|
}
|
||||||
|
return sameText(waybill.getCarrierName(), carrierName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sameText(String left, String right) {
|
||||||
|
String a = left == null ? "" : left.trim();
|
||||||
|
String b = right == null ? "" : right.trim();
|
||||||
|
return a.equalsIgnoreCase(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean samePlate(String left, String right) {
|
||||||
|
return VehiclePlateSupport.normalize(left).equals(VehiclePlateSupport.normalize(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 判重时的日期解析不阻断查询:无法识别的日期返回 null,按「与空值相同」处理。 */
|
||||||
|
private LocalDate duplicateDate(Object value) {
|
||||||
|
if (value == null) return null;
|
||||||
|
String text = String.valueOf(value).trim();
|
||||||
|
return text.isEmpty() ? null : LenientDateParser.parseDateLenientlyOrNull(text);
|
||||||
|
}
|
||||||
|
|
||||||
/** 草稿与确认导入共用落库流程,差异仅在于运单是否走校验以及是否生成应收应付明细。 */
|
/** 草稿与确认导入共用落库流程,差异仅在于运单是否走校验以及是否生成应收应付明细。 */
|
||||||
private WaybillImportBatch persist(WaybillImportBatchRequest request, String importStatus) {
|
private WaybillImportBatch persist(WaybillImportBatchRequest request, String importStatus) {
|
||||||
boolean draft = STATUS_DRAFT.equals(importStatus);
|
boolean draft = STATUS_DRAFT.equals(importStatus);
|
||||||
|
|||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
package org.springblade.transport.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springblade.system.feign.ISysClient;
|
||||||
|
import org.springblade.transport.pojo.dto.WaybillImportBatchRequest;
|
||||||
|
import org.springblade.transport.pojo.entity.Waybill;
|
||||||
|
import org.springblade.transport.pojo.vo.WaybillImportDuplicateVO;
|
||||||
|
import org.springblade.transport.service.ICustomerArchiveService;
|
||||||
|
import org.springblade.transport.service.IDriverService;
|
||||||
|
import org.springblade.transport.service.ILoadingManageService;
|
||||||
|
import org.springblade.transport.service.IProjectApplyService;
|
||||||
|
import org.springblade.transport.service.IReceivablePayableDetailService;
|
||||||
|
import org.springblade.transport.service.ITransportPlanService;
|
||||||
|
import org.springblade.transport.service.IWaybillService;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入行与历史运单的判重测试。
|
||||||
|
* <p>
|
||||||
|
* 口径为导入必填项逐一相同;历史手工运单的承运商档案 ID 与时间列常为空,
|
||||||
|
* 这类字段双方都空时按相同处理,否则手工运单永远判不出重复。
|
||||||
|
*/
|
||||||
|
class WaybillImportDuplicateTest {
|
||||||
|
|
||||||
|
private static final Long PROJECT_ID = 1001L;
|
||||||
|
private static final Long CARRIER_ID = 2002L;
|
||||||
|
private static final String CARRIER_NAME = "广西驰远干线运输有限公司";
|
||||||
|
private static final String VEHICLE_NO = "桂A5D61N";
|
||||||
|
private static final String CARGO_NAME = "混煤";
|
||||||
|
private static final String DEPARTURE = "鄂尔多斯市达拉特旗树林召镇";
|
||||||
|
private static final String ARRIVAL = "南宁市江南区沙井大道";
|
||||||
|
|
||||||
|
private IWaybillService waybillService;
|
||||||
|
private WaybillImportBatchServiceImpl service;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
waybillService = mock(IWaybillService.class);
|
||||||
|
service = new WaybillImportBatchServiceImpl(
|
||||||
|
waybillService,
|
||||||
|
mock(ICustomerArchiveService.class),
|
||||||
|
mock(IProjectApplyService.class),
|
||||||
|
mock(ITransportPlanService.class),
|
||||||
|
mock(IReceivablePayableDetailService.class),
|
||||||
|
mock(ILoadingManageService.class),
|
||||||
|
mock(ISysClient.class),
|
||||||
|
mock(IDriverService.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("必填项全一致:返回命中的已有运单号")
|
||||||
|
void reportsDuplicateWaybillNo() {
|
||||||
|
stubCandidates(waybill("YD-20261023-0012"));
|
||||||
|
|
||||||
|
WaybillImportBatchRequest req = request(importRow());
|
||||||
|
List<WaybillImportDuplicateVO> duplicates = service.checkDuplicates(req);
|
||||||
|
|
||||||
|
assertEquals(1, duplicates.size(), "rows=" + req.getRows() + " projectId=" + req.getProjectId());
|
||||||
|
assertEquals(0, duplicates.get(0).getRowIndex());
|
||||||
|
assertEquals(List.of("YD-20261023-0012"), duplicates.get(0).getWaybillNos());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("数量不同:不判为重复")
|
||||||
|
void ignoresDifferentQuantity() {
|
||||||
|
stubCandidates(waybill("YD-20261023-0012"));
|
||||||
|
Map<String, Object> row = importRow();
|
||||||
|
row.put("quantity", "21");
|
||||||
|
|
||||||
|
assertTrue(service.checkDuplicates(request(row)).isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("车牌大小写与空格不同:仍判为重复")
|
||||||
|
void ignoresPlateFormatting() {
|
||||||
|
stubCandidates(waybill("YD-20261023-0012"));
|
||||||
|
Map<String, Object> row = importRow();
|
||||||
|
row.put("vehicleNo", " 桂a5d61n ");
|
||||||
|
|
||||||
|
assertEquals(1, service.checkDuplicates(request(row)).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("历史运单没有承运商档案 ID:退化为承运商名称比对")
|
||||||
|
void matchesByCarrierNameWhenHistoryHasNoId() {
|
||||||
|
Waybill waybill = waybill("YD-20261023-0012");
|
||||||
|
waybill.setCarrierId(null);
|
||||||
|
stubCandidates(waybill);
|
||||||
|
|
||||||
|
assertEquals(1, service.checkDuplicates(request(importRow())).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("项目为空:直接返回空,不查库")
|
||||||
|
void returnsEmptyWithoutProject() {
|
||||||
|
WaybillImportBatchRequest request = request(importRow());
|
||||||
|
request.setProjectId(null);
|
||||||
|
|
||||||
|
assertTrue(service.checkDuplicates(request).isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stubCandidates(Waybill... waybills) {
|
||||||
|
when(waybillService.list(any(Wrapper.class))).thenReturn(List.of(waybills));
|
||||||
|
}
|
||||||
|
|
||||||
|
private WaybillImportBatchRequest request(Map<String, Object> row) {
|
||||||
|
WaybillImportBatchRequest request = new WaybillImportBatchRequest();
|
||||||
|
request.setProjectId(PROJECT_ID);
|
||||||
|
request.setCarrierId(CARRIER_ID);
|
||||||
|
request.setCarrierName(CARRIER_NAME);
|
||||||
|
request.setRows(List.of(row));
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> importRow() {
|
||||||
|
Map<String, Object> row = new HashMap<>();
|
||||||
|
row.put("vehicleNo", VEHICLE_NO);
|
||||||
|
// 无 Spring 上下文时字典读不到,运输方式按“名称即键”兜底,故此处直接给内部键。
|
||||||
|
row.put("transportType", "road");
|
||||||
|
row.put("departureAddress", DEPARTURE);
|
||||||
|
row.put("arrivalAddress", ARRIVAL);
|
||||||
|
row.put("cargoName", CARGO_NAME);
|
||||||
|
row.put("cargoType", "煤制品");
|
||||||
|
row.put("quantity", "20");
|
||||||
|
row.put("quantityUnit", "吨");
|
||||||
|
row.put("actualStartDate", "2026-10-13");
|
||||||
|
row.put("actualEndDate", "2026-10-14");
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Waybill waybill(String waybillNo) {
|
||||||
|
Waybill waybill = new Waybill();
|
||||||
|
waybill.setWaybillNo(waybillNo);
|
||||||
|
waybill.setProjectId(PROJECT_ID);
|
||||||
|
waybill.setCarrierId(CARRIER_ID);
|
||||||
|
waybill.setCarrierName(CARRIER_NAME);
|
||||||
|
waybill.setVehicleNo(VEHICLE_NO);
|
||||||
|
waybill.setTransportType("road");
|
||||||
|
waybill.setDepartureAddress(DEPARTURE);
|
||||||
|
waybill.setArrivalAddress(ARRIVAL);
|
||||||
|
waybill.setCargoName(CARGO_NAME);
|
||||||
|
waybill.setCargoType("煤制品");
|
||||||
|
waybill.setQuantity(new BigDecimal("20.00"));
|
||||||
|
waybill.setQuantityUnit("吨");
|
||||||
|
waybill.setStartDate(LocalDate.of(2026, 10, 13));
|
||||||
|
waybill.setEndDate(LocalDate.of(2026, 10, 14));
|
||||||
|
return waybill;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user