✨ 正式结算单新增全票池发票认领查询端点(多条件+内存精确分页)

This commit is contained in:
刘泉佳
2026-09-20 19:41:19 +08:00
parent 31a039245c
commit 9a35d408dc
5 changed files with 278 additions and 0 deletions
@@ -0,0 +1,74 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.transport.pojo.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 正式结算单认领查询 全票池发票查询条件
*
* @author Chill
*/
@Data
public class FormalSettlementInvoicePoolQueryDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 页码,默认 1
*/
private Long current = 1L;
/**
* 每页条数,默认 10,最大 500
*/
private Long size = 10L;
/**
* 发票号码
*/
private String invoiceNo;
/**
* 开票日期起 yyyy-MM-dd
*/
private String invoiceDateStart;
/**
* 开票日期止 yyyy-MM-dd
*/
private String invoiceDateEnd;
/**
* 销方税号
*/
private String salerTaxNo;
/**
* 购方税号
*/
private String buyerTaxNo;
/**
* 单据使用状态,多个用英文逗号分隔[1未用,30在用,60已用,65已入账]
*/
private String expenseStatus;
}
@@ -0,0 +1,87 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.transport.pojo.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 正式结算单认领查询 全票池发票VO
*
* @author Chill
*/
@Data
public class FormalSettlementInvoicePoolVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 发票号码
*/
private String invoiceNo;
/**
* 发票流水号
*/
private String serialNo;
/**
* 开票日期
*/
private String invoiceDate;
/**
* 发票类型名称
*/
private String invoiceTypeName;
/**
* 税率(百分数,如 13);票池明细缺失时为 null
*/
private BigDecimal taxRate;
/**
* 发票金额(含税,取票池价税合计)
*/
private BigDecimal invoiceAmount;
/**
* 可匹配发票金额(含税,本次口径等于价税合计)
*/
private BigDecimal availableInvoiceAmount;
/**
* 认领金额(默认等于可匹配金额,由前端按结算余额分配)
*/
private BigDecimal matchedAmount;
/**
* 开票单位(销方名称)
*/
private String salerName;
/**
* 受票单位(购方名称)
*/
private String buyerName;
/**
* 附件信息JSON,结构 {"source":"piaozone","path":"A801-2下载path","name":"文件名"}
*/
private String attachmentJson;
}
@@ -27,8 +27,10 @@ import org.springblade.transport.pojo.dto.FormalSettlementPaymentRequest;
import org.springblade.transport.pojo.dto.FormalSettlementBatchPaymentRequest;
import org.springblade.transport.pojo.dto.FormalSettlementStatusRequest;
import org.springblade.transport.pojo.dto.FormalSettlementInvoiceClaimRequest;
import org.springblade.transport.pojo.dto.FormalSettlementInvoicePoolQueryDTO;
import org.springblade.transport.pojo.dto.PreSettlementDetailAdjustRequest;
import org.springblade.transport.pojo.entity.FormalSettlementDetailFee;
import org.springblade.transport.pojo.vo.FormalSettlementInvoicePoolVO;
import org.springblade.transport.pojo.vo.FormalSettlementVO;
import org.springblade.transport.excel.FormalSettlementExcel;
import org.springblade.transport.pojo.vo.PreSettlementVO;
@@ -250,4 +252,11 @@ public class FormalSettlementController extends BladeController {
public R<List<Map<String, Object>>> receiptClaims(@RequestParam Long formalSettlementId) {
return R.data(receiptFlowService.settlementClaims(formalSettlementId));
}
@GetMapping("/invoice-pool")
@ApiOperationSupport(order = 21)
@Operation(summary = "查询发票云全票池发票(认领查询)")
public R<IPage<FormalSettlementInvoicePoolVO>> invoicePool(FormalSettlementInvoicePoolQueryDTO query) {
return R.data(formalSettlementService.invoicePoolQuery(query));
}
}
@@ -13,6 +13,7 @@ import org.springblade.core.mp.base.BaseService;
import org.springblade.transport.pojo.dto.FormalSettlementSaveRequest;
import org.springblade.transport.pojo.dto.FormalSettlementStatusRequest;
import org.springblade.transport.pojo.entity.FormalSettlement;
import org.springblade.transport.pojo.vo.FormalSettlementInvoicePoolVO;
import org.springblade.transport.pojo.vo.FormalSettlementVO;
import org.springblade.transport.pojo.vo.PreSettlementVO;
import org.springblade.transport.pojo.entity.PreSettlement;
@@ -22,6 +23,7 @@ import java.util.List;
import org.springblade.transport.pojo.dto.FormalSettlementPaymentRequest;
import org.springblade.transport.pojo.dto.FormalSettlementBatchPaymentRequest;
import org.springblade.transport.pojo.dto.FormalSettlementInvoiceClaimRequest;
import org.springblade.transport.pojo.dto.FormalSettlementInvoicePoolQueryDTO;
/**
* 正式结算单服务
@@ -47,4 +49,5 @@ public interface IFormalSettlementService extends BaseService<FormalSettlement>
String applyPayment(FormalSettlementPaymentRequest request);
List<String> applyPayments(FormalSettlementBatchPaymentRequest request);
void claimInvoices(FormalSettlementInvoiceClaimRequest request);
IPage<FormalSettlementInvoicePoolVO> invoicePoolQuery(FormalSettlementInvoicePoolQueryDTO query);
}
@@ -11,12 +11,21 @@ package org.springblade.transport.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.thirdparty.bfi.constant.BfiConstant;
import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoicePoolQueryDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoicePoolQueryDataDTO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoicePoolItemVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoicePoolVO;
import org.springblade.thirdparty.bfi.service.IBfiService;
import org.springblade.transport.mapper.FormalSettlementDetailMapper;
import org.springblade.transport.mapper.FormalSettlementDetailFeeMapper;
import org.springblade.transport.mapper.FormalSettlementChangeRecordMapper;
@@ -44,6 +53,7 @@ import org.springblade.transport.pojo.dto.FormalSettlementSaveRequest;
import org.springblade.transport.pojo.dto.FormalSettlementPaymentRequest;
import org.springblade.transport.pojo.dto.FormalSettlementBatchPaymentRequest;
import org.springblade.transport.pojo.dto.FormalSettlementInvoiceClaimRequest;
import org.springblade.transport.pojo.dto.FormalSettlementInvoicePoolQueryDTO;
import org.springblade.transport.pojo.dto.FormalSettlementStatusRequest;
import org.springblade.transport.pojo.dto.PreSettlementDetailAdjustRequest;
import org.springblade.transport.pojo.entity.FormalSettlement;
@@ -69,6 +79,7 @@ import org.springblade.transport.pojo.entity.ReceivablePayableDetail;
import org.springblade.transport.pojo.entity.ReceivablePayableCargoFee;
import org.springblade.transport.pojo.entity.SettlementAdjustment;
import org.springblade.transport.pojo.entity.Waybill;
import org.springblade.transport.pojo.vo.FormalSettlementInvoicePoolVO;
import org.springblade.transport.pojo.vo.FormalSettlementVO;
import org.springblade.transport.pojo.vo.PreSettlementVO;
import org.springblade.transport.pojo.vo.SettlementAdjustmentVO;
@@ -97,6 +108,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -108,6 +120,7 @@ import java.util.stream.Collectors;
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlementMapper, FormalSettlement>
implements IFormalSettlementService {
@@ -140,6 +153,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
private final IPreSettlementService preSettlementService;
private final IWaybillService waybillService;
private final KingdeeApBillSyncSupport kingdeeApBillSyncSupport;
private final IBfiService bfiService;
@Override
public IPage<FormalSettlementVO> selectPage(IPage<FormalSettlement> page, FormalSettlementVO query) {
@@ -1369,4 +1383,95 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
private String required(String value, String field) { if (Func.isEmpty(value)) throw new ServiceException("请填写" + field); return value; }
private String firstNotEmpty(Object first, Object second) { return Func.isNotEmpty(first) ? String.valueOf(first) : String.valueOf(second); }
private String limit(String value, int max) { if (value != null && value.length() > max) throw new ServiceException("内容不能超过" + max + "个字"); return value; }
@Override
public IPage<FormalSettlementInvoicePoolVO> invoicePoolQuery(FormalSettlementInvoicePoolQueryDTO query) {
long pageNo = Math.max(query.getCurrent() == null ? 1 : query.getCurrent(), 1);
long pageSize = Math.min(Math.max(query.getSize() == null ? 10 : query.getSize(), 1), 500);
String invoiceNo = query.getInvoiceNo();
String invoiceDateStart = query.getInvoiceDateStart();
String invoiceDateEnd = query.getInvoiceDateEnd();
String salerTaxNo = query.getSalerTaxNo();
String buyerTaxNo = query.getBuyerTaxNo();
String expenseStatus = query.getExpenseStatus();
// A801-1 不返回总数:单次按最大页大小全量翻页拉取后,在内存中做精确分页
BfiInvoicePoolQueryDataDTO data = new BfiInvoicePoolQueryDataDTO();
data.setPageSize((int) pageSize);
if (StringUtil.isNotBlank(invoiceNo)) {
data.setInvoiceNo(invoiceNo.trim());
}
if (StringUtil.isNotBlank(invoiceDateStart)) {
data.setInvoiceDateStart(invoiceDateStart.trim());
}
if (StringUtil.isNotBlank(invoiceDateEnd)) {
data.setInvoiceDateEnd(invoiceDateEnd.trim());
}
if (StringUtil.isNotBlank(salerTaxNo)) {
data.setSalerTaxNo(salerTaxNo.trim());
}
if (StringUtil.isNotBlank(buyerTaxNo)) {
data.setBuyerTaxNo(buyerTaxNo.trim());
}
if (StringUtil.isNotBlank(expenseStatus)) {
data.setExpenseStatus(expenseStatus.trim());
}
BfiInvoicePoolQueryDTO param = new BfiInvoicePoolQueryDTO();
param.setData(data);
List<BfiInvoicePoolVO> poolInvoices = bfiService.queryInvoicePool(param);
List<FormalSettlementInvoicePoolVO> invoices = poolInvoices.stream()
.filter(poolInvoice -> !"1".equals(poolInvoice.getDelete()))
.map(this::toInvoicePoolVO)
.toList();
log.info("正式结算单认领查询 全票池原始 {} 条,过滤作废后 {} 条,返回第 {} 页 {} 条/页",
poolInvoices.size(), invoices.size(), pageNo, pageSize);
// 内存精确分页:total 为过滤后精确总数,请求页超出范围时返回空records
int from = (int) ((pageNo - 1) * pageSize);
int to = (int) Math.min(from + pageSize, invoices.size());
List<FormalSettlementInvoicePoolVO> records = from >= invoices.size() ? List.of() : invoices.subList(from, to);
Page<FormalSettlementInvoicePoolVO> page = new Page<>(pageNo, pageSize);
page.setRecords(records);
page.setTotal(invoices.size());
return page;
}
private FormalSettlementInvoicePoolVO toInvoicePoolVO(BfiInvoicePoolVO poolInvoice) {
FormalSettlementInvoicePoolVO vo = new FormalSettlementInvoicePoolVO();
vo.setInvoiceNo(poolInvoice.getInvoiceNo());
vo.setSerialNo(poolInvoice.getSerialNo());
vo.setInvoiceDate(poolInvoice.getInvoiceDate());
vo.setInvoiceTypeName(BfiConstant.INVOICE_TYPE_NAMES.getOrDefault(poolInvoice.getInvoiceType(),
poolInvoice.getInvoiceType()));
vo.setTaxRate(resolveTaxRate(poolInvoice));
BigDecimal totalAmount = parseAmount(poolInvoice.getTotalAmount());
vo.setInvoiceAmount(totalAmount);
vo.setAvailableInvoiceAmount(totalAmount);
vo.setMatchedAmount(totalAmount);
vo.setSalerName(poolInvoice.getSalerName());
vo.setBuyerName(poolInvoice.getBuyerName());
Map<String, Object> attachment = new LinkedHashMap<>();
attachment.put("source", "piaozone");
attachment.put("path", poolInvoice.getDownloadUrl());
attachment.put("name", poolInvoice.getInvoiceNo() + ".pdf");
vo.setAttachmentJson(JsonUtil.toJson(attachment));
return vo;
}
/**
* 税率优先取顶层税率的小数值换算为百分数,如 0.13 -> 13;顶层为空时回退取首条明细税率,均无时返回 null
*/
private BigDecimal resolveTaxRate(BfiInvoicePoolVO poolInvoice) {
if (StringUtil.isNotBlank(poolInvoice.getTaxRate())) {
return new BigDecimal(poolInvoice.getTaxRate()).multiply(BigDecimal.valueOf(100));
}
return Optional.ofNullable(poolInvoice.getItems()).orElse(List.of()).stream()
.map(BfiInvoicePoolItemVO::getTaxRate)
.filter(StringUtil::isNotBlank)
.findFirst()
.map(taxRate -> new BigDecimal(taxRate).multiply(BigDecimal.valueOf(100)))
.orElse(null);
}
private BigDecimal parseAmount(String value) {
return StringUtil.isBlank(value) ? BigDecimal.ZERO : new BigDecimal(value);
}
}