✨ 支持按单号批量排除账单订单

This commit is contained in:
caojia
2026-10-20 15:11:27 +08:00
parent 29e804dae2
commit b772c95813
9 changed files with 159 additions and 107 deletions
@@ -32,6 +32,8 @@ public class ArBillGenerateRequest
private String selectionMode = ArBillSelectionMode.EXPLICIT.getCode();
/** 全选模式中用户明确排除的来源订单 ID。 */
private List<String> excludedSourceBillIds;
/** 全选模式下按订单号、运单号或转单号排除;后台生成任务按批次应用该条件。 */
private String excludedOrderOrTrackingNo;
@JsonFormat(pattern = "yyyy-MM-dd")
@JsonDeserialize(using = StrictDateDeserializer.class)
private Date billDate;
@@ -8,7 +8,7 @@ public enum ArBillSelectionMode
{
/** 手动选择当前预览结果中的订单。 */
EXPLICIT("EXPLICIT", "手动选择"),
/** 选择全部查询结果,仅排除用户明确勾选的订单。 */
/** 选择全部查询结果,再按用户提供的排除条件移除匹配订单。 */
ALL_MATCHED("ALL_MATCHED", "全选查询结果");
private final String code;
@@ -37,6 +37,8 @@ public interface IArBillService
List<PrcOrderBill> previewBill(ArBillGenerateQuery query);
List<PrcOrderBill> previewExcludedBill(ArBillGenerateQuery query);
BigDecimal sumPreviewBill(ArBillGenerateQuery query);
ArBillGenerateTask generateBill(ArBillGenerateRequest request, String operator, String requestId);
@@ -299,6 +299,15 @@ public class ArBillServiceImpl implements IArBillService {
return result;
}
/** 查询待排除的订单;必须带至少一个精确单号,避免空条件返回整个账单范围。 */
@Override
public List<PrcOrderBill> previewExcludedBill(ArBillGenerateQuery query) {
if (query == null || normalizeOrderIdentifiers(query.getExcludeOrderOrTrackingNo()).isEmpty()) {
throw new ServiceException("请输入订单号、运单号或转单号后再查询");
}
return previewBill(query);
}
/**
* 汇总当前生成条件下的订单应收金额,用于弹窗顶部合计展示。
*/
@@ -358,8 +367,18 @@ public class ArBillServiceImpl implements IArBillService {
throw new ServiceException("全选模式最多排除 " + EXCLUDED_SOURCE_ID_LIMIT
+ " 条订单,请缩小查询条件后再生成");
}
List<String> excludedOrderIdentifiers = allMatched
? normalizeOrderIdentifiers(request.getExcludedOrderOrTrackingNo()) : new ArrayList<>();
if (!allMatched) {
request.setExcludedOrderOrTrackingNo(null);
}
int orderIdentifierCount = normalizeOrderIdentifiers(request.getOrderOrTrackingNo()).size()
+ excludedOrderIdentifiers.size();
if (orderIdentifierCount > ORDER_IDENTIFIER_LIMIT) {
throw new ServiceException("筛选单号和排除单号合计最多输入 " + ORDER_IDENTIFIER_LIMIT + " 个");
}
validateOrderIdentifierParameterCount(
normalizeOrderIdentifiers(request.getOrderOrTrackingNo()).size(),
orderIdentifierCount,
allMatched ? excludedSourceBillIds.size() : sourceBillIds.size());
request.setSelectionMode(selectionMode);
@@ -535,6 +554,8 @@ public class ArBillServiceImpl implements IArBillService {
ArBillGenerateRequest request = JSON.parseObject(task.getRequestPayload(), ArBillGenerateRequest.class);
List<String> sourceBillIds = normalizeSourceBillIds(request.getSourceBillIds());
List<String> excludedSourceBillIds = normalizeSourceBillIds(request.getExcludedSourceBillIds());
List<String> excludedOrderIdentifiers = ArBillSelectionMode.ALL_MATCHED.getCode().equals(task.getSelectionMode())
? normalizeOrderIdentifiers(request.getExcludedOrderOrTrackingNo()) : new ArrayList<>();
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setTimeout(300);
@@ -544,8 +565,8 @@ public class ArBillServiceImpl implements IArBillService {
request.getCustomerId(), request.getCustomerCode(),
normalizeOrderIdentifiers(request.getOrderOrTrackingNo()),
request.getBillPeriodEnd(), request.getCurrency(), eligibleOrderBillStatuses(),
ArIncomeBillFeeStatus.UNBILLED.getCode(),
task.getSnapshotCreateTime(), null, excludedSourceBillIds);
ArIncomeBillFeeStatus.UNBILLED.getCode(), task.getSnapshotCreateTime(), null,
excludedSourceBillIds, excludedOrderIdentifiers);
if (totalCount <= 0) {
throw new ServiceException("当前全选范围内没有可生成的订单");
}
@@ -572,7 +593,7 @@ public class ArBillServiceImpl implements IArBillService {
}
ArBillGenerateTask currentTask = task;
GenerateBatchResult result = transactionTemplate.execute(status -> processGenerateBatch(
currentTask, request, sourceBillIds, excludedSourceBillIds));
currentTask, request, sourceBillIds, excludedSourceBillIds, excludedOrderIdentifiers));
if (result == null || result.finished) {
transactionTemplate.execute(status -> {
completeGenerateTask(taskId);
@@ -659,7 +680,8 @@ public class ArBillServiceImpl implements IArBillService {
* 每批最多 GENERATE_PAGE_SIZE 条,成功后原子更新任务进度。
*/
private GenerateBatchResult processGenerateBatch(ArBillGenerateTask task, ArBillGenerateRequest request,
List<String> sourceBillIds, List<String> excludedSourceBillIds) {
List<String> sourceBillIds, List<String> excludedSourceBillIds,
List<String> excludedOrderIdentifiers) {
ArBillSelectionMode selectionMode = ArBillSelectionMode.of(task.getSelectionMode());
if (selectionMode == null) {
throw new ServiceException("账单生成任务的订单选择模式无效");
@@ -673,6 +695,7 @@ public class ArBillServiceImpl implements IArBillService {
eligibleOrderBillStatuses(), ArIncomeBillFeeStatus.UNBILLED.getCode(),
task.getSnapshotCreateTime(), task.getLastSourceBillId(),
explicitIds, selectionMode == ArBillSelectionMode.ALL_MATCHED ? excludedSourceBillIds : null,
selectionMode == ArBillSelectionMode.ALL_MATCHED ? excludedOrderIdentifiers : null,
GENERATE_PAGE_SIZE);
if (sources == null || sources.isEmpty()) {
return new GenerateBatchResult(true);
@@ -68,14 +68,16 @@ public interface PrcOrderBillMapper
@Param("arBillStatuses") List<String> arBillStatuses, @Param("feeArBillStatus") String feeArBillStatus,
@Param("snapshotCreateTime") Date snapshotCreateTime,
@Param("lastSourceBillId") String lastSourceBillId, @Param("billIds") List<String> billIds,
@Param("excludedBillIds") List<String> excludedBillIds, @Param("limit") int limit);
@Param("excludedBillIds") List<String> excludedBillIds,
@Param("excludedOrderIdentifiers") List<String> excludedOrderIdentifiers, @Param("limit") int limit);
int countPrcOrderBillByCustomerPeriod(@Param("customerId") Long customerId,
@Param("customerCode") String customerCode, @Param("orderIdentifiers") List<String> orderIdentifiers,
@Param("cutoffDate") Date cutoffDate, @Param("currency") String currency,
@Param("arBillStatuses") List<String> arBillStatuses, @Param("feeArBillStatus") String feeArBillStatus,
@Param("snapshotCreateTime") Date snapshotCreateTime,
@Param("billIds") List<String> billIds, @Param("excludedBillIds") List<String> excludedBillIds);
@Param("billIds") List<String> billIds, @Param("excludedBillIds") List<String> excludedBillIds,
@Param("excludedOrderIdentifiers") List<String> excludedOrderIdentifiers);
/** 按生成账单时用户勾选的订单读取,并再次校验账单筛选条件。 */
public List<PrcOrderBill> selectPrcOrderBillByCustomerCutoffAndIds(@Param("customerId") Long customerId,
@@ -693,6 +693,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{excludedBillId}
</foreach>
</if>
<if test="excludedOrderIdentifiers != null and excludedOrderIdentifiers.size() > 0">
and not (
coalesce(p.order_no, '') in
<foreach collection="excludedOrderIdentifiers" item="excludedIdentifier" open="(" separator="," close=")">
#{excludedIdentifier}
</foreach>
or coalesce(p.tracking_no, '') in
<foreach collection="excludedOrderIdentifiers" item="excludedIdentifier" open="(" separator="," close=")">
#{excludedIdentifier}
</foreach>
or coalesce(p.transfer_no, '') in
<foreach collection="excludedOrderIdentifiers" item="excludedIdentifier" open="(" separator="," close=")">
#{excludedIdentifier}
</foreach>
)
</if>
</sql>
<!-- 异步生成使用 bill_id 游标批量读取,避免深分页和一次加载全量订单。 -->