🔀 合并读侧可配载判据与写侧准入校验、子运单状态推导

冲突仅限 LoadingManageServiceImpl 的 import 段:读侧判据分支引入 LiveProcessConfigSupport、
写侧准入分支引入 LoadableAdmissionSupport,两处按字母序并存。
This commit is contained in:
2026-10-18 14:36:57 +08:00
3 changed files with 452 additions and 13 deletions
@@ -31,6 +31,7 @@ import org.springblade.transport.service.ILoadingManageService;
import org.springblade.transport.service.IProcessConfigService;
import org.springblade.transport.service.IReceivablePayableDetailService;
import org.springblade.transport.support.LiveProcessConfigSupport;
import org.springblade.transport.support.LoadableAdmissionSupport;
import org.springblade.transport.support.TransportBusinessSupport;
import org.springblade.transport.support.WaybillProcessSupport;
import org.springblade.transport.wrapper.LoadingManageWrapper;
@@ -751,16 +752,61 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
if (waybills.size() != waybillIds.size()) {
throw new ServiceException("待配载运单不存在或已删除");
}
String ownLoadingNo = loadingManage.getLoadingNo();
for (Waybill waybill : waybills) {
TransportBusinessSupport.assertCurrentDept(waybill.getDeptId(), "运单管理");
if (Func.isNotEmpty(waybill.getLoadingNo()) && !Objects.equals(waybill.getLoadingNo(), loadingManage.getLoadingNo())) {
throw new ServiceException("运单" + waybill.getWaybillNo() + "已关联其他配载单");
// 本单既有子运单在先前提交时已过准入判据,重新提交/编辑不再判:
// 不需接单项目的票提交后即为进行中,重复判会让本单再也保存不了
if (LoadableAdmissionSupport.isOwnChild(waybill, ownLoadingNo)) {
continue;
}
if ((Objects.equals(waybill.getBusinessStatus(), STATUS_COMPLETED) || Objects.equals(waybill.getBusinessStatus(), STATUS_CANCELLED))
&& !Objects.equals(waybill.getLoadingNo(), loadingManage.getLoadingNo())) {
throw new ServiceException("运单" + waybill.getWaybillNo() + "当前状态不允许配载");
if (!LoadableAdmissionSupport.isLoadable(waybill, ownLoadingNo)) {
throw new ServiceException(unloadableReason(waybill));
}
}
validateSameProject(waybills);
}
/**
* 逐张准入失败的可读原因。判据本身在可配载支持类里,这里只把命中的那一条翻成文案。
*
* @param waybill 未通过准入判据的运单
* @return 含运单号与原因的提示
*/
private String unloadableReason(Waybill waybill) {
String prefix = "运单" + waybill.getWaybillNo();
if (Func.isNotEmpty(waybill.getLoadingNo())) {
return prefix + "已关联其他配载单";
}
if (!LoadableAdmissionSupport.isRoadTransport(waybill.getTransportType())) {
return prefix + "运输方式不是公路,不可配载";
}
if (!LoadableAdmissionSupport.LOADABLE_BUSINESS_STATUS.equals(waybill.getBusinessStatus())) {
return prefix + "当前状态不是待执行,不可配载";
}
if (WaybillProcessSupport.isRejected(waybill.getDriverAcceptStatus())) {
return prefix + "已被司机拒绝接单,不可配载";
}
return prefix + "不可配载";
}
/**
* 一张配载单的全部子运单必须属于同一个项目,否则点名与其它子运单项目不同的运单。
* <p>
* 查询条件表达不了跨行约束,只能整批取回后在内存里比。
*
* @param waybills 本次要写入的子运单全集
*/
private void validateSameProject(List<Waybill> waybills) {
Long projectId = waybills.get(0).getProjectId();
List<String> others = waybills.stream()
.filter(waybill -> !Objects.equals(waybill.getProjectId(), projectId))
.map(Waybill::getWaybillNo)
.toList();
if (Func.isNotEmpty(others)) {
throw new ServiceException("配载单的子运单必须属于同一项目,运单"
+ String.join("、", others) + "与其它子运单不属于同一项目");
}
}
private void validateCarrierContract(LoadingManage loadingManage, boolean required) {
@@ -778,10 +824,14 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
loadingManage.setCarrierName(null);
return;
}
ContractManage carrierContract = availableCarrierContracts(projectIdsByWaybills(loadingManage)).stream()
.filter(contract -> Objects.equals(contract.getId(), loadingManage.getCarrierContractId()))
.findFirst()
.orElseThrow(() -> new ServiceException("所选承运商合同不存在、未审核通过或已失效"));
ContractManage carrierContract = availableCarrierContractById(loadingManage.getCarrierContractId());
if (carrierContract == null) {
throw new ServiceException("所选承运商合同不存在、未审核通过或已失效");
}
// 合同自带项目与乙方:合同的项目不属于本单子运单的项目,等于换了项目却没换乙方
if (!projectIdsByWaybills(loadingManage).contains(carrierContract.getProjectId())) {
throw new ServiceException("所选承运商合同不属于本项目");
}
loadingManage.setCarrierName(carrierContract.getPartyB());
}
@@ -814,15 +864,37 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
if (Func.isEmpty(projectIds)) {
return List.of();
}
return contractManageService.list(Wrappers.<ContractManage>lambdaQuery()
return contractManageService.list(availableCarrierContractQuery()
.in(ContractManage::getProjectId, projectIds)
.orderByDesc(ContractManage::getCreateTime));
}
/**
* 按主键取一张可用的承运商合同。
* <p>
* 不按项目过滤,才能把「合同不可用」与「合同不属于本项目」分成两种提示。
*
* @param contractId 合同主键
* @return 可用的合同;不存在或不可用返回 null
*/
private ContractManage availableCarrierContractById(Long contractId) {
if (Func.isEmpty(contractId)) {
return null;
}
return contractManageService.list(availableCarrierContractQuery()
.eq(ContractManage::getId, contractId)
.orderByDesc(ContractManage::getCreateTime))
.stream().findFirst().orElse(null);
}
private LambdaQueryWrapper<ContractManage> availableCarrierContractQuery() {
return Wrappers.<ContractManage>lambdaQuery()
.eq(ContractManage::getIsDeleted, 0)
.eq(ContractManage::getStatus, 1)
.eq(ContractManage::getContractCategory, "承运商合同")
.in(ContractManage::getProjectId, projectIds)
.in(ContractManage::getApprovalStatus, "approved", "change_approved")
.and(wrapper -> wrapper.isNull(ContractManage::getContractStage)
.or().ne(ContractManage::getContractStage, "terminated"))
.orderByDesc(ContractManage::getCreateTime));
.or().ne(ContractManage::getContractStage, "terminated"));
}
/**
@@ -107,6 +107,24 @@ public final class LoadableAdmissionSupport {
return loadingNo.isEmpty() || loadingNo.equals(text(ownLoadingNo));
}
/**
* 该运单是否已挂在本配载单上。
* <p>
* 本单既有子运单在先前提交时已过准入判据,重新提交/编辑时不再重复判:
* 不需接单项目的票提交后即为进行中,重复判会让本单再也保存不了,重新派单也无从下手。
*
* @param waybill 运单
* @param ownLoadingNo 本配载单号;新建时传 null
* @return true-已是本单子运单
*/
public static boolean isOwnChild(Waybill waybill, String ownLoadingNo) {
if (waybill == null) {
return false;
}
String own = text(ownLoadingNo);
return !own.isEmpty() && own.equals(text(waybill.getLoadingNo()));
}
/**
* 把整条可配载判据加到运单查询上:公路 + 待执行 + 未配载 + 非拒单。
*