实现扣减额度功能

This commit is contained in:
wuweihua
2026-09-17 16:05:46 +08:00
parent 5dbee51a3a
commit 78f2da431b
7 changed files with 174 additions and 11 deletions
@@ -246,8 +246,8 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
validateUsageCommand(command);
Long customerId = resolveCustomerId(command);
String currency = resolveCurrency(command.getCurrency());
String username = SecurityUtils.getUsername();
Long userId = SecurityUtils.getUserId();
String username = currentUsername();
Long userId = currentUserId();
CustomerAccount account = lockAccountForUsage(customerId, currency, username);
if (account == null)
{
@@ -281,8 +281,8 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
validateUsageCommand(command);
Long customerId = resolveCustomerId(command);
String currency = resolveCurrency(command.getCurrency());
String username = SecurityUtils.getUsername();
Long userId = SecurityUtils.getUserId();
String username = currentUsername();
Long userId = currentUserId();
CustomerAccount account = lockAccountForUsage(customerId, currency, username);
if (account == null)
{
@@ -635,19 +635,23 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
private String bizTypeLabel(String bizType)
{
if (CustomerFundBizType.RECEIPT.equals(bizType))
{
return "\u6536\u6b3e";
}
if (CustomerFundBizType.AR_BILL.equals(bizType))
{
return "应收账单";
return "\u5e94\u6536\u8d26\u5355";
}
if (CustomerFundBizType.ORDER.equals(bizType))
{
return "计费订单";
return "\u8ba1\u8d39\u8ba2\u5355";
}
if (CustomerFundBizType.BILL_PAY.equals(bizType))
{
return "账单核销";
return "\u8d26\u5355\u6838\u9500";
}
return "业务";
return "\u4e1a\u52a1";
}
/** 临时额度是否已过期:有失效日期且今天已晚于失效日(失效日当天仍有效) */
@@ -8,6 +8,8 @@ public final class EsLogType
{
public static final String BILL = "BILL";
public static final String QUOTE = "QUOTE";
/** Order credit occupy/release */
public static final String CREDIT = "CREDIT";
private EsLogType()
{
@@ -28,6 +28,9 @@ import com.ruoyi.price.mapper.PrcPayBillFeeMapper;
import com.ruoyi.price.search.EsBizLogSupport;
import com.ruoyi.price.search.EsLogType;
import com.ruoyi.price.search.IBillSearchStore;
import com.ruoyi.customer.customermoney.api.CreditUsageCommand;
import com.ruoyi.customer.customermoney.api.CustomerFundBizType;
import com.ruoyi.customer.customermoney.api.ICustomerAccountApi;
import com.ruoyi.price.service.IPrcOrderBillService;
import com.ruoyi.price.service.ITmsPriceQuoteService;
@@ -49,6 +52,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
@Autowired
private IBillSearchStore billSearchStore;
@Autowired
private ICustomerAccountApi customerAccountApi;
@Override
@Transactional(rollbackFor = Exception.class)
public TmsPriceQuoteResult bill(TmsPriceBillRequest request)
@@ -121,6 +127,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
String orderNo = request.getOrderNo();
String billKey = Md5Utils.hash(orderNo);
PrcOrderBill existed = orderBillMapper.selectPrcOrderBillByOrderNo(orderNo);
BigDecimal previousIncome = existed == null ? BigDecimal.ZERO : nz(existed.getIncomeTotal());
PrcOrderBill bill = existed == null ? new PrcOrderBill() : existed;
if (existed == null)
{
@@ -209,9 +216,105 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
{
incomeBillFeeMapper.insertBatch(fees);
}
syncOrderCredit(bill, previousIncome);
billSearchStore.save(EsLogType.BILL, orderNo, toRequestJson(request), toResponseJson(result));
}
/**
* Occupy/release credit by income total delta (bizType=ORDER).
* Always write CREDIT log to ES (SUCCESS / FAIL), then rethrow on failure.
*/
private void syncOrderCredit(PrcOrderBill bill, BigDecimal previousIncome)
{
if (bill == null)
{
return;
}
Long customerId = bill.getCustomerId();
if (customerId == null || customerId.longValue() <= 0L)
{
ServiceException ex = new ServiceException(MessageUtils.message("price.bill.customerRequired"));
saveCreditEsLog(bill, null, "SKIP", false, EsBizLogSupport.errorText(ex));
throw ex;
}
BigDecimal oldAmt = nz(previousIncome);
BigDecimal newAmt = nz(bill.getIncomeTotal());
BigDecimal delta = newAmt.subtract(oldAmt);
if (delta.compareTo(BigDecimal.ZERO) == 0)
{
return;
}
CreditUsageCommand cmd = new CreditUsageCommand();
cmd.setCustomerId(customerId);
cmd.setCustomerCode(bill.getCustomerCode());
cmd.setCurrency(StringUtils.isEmpty(bill.getCurrency()) ? "CNY" : bill.getCurrency());
cmd.setBizType(CustomerFundBizType.ORDER);
cmd.setBizNo(bill.getOrderNo());
cmd.setCheckAvailable(Boolean.TRUE);
String action = delta.compareTo(BigDecimal.ZERO) > 0 ? "OCCUPY" : "RELEASE";
try
{
if (delta.compareTo(BigDecimal.ZERO) > 0)
{
cmd.setAmount(delta);
customerAccountApi.occupyCredit(cmd);
}
else
{
cmd.setAmount(delta.abs());
customerAccountApi.releaseCredit(cmd);
}
saveCreditEsLog(bill, cmd, action, true, null);
}
catch (RuntimeException e)
{
saveCreditEsLog(bill, cmd, action, false, EsBizLogSupport.errorText(e));
throw e;
}
}
/** CREDIT ES log; does not set ATTR_FAIL_LOGGED so BILL fail can still be written. */
private void saveCreditEsLog(PrcOrderBill bill, CreditUsageCommand cmd, String action, boolean success,
String errorMessage)
{
if (billSearchStore == null || bill == null || StringUtils.isEmpty(bill.getOrderNo()))
{
return;
}
JSONObject req = new JSONObject();
req.put("orderNo", bill.getOrderNo());
req.put("billId", bill.getBillId());
req.put("customerId", bill.getCustomerId());
req.put("customerCode", bill.getCustomerCode());
req.put("currency", bill.getCurrency());
req.put("incomeTotal", bill.getIncomeTotal());
req.put("action", action);
req.put("bizType", CustomerFundBizType.ORDER);
if (cmd != null)
{
req.put("amount", cmd.getAmount());
req.put("checkAvailable", cmd.getCheckAvailable());
}
JSONObject resp = new JSONObject();
resp.put("success", Boolean.valueOf(success));
if (success)
{
resp.put("msg", action);
}
else
{
resp.put("msg", errorMessage);
}
String status = success ? EsBizLogSupport.STATUS_SUCCESS : EsBizLogSupport.STATUS_FAIL;
String err = success ? null : EsBizLogSupport.cut(errorMessage);
billSearchStore.save(EsLogType.CREDIT, bill.getOrderNo(), req.toJSONString(), resp.toJSONString(), status, err);
}
private static BigDecimal nz(BigDecimal v)
{
return v == null ? BigDecimal.ZERO : v;
}
private List<PrcIncomeBillFee> toFeeRows(String billId, TmsPriceQuoteResult result)
{
List<PrcIncomeBillFee> fees = new ArrayList<PrcIncomeBillFee>();