diff --git a/bms-back/ruoyi-admin/src/main/resources/application.yml b/bms-back/ruoyi-admin/src/main/resources/application.yml index a5ebecd7..b9af85d3 100644 --- a/bms-back/ruoyi-admin/src/main/resources/application.yml +++ b/bms-back/ruoyi-admin/src/main/resources/application.yml @@ -47,6 +47,9 @@ user: # Spring配置 spring: + # 允许循环引用(price 模块 PrcOrderBillServiceImpl <-> PrcCreditArrearsClearAsyncRunner 互依赖) + main: + allow-circular-references: true # 资源信息 messages: # 国际化资源文件路径 diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/impl/ArBalanceWriteoffServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/impl/ArBalanceWriteoffServiceImpl.java index 40df1189..6eb0e952 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/impl/ArBalanceWriteoffServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/ar/service/impl/ArBalanceWriteoffServiceImpl.java @@ -28,10 +28,6 @@ import com.ruoyi.ar.service.IArBillLogService; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.uuid.IdUtils; -import com.ruoyi.customer.customermoney.api.BalanceChangeCommand; -import com.ruoyi.customer.customermoney.api.CustomerFundBizType; -import com.ruoyi.customer.customermoney.api.ICustomerAccountApi; -import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow; /** * 应收账单余额核销 Service实现 @@ -53,9 +49,6 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService { @Autowired private IArBillLogService logService; - @Autowired - private ICustomerAccountApi customerAccountApi; - /** * 按客户查询允许余额核销的未结清账单。 */ @@ -76,8 +69,9 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService { } /** - * 执行余额核销主流程:锁账单、登记核销单、扣减客户余额、更新账单金额和状态。 - * 任一步失败都会回滚当前事务,避免核销单、资金流水和账单状态不一致。 + * 执行余额核销主流程:锁账单、登记核销单、更新账单金额和状态。 + * 口径变更(2026-09-23):核销不再变动账户余额与已用额度,资金变动由收款/计费入口产生。 + * 任一步失败都会回滚当前事务,避免核销单与账单状态不一致。 */ @Override @Transactional(rollbackFor = Exception.class) @@ -131,20 +125,8 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService { writeoff.setCreateBy(operator); writeoffMapper.insertArBalanceWriteoff(writeoff); - // 3. 扣减账户余额并释放等额已用额度,写 BILL_PAY 扣款流水(余额不足在此处拦截) - String flowRemark = "余额核销账单:" + bill.getBillNo() - + (StringUtils.hasText(writeoff.getRemark()) ? "(" + writeoff.getRemark() + ")" : ""); - BalanceChangeCommand payCmd = new BalanceChangeCommand(); - payCmd.setCustomerId(bill.getCustomerId()); - payCmd.setCurrency(currency); - payCmd.setAmount(amount); - payCmd.setBizType(CustomerFundBizType.BILL_PAY); - payCmd.setBizId(writeoff.getWriteoffId()); - payCmd.setBizNo(bill.getBillNo()); - payCmd.setRemark(flowRemark); - CustomerBalanceFlow flow = customerAccountApi.deductBalance(payCmd); - writeoffMapper.updateFlowRefs(writeoff.getWriteoffId(), flow.getAccountId(), flow.getId()); - + // 3. 账单核销口径变更(2026-09-23):核销不再变动账户余额与已用额度, + // 仅更新账单已核销/未核销金额与状态,资金变动由收款/计费入口产生。 // 4. 更新账单已核销/未核销缓存与状态(乐观条件更新,状态被并发改动则整体回滚) BigDecimal written = nvl(bill.getWrittenOffAmount()).add(amount); BigDecimal newUnwritten = unwritten.subtract(amount); @@ -155,16 +137,14 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService { } recordLog(bill.getArBillId(), writeoff.getWriteoffId(), ArLogOperationType.BALANCE_WRITEOFF, - "账户余额核销账单 " + amount.toPlainString() + " " + currency, + "余额核销账单 " + amount.toPlainString() + " " + currency, fromStatus, nextStatus, operator, requestId); - writeoff.setAccountId(flow.getAccountId()); - writeoff.setBalanceFlowId(flow.getId()); return writeoff; } /** - * 执行反核销主流程:锁定核销单和账单、回补客户余额、恢复账单未核销金额, - * 最后将原核销记录标记为已取消。 + * 执行反核销主流程:锁定核销单和账单、恢复账单未核销金额, + * 最后将原核销记录标记为已取消(口径变更后不再回补账户余额、不再重新占用已用额度)。 */ @Override @Transactional(rollbackFor = Exception.class) @@ -190,20 +170,8 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService { String cancelRemark = StringUtils.hasText(remark) ? remark.trim() : "取消余额核销"; String fromStatus = bill.getStatus(); - // 2. 余额回补、已用额度重新占用,写反向 BILL_PAY 流水 - String flowRemark = "反核销回补账单:" + bill.getBillNo() + "(" + cancelRemark + ")"; - BalanceChangeCommand reverseCmd = new BalanceChangeCommand(); - reverseCmd.setCustomerId(writeoff.getCustomerId()); - reverseCmd.setCurrency(writeoff.getCurrency()); - reverseCmd.setAmount(nvl(writeoff.getWriteoffAmount())); - reverseCmd.setBizType(CustomerFundBizType.BILL_PAY); - reverseCmd.setBizId(writeoffId); - reverseCmd.setBizNo(bill.getBillNo()); - reverseCmd.setRemark(flowRemark); - reverseCmd.setReoccupyUsedCredit(Boolean.TRUE); - CustomerBalanceFlow reverseFlow = customerAccountApi.refundBalance(reverseCmd); - - // 3. 账单核销金额回退,状态按剩余已核销额重算(归零则回到已确认) + // 2. 账单核销金额回退,状态按剩余已核销额重算(归零则回到已确认) + // 口径变更(2026-09-23):反核销不再回补账户余额、不再重新占用已用额度。 BigDecimal written = nvl(bill.getWrittenOffAmount()).subtract(nvl(writeoff.getWriteoffAmount())); if (written.compareTo(BigDecimal.ZERO) < 0) { written = BigDecimal.ZERO; @@ -215,13 +183,13 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService { throw new ServiceException("账单状态已发生变化,请刷新后重试"); } - // 4. 核销单置为CANCELLED并登记回补流水 - if (writeoffMapper.updateCancelled(writeoffId, reverseFlow.getId(), operator, cancelRemark, + // 3. 核销单置为CANCELLED(口径变更后无回补流水,reverse_flow_id 置空) + if (writeoffMapper.updateCancelled(writeoffId, null, operator, cancelRemark, ArBalanceWriteoffStatus.CANCELLED.getCode(), ArBalanceWriteoffStatus.EFFECTIVE.getCode()) != 1) { throw new ServiceException("核销记录状态已变化,请刷新后重试"); } recordLog(bill.getArBillId(), writeoffId, ArLogOperationType.CANCEL_BALANCE_WRITEOFF, - "反核销回补账单:" + cancelRemark, + "反核销取消核销:" + cancelRemark, fromStatus, nextStatus, operator, requestId); } diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/BalanceChangeCommand.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/BalanceChangeCommand.java index f0026866..021c0ba5 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/BalanceChangeCommand.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/BalanceChangeCommand.java @@ -52,10 +52,10 @@ public class BalanceChangeCommand /** 流水备注 */ private String remark; - /** 扣减余额时是否同步释放等额已用额度(账单核销场景为true),默认true */ + /** 扣减余额时是否同步释放等额已用额度(口径变更 2026-09-23 后已忽略,保留兼容历史调用) */ private Boolean releaseUsedCredit = Boolean.TRUE; - /** 回补余额时是否同步重新占用等额已用额度(反核销场景为true,收款入账为false),默认false */ + /** 回补余额时是否同步重新占用等额已用额度(口径变更 2026-09-23 后已忽略,保留兼容历史调用) */ private Boolean reoccupyUsedCredit = Boolean.FALSE; /** 扣减时是否校验余额充足(不足抛异常),默认true */ diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/ICustomerAccountApi.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/ICustomerAccountApi.java index c76cdc52..40c5db72 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/ICustomerAccountApi.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/ICustomerAccountApi.java @@ -59,25 +59,27 @@ public interface ICustomerAccountApi int initDefaultCnyAccount(Long customerId); /** - * 占用授信额度:used_credit += amount,写 OCCUPY 额度流水(关联来源单据)。 - * 命令中 customerId 与 customerCode 二选一;超额不拦截,允许可用额度(总额度-已占用)为负; - * CNY账户不存在自动初始化,外币账户不存在时静默跳过(返回null,兼容历史口径)。 + * 计费占用:扣减账户余额(balance -= amount),写 -1 余额流水(口径变更 2026-09-23)。 + * 不再变动 used_credit;额度仅在调整/到期失效时变化。允许负余额(欠费)继续计费。 + * 命令中 customerId 与 customerCode 二选一; + * CNY账户不存在自动初始化,外币账户不存在时静默跳过(返回null)。 * - * @return 已落库的额度流水(含ID);账户不存在跳过场景返回null + * @return 口径变更后不再产生额度流水,固定返回 null;账户不存在跳过场景也返回null */ CustomerCreditFlow occupyCredit(CreditUsageCommand command); /** - * 释放授信额度:used_credit -= amount(不小于0),写 RELEASE 额度流水。 - * 命令中 customerId 与 customerCode 二选一;账户不存在时静默跳过(返回null)。 + * 计费释放:回补账户余额(balance += amount),写 +1 余额流水(口径变更 2026-09-23)。 + * 不再变动 used_credit。命令中 customerId 与 customerCode 二选一; + * 账户不存在时静默跳过(返回null)。 * - * @return 已落库的额度流水(含ID);账户不存在跳过场景返回null + * @return 口径变更后不再产生额度流水,固定返回 null;账户不存在跳过场景也返回null */ CustomerCreditFlow releaseCredit(CreditUsageCommand command); /** - * 扣减账户余额:balance -= amount,写 -1 余额流水; - * releaseUsedCredit=true 时同步释放等额已用额度(余额清偿应收场景)。 + * 扣减账户余额:balance -= amount,写 -1 余额流水。 + * 口径变更后不再联动 used_credit(releaseUsedCredit 字段已忽略)。 * 账户不存在或余额不足(checkBalanceEnough=true)抛 ServiceException。 * * @return 已落库的余额流水(含ID、账户ID) @@ -85,9 +87,8 @@ public interface ICustomerAccountApi CustomerBalanceFlow deductBalance(BalanceChangeCommand command); /** - * 回补/入账账户余额:balance += amount,写 +1 余额流水; - * reoccupyUsedCredit=true 时同步加回等额已用额度(反核销场景), - * 收款审核入账等场景保持false。 + * 回补/入账账户余额:balance += amount,写 +1 余额流水。 + * 口径变更后不再联动 used_credit(reoccupyUsedCredit 字段已忽略)。 * * @return 已落库的余额流水(含ID、账户ID) */ @@ -98,7 +99,7 @@ public interface ICustomerAccountApi * 不锁账户也不物化临时额度失效——临时额度已过期时按"视为0"计算可用额度,但 DB 保持原状, * 由定时任务或后续真实资金入口落库。 * - *

