支持customerid和customerCode

This commit is contained in:
luojiaquan
2026-09-17 10:55:32 +08:00
parent 85a3752107
commit 72280c86a0
5 changed files with 95 additions and 27 deletions
@@ -11,6 +11,7 @@ import lombok.Data;
* <li>计费订单开单/应收账单确认 -> {@link ICustomerAccountApi#occupyCredit}(已用额度增加)</li>
* <li>账单作废/计费变动核减 -> {@link ICustomerAccountApi#releaseCredit}(已用额度减少)</li>
* </ul>
* 客户标识 customerId / customerCode 二选一,传一个即可。
*
* @author ruoyi
* @date 2026-09-17
@@ -18,9 +19,19 @@ import lombok.Data;
@Data
public class CreditUsageCommand
{
/** 客户ID(必填) */
/**
* 客户ID,与 {@link #customerCode} 二选一(至少传一个)。
* 两者同时传值时以 customerId 为准。
*/
private Long customerId;
/**
* 客户编码,与 {@link #customerId} 二选一(至少传一个)。
* 未传 customerId 时由公共服务按编码解析客户档案(del_flag=0),
* 编码不存在或匹配多条时抛 ServiceException。
*/
private String customerCode;
/** 币种(为空按本位币 CNY 处理) */
private String currency;
@@ -49,8 +49,8 @@ public interface ICustomerAccountApi
/**
* 占用授信额度:used_credit += amount,写 OCCUPY 额度流水(关联来源单据)。
* checkAvailable=true 时可用额度不足抛 ServiceException;CNY账户不存在自动初始化,
* 外币账户不存在时静默跳过(返回null,兼容历史口径)。
* 命令中 customerId 与 customerCode 二选一;checkAvailable=true 时可用额度不足抛 ServiceException;
* CNY账户不存在自动初始化,外币账户不存在时静默跳过(返回null,兼容历史口径)。
*
* @return 已落库的额度流水(含ID);账户不存在跳过场景返回null
*/
@@ -58,7 +58,7 @@ public interface ICustomerAccountApi
/**
* 释放授信额度:used_credit -= amount(不小于0),写 RELEASE 额度流水。
* 账户不存在时静默跳过(返回null)。
* 命令中 customerId 与 customerCode 二选一;账户不存在时静默跳过(返回null)。
*
* @return 已落库的额度流水(含ID);账户不存在跳过场景返回null
*/
@@ -6,6 +6,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -156,10 +157,11 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
public CustomerCreditFlow occupyCredit(CreditUsageCommand command)
{
validateUsageCommand(command);
Long customerId = resolveCustomerId(command);
String currency = resolveCurrency(command.getCurrency());
String username = SecurityUtils.getUsername();
Long userId = SecurityUtils.getUserId();
CustomerAccount account = lockAccountForUsage(command.getCustomerId(), currency, username);
CustomerAccount account = lockAccountForUsage(customerId, currency, username);
if (account == null)
{
// 外币账户尚未开立时不自动占用,与历史账单口径保持一致(CNY账户会自动初始化)
@@ -190,10 +192,11 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
public CustomerCreditFlow releaseCredit(CreditUsageCommand command)
{
validateUsageCommand(command);
Long customerId = resolveCustomerId(command);
String currency = resolveCurrency(command.getCurrency());
String username = SecurityUtils.getUsername();
Long userId = SecurityUtils.getUserId();
CustomerAccount account = lockAccountForUsage(command.getCustomerId(), currency, username);
CustomerAccount account = lockAccountForUsage(customerId, currency, username);
if (account == null)
{
return null;
@@ -469,9 +472,9 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
private void validateUsageCommand(CreditUsageCommand command)
{
if (command == null || command.getCustomerId() == null)
if (command == null || (command.getCustomerId() == null && !StringUtils.hasText(command.getCustomerCode())))
{
throw new ServiceException("客户ID不能为空");
throw new ServiceException("客户ID与客户编码至少传一个");
}
if (!StringUtils.hasText(command.getBizType()))
{
@@ -483,6 +486,34 @@ public class CustomerAccountApiImpl implements ICustomerAccountApi
}
}
/**
* 解析客户标识:customerId 优先;未传时按 customerCode 精确查询有效客户档案。
* 编码不存在或匹配多条时抛异常,避免资金变动落到错误客户。
*/
private Long resolveCustomerId(CreditUsageCommand command)
{
if (command.getCustomerId() != null)
{
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();
// 回填,便于调用方/流水排查时直接取到解析结果
command.setCustomerId(customerId);
return customerId;
}
private void validateBalanceCommand(BalanceChangeCommand command)
{
if (command == null || command.getCustomerId() == null)
@@ -49,6 +49,19 @@
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="业务类型" prop="customerType">
<el-select v-model="form.customerType" placeholder="请选择业务类型" clearable style="width: 100%;">
<el-option
v-for="dict in customer_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="启用状态" prop="isEnable">
<el-select v-model="form.isEnable" placeholder="请选择启用状态" clearable style="width: 100%;">
@@ -66,11 +79,11 @@
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="资金额度" prop="ed">
<el-input v-model="form.ed" placeholder="请输入资金额度" />
</el-form-item>
</el-col>
<!-- <el-col :span="12">-->
<!-- <el-form-item label="资金额度" prop="ed">-->
<!-- <el-input v-model="form.ed" placeholder="请输入资金额度" />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<el-col :span="12">
<el-form-item label="结算方式" prop="settlementType">
<el-select v-model="form.settlementType" placeholder="请选择结算方式" clearable style="width: 100%;">
@@ -106,9 +119,17 @@
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="临时额度" prop="tempEd">
<el-input v-model="form.tempEd" :disabled="!isAdd" placeholder="请输入临时额度">
<el-form-item label="账户余额">
<el-input :model-value="formatBalance(form.accountBalance)" disabled placeholder="0.00">
<template #append>CNY</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="信用额度" prop="creditEd">
<el-input v-model="form.creditEd" :disabled="!isAdd" placeholder="请输入信用额度">
<template #append v-if="!isAdd">
<span style="color: #e6a23c">请在客户余额-额度管理中调整</span>
</template>
@@ -116,9 +137,11 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="账户余额">
<el-input :model-value="formatBalance(form.accountBalance)" disabled placeholder="0.00">
<template #append>CNY</template>
<el-form-item label="临时额度" prop="tempEd">
<el-input v-model="form.tempEd" :disabled="!isAdd" placeholder="请输入临时额度">
<template #append v-if="!isAdd">
<span style="color: #e6a23c">请在客户余额-额度管理中调整</span>
</template>
</el-input>
</el-form-item>
</el-col>
@@ -132,15 +155,7 @@
<el-input v-model="form.throwRatio" placeholder="请输入抛比" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="信用额度" prop="creditEd">
<el-input v-model="form.creditEd" :disabled="!isAdd" placeholder="请输入信用额度">
<template #append v-if="!isAdd">
<span style="color: #e6a23c">请在客户余额-额度管理中调整</span>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注说明" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入内容" />
@@ -717,6 +732,8 @@ const route = useRoute()
const router = useRouter()
const { settlement_type } = useDict('settlement_type')
const { customer_type } = useDict('customer_type')
const { ensureProducts, findProduct } = useProducts()
const pageLoading = ref(false)
@@ -1884,6 +1884,13 @@ watch(() => route.query.customerId, (newId, oldId) => {
margin-bottom: 12px;
text-align: center;
}
.balance-card {
min-height: 120px; /* 根据你的实际 UI 调整数值 */
display: flex;
flex-direction: column;
justify-content: center; /* 内容垂直居中 */
}
.balance-label {
color: var(--el-text-color-secondary);
font-size: 13px;
@@ -1976,4 +1983,6 @@ watch(() => route.query.customerId, (newId, oldId) => {
margin-left: 8px;
color: var(--el-text-color-secondary);
}
</style>