额度流水显示问题修改
This commit is contained in:
+12
@@ -3,6 +3,7 @@ package com.ruoyi.customer.customermoney.api;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerAccount;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerCreditFlow;
|
||||
import com.ruoyi.customer.customermoney.vo.CustomerAccountStatusVO;
|
||||
|
||||
/**
|
||||
* 客户资金公共服务:客户余额扣减/回补、授信额度占用/释放的统一入口。
|
||||
@@ -90,4 +91,15 @@ public interface ICustomerAccountApi
|
||||
* @return 已落库的余额流水(含ID、账户ID)
|
||||
*/
|
||||
CustomerBalanceFlow refundBalance(BalanceChangeCommand command);
|
||||
|
||||
/**
|
||||
* 查询客户账号状态(收货扣件准入控制用)。支持 customerId / customerCode 二选一,只读轻量查询,
|
||||
* 不锁账户也不物化临时额度失效——临时额度已过期时按"视为0"计算可用额度,但 DB 保持原状,
|
||||
* 由定时任务或后续真实资金入口落库。
|
||||
*
|
||||
* <p>欠费定义:可用额度 < 0(已用额度超过授信总额+有效临时额度)。</p>
|
||||
*
|
||||
* @return 账号状态VO;客户不存在时返回 null
|
||||
*/
|
||||
CustomerAccountStatusVO getAccountStatus(Long customerId, String customerCode);
|
||||
}
|
||||
|
||||
+99
-14
@@ -28,6 +28,7 @@ import com.ruoyi.customer.customermoney.api.CustomerFundBizType;
|
||||
import com.ruoyi.customer.customermoney.api.ICustomerAccountApi;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerAccount;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow;
|
||||
import com.ruoyi.customer.customermoney.vo.CustomerAccountStatusVO;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerCreditFlow;
|
||||
import com.ruoyi.customer.customermoney.mapper.CustomerMoneyMapper;
|
||||
import com.ruoyi.system.domain.BmsExchangeRate;
|
||||
@@ -574,7 +575,7 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析客户标识:customerId 优先;未传时按 customerCode 精确查询有效客户档案。
|
||||
* 解析额度命令的客户标识:customerId 优先;未传时按 customerCode 精确查询有效客户档案。
|
||||
* 编码不存在或匹配多条时抛异常,避免资金变动落到错误客户。
|
||||
*/
|
||||
private Long resolveCustomerId(CreditUsageCommand command)
|
||||
@@ -583,24 +584,108 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
|
||||
{
|
||||
return command.getCustomerId();
|
||||
}
|
||||
String customerCode = command.getCustomerCode().trim();
|
||||
BmsCustomer query = new BmsCustomer();
|
||||
query.setCustomerCode(customerCode);
|
||||
List<BmsCustomer> customers = bmsCustomerMapper.selectBmsCustomerList(query);
|
||||
if (customers == null || customers.isEmpty())
|
||||
{
|
||||
throw new ServiceException("客户编码不存在:" + customerCode);
|
||||
}
|
||||
if (customers.size() > 1)
|
||||
{
|
||||
throw new ServiceException("客户编码匹配到多个客户:" + customerCode);
|
||||
}
|
||||
Long customerId = customers.get(0).getCustomerId();
|
||||
Long customerId = resolveCustomerIdByCode(command.getCustomerCode());
|
||||
// 回填,便于调用方/流水排查时直接取到解析结果
|
||||
command.setCustomerId(customerId);
|
||||
return customerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按客户编码精确查询有效客户(selectBmsCustomerList 自带 del_flag='0' 过滤)。
|
||||
* 编码不存在或匹配多条时抛异常,避免资金/状态判定落到错误客户。
|
||||
*/
|
||||
Long resolveCustomerIdByCode(String customerCode)
|
||||
{
|
||||
if (!StringUtils.hasText(customerCode))
|
||||
{
|
||||
throw new ServiceException("客户编码不能为空");
|
||||
}
|
||||
String code = customerCode.trim();
|
||||
BmsCustomer query = new BmsCustomer();
|
||||
query.setCustomerCode(code);
|
||||
List<BmsCustomer> customers = bmsCustomerMapper.selectBmsCustomerList(query);
|
||||
if (customers == null || customers.isEmpty())
|
||||
{
|
||||
throw new ServiceException("客户编码不存在:" + code);
|
||||
}
|
||||
if (customers.size() > 1)
|
||||
{
|
||||
throw new ServiceException("客户编码匹配到多个客户:" + code);
|
||||
}
|
||||
return customers.get(0).getCustomerId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomerAccountStatusVO getAccountStatus(Long customerId, String customerCode)
|
||||
{
|
||||
if (customerId == null && !StringUtils.hasText(customerCode))
|
||||
{
|
||||
throw new ServiceException("客户ID与客户编码至少传一个");
|
||||
}
|
||||
Long cid = customerId != null ? customerId : resolveCustomerIdByCode(customerCode);
|
||||
|
||||
// 1. 读客户档案(不加锁)
|
||||
BmsCustomer cust = bmsCustomerMapper.selectBmsCustomerByCustomerId(cid);
|
||||
if (cust == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 读CNY账户(普通读不加锁;新客户尚未初始化账户时按0额度处理)
|
||||
CustomerAccount account = customerMoneyMapper.selectAccountByCustomerCurrency(cid, BASE_CURRENCY);
|
||||
|
||||
// 3. 组装状态VO
|
||||
CustomerAccountStatusVO vo = new CustomerAccountStatusVO();
|
||||
vo.setCustomerId(cid);
|
||||
vo.setCustomerCode(cust.getCustomerCode());
|
||||
vo.setCustomerName(cust.getCustomerName());
|
||||
vo.setIsEnable(cust.getIsEnable());
|
||||
vo.setIsEnableLabel("0".equals(cust.getIsEnable()) ? "正常" : "停用");
|
||||
|
||||
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 expiredPending = isTempCreditExpired(account);
|
||||
BigDecimal effectiveExtra = expiredPending ? BigDecimal.ZERO : rawExtra;
|
||||
|
||||
vo.setBalance(account != null ? nz(account.getBalance()) : BigDecimal.ZERO);
|
||||
vo.setFrozenBalance(account != null ? nz(account.getFrozenBalance()) : BigDecimal.ZERO);
|
||||
vo.setTotalCredit(totalCredit);
|
||||
vo.setExtraCredit(rawExtra);
|
||||
vo.setEffectiveExtraCredit(effectiveExtra);
|
||||
vo.setUsedCredit(usedCredit);
|
||||
BigDecimal available = totalCredit.add(effectiveExtra).subtract(usedCredit);
|
||||
vo.setAvailableCredit(available);
|
||||
boolean overdue = available.compareTo(BigDecimal.ZERO) < 0;
|
||||
vo.setOverdue(overdue);
|
||||
vo.setExpiredTempCreditPending(expiredPending);
|
||||
|
||||
boolean disabled = "1".equals(cust.getIsEnable());
|
||||
if (disabled && overdue)
|
||||
{
|
||||
vo.setAccountStatus(CustomerAccountStatusVO.STATUS_DISABLED_AND_OVERDUE);
|
||||
vo.setAccountStatusDesc("客户已停用且可用额度不足(欠费),不可新下单");
|
||||
}
|
||||
else if (disabled)
|
||||
{
|
||||
vo.setAccountStatus(CustomerAccountStatusVO.STATUS_DISABLED);
|
||||
vo.setAccountStatusDesc("客户已停用,不可新下单");
|
||||
}
|
||||
else if (overdue)
|
||||
{
|
||||
vo.setAccountStatus(CustomerAccountStatusVO.STATUS_OVERDUE);
|
||||
vo.setAccountStatusDesc("客户可用额度不足(欠费),需先回款核销恢复额度");
|
||||
}
|
||||
else
|
||||
{
|
||||
vo.setAccountStatus(CustomerAccountStatusVO.STATUS_NORMAL);
|
||||
vo.setAccountStatusDesc("客户状态正常,可正常下单");
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
private void validateBalanceCommand(BalanceChangeCommand command)
|
||||
{
|
||||
if (command == null || command.getCustomerId() == null)
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package com.ruoyi.customer.customermoney.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 客户账号状态查询VO(收货扣件准入控制用)。
|
||||
*
|
||||
* <p>只读轻量,不锁账户也不物化临时额度失效:临时额度已过期时按"视为0"计算可用额度,
|
||||
* 但 DB 保持原状——由定时任务或后续真实资金入口落库。</p>
|
||||
*
|
||||
* <p>欠费定义:availableCredit < 0(已用额度超过授信总额+有效临时额度)。</p>
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-17
|
||||
*/
|
||||
public class CustomerAccountStatusVO implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 综合状态:正常 */
|
||||
public static final String STATUS_NORMAL = "NORMAL";
|
||||
/** 综合状态:停用 */
|
||||
public static final String STATUS_DISABLED = "DISABLED";
|
||||
/** 综合状态:欠费(可用额度不足) */
|
||||
public static final String STATUS_OVERDUE = "OVERDUE";
|
||||
/** 综合状态:停用且欠费 */
|
||||
public static final String STATUS_DISABLED_AND_OVERDUE = "DISABLED_AND_OVERDUE";
|
||||
|
||||
/** 客户ID */
|
||||
private Long customerId;
|
||||
/** 客户编码 */
|
||||
private String customerCode;
|
||||
/** 客户名称 */
|
||||
private String customerName;
|
||||
/** 启用状态(0正常 1停用)—— 来自 bms_customer.is_enable */
|
||||
private String isEnable;
|
||||
/** 启用状态文案(正常 / 停用) */
|
||||
private String isEnableLabel;
|
||||
|
||||
/** CNY账户余额 */
|
||||
private BigDecimal balance;
|
||||
/** CNY账户冻结余额 */
|
||||
private BigDecimal frozenBalance;
|
||||
/** 授信总额度(CNY) */
|
||||
private BigDecimal totalCredit;
|
||||
/** 临时额度原值(CNY,DB 中 extra_credit 字段) */
|
||||
private BigDecimal extraCredit;
|
||||
/** 临时额度实际有效值(过期时为0,接口计算得出,不物化DB) */
|
||||
private BigDecimal effectiveExtraCredit;
|
||||
/** 已用额度(CNY) */
|
||||
private BigDecimal usedCredit;
|
||||
/** 可用额度(总额度 + 有效临时额度 - 已用额度;可能为负) */
|
||||
private BigDecimal availableCredit;
|
||||
|
||||
/** 是否欠费(可用额度 < 0) */
|
||||
private boolean overdue;
|
||||
/** 是否存在过期未物化的临时额度(仅作提示,不影响判定) */
|
||||
private boolean expiredTempCreditPending;
|
||||
/** 综合状态:NORMAL / DISABLED / OVERDUE / DISABLED_AND_OVERDUE */
|
||||
private String accountStatus;
|
||||
/** 综合状态说明 */
|
||||
private String accountStatusDesc;
|
||||
|
||||
public Long getCustomerId() { return customerId; }
|
||||
public void setCustomerId(Long customerId) { this.customerId = customerId; }
|
||||
public String getCustomerCode() { return customerCode; }
|
||||
public void setCustomerCode(String customerCode) { this.customerCode = customerCode; }
|
||||
public String getCustomerName() { return customerName; }
|
||||
public void setCustomerName(String customerName) { this.customerName = customerName; }
|
||||
public String getIsEnable() { return isEnable; }
|
||||
public void setIsEnable(String isEnable) { this.isEnable = isEnable; }
|
||||
public String getIsEnableLabel() { return isEnableLabel; }
|
||||
public void setIsEnableLabel(String isEnableLabel) { this.isEnableLabel = isEnableLabel; }
|
||||
public BigDecimal getBalance() { return balance; }
|
||||
public void setBalance(BigDecimal balance) { this.balance = balance; }
|
||||
public BigDecimal getFrozenBalance() { return frozenBalance; }
|
||||
public void setFrozenBalance(BigDecimal frozenBalance) { this.frozenBalance = frozenBalance; }
|
||||
public BigDecimal getTotalCredit() { return totalCredit; }
|
||||
public void setTotalCredit(BigDecimal totalCredit) { this.totalCredit = totalCredit; }
|
||||
public BigDecimal getExtraCredit() { return extraCredit; }
|
||||
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; }
|
||||
public void setOverdue(boolean overdue) { this.overdue = overdue; }
|
||||
public boolean isExpiredTempCreditPending() { return expiredTempCreditPending; }
|
||||
public void setExpiredTempCreditPending(boolean expiredTempCreditPending) { this.expiredTempCreditPending = expiredTempCreditPending; }
|
||||
public String getAccountStatus() { return accountStatus; }
|
||||
public void setAccountStatus(String accountStatus) { this.accountStatus = accountStatus; }
|
||||
public String getAccountStatusDesc() { return accountStatusDesc; }
|
||||
public void setAccountStatusDesc(String accountStatusDesc) { this.accountStatusDesc = accountStatusDesc; }
|
||||
}
|
||||
Reference in New Issue
Block a user