欠费定义:可用额度 < 0(已用额度超过授信总额+有效临时额度)。

+ *

欠费定义:可用额度 < 0(账户余额 + 信用额度 + 有效临时额度 不足)。

* * @return 账号状态VO;客户不存在时返回 null */ diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/impl/CustomerAccountApiImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/impl/CustomerAccountApiImpl.java index eb18ec74..7a499726 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/impl/CustomerAccountApiImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/api/impl/CustomerAccountApiImpl.java @@ -281,7 +281,9 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi } } - /* ================= 授信额度占用 / 释放 ================= */ + /* ================= 计费扣减 / 回补余额(口径变更:不再变动 used_credit) ================= */ + // 2026-09-23 口径变更:计费占用/释放改为扣减/回补账户余额,允许负余额(欠费)继续计费。 + // 额度(totalCredit/extraCredit)仅在额度调整或到期失效时变化,计费不再影响 used_credit。 @Override @Transactional(rollbackFor = Exception.class) @@ -292,10 +294,6 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi String currency = resolveCurrency(command.getCurrency()); String username = currentUsername(); Long userId = currentUserId(); - // 计费开单准入:客户被停用(含删除)时直接拦截,与账户状态接口口径保持一致。 - // 仅占用(新增计费)校验;释放/核销等清欠动作不受限,避免客户被停用后无法平账。 - //9月20 调整,客户被停用不拦截计费,继续占用信用额度, -// assertCustomerActive(customerId); CustomerAccount account = lockAccountForUsage(customerId, currency, username); if (account == null) { @@ -303,18 +301,22 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi return null; } BigDecimal amount = money(command.getAmount()); - BigDecimal beforeUsed = nz(account.getUsedCredit()); - // 计费策略(2026-09-20 调整):超额客户不拦截计费,继续占用信用额度, - // 允许可用额度(总额度-已占用)为负;后续基于负额度做超额提醒,不做硬拦截。 - BigDecimal afterUsed = beforeUsed.add(amount).setScale(MONEY_SCALE, RoundingMode.HALF_UP); - account.setUsedCredit(afterUsed); + BigDecimal beforeBalance = nz(account.getBalance()); + // 计费口径变更:occupyCredit 改为扣减账户余额,允许负余额(欠费),不再变动 used_credit + BigDecimal afterBalance = beforeBalance.subtract(amount).setScale(MONEY_SCALE, RoundingMode.HALF_UP); + account.setBalance(afterBalance); account.setUpdateBy(username); if (customerMoneyMapper.updateAccountBalance(account) == 0) { throw new ServiceException("账户已被其他操作变动,请刷新后重试"); } - return writeUsedCreditFlow(account, command, CustomerCreditFlow.CHANGE_OCCUPY, 1, - beforeUsed, afterUsed, amount, username, userId); + // 写余额流水(计费扣减),不再写额度流水 + BigDecimal frozenBalance = nz(account.getFrozenBalance()); + CustomerBalanceFlow flow = newBalanceFlow(account, toBalanceCommand(command, currency, "计费扣减余额"), + currency, amount, -1, beforeBalance, afterBalance, frozenBalance, frozenBalance, username, userId); + customerMoneyMapper.insertFlow(flow); + // 接口契约返回额度流水;口径变更后不再产生额度变动,返回 null(计费侧未使用返回值) + return null; } @Override @@ -332,22 +334,20 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi return null; } BigDecimal amount = money(command.getAmount()); - BigDecimal beforeUsed = nz(account.getUsedCredit()); - BigDecimal afterUsed = beforeUsed.subtract(amount).max(BigDecimal.ZERO) - .setScale(MONEY_SCALE, RoundingMode.HALF_UP); - if (afterUsed.compareTo(beforeUsed) == 0) - { - // 无实际占用可释放,仍返回null不产生流水 - return null; - } - account.setUsedCredit(afterUsed); + BigDecimal beforeBalance = nz(account.getBalance()); + // 计费口径变更:releaseCredit 改为回补账户余额,不再变动 used_credit + BigDecimal afterBalance = beforeBalance.add(amount).setScale(MONEY_SCALE, RoundingMode.HALF_UP); + account.setBalance(afterBalance); account.setUpdateBy(username); if (customerMoneyMapper.updateAccountBalance(account) == 0) { throw new ServiceException("账户已被其他操作变动,请刷新后重试"); } - return writeUsedCreditFlow(account, command, CustomerCreditFlow.CHANGE_RELEASE, -1, - beforeUsed, afterUsed, amount, username, userId); + BigDecimal frozenBalance = nz(account.getFrozenBalance()); + CustomerBalanceFlow flow = newBalanceFlow(account, toBalanceCommand(command, currency, "计费回补余额"), + currency, amount, 1, beforeBalance, afterBalance, frozenBalance, frozenBalance, username, userId); + customerMoneyMapper.insertFlow(flow); + return null; } /* ================= 余额扣减 / 回补 ================= */ @@ -376,12 +376,6 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi BigDecimal afterBalance = beforeBalance.subtract(changeAmount); BigDecimal frozenBalance = nz(account.getFrozenBalance()); account.setBalance(afterBalance); - BigDecimal beforeUsedCredit = nz(account.getUsedCredit()); - if (Boolean.TRUE.equals(command.getReleaseUsedCredit())) - { - account.setUsedCredit(beforeUsedCredit.subtract(changeAmount).max(BigDecimal.ZERO) - .setScale(MONEY_SCALE, RoundingMode.HALF_UP)); - } account.setUpdateBy(username); if (customerMoneyMapper.updateAccountBalance(account) == 0) { @@ -391,18 +385,6 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi CustomerBalanceFlow flow = newBalanceFlow(account, command, flowCurrency, originalAmount, -1, beforeBalance, afterBalance, frozenBalance, frozenBalance, username, userId); customerMoneyMapper.insertFlow(flow); - - // 核销扣款联动释放已用额度:补写额度流水(RELEASE),保证 used_credit 变动台账完整 - BigDecimal afterUsedCredit = nz(account.getUsedCredit()); - if (Boolean.TRUE.equals(command.getReleaseUsedCredit()) - && afterUsedCredit.compareTo(beforeUsedCredit) < 0) - { - writeUsedCreditFlow(account, toCreditUsageCommand(command, accountCurrency, null), - CustomerCreditFlow.CHANGE_RELEASE, -1, - beforeUsedCredit, afterUsedCredit, - beforeUsedCredit.subtract(afterUsedCredit), - username, userId); - } return flow; } @@ -425,13 +407,6 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi BigDecimal afterBalance = beforeBalance.add(changeAmount); BigDecimal frozenBalance = nz(account.getFrozenBalance()); account.setBalance(afterBalance); - BigDecimal beforeUsedCredit = nz(account.getUsedCredit()); - if (Boolean.TRUE.equals(command.getReoccupyUsedCredit())) - { - // 反核销回补:账单恢复未清偿,授信占用同步加回(允许出现高于总额度的占用存量) - account.setUsedCredit(beforeUsedCredit.add(changeAmount) - .setScale(MONEY_SCALE, RoundingMode.HALF_UP)); - } account.setUpdateBy(username); if (customerMoneyMapper.updateAccountBalance(account) == 0) { @@ -441,66 +416,25 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi CustomerBalanceFlow flow = newBalanceFlow(account, command, flowCurrency, originalAmount, 1, beforeBalance, afterBalance, frozenBalance, frozenBalance, username, userId); customerMoneyMapper.insertFlow(flow); - - // 反核销回补联动重新占用已用额度:补写额度流水(OCCUPY),与核销释放流水方向相反、可勾稽 - BigDecimal afterUsedCredit = nz(account.getUsedCredit()); - if (Boolean.TRUE.equals(command.getReoccupyUsedCredit()) - && afterUsedCredit.compareTo(beforeUsedCredit) > 0) - { - String reoccupyReason = "反核销重新占用授信额度" - + (StringUtils.hasText(command.getBizNo()) ? ":" + command.getBizNo() : ""); - writeUsedCreditFlow(account, toCreditUsageCommand(command, accountCurrency, reoccupyReason), - CustomerCreditFlow.CHANGE_OCCUPY, 1, - beforeUsedCredit, afterUsedCredit, - afterUsedCredit.subtract(beforeUsedCredit), - username, userId); - } return flow; } /* ================= 内部实现 ================= */ - /** 余额变动命令 -> 额度占用命令(复用业务单据信息,金额取实际联动额由调用方写流水时传入) */ - private CreditUsageCommand toCreditUsageCommand(BalanceChangeCommand source, String accountCurrency, String reason) + /** 额度占用命令 -> 余额变动命令(计费口径变更后,occupy/release 复用余额流水落库) */ + private BalanceChangeCommand toBalanceCommand(CreditUsageCommand source, String currency, String reason) { - CreditUsageCommand target = new CreditUsageCommand(); + BalanceChangeCommand target = new BalanceChangeCommand(); target.setCustomerId(source.getCustomerId()); - target.setCurrency(accountCurrency); + target.setCurrency(currency); target.setAmount(money(source.getAmount())); target.setBizType(source.getBizType()); target.setBizId(source.getBizId()); target.setBizNo(source.getBizNo()); - target.setReason(reason); + target.setRemark(reason); return target; } - /** 额度占用/释放写额度流水(before/after 为已用额度快照) */ - private CustomerCreditFlow writeUsedCreditFlow(CustomerAccount account, CreditUsageCommand command, - String changeType, int direction, BigDecimal beforeUsed, BigDecimal afterUsed, - BigDecimal amount, String username, Long userId) - { - CustomerCreditFlow flow = new CustomerCreditFlow(); - flow.setFlowNo(generateNo("EDLS")); - flow.setCustomerId(account.getCustomerId()); - flow.setAccountId(account.getId()); - flow.setCreditType(CustomerCreditFlow.CREDIT_TYPE_CREDIT); - flow.setChangeType(changeType); - flow.setBizType(command.getBizType()); - flow.setBizId(command.getBizId()); - flow.setBizNo(command.getBizNo()); - flow.setDirection(direction); - flow.setBeforeAmount(beforeUsed); - flow.setChangeAmount(amount); - flow.setAfterAmount(afterUsed); - flow.setUsedCredit(afterUsed); - flow.setReason(resolveUsageReason(command, changeType)); - flow.setOperatorId(userId); - flow.setCreateBy(username); - flow.setDelFlag("0"); - customerMoneyMapper.insertCreditFlow(flow); - return flow; - } - /** 余额变动写余额流水(原币金额留痕,本位币金额参与余额变动) */ private CustomerBalanceFlow newBalanceFlow(CustomerAccount account, BalanceChangeCommand command, String currency, BigDecimal amount, int direction, @@ -792,9 +726,9 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi vo.setIsEnable(cust.getIsEnable()); vo.setIsEnableLabel("0".equals(cust.getIsEnable()) ? "正常" : "停用"); + BigDecimal balance = account != null ? nz(account.getBalance()) : BigDecimal.ZERO; BigDecimal totalCredit = account != null ? nz(account.getTotalCredit()) : BigDecimal.ZERO; BigDecimal rawExtra = account != null ? nz(account.getExtraCredit()) : BigDecimal.ZERO; - BigDecimal usedCredit = account != null ? nz(account.getUsedCredit()) : BigDecimal.ZERO; // 已过期的信用/临时额度视为0(仅内存计算,不物化DB;由定时任务/真实资金入口落库) boolean creditExpiredPending = isCreditExpired(account); @@ -802,14 +736,14 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi BigDecimal effectiveTotal = creditExpiredPending ? BigDecimal.ZERO : totalCredit; BigDecimal effectiveExtra = expiredPending ? BigDecimal.ZERO : rawExtra; - vo.setBalance(account != null ? nz(account.getBalance()) : BigDecimal.ZERO); + vo.setBalance(balance); vo.setFrozenBalance(account != null ? nz(account.getFrozenBalance()) : BigDecimal.ZERO); vo.setTotalCredit(totalCredit); vo.setEffectiveTotalCredit(effectiveTotal); vo.setExtraCredit(rawExtra); vo.setEffectiveExtraCredit(effectiveExtra); - vo.setUsedCredit(usedCredit); - BigDecimal available = effectiveTotal.add(effectiveExtra).subtract(usedCredit); + // 可用额度 = 账户余额 + 信用额度 + 有效临时额度(2026-09-23 口径:计费扣减余额,已用额度不再参与) + BigDecimal available = balance.add(effectiveTotal).add(effectiveExtra); vo.setAvailableCredit(available); boolean overdue = available.compareTo(BigDecimal.ZERO) < 0; vo.setOverdue(overdue); @@ -855,43 +789,6 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi } } - private String resolveUsageReason(CreditUsageCommand command, String changeType) - { - if (StringUtils.hasText(command.getReason())) - { - return command.getReason().trim(); - } - String action = CustomerCreditFlow.CHANGE_OCCUPY.equals(changeType) ? "占用授信额度" : "释放授信额度"; - String biz = bizTypeLabel(command.getBizType()); - String reason = biz + action; - if (StringUtils.hasText(command.getBizNo())) - { - reason = reason + ":" + command.getBizNo(); - } - return reason; - } - - private String bizTypeLabel(String bizType) - { - if (CustomerFundBizType.RECEIPT.equals(bizType)) - { - return "\u6536\u6b3e"; - } - if (CustomerFundBizType.AR_BILL.equals(bizType)) - { - return "\u5e94\u6536\u8d26\u5355"; - } - if (CustomerFundBizType.ORDER.equals(bizType)) - { - return "\u8ba1\u8d39\u8ba2\u5355"; - } - if (CustomerFundBizType.BILL_PAY.equals(bizType)) - { - return "\u8d26\u5355\u6838\u9500"; - } - return "\u4e1a\u52a1"; - } - /** 临时额度是否已过期:有失效日期且今天已晚于失效日(失效日当天仍有效) */ private boolean isTempCreditExpired(CustomerAccount account) { diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/impl/CustomerMoneyServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/impl/CustomerMoneyServiceImpl.java index 87868983..e9044fca 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/impl/CustomerMoneyServiceImpl.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/service/impl/CustomerMoneyServiceImpl.java @@ -700,7 +700,6 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService customerAccountApi.expireDueCreditsIfNeeded(account); BigDecimal before = isTemp ? nz(account.getExtraCredit()) : nz(account.getTotalCredit()); - BigDecimal otherCredit = isTemp ? nz(account.getTotalCredit()) : nz(account.getExtraCredit()); BigDecimal after; switch (adjustMode) { @@ -719,11 +718,7 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService { throw new ServiceException("调整后额度不能为负数"); } - if (otherCredit.add(after).compareTo(nz(account.getUsedCredit())) < 0) - { - throw new ServiceException("调整后授信总额度不能小于已用额度:" - + nz(account.getUsedCredit()).toPlainString()); - } + // 口径变更(2026-09-23):计费不再变动 used_credit,额度调整无需校验已用额度 if (after.compareTo(before) == 0) { throw new ServiceException("额度未发生变化,无需调整"); diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/vo/CustomerAccountStatusVO.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/vo/CustomerAccountStatusVO.java index 7ac3303b..78d9cdfa 100644 --- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/vo/CustomerAccountStatusVO.java +++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/customer/customermoney/vo/CustomerAccountStatusVO.java @@ -9,7 +9,7 @@ import java.math.BigDecimal; *

只读轻量,不锁账户也不物化临时额度失效:临时额度已过期时按"视为0"计算可用额度, * 但 DB 保持原状——由定时任务或后续真实资金入口落库。

* - *

欠费定义:availableCredit < 0(已用额度超过授信总额+有效临时额度)。

+ *

欠费定义:availableCredit < 0(可用额度 = 账户余额 + 信用额度 + 有效临时额度)。

* * @author ruoyi * @date 2026-09-17 @@ -50,9 +50,7 @@ public class CustomerAccountStatusVO implements Serializable private BigDecimal extraCredit; /** 临时额度实际有效值(过期时为0,接口计算得出,不物化DB) */ private BigDecimal effectiveExtraCredit; - /** 已用额度(CNY) */ - private BigDecimal usedCredit; - /** 可用额度(总额度 + 有效临时额度 - 已用额度;可能为负) */ + /** 可用额度(账户余额 + 授信总额度 + 有效临时额度;可能为负表示欠费) */ private BigDecimal availableCredit; /** 是否欠费(可用额度 < 0) */ @@ -86,8 +84,6 @@ public class CustomerAccountStatusVO implements Serializable public void setExtraCredit(BigDecimal extraCredit) { this.extraCredit = extraCredit; } public BigDecimal getEffectiveExtraCredit() { return effectiveExtraCredit; } public void setEffectiveExtraCredit(BigDecimal effectiveExtraCredit) { this.effectiveExtraCredit = effectiveExtraCredit; } - public BigDecimal getUsedCredit() { return usedCredit; } - public void setUsedCredit(BigDecimal usedCredit) { this.usedCredit = usedCredit; } public BigDecimal getAvailableCredit() { return availableCredit; } public void setAvailableCredit(BigDecimal availableCredit) { this.availableCredit = availableCredit; } public boolean isOverdue() { return overdue; } diff --git a/bms-front/src/views/customersuppliermanagement/customerinfo/customermoney/index.vue b/bms-front/src/views/customersuppliermanagement/customerinfo/customermoney/index.vue index b7e8da19..ace00240 100644 --- a/bms-front/src/views/customersuppliermanagement/customerinfo/customermoney/index.vue +++ b/bms-front/src/views/customersuppliermanagement/customerinfo/customermoney/index.vue @@ -62,12 +62,6 @@
{{ tempCreditPeriodText }}
- - -
已用额度
-
{{ formatMoney(currentAccount.usedCredit) }}
-
-
可用额度
@@ -796,7 +790,7 @@ >
当前{{ creditDialog.form.creditType === 'TEMP' ? '临时额度' : '信用额度' }}: - {{ formatMoney(creditBefore) }},已用额度:{{ formatMoney(currentAccount.usedCredit) }}
+ {{ formatMoney(creditBefore) }},账户余额:{{ formatMoney(currentAccount.balance) }},可用额度:{{ availableCredit }}
@@ -853,7 +847,7 @@
{{ creditDialog.form.creditType === 'TEMP' ? '失效日当天仍有效,次日访问账户时自动清零并记到期流水' - : '信用额度失效日期必填;失效日当天仍有效,次日自动清零,已用额度未回款时将形成超额欠费(可继续计费,请及时催收)' }} + : '信用额度失效日期必填;失效日当天仍有效,次日自动清零;额度失效后可用额度相应减少,若账户余额为负将形成欠费(可继续计费,请及时催收回款)' }}
@@ -877,17 +871,12 @@ {{ formatMoney(creditAfter) }}
调整后授信总额:{{ formatMoney(totalCreditAfter) }} - (可用 = 授信总额 - 已用 {{ formatMoney(currentAccount.usedCredit) }})
+ (可用 = 账户余额 {{ formatMoney(currentAccount.balance) }} + 授信总额 + 额外额度) -