修改更新欠费状态

This commit is contained in:
wuweihua
2026-09-23 11:37:30 +08:00
parent 67c9d28c8c
commit c038621b3c
9 changed files with 273 additions and 20 deletions
@@ -32,6 +32,7 @@ import com.ruoyi.customer.customermoney.mapper.CustomerCreditApplyMapper;
import com.ruoyi.customer.customermoney.mapper.CustomerMoneyMapper;
import com.ruoyi.customer.customermoney.service.ICreditQuotaService;
import com.ruoyi.customer.customermoney.service.ICustomerMoneyService;
import com.ruoyi.price.service.IPrcOrderBillService;
import com.ruoyi.system.domain.BmsExchangeRate;
import com.ruoyi.system.service.IBmsExchangeRateService;
@@ -60,6 +61,9 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
@Autowired
private BmsCustomerMapper bmsCustomerMapper;
@Autowired
private IPrcOrderBillService orderBillService;
@Autowired
private IBmsExchangeRateService exchangeRateService;
@@ -321,6 +325,14 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
fundCmd.setRemark("收款单审核通过:" + receipt.getReceiptNo());
customerAccountApi.refundBalance(fundCmd);
// 余额入账后:按 FIFO 清除订单欠费状态(ARREARS -> NORMAL)
BmsCustomer customer = bmsCustomerMapper.selectBmsCustomerByCustomerId(receipt.getCustomerId());
if (customer != null && StringUtils.hasText(customer.getCustomerCode())
&& baseAmount != null && baseAmount.compareTo(BigDecimal.ZERO) > 0)
{
orderBillService.clearCreditArrearsByAmount(customer.getCustomerCode().trim(), baseAmount);
}
// 2. 收款单置为已确认
receipt.setStatus(CustomerReceipt.STATUS_CONFIRMED);
receipt.setConfirmedAt(new Date());
@@ -88,6 +88,16 @@ public interface PrcOrderBillMapper
public int updatePrcOrderBill(PrcOrderBill bill);
/** 根据客户代码+欠费状态查询订单,按下单时间升序(核销清欠用) */
List<PrcOrderBill> selectArrearsByCustomerCode(@Param("customerCode") String customerCode,
@Param("creditArrearsStatus") String creditArrearsStatus);
/** 更新订单额度欠费状态 */
int updateCreditArrearsStatus(@Param("billId") String billId,
@Param("creditArrearsStatus") String creditArrearsStatus,
@Param("updateBy") String updateBy);
public int updateOrderBillAmounts(@Param("billId") String billId,
@Param("surchargeAmount") BigDecimal surchargeAmount,
@Param("incomeTotal") BigDecimal incomeTotal,
@@ -10,6 +10,8 @@ public final class EsLogType
public static final String QUOTE = "QUOTE";
/** Order credit occupy/release */
public static final String CREDIT = "CREDIT";
/** Clear order credit arrears after balance in */
public static final String ARREARS = "ARREARS";
private EsLogType()
{
@@ -1,5 +1,6 @@
package com.ruoyi.price.service;
import java.math.BigDecimal;
import java.util.List;
import com.ruoyi.price.domain.PrcOrderBill;
import com.ruoyi.price.domain.vo.AddIncomeBillFeeRequest;
@@ -39,4 +40,24 @@ public interface IPrcOrderBillService
/** 提供给 TMS 判断订单是否可以继续处理的客户状态查询。 */
TmsOrderBillStatusResult queryTmsOrderBillStatus(String identifier);
/**
* 余额充值等审核入账后调用(异步):按客户代码查询欠费(ARREARS)订单,
* 按下单时间升序依次更为 NORMAL;更新日志写入 ES(logType=ARREARS)。
*
* @param customerCode 客户代码
* @param amount 入账金额(依次抵扣订单 income_total,通常为本位币CNY)
*/
void clearCreditArrearsByAmount(String customerCode, BigDecimal amount);
/**
* 异步执行体:实际清欠并写 ES日志(供异步 Runner 调用)。
*
* @return 已更新为 NORMAL 的订单数
*/
int processClearCreditArrearsByAmount(String customerCode, BigDecimal amount);
/** 异步清欠失败时写 ES 摘要日志 */
void logClearCreditArrearsFail(String customerCode, BigDecimal amount, String errorMessage);
}
@@ -14,6 +14,8 @@ import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.ar.enums.ArIncomeBillFeeStatus;
@@ -55,6 +57,7 @@ import com.ruoyi.customer.customermoney.vo.CustomerAccountStatusVO;
import com.ruoyi.price.service.IPrcOrderBillService;
import com.ruoyi.price.service.IPrcSurchargeService;
import com.ruoyi.price.service.ITmsPriceQuoteService;
import com.ruoyi.price.task.PrcCreditArrearsClearAsyncRunner;
@Service
public class PrcOrderBillServiceImpl implements IPrcOrderBillService
@@ -89,6 +92,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
@Autowired
private IPrcSurchargeService surchargeService;
@Autowired
private PrcCreditArrearsClearAsyncRunner creditArrearsClearAsyncRunner;
@Override
@Transactional(rollbackFor = Exception.class)
public TmsPriceQuoteResult bill(TmsPriceBillRequest request)
@@ -489,6 +495,148 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
return result;
}
/**
* 余额充值等审核入账后调用:校验参数后投递异步清欠(数据量大时避免阻塞审核事务)。
*/
@Override
public void clearCreditArrearsByAmount(String customerCode, BigDecimal amount)
{
String code = StringUtils.trim(customerCode);
if (StringUtils.isEmpty(code))
{
throw new ServiceException("客户代码不能为空");
}
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0)
{
throw new ServiceException("入账金额必须大于0");
}
// 审核事务提交后再投递异步,避免回滚后误清欠
if (TransactionSynchronizationManager.isSynchronizationActive())
{
final String asyncCode = code;
final BigDecimal asyncAmount = amount;
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization()
{
@Override
public void afterCommit()
{
creditArrearsClearAsyncRunner.run(asyncCode, asyncAmount);
}
});
}
else
{
creditArrearsClearAsyncRunner.run(code, amount);
}
}
/**
* 异步执行:按下单时间升序 FIFO 清欠,逐单与摘要日志写入 ES。
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int processClearCreditArrearsByAmount(String customerCode, BigDecimal amount)
{
String code = StringUtils.trim(customerCode);
if (StringUtils.isEmpty(code))
{
throw new ServiceException("客户代码不能为空");
}
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0)
{
throw new ServiceException("入账金额必须大于0");
}
BigDecimal remaining = amount;
List<PrcOrderBill> bills = orderBillMapper.selectArrearsByCustomerCode(code,
BillCreditArrearsStatus.ARREARS.getCode());
if (bills == null || bills.isEmpty())
{
saveArrearsClearSummaryEsLog(code, amount, 0, remaining, true, "no arrears orders");
return 0;
}
String normal = BillCreditArrearsStatus.NORMAL.getCode();
int updated = 0;
for (PrcOrderBill bill : bills)
{
BigDecimal orderAmt = nz(bill.getIncomeTotal());
// 当前订单额度大于剩余金额:停止更新
if (orderAmt.compareTo(remaining) > 0)
{
break;
}
orderBillMapper.updateCreditArrearsStatus(bill.getBillId(), normal, "receipt");
remaining = remaining.subtract(orderAmt);
updated++;
saveArrearsClearOrderEsLog(bill, amount, orderAmt, remaining, true, null);
if (remaining.compareTo(BigDecimal.ZERO) <= 0)
{
break;
}
}
saveArrearsClearSummaryEsLog(code, amount, updated, remaining, true, null);
return updated;
}
@Override
public void logClearCreditArrearsFail(String customerCode, BigDecimal amount, String errorMessage)
{
saveArrearsClearSummaryEsLog(StringUtils.trim(customerCode), amount, 0, amount, false, errorMessage);
}
private void saveArrearsClearSummaryEsLog(String customerCode, BigDecimal amount, int updated,
BigDecimal remaining, boolean success, String note)
{
if (billSearchStore == null || StringUtils.isEmpty(customerCode))
{
return;
}
JSONObject req = new JSONObject();
req.put("action", "CLEAR_ARREARS");
req.put("scope", "SUMMARY");
req.put("customerCode", customerCode);
req.put("amount", amount);
JSONObject resp = new JSONObject();
resp.put("success", Boolean.valueOf(success));
resp.put("updatedCount", Integer.valueOf(updated));
resp.put("remainingAmount", remaining);
if (StringUtils.isNotEmpty(note))
{
resp.put("msg", note);
}
String status = success ? EsBizLogSupport.STATUS_SUCCESS : EsBizLogSupport.STATUS_FAIL;
String err = success ? null : EsBizLogSupport.cut(note);
billSearchStore.save(EsLogType.ARREARS, customerCode, req.toJSONString(), resp.toJSONString(), status, err);
}
private void saveArrearsClearOrderEsLog(PrcOrderBill bill, BigDecimal batchAmount, BigDecimal orderAmt,
BigDecimal remaining, boolean success, String errorMessage)
{
if (billSearchStore == null || bill == null || StringUtils.isEmpty(bill.getOrderNo()))
{
return;
}
JSONObject req = new JSONObject();
req.put("action", "CLEAR_ARREARS");
req.put("scope", "ORDER");
req.put("orderNo", bill.getOrderNo());
req.put("billId", bill.getBillId());
req.put("customerCode", bill.getCustomerCode());
req.put("batchAmount", batchAmount);
req.put("orderIncomeTotal", orderAmt);
req.put("fromStatus", BillCreditArrearsStatus.ARREARS.getCode());
req.put("toStatus", BillCreditArrearsStatus.NORMAL.getCode());
JSONObject resp = new JSONObject();
resp.put("success", Boolean.valueOf(success));
resp.put("remainingAmount", remaining);
if (StringUtils.isNotEmpty(errorMessage))
{
resp.put("msg", errorMessage);
}
String status = success ? EsBizLogSupport.STATUS_SUCCESS : EsBizLogSupport.STATUS_FAIL;
String err = success ? null : EsBizLogSupport.cut(errorMessage);
billSearchStore.save(EsLogType.ARREARS, bill.getOrderNo(), req.toJSONString(), resp.toJSONString(), status, err);
}
private TmsOrderBillMatchType resolveTmsMatchType(String identifier, PrcOrderBill bill)
{
if (identifier.equalsIgnoreCase(StringUtils.trim(bill.getOrderNo())))
@@ -0,0 +1,44 @@
package com.ruoyi.price.task;
import java.math.BigDecimal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.price.service.IPrcOrderBillService;
/**
* 异步清除订单额度欠费状态(余额充值等审核入账后)。
*/
@Component
public class PrcCreditArrearsClearAsyncRunner
{
private static final Logger log = LoggerFactory.getLogger(PrcCreditArrearsClearAsyncRunner.class);
@Autowired
private IPrcOrderBillService orderBillService;
@Async("threadPoolTaskExecutor")
public void run(String customerCode, BigDecimal amount)
{
try
{
int updated = orderBillService.processClearCreditArrearsByAmount(customerCode, amount);
log.info("async clear credit arrears done, customerCode={}, amount={}, updated={}",
customerCode, amount, Integer.valueOf(updated));
}
catch (Exception e)
{
log.error("async clear credit arrears failed, customerCode={}, amount={}",
customerCode, amount, e);
String msg = e.getMessage();
if (StringUtils.isEmpty(msg))
{
msg = e.getClass().getSimpleName();
}
orderBillService.logClearCreditArrearsFail(customerCode, amount, msg);
}
}
}
@@ -621,4 +621,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
end,
o.update_time = sysdate()
</update>
<!-- 核销清欠:按客户代码+欠费状态查询,下单时间升序 -->
<select id="selectArrearsByCustomerCode" resultMap="PrcOrderBillResult">
<include refid="selectPrcOrderBillVo"/>
where customer_code = #{customerCode}
and credit_arrears_status = #{creditArrearsStatus}
order by (order_time is null), order_time asc, bill_id asc
</select>
<update id="updateCreditArrearsStatus">
update prc_order_bill
set credit_arrears_status = #{creditArrearsStatus},
updated_by = #{updateBy},
update_time = sysdate()
where bill_id = #{billId}
</update>
</mapper>
@@ -69,16 +69,16 @@ const schemas = computed(() => [
width: '100%',
props: { maxlength: 50 }
},
{
label: '税模式',
prop: 'taxMode',
component: 'select',
placeholder: '请选择',
dict: 'bms_tax_mode',
span: 12,
width: '100%',
rules: [{ required: true, message: '请选择税模式', trigger: 'change' }]
},
// {
// label: '税模式',
// prop: 'taxMode',
// component: 'select',
// placeholder: '请选择',
// dict: 'bms_tax_mode',
// span: 12,
// width: '100%',
// rules: [{ required: true, message: '请选择税模式', trigger: 'change' }]
// },
{
label: '生效日期',
prop: 'effectiveDate',
@@ -113,7 +113,7 @@ const columns = [
{ prop: 'serviceLevel', label: '时效等级', width: 90, align: 'center' },
{ prop: 'cargoAttr', label: '走货属性', minWidth: 120, slot: 'cargoAttr' },
{ prop: 'destRegion', label: '目的区域', width: 90, align: 'center' },
{ prop: 'taxMode', label: '税模式', width: 90, align: 'center', dict: 'bms_tax_mode' },
// { prop: 'taxMode', label: '税模式', width: 90, align: 'center', dict: 'bms_tax_mode' },
{ prop: 'effectiveDate', label: '生效日期', width: 120, align: 'center' },
{ prop: 'expireDate', label: '失效日期', width: 120, align: 'center' },
{ prop: 'countryCount', label: '国家数', width: 80, align: 'center' },
@@ -125,15 +125,15 @@ const columns = [
const searchColumns = [
{ prop: 'productCode', label: '产品代码', component: 'input', placeholder: '产品代码' },
{ prop: 'productName', label: '产品名称', component: 'input', placeholder: '产品名称' },
{
prop: 'taxMode',
label: '税模式',
component: 'select',
dict: 'bms_tax_mode',
placeholder: '税模式',
clearable: true
}
{ prop: 'productName', label: '产品名称', component: 'input', placeholder: '产品名称' }
// {
// prop: 'taxMode',
// label: '税模式',
// component: 'select',
// dict: 'bms_tax_mode',
// placeholder: '税模式',
// clearable: true
// }
]
function queryList(params) {