|
|
|
|
@ -1,6 +1,5 @@
|
|
|
|
|
package com.kexue.skills.service.impl;
|
|
|
|
|
|
|
|
|
|
import com.kexue.skills.common.Assert;
|
|
|
|
|
import com.kexue.skills.entity.Account;
|
|
|
|
|
import com.kexue.skills.entity.AccountFrozen;
|
|
|
|
|
import com.kexue.skills.entity.SysUser;
|
|
|
|
|
@ -19,6 +18,8 @@ import com.kexue.skills.mapper.AccountTransactionMapper;
|
|
|
|
|
import com.kexue.skills.common.util.IDUtils;
|
|
|
|
|
import com.kexue.skills.common.ResultCode;
|
|
|
|
|
import com.kexue.skills.config.AccountDeductionProperties;
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
|
|
@ -38,6 +39,14 @@ import java.util.Objects;
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
public class AccountFrozenServiceImpl implements AccountFrozenService {
|
|
|
|
|
|
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(AccountFrozenServiceImpl.class);
|
|
|
|
|
|
|
|
|
|
private static final Integer FROZEN_TYPE_TOKEN = 1;
|
|
|
|
|
|
|
|
|
|
private static final Integer FROZEN_TYPE_RMB = 2;
|
|
|
|
|
|
|
|
|
|
private static final BigDecimal YUAN_TO_POINTS_COEFFICIENT = BigDecimal.valueOf(100);
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
private AccountFrozenMapper accountFrozenMapper;
|
|
|
|
|
|
|
|
|
|
@ -58,12 +67,45 @@ public class AccountFrozenServiceImpl implements AccountFrozenService {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建冻结单
|
|
|
|
|
*
|
|
|
|
|
* @param accountFrozenDto 冻结单DTO
|
|
|
|
|
* @return 冻结单信息
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public AccountFrozen createFrozen(AccountFrozenDto accountFrozenDto) {
|
|
|
|
|
// 1. 验证参数
|
|
|
|
|
// 1. 验证创建冻结单参数
|
|
|
|
|
validateCreateFrozenParams(accountFrozenDto);
|
|
|
|
|
|
|
|
|
|
// 2. 通过sessionId获取用户信息
|
|
|
|
|
SysUser sysUser = getUserBySessionId(accountFrozenDto.getSessionId());
|
|
|
|
|
|
|
|
|
|
// 3. 通过用户ID获取账户信息
|
|
|
|
|
Account account = getAccountByUserId(sysUser.getUserId());
|
|
|
|
|
|
|
|
|
|
// 4. 根据冻结类型计算冻结金额
|
|
|
|
|
// - 如果是token类型,根据预估tokens和模型价格计算
|
|
|
|
|
// - 如果是RMB类型,将元转换为积分(1元=100积分)
|
|
|
|
|
BigDecimal finalFrozenAmount = calculateFrozenAmount(accountFrozenDto);
|
|
|
|
|
|
|
|
|
|
// 5. 检查账户余额是否足够冻结
|
|
|
|
|
// 计算可用余额(总余额 - 已冻结金额),确保足够本次冻结
|
|
|
|
|
checkBalanceSufficient(account, finalFrozenAmount);
|
|
|
|
|
|
|
|
|
|
// 6. 更新账户冻结金额
|
|
|
|
|
// 将本次冻结金额加到账户的已冻结金额中
|
|
|
|
|
updateAccountFrozenAmount(account, finalFrozenAmount);
|
|
|
|
|
|
|
|
|
|
// 7. 执行创建冻结单
|
|
|
|
|
// 构建冻结单对象并保存到数据库
|
|
|
|
|
return doCreateFrozen(accountFrozenDto, sysUser.getUserId(), finalFrozenAmount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 验证创建冻结单参数
|
|
|
|
|
*
|
|
|
|
|
* @param accountFrozenDto 冻结单DTO
|
|
|
|
|
*/
|
|
|
|
|
private void validateCreateFrozenParams(AccountFrozenDto accountFrozenDto) {
|
|
|
|
|
if (accountFrozenDto == null) {
|
|
|
|
|
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), ResultCode.PARAMETER_EMPTY.getMessage());
|
|
|
|
|
}
|
|
|
|
|
@ -73,77 +115,252 @@ public class AccountFrozenServiceImpl implements AccountFrozenService {
|
|
|
|
|
if (accountFrozenDto.getFrozenType() == null) {
|
|
|
|
|
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), "冻结类型不能为空");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据冻结类型进行不同的参数校验
|
|
|
|
|
if (FROZEN_TYPE_TOKEN.equals(accountFrozenDto.getFrozenType())) {
|
|
|
|
|
// token类型需要校验:estimatedInputTokens、estimatedOutputTokens、modelName
|
|
|
|
|
if (accountFrozenDto.getEstimatedInputTokens() == null) {
|
|
|
|
|
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), "预估输入tokens不能为空");
|
|
|
|
|
}
|
|
|
|
|
if (accountFrozenDto.getEstimatedOutputTokens() == null) {
|
|
|
|
|
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), "预估输出tokens不能为空");
|
|
|
|
|
}
|
|
|
|
|
if (accountFrozenDto.getModelName() == null) {
|
|
|
|
|
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), "模型名称不能为空");
|
|
|
|
|
}
|
|
|
|
|
} else if (FROZEN_TYPE_RMB.equals(accountFrozenDto.getFrozenType())) {
|
|
|
|
|
// RMB类型需要校验:frozenAmount
|
|
|
|
|
if (accountFrozenDto.getFrozenAmount() == null) {
|
|
|
|
|
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), "冻结金额不能为空");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 其他类型的校验可以在此添加
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 通过sessionId获取用户ID
|
|
|
|
|
SysUser sysUser = sysUserMapper.getBySessionId(accountFrozenDto.getSessionId());
|
|
|
|
|
/**
|
|
|
|
|
* 通过sessionId获取用户信息
|
|
|
|
|
*
|
|
|
|
|
* @param sessionId 会话ID
|
|
|
|
|
* @return 用户信息
|
|
|
|
|
*/
|
|
|
|
|
private SysUser getUserBySessionId(String sessionId) {
|
|
|
|
|
SysUser sysUser = sysUserMapper.getBySessionId(sessionId);
|
|
|
|
|
if (sysUser == null) {
|
|
|
|
|
throw new BizException(ResultCode.SESSION_ID_NOT_EXIST.getCode(), ResultCode.SESSION_ID_NOT_EXIST.getMessage());
|
|
|
|
|
}
|
|
|
|
|
Long userId = sysUser.getUserId();
|
|
|
|
|
return sysUser;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 查询用户账户信息
|
|
|
|
|
/**
|
|
|
|
|
* 通过用户ID获取账户信息
|
|
|
|
|
*
|
|
|
|
|
* @param userId 用户ID
|
|
|
|
|
* @return 账户信息
|
|
|
|
|
*/
|
|
|
|
|
private Account getAccountByUserId(Long userId) {
|
|
|
|
|
Account account = accountMapper.queryByUserId(userId);
|
|
|
|
|
if (account == null) {
|
|
|
|
|
throw new BizException(ResultCode.ACCOUNT_NOT_EXIST.getCode(), ResultCode.ACCOUNT_NOT_EXIST.getMessage());
|
|
|
|
|
}
|
|
|
|
|
return account;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 当冻结类型为余额时,根据预估tokens计算冻结金额
|
|
|
|
|
BigDecimal finalFrozenAmount = accountFrozenDto.getFrozenAmount();
|
|
|
|
|
if (accountFrozenDto.getFrozenType() != null && accountFrozenDto.getFrozenType() == 1) {
|
|
|
|
|
if (accountFrozenDto.getEstimatedInputTokens() != null &&
|
|
|
|
|
accountFrozenDto.getEstimatedOutputTokens() != null &&
|
|
|
|
|
accountFrozenDto.getModelName() != null) {
|
|
|
|
|
|
|
|
|
|
if (accountFrozenDto.getModelName().equals("Qwen 3.5 Plus")) {
|
|
|
|
|
accountFrozenDto.setModelName("qwen3.5-plus");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查询模型价格信息
|
|
|
|
|
ModelPrice modelPrice = modelPriceService.queryByModelName(accountFrozenDto.getModelName());
|
|
|
|
|
if (modelPrice != null) {
|
|
|
|
|
// 计算token费用
|
|
|
|
|
long inputFee = accountFrozenDto.getEstimatedInputTokens() / modelPrice.getInputPerCent();
|
|
|
|
|
if (accountFrozenDto.getEstimatedInputTokens() % modelPrice.getInputPerCent() > 0) {
|
|
|
|
|
inputFee += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long outputFee = accountFrozenDto.getEstimatedOutputTokens() / modelPrice.getOutputPerCent();
|
|
|
|
|
if (accountFrozenDto.getEstimatedOutputTokens() % modelPrice.getOutputPerCent() > 0) {
|
|
|
|
|
outputFee += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 总费用(分)
|
|
|
|
|
// 注意:因为1分=1积分,所以totalFee直接就是积分数量
|
|
|
|
|
long totalFee = inputFee + outputFee;
|
|
|
|
|
// 转换为积分(1分=1积分,无需转换)
|
|
|
|
|
BigDecimal baseAmount = BigDecimal.valueOf(totalFee);
|
|
|
|
|
// 应用扣费系数
|
|
|
|
|
finalFrozenAmount = baseAmount.multiply(accountDeductionProperties.getCoefficient());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 根据冻结类型计算冻结金额
|
|
|
|
|
*
|
|
|
|
|
* @param accountFrozenDto 冻结单DTO
|
|
|
|
|
* @return 计算后的冻结金额(单位:积分)
|
|
|
|
|
*/
|
|
|
|
|
private BigDecimal calculateFrozenAmount(AccountFrozenDto accountFrozenDto) {
|
|
|
|
|
BigDecimal frozenAmount = accountFrozenDto.getFrozenAmount();
|
|
|
|
|
if (FROZEN_TYPE_TOKEN.equals(accountFrozenDto.getFrozenType())) {
|
|
|
|
|
return calculateTokenFrozenAmount(accountFrozenDto);
|
|
|
|
|
} else if (FROZEN_TYPE_RMB.equals(accountFrozenDto.getFrozenType())) {
|
|
|
|
|
return calculateRmbFrozenAmount(frozenAmount);
|
|
|
|
|
}
|
|
|
|
|
return frozenAmount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5. 检查余额是否足够(账户总余额 - 已冻结金额 >= 本次冻结金额)
|
|
|
|
|
/**
|
|
|
|
|
* 计算token类型的冻结金额
|
|
|
|
|
* 根据预估输入输出tokens和模型价格计算冻结金额,并应用扣费系数
|
|
|
|
|
*
|
|
|
|
|
* @param accountFrozenDto 冻结单DTO
|
|
|
|
|
* @return 冻结金额(单位:积分)
|
|
|
|
|
*/
|
|
|
|
|
private BigDecimal calculateTokenFrozenAmount(AccountFrozenDto accountFrozenDto) {
|
|
|
|
|
if (accountFrozenDto.getEstimatedInputTokens() == null ||
|
|
|
|
|
accountFrozenDto.getEstimatedOutputTokens() == null ||
|
|
|
|
|
accountFrozenDto.getModelName() == null) {
|
|
|
|
|
return accountFrozenDto.getFrozenAmount();
|
|
|
|
|
}
|
|
|
|
|
String modelName = normalizeModelName(accountFrozenDto.getModelName());
|
|
|
|
|
List<ModelPrice> modelPriceList = getModelPriceList(modelName);
|
|
|
|
|
if (modelPriceList.isEmpty()) {
|
|
|
|
|
return accountFrozenDto.getFrozenAmount();
|
|
|
|
|
}
|
|
|
|
|
ModelPrice inputModelPrice = findInputModelPrice(modelPriceList, accountFrozenDto.getEstimatedInputTokens());
|
|
|
|
|
ModelPrice outputModelPrice = findOutputModelPrice(modelPriceList, accountFrozenDto.getEstimatedOutputTokens());
|
|
|
|
|
if (inputModelPrice == null || outputModelPrice == null) {
|
|
|
|
|
return accountFrozenDto.getFrozenAmount();
|
|
|
|
|
}
|
|
|
|
|
long totalFee = calculateTotalTokenFee(accountFrozenDto, inputModelPrice, outputModelPrice);
|
|
|
|
|
BigDecimal baseAmount = BigDecimal.valueOf(totalFee);
|
|
|
|
|
return baseAmount.multiply(accountDeductionProperties.getCoefficient());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 标准化模型名称
|
|
|
|
|
* 将前端传入的模型名称转换为数据库中对应的名称
|
|
|
|
|
*
|
|
|
|
|
* @param modelName 前端传入的模型名称
|
|
|
|
|
* @return 标准化后的模型名称
|
|
|
|
|
*/
|
|
|
|
|
private String normalizeModelName(String modelName) {
|
|
|
|
|
if ("Qwen 3.5 Plus".equals(modelName)) {
|
|
|
|
|
return "qwen3.5-plus";
|
|
|
|
|
}
|
|
|
|
|
return modelName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取模型价格列表
|
|
|
|
|
*
|
|
|
|
|
* @param modelName 模型名称
|
|
|
|
|
* @return 模型价格列表
|
|
|
|
|
*/
|
|
|
|
|
private List<ModelPrice> getModelPriceList(String modelName) {
|
|
|
|
|
ModelPriceDto modelPriceDto = new ModelPriceDto();
|
|
|
|
|
modelPriceDto.setModelName(modelName);
|
|
|
|
|
return modelPriceService.getList(modelPriceDto);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据输入tokens查找匹配的输入模型价格
|
|
|
|
|
* 使用standard模式过滤价格规则
|
|
|
|
|
*
|
|
|
|
|
* @param modelPriceList 模型价格列表
|
|
|
|
|
* @param estimatedInputTokens 预估输入tokens
|
|
|
|
|
* @return 匹配的输入模型价格,若无匹配返回null
|
|
|
|
|
*/
|
|
|
|
|
private ModelPrice findInputModelPrice(List<ModelPrice> modelPriceList, Long estimatedInputTokens) {
|
|
|
|
|
return modelPriceList.stream()
|
|
|
|
|
.filter(mp -> "standard".equals(mp.getOutputMode()))
|
|
|
|
|
.filter(mp -> mp.getMinTokens() < estimatedInputTokens)
|
|
|
|
|
.filter(mp -> mp.getMaxTokens() == -1 || mp.getMaxTokens() >= estimatedInputTokens)
|
|
|
|
|
.max((mp1, mp2) -> mp1.getMinTokens().compareTo(mp2.getMinTokens()))
|
|
|
|
|
.orElse(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据输出tokens查找匹配的输出模型价格
|
|
|
|
|
* 使用thinking模式过滤价格规则
|
|
|
|
|
*
|
|
|
|
|
* @param modelPriceList 模型价格列表
|
|
|
|
|
* @param estimatedOutputTokens 预估输出tokens
|
|
|
|
|
* @return 匹配的输出模型价格,若无匹配返回null
|
|
|
|
|
*/
|
|
|
|
|
private ModelPrice findOutputModelPrice(List<ModelPrice> modelPriceList, Long estimatedOutputTokens) {
|
|
|
|
|
return modelPriceList.stream()
|
|
|
|
|
.filter(mp -> "thinking".equals(mp.getOutputMode()))
|
|
|
|
|
.filter(mp -> mp.getMinTokens() < estimatedOutputTokens)
|
|
|
|
|
.filter(mp -> mp.getMaxTokens() == -1 || mp.getMaxTokens() >= estimatedOutputTokens)
|
|
|
|
|
.max((mp1, mp2) -> mp1.getMinTokens().compareTo(mp2.getMinTokens()))
|
|
|
|
|
.orElse(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 计算总的token费用
|
|
|
|
|
*
|
|
|
|
|
* @param accountFrozenDto 冻结单DTO
|
|
|
|
|
* @param inputModelPrice 输入模型价格
|
|
|
|
|
* @param outputModelPrice 输出模型价格
|
|
|
|
|
* @return 总费用(单位:分)
|
|
|
|
|
*/
|
|
|
|
|
private long calculateTotalTokenFee(AccountFrozenDto accountFrozenDto, ModelPrice inputModelPrice, ModelPrice outputModelPrice) {
|
|
|
|
|
long inputFee = calculateTokenFee(accountFrozenDto.getEstimatedInputTokens(), inputModelPrice.getInputPerCent());
|
|
|
|
|
long outputFee = calculateTokenFee(accountFrozenDto.getEstimatedOutputTokens(), outputModelPrice.getOutputPerCent());
|
|
|
|
|
return inputFee + outputFee;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 计算单个token费用
|
|
|
|
|
* 采用向上取整的方式计算
|
|
|
|
|
*
|
|
|
|
|
* @param tokens token数量
|
|
|
|
|
* @param perCent 每token对应的分数
|
|
|
|
|
* @return 费用(单位:分)
|
|
|
|
|
*/
|
|
|
|
|
private long calculateTokenFee(long tokens, long perCent) {
|
|
|
|
|
long fee = tokens / perCent;
|
|
|
|
|
if (tokens % perCent > 0) {
|
|
|
|
|
fee += 1;
|
|
|
|
|
}
|
|
|
|
|
return fee;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 计算RMB类型的冻结金额
|
|
|
|
|
* 将元转换为积分(1元=100积分)
|
|
|
|
|
*
|
|
|
|
|
* @param frozenAmount 冻结金额(单位:元)
|
|
|
|
|
* @return 冻结金额(单位:积分)
|
|
|
|
|
*/
|
|
|
|
|
private BigDecimal calculateRmbFrozenAmount(BigDecimal frozenAmount) {
|
|
|
|
|
if (frozenAmount == null) {
|
|
|
|
|
return BigDecimal.ZERO;
|
|
|
|
|
}
|
|
|
|
|
return frozenAmount.multiply(YUAN_TO_POINTS_COEFFICIENT).multiply(accountDeductionProperties.getCoefficient());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 检查账户余额是否足够冻结
|
|
|
|
|
*
|
|
|
|
|
* @param account 账户信息
|
|
|
|
|
* @param frozenAmount 本次冻结金额
|
|
|
|
|
*/
|
|
|
|
|
private void checkBalanceSufficient(Account account, BigDecimal frozenAmount) {
|
|
|
|
|
BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
|
|
|
|
BigDecimal frozenAmount = account.getFrozenAmount() == null ? BigDecimal.ZERO : account.getFrozenAmount();
|
|
|
|
|
BigDecimal availableBalance = balance.subtract(frozenAmount);
|
|
|
|
|
if (availableBalance.compareTo(finalFrozenAmount) < 0) {
|
|
|
|
|
BigDecimal frozen = account.getFrozenAmount() == null ? BigDecimal.ZERO : account.getFrozenAmount();
|
|
|
|
|
BigDecimal availableBalance = balance.subtract(frozen);
|
|
|
|
|
LOG.debug("检查余额是否足够 - userId: {}, 总余额: {}, 已冻结: {}, 可用余额: {}, 本次冻结: {}",
|
|
|
|
|
account.getUserId(), balance, frozen, availableBalance, frozenAmount);
|
|
|
|
|
if (availableBalance.compareTo(frozenAmount) < 0) {
|
|
|
|
|
LOG.warn("余额不足 - userId: {}, 可用余额: {}, 本次冻结: {}", account.getUserId(), availableBalance, frozenAmount);
|
|
|
|
|
throw new BizException(ResultCode.INSUFFICIENT_BALANCE.getCode(), ResultCode.INSUFFICIENT_BALANCE.getMessage());
|
|
|
|
|
}
|
|
|
|
|
LOG.debug("余额检查通过 - userId: {}, 可用余额: {}, 本次冻结: {}", account.getUserId(), availableBalance, frozenAmount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5. 更新账户冻结金额
|
|
|
|
|
account.setFrozenAmount(frozenAmount.add(finalFrozenAmount));
|
|
|
|
|
/**
|
|
|
|
|
* 更新账户冻结金额
|
|
|
|
|
*
|
|
|
|
|
* @param account 账户信息
|
|
|
|
|
* @param frozenAmount 本次冻结金额
|
|
|
|
|
*/
|
|
|
|
|
private void updateAccountFrozenAmount(Account account, BigDecimal frozenAmount) {
|
|
|
|
|
BigDecimal currentFrozen = account.getFrozenAmount() == null ? BigDecimal.ZERO : account.getFrozenAmount();
|
|
|
|
|
account.setFrozenAmount(currentFrozen.add(frozenAmount));
|
|
|
|
|
account.setUpdateTime(new Date());
|
|
|
|
|
accountMapper.update(account);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6. 创建冻结单(单位:积分)
|
|
|
|
|
/**
|
|
|
|
|
* 执行创建冻结单
|
|
|
|
|
*
|
|
|
|
|
* @param accountFrozenDto 冻结单DTO
|
|
|
|
|
* @param userId 用户ID
|
|
|
|
|
* @param finalFrozenAmount 最终冻结金额
|
|
|
|
|
* @return 冻结单信息
|
|
|
|
|
*/
|
|
|
|
|
private AccountFrozen doCreateFrozen(AccountFrozenDto accountFrozenDto, Long userId, BigDecimal finalFrozenAmount) {
|
|
|
|
|
AccountFrozen accountFrozen = new AccountFrozen();
|
|
|
|
|
accountFrozen.setFrozenId(IDUtils.getSnowflakeIdStr());
|
|
|
|
|
accountFrozen.setUserId(userId);
|
|
|
|
|
accountFrozen.setSessionId(accountFrozenDto.getSessionId());
|
|
|
|
|
accountFrozen.setCallId(accountFrozenDto.getCallId());
|
|
|
|
|
accountFrozen.setModelName(accountFrozenDto.getModelName());
|
|
|
|
|
if(Objects.nonNull(accountFrozenDto.getQuestion())){
|
|
|
|
|
if (Objects.nonNull(accountFrozenDto.getQuestion())) {
|
|
|
|
|
accountFrozen.setQuestion(accountFrozenDto.getQuestion());
|
|
|
|
|
}
|
|
|
|
|
accountFrozen.setFrozenAmount(finalFrozenAmount);
|
|
|
|
|
@ -152,173 +369,268 @@ public class AccountFrozenServiceImpl implements AccountFrozenService {
|
|
|
|
|
accountFrozen.setExpireAt(accountFrozenDto.getExpireAt());
|
|
|
|
|
accountFrozen.setCreateTime(new Date());
|
|
|
|
|
accountFrozen.setUpdateTime(new Date());
|
|
|
|
|
|
|
|
|
|
accountFrozenMapper.insert(accountFrozen);
|
|
|
|
|
return accountFrozen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 释放冻结单
|
|
|
|
|
*
|
|
|
|
|
* @param accountReleaseDto 冻结单释放DTO
|
|
|
|
|
* @return 冻结单信息
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public AccountFrozen releaseFrozen(AccountReleaseDto accountReleaseDto) {
|
|
|
|
|
// 1. 验证参数
|
|
|
|
|
// 1. 验证释放冻结单参数
|
|
|
|
|
validateReleaseParams(accountReleaseDto);
|
|
|
|
|
|
|
|
|
|
// 2. 通过冻结单ID获取冻结单信息
|
|
|
|
|
AccountFrozen accountFrozen = getFrozenById(accountReleaseDto.getFrozenId());
|
|
|
|
|
|
|
|
|
|
// 3. 验证冻结单状态是否为预留状态
|
|
|
|
|
// 只有状态为RESERVED的冻结单才能被释放
|
|
|
|
|
validateFrozenStatus(accountFrozen);
|
|
|
|
|
|
|
|
|
|
// 4. 通过用户ID获取账户信息
|
|
|
|
|
Account account = getAccountByUserId(accountFrozen.getUserId());
|
|
|
|
|
|
|
|
|
|
// 5. 根据冻结类型计算最终释放金额
|
|
|
|
|
// - 如果是token类型,根据实际使用的tokens和模型价格计算
|
|
|
|
|
// - 如果是RMB类型,将元转换为积分(1元=100积分)
|
|
|
|
|
BigDecimal finalAmount = calculateReleaseAmount(accountReleaseDto, accountFrozen);
|
|
|
|
|
|
|
|
|
|
// 6. 执行释放时的账户更新
|
|
|
|
|
// - 扣减账户的冻结金额
|
|
|
|
|
// - 如果有实际扣减金额,更新账户余额
|
|
|
|
|
doUpdateAccountForRelease(account, accountFrozen, finalAmount);
|
|
|
|
|
|
|
|
|
|
// 7. 创建交易流水记录
|
|
|
|
|
// 当有实际扣减金额时,生成交易流水记录
|
|
|
|
|
doCreateTransaction(accountFrozen, account, finalAmount);
|
|
|
|
|
|
|
|
|
|
// 8. 执行冻结单终结
|
|
|
|
|
// 更新冻结单状态为已终结,并记录最终扣减金额和使用情况
|
|
|
|
|
return doFinalizeFrozen(accountFrozen, accountReleaseDto, finalAmount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 验证释放冻结单参数
|
|
|
|
|
*
|
|
|
|
|
* @param accountReleaseDto 冻结单释放DTO
|
|
|
|
|
*/
|
|
|
|
|
private void validateReleaseParams(AccountReleaseDto accountReleaseDto) {
|
|
|
|
|
if (accountReleaseDto == null) {
|
|
|
|
|
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), ResultCode.PARAMETER_EMPTY.getMessage());
|
|
|
|
|
}
|
|
|
|
|
if (accountReleaseDto.getFrozenId() == null) {
|
|
|
|
|
throw new BizException(ResultCode.FROZEN_ID_EMPTY.getCode(), ResultCode.FROZEN_ID_EMPTY.getMessage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 注:释放冻结单时的frozenType需要从冻结单中获取,所以这里不做类型相关的校验
|
|
|
|
|
// 具体的类型相关校验会在calculateReleaseAmount方法中处理
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 查询冻结单
|
|
|
|
|
AccountFrozen accountFrozen = accountFrozenMapper.selectByPrimaryKey(accountReleaseDto.getFrozenId());
|
|
|
|
|
/**
|
|
|
|
|
* 通过冻结单ID获取冻结单信息
|
|
|
|
|
*
|
|
|
|
|
* @param frozenId 冻结单ID
|
|
|
|
|
* @return 冻结单信息
|
|
|
|
|
*/
|
|
|
|
|
private AccountFrozen getFrozenById(String frozenId) {
|
|
|
|
|
AccountFrozen accountFrozen = accountFrozenMapper.selectByPrimaryKey(frozenId);
|
|
|
|
|
if (accountFrozen == null) {
|
|
|
|
|
throw new BizException(ResultCode.FROZEN_NOT_EXIST.getCode(), ResultCode.FROZEN_NOT_EXIST.getMessage());
|
|
|
|
|
}
|
|
|
|
|
return accountFrozen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 检查冻结单状态
|
|
|
|
|
/**
|
|
|
|
|
* 验证冻结单状态是否为预留状态
|
|
|
|
|
*
|
|
|
|
|
* @param accountFrozen 冻结单信息
|
|
|
|
|
*/
|
|
|
|
|
private void validateFrozenStatus(AccountFrozen accountFrozen) {
|
|
|
|
|
if (!"RESERVED".equals(accountFrozen.getStatus())) {
|
|
|
|
|
throw new BizException(ResultCode.FROZEN_STATUS_ERROR.getCode(), ResultCode.FROZEN_STATUS_ERROR.getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 查询用户账户信息
|
|
|
|
|
Account account = accountMapper.queryByUserId(accountFrozen.getUserId());
|
|
|
|
|
if (account == null) {
|
|
|
|
|
throw new BizException(ResultCode.ACCOUNT_NOT_EXIST.getCode(), ResultCode.ACCOUNT_NOT_EXIST.getMessage());
|
|
|
|
|
/**
|
|
|
|
|
* 根据冻结类型计算最终释放金额
|
|
|
|
|
*
|
|
|
|
|
* @param accountReleaseDto 冻结单释放DTO
|
|
|
|
|
* @param accountFrozen 冻结单信息
|
|
|
|
|
* @return 最终释放金额(单位:积分)
|
|
|
|
|
*/
|
|
|
|
|
private BigDecimal calculateReleaseAmount(AccountReleaseDto accountReleaseDto, AccountFrozen accountFrozen) {
|
|
|
|
|
BigDecimal finalAmount = accountReleaseDto.getFinalAmount() != null ?
|
|
|
|
|
accountReleaseDto.getFinalAmount() : BigDecimal.ZERO;
|
|
|
|
|
if (FROZEN_TYPE_TOKEN.equals(accountFrozen.getFrozenType())) {
|
|
|
|
|
return calculateTokenReleaseAmount(accountReleaseDto, accountFrozen, finalAmount);
|
|
|
|
|
} else if (FROZEN_TYPE_RMB.equals(accountFrozen.getFrozenType())) {
|
|
|
|
|
return calculateRmbFrozenAmount(finalAmount);
|
|
|
|
|
}
|
|
|
|
|
return finalAmount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5. 计算最终扣减金额
|
|
|
|
|
BigDecimal finalAmount = accountReleaseDto.getFinalAmount() != null ? accountReleaseDto.getFinalAmount() : BigDecimal.ZERO;
|
|
|
|
|
|
|
|
|
|
// 6. 当冻结类型为余额时,考虑token消费逻辑
|
|
|
|
|
if (accountFrozen.getFrozenType() != null && accountFrozen.getFrozenType() == 1) {
|
|
|
|
|
// 如果没有提供最终扣减金额,但提供了tokens使用情况,则根据tokens计算费用
|
|
|
|
|
if (finalAmount.compareTo(BigDecimal.ZERO) == 0 &&
|
|
|
|
|
accountReleaseDto.getUsageInputTokens() != null &&
|
|
|
|
|
accountReleaseDto.getUsageOutputTokens() != null &&
|
|
|
|
|
accountFrozen.getModelName() != null) {
|
|
|
|
|
|
|
|
|
|
// 查询模型价格信息(一次性查询所有价格规则,减少数据库IO)
|
|
|
|
|
ModelPriceDto modelPriceDto = new ModelPriceDto();
|
|
|
|
|
modelPriceDto.setModelName(accountFrozen.getModelName());
|
|
|
|
|
List<ModelPrice> modelPriceList = modelPriceService.getList(modelPriceDto);
|
|
|
|
|
|
|
|
|
|
if (!modelPriceList.isEmpty()) {
|
|
|
|
|
// 过滤输入token的价格规则(使用standard模式)
|
|
|
|
|
String inputOutputMode = "standard";
|
|
|
|
|
ModelPrice inputModelPrice = modelPriceList.stream()
|
|
|
|
|
.filter(mp -> inputOutputMode.equals(mp.getOutputMode()))
|
|
|
|
|
.filter(mp -> mp.getMinTokens() < accountReleaseDto.getUsageInputTokens())
|
|
|
|
|
.filter(mp -> mp.getMaxTokens() == -1 || mp.getMaxTokens() >= accountReleaseDto.getUsageInputTokens())
|
|
|
|
|
.max((mp1, mp2) -> mp1.getMinTokens().compareTo(mp2.getMinTokens()))
|
|
|
|
|
.orElse(null);
|
|
|
|
|
|
|
|
|
|
// 过滤输出token的价格规则(使用thinking模式)
|
|
|
|
|
String outputOutputMode = "thinking";
|
|
|
|
|
ModelPrice outputModelPrice = modelPriceList.stream()
|
|
|
|
|
.filter(mp -> outputOutputMode.equals(mp.getOutputMode()))
|
|
|
|
|
.filter(mp -> mp.getMinTokens() < accountReleaseDto.getUsageOutputTokens())
|
|
|
|
|
.filter(mp -> mp.getMaxTokens() == -1 || mp.getMaxTokens() >= accountReleaseDto.getUsageOutputTokens())
|
|
|
|
|
.max((mp1, mp2) -> mp1.getMinTokens().compareTo(mp2.getMinTokens()))
|
|
|
|
|
.orElse(null);
|
|
|
|
|
|
|
|
|
|
if (inputModelPrice != null && outputModelPrice != null) {
|
|
|
|
|
// 计算token费用
|
|
|
|
|
long inputFee = accountReleaseDto.getUsageInputTokens() / inputModelPrice.getInputPerCent();
|
|
|
|
|
if (accountReleaseDto.getUsageInputTokens() % inputModelPrice.getInputPerCent() > 0) {
|
|
|
|
|
inputFee += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long outputFee = accountReleaseDto.getUsageOutputTokens() / outputModelPrice.getOutputPerCent();
|
|
|
|
|
if (accountReleaseDto.getUsageOutputTokens() % outputModelPrice.getOutputPerCent() > 0) {
|
|
|
|
|
outputFee += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 总费用(分)
|
|
|
|
|
// 注意:因为1分=1积分,所以totalFee直接就是积分数量
|
|
|
|
|
long totalFee = inputFee + outputFee;
|
|
|
|
|
// 转换为积分(1分=1积分,无需转换)
|
|
|
|
|
BigDecimal baseAmount = BigDecimal.valueOf(totalFee);
|
|
|
|
|
// 应用扣费系数
|
|
|
|
|
finalAmount = baseAmount.multiply(accountDeductionProperties.getCoefficient());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 计算token类型的释放金额
|
|
|
|
|
* 根据实际使用的tokens和模型价格计算释放金额,并应用扣费系数
|
|
|
|
|
*
|
|
|
|
|
* @param accountReleaseDto 冻结单释放DTO
|
|
|
|
|
* @param accountFrozen 冻结单信息
|
|
|
|
|
* @param finalAmount 当前计算的最终金额
|
|
|
|
|
* @return 计算后的释放金额(单位:积分)
|
|
|
|
|
*/
|
|
|
|
|
private BigDecimal calculateTokenReleaseAmount(AccountReleaseDto accountReleaseDto, AccountFrozen accountFrozen,
|
|
|
|
|
BigDecimal finalAmount) {
|
|
|
|
|
if (finalAmount.compareTo(BigDecimal.ZERO) != 0 ||
|
|
|
|
|
accountReleaseDto.getUsageInputTokens() == null ||
|
|
|
|
|
accountReleaseDto.getUsageOutputTokens() == null ||
|
|
|
|
|
accountFrozen.getModelName() == null) {
|
|
|
|
|
return finalAmount;
|
|
|
|
|
}
|
|
|
|
|
List<ModelPrice> modelPriceList = getModelPriceList(accountFrozen.getModelName());
|
|
|
|
|
if (modelPriceList.isEmpty()) {
|
|
|
|
|
return finalAmount;
|
|
|
|
|
}
|
|
|
|
|
ModelPrice inputModelPrice = findInputModelPrice(modelPriceList, accountReleaseDto.getUsageInputTokens());
|
|
|
|
|
ModelPrice outputModelPrice = findOutputModelPrice(modelPriceList, accountReleaseDto.getUsageOutputTokens());
|
|
|
|
|
if (inputModelPrice == null || outputModelPrice == null) {
|
|
|
|
|
return finalAmount;
|
|
|
|
|
}
|
|
|
|
|
long totalFee = calculateReleaseTokenFee(accountReleaseDto, inputModelPrice, outputModelPrice);
|
|
|
|
|
BigDecimal baseAmount = BigDecimal.valueOf(totalFee);
|
|
|
|
|
return baseAmount.multiply(accountDeductionProperties.getCoefficient());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 7. 更新账户余额和冻结金额(单位:积分)
|
|
|
|
|
/**
|
|
|
|
|
* 计算释放时的token总费用
|
|
|
|
|
*
|
|
|
|
|
* @param accountReleaseDto 冻结单释放DTO
|
|
|
|
|
* @param inputModelPrice 输入模型价格
|
|
|
|
|
* @param outputModelPrice 输出模型价格
|
|
|
|
|
* @return 总费用(单位:分)
|
|
|
|
|
*/
|
|
|
|
|
private long calculateReleaseTokenFee(AccountReleaseDto accountReleaseDto,
|
|
|
|
|
ModelPrice inputModelPrice, ModelPrice outputModelPrice) {
|
|
|
|
|
long inputFee = calculateTokenFee(accountReleaseDto.getUsageInputTokens(), inputModelPrice.getInputPerCent());
|
|
|
|
|
long outputFee = calculateTokenFee(accountReleaseDto.getUsageOutputTokens(), outputModelPrice.getOutputPerCent());
|
|
|
|
|
return inputFee + outputFee;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 执行释放时的账户更新
|
|
|
|
|
* 包括扣减冻结金额和实际余额
|
|
|
|
|
*
|
|
|
|
|
* @param account 账户信息
|
|
|
|
|
* @param accountFrozen 冻结单信息
|
|
|
|
|
* @param finalAmount 最终扣减金额
|
|
|
|
|
*/
|
|
|
|
|
private void doUpdateAccountForRelease(Account account, AccountFrozen accountFrozen, BigDecimal finalAmount) {
|
|
|
|
|
BigDecimal frozenAmount = account.getFrozenAmount() == null ? BigDecimal.ZERO : account.getFrozenAmount();
|
|
|
|
|
BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
|
|
|
|
BigDecimal beforeBalance = balance;
|
|
|
|
|
|
|
|
|
|
// 释放冻结金额
|
|
|
|
|
account.setFrozenAmount(frozenAmount.subtract(accountFrozen.getFrozenAmount()));
|
|
|
|
|
|
|
|
|
|
// 如果需要扣减余额
|
|
|
|
|
if (finalAmount.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
// 检查实际扣减是否大于预扣减
|
|
|
|
|
if (finalAmount.compareTo(accountFrozen.getFrozenAmount()) > 0) {
|
|
|
|
|
// 实际扣减大于预扣减,根据实际扣减进行扣除
|
|
|
|
|
// 如果余额不够实际扣减,将balance设置为0
|
|
|
|
|
if (balance.compareTo(finalAmount) < 0) {
|
|
|
|
|
account.setBalance(BigDecimal.ZERO);
|
|
|
|
|
} else {
|
|
|
|
|
account.setBalance(balance.subtract(finalAmount));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 实际扣减小于等于预扣减,正常扣减
|
|
|
|
|
if (balance.compareTo(finalAmount) < 0) {
|
|
|
|
|
account.setBalance(BigDecimal.ZERO);
|
|
|
|
|
} else {
|
|
|
|
|
account.setBalance(balance.subtract(finalAmount));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
balance = calculateNewBalance(balance, finalAmount, accountFrozen.getFrozenAmount());
|
|
|
|
|
account.setBalance(balance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
account.setUpdateTime(new Date());
|
|
|
|
|
accountMapper.update(account);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 8. 生成流水记录(单位:积分)
|
|
|
|
|
if (finalAmount.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
AccountTransaction transaction = new AccountTransaction();
|
|
|
|
|
transaction.setUserId(accountFrozen.getUserId());
|
|
|
|
|
transaction.setUserName(account.getUserName());
|
|
|
|
|
transaction.setTransactionType(3); // 购买内容
|
|
|
|
|
transaction.setAmount(finalAmount);
|
|
|
|
|
transaction.setBeforeBalance(beforeBalance);
|
|
|
|
|
transaction.setAfterBalance(account.getBalance());
|
|
|
|
|
transaction.setStatus(1); // 成功
|
|
|
|
|
transaction.setTransactionNo(IDUtils.getSnowflakeIdStr());
|
|
|
|
|
transaction.setPayType(3); // 余额支付
|
|
|
|
|
transaction.setBusinessType("frozen_release"); // 冻结单释放
|
|
|
|
|
transaction.setRemark("冻结单释放扣减: " + accountFrozen.getFrozenId());
|
|
|
|
|
transaction.setIsExpense(1); // 支出
|
|
|
|
|
transaction.setQuestion(accountFrozen.getQuestion());
|
|
|
|
|
if(Objects.nonNull(accountFrozen.getQuestion())){
|
|
|
|
|
transaction.setQuestion(accountFrozen.getQuestion());
|
|
|
|
|
}
|
|
|
|
|
if(Objects.nonNull(accountFrozen.getCallId())){
|
|
|
|
|
transaction.setCallId(accountFrozen.getCallId()); // 设置调用ID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果是token消费,记录token信息
|
|
|
|
|
if (accountReleaseDto.getUsageInputTokens() != null && accountReleaseDto.getUsageOutputTokens() != null) {
|
|
|
|
|
transaction.setInputToken(accountReleaseDto.getUsageInputTokens().intValue());
|
|
|
|
|
transaction.setOutputToken(accountReleaseDto.getUsageOutputTokens().intValue());
|
|
|
|
|
if (accountReleaseDto.getUsageTotalTokens() != null) {
|
|
|
|
|
transaction.setTotalTokens(accountReleaseDto.getUsageTotalTokens().intValue());
|
|
|
|
|
}
|
|
|
|
|
transaction.setModelName(accountFrozen.getModelName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transaction.setCreateTime(new Date());
|
|
|
|
|
transaction.setUpdateTime(new Date());
|
|
|
|
|
accountTransactionMapper.insert(transaction);
|
|
|
|
|
/**
|
|
|
|
|
* 计算释放后的新余额
|
|
|
|
|
* 比较实际扣减金额和预扣减金额,取较大值进行扣减
|
|
|
|
|
*
|
|
|
|
|
* @param balance 当前余额
|
|
|
|
|
* @param finalAmount 实际扣减金额
|
|
|
|
|
* @param frozenAmount 预扣减金额
|
|
|
|
|
* @return 新的余额
|
|
|
|
|
*/
|
|
|
|
|
private BigDecimal calculateNewBalance(BigDecimal balance, BigDecimal finalAmount, BigDecimal frozenAmount) {
|
|
|
|
|
BigDecimal amountToDeduct = finalAmount.compareTo(frozenAmount) > 0 ? finalAmount : frozenAmount;
|
|
|
|
|
if (balance.compareTo(amountToDeduct) < 0) {
|
|
|
|
|
return BigDecimal.ZERO;
|
|
|
|
|
}
|
|
|
|
|
return balance.subtract(amountToDeduct);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 9. 更新冻结单状态(单位:积分)
|
|
|
|
|
/**
|
|
|
|
|
* 创建交易流水记录
|
|
|
|
|
*
|
|
|
|
|
* @param accountFrozen 冻结单信息
|
|
|
|
|
* @param account 账户信息
|
|
|
|
|
* @param finalAmount 最终扣减金额
|
|
|
|
|
*/
|
|
|
|
|
private void doCreateTransaction(AccountFrozen accountFrozen, Account account, BigDecimal finalAmount) {
|
|
|
|
|
if (finalAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
BigDecimal beforeBalance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
|
|
|
|
AccountTransaction transaction = buildTransaction(accountFrozen, account, finalAmount, beforeBalance);
|
|
|
|
|
accountTransactionMapper.insert(transaction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 构建交易流水对象
|
|
|
|
|
*
|
|
|
|
|
* @param accountFrozen 冻结单信息
|
|
|
|
|
* @param account 账户信息
|
|
|
|
|
* @param finalAmount 最终扣减金额
|
|
|
|
|
* @param beforeBalance 扣减前余额
|
|
|
|
|
* @return 交易流水对象
|
|
|
|
|
*/
|
|
|
|
|
private AccountTransaction buildTransaction(AccountFrozen accountFrozen, Account account,
|
|
|
|
|
BigDecimal finalAmount, BigDecimal beforeBalance) {
|
|
|
|
|
AccountTransaction transaction = new AccountTransaction();
|
|
|
|
|
transaction.setUserId(accountFrozen.getUserId());
|
|
|
|
|
transaction.setUserName(account.getUserName());
|
|
|
|
|
transaction.setTransactionType(3);
|
|
|
|
|
transaction.setAmount(finalAmount);
|
|
|
|
|
transaction.setBeforeBalance(beforeBalance);
|
|
|
|
|
transaction.setAfterBalance(account.getBalance());
|
|
|
|
|
transaction.setStatus(1);
|
|
|
|
|
transaction.setTransactionNo(IDUtils.getSnowflakeIdStr());
|
|
|
|
|
transaction.setPayType(3);
|
|
|
|
|
transaction.setBusinessType("frozen_release");
|
|
|
|
|
transaction.setRemark("冻结单释放扣减: " + accountFrozen.getFrozenId());
|
|
|
|
|
transaction.setIsExpense(1);
|
|
|
|
|
if (Objects.nonNull(accountFrozen.getQuestion())) {
|
|
|
|
|
transaction.setQuestion(accountFrozen.getQuestion());
|
|
|
|
|
}
|
|
|
|
|
if (Objects.nonNull(accountFrozen.getCallId())) {
|
|
|
|
|
transaction.setCallId(accountFrozen.getCallId());
|
|
|
|
|
}
|
|
|
|
|
if (accountFrozen.getFrozenType().equals(FROZEN_TYPE_TOKEN) &&
|
|
|
|
|
accountFrozen.getModelName() != null) {
|
|
|
|
|
if (accountFrozen.getUsageInputTokens() != null) {
|
|
|
|
|
transaction.setInputToken(accountFrozen.getUsageInputTokens().intValue());
|
|
|
|
|
}
|
|
|
|
|
if (accountFrozen.getUsageOutputTokens() != null) {
|
|
|
|
|
transaction.setOutputToken(accountFrozen.getUsageOutputTokens().intValue());
|
|
|
|
|
}
|
|
|
|
|
if (accountFrozen.getUsageTotalTokens() != null) {
|
|
|
|
|
transaction.setTotalTokens(accountFrozen.getUsageTotalTokens().intValue());
|
|
|
|
|
}
|
|
|
|
|
transaction.setModelName(accountFrozen.getModelName());
|
|
|
|
|
}
|
|
|
|
|
transaction.setCreateTime(new Date());
|
|
|
|
|
transaction.setUpdateTime(new Date());
|
|
|
|
|
return transaction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 执行冻结单终结
|
|
|
|
|
* 更新冻结单状态为已终结
|
|
|
|
|
*
|
|
|
|
|
* @param accountFrozen 冻结单信息
|
|
|
|
|
* @param accountReleaseDto 冻结单释放DTO
|
|
|
|
|
* @param finalAmount 最终扣减金额
|
|
|
|
|
* @return 冻结单信息
|
|
|
|
|
*/
|
|
|
|
|
private AccountFrozen doFinalizeFrozen(AccountFrozen accountFrozen, AccountReleaseDto accountReleaseDto,
|
|
|
|
|
BigDecimal finalAmount) {
|
|
|
|
|
accountFrozen.setFinalAmount(finalAmount);
|
|
|
|
|
accountFrozen.setUsageInputTokens(accountReleaseDto.getUsageInputTokens());
|
|
|
|
|
accountFrozen.setUsageOutputTokens(accountReleaseDto.getUsageOutputTokens());
|
|
|
|
|
@ -326,13 +638,13 @@ public class AccountFrozenServiceImpl implements AccountFrozenService {
|
|
|
|
|
accountFrozen.setFinalizeReason(accountReleaseDto.getFinalizeReason());
|
|
|
|
|
accountFrozen.setStatus("FINALIZED");
|
|
|
|
|
accountFrozen.setUpdateTime(new Date());
|
|
|
|
|
|
|
|
|
|
accountFrozenMapper.updateByPrimaryKey(accountFrozen);
|
|
|
|
|
return accountFrozen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据ID查询冻结单
|
|
|
|
|
*
|
|
|
|
|
* @param frozenId 冻结单ID
|
|
|
|
|
* @return 冻结单信息
|
|
|
|
|
*/
|
|
|
|
|
@ -343,6 +655,7 @@ public class AccountFrozenServiceImpl implements AccountFrozenService {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据会话ID查询冻结单
|
|
|
|
|
*
|
|
|
|
|
* @param sessionId 会话ID
|
|
|
|
|
* @return 冻结单信息
|
|
|
|
|
*/
|
|
|
|
|
@ -351,4 +664,4 @@ public class AccountFrozenServiceImpl implements AccountFrozenService {
|
|
|
|
|
return accountFrozenMapper.selectBySessionId(sessionId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|