@ -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 ( ) ) ;
}
@ -74,76 +116,251 @@ public class AccountFrozenServiceImpl implements AccountFrozenService {
throw new BizException ( ResultCode . PARAMETER_EMPTY . getCode ( ) , " 冻结类型不能为空 " ) ;
}
/ / 2 . 通过sessionId获取用户ID
SysUser sysUser = sysUserMapper . getBySessionId ( accountFrozenDto . getSessionId ( ) ) ;
/ / 根据冻结类型进行不同的参数校验
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 ( ) , " 冻结金额不能为空 " ) ;
}
}
/ / 其他类型的校验可以在此添加
}
/ * *
* 通过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 ( ) ) ;
}
/ / 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 " ) ;
return account ;
}
/ / 查询模型价格信息
ModelPrice modelPrice = modelPriceService . queryByModelName ( accountFrozenDto . getModelName ( ) ) ;
if ( modelPrice ! = null ) {
/ / 计算token费用
long inputFee = accountFrozenDto . getEstimatedInputTokens ( ) / modelPrice . getInputPerCent ( ) ;
if ( accountFrozenDto . getEstimatedInputTokens ( ) % modelPrice . getInputPerCent ( ) > 0 ) {
inputFee + = 1 ;
/ * *
* 根据冻结类型计算冻结金额
*
* @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 ;
}
long outputFee = accountFrozenDto . getEstimatedOutputTokens ( ) / modelPrice . getOutputPerCent ( ) ;
if ( accountFrozenDto . getEstimatedOutputTokens ( ) % modelPrice . getOutputPerCent ( ) > 0 ) {
outputFee + = 1 ;
/ * *
* 计算token类型的冻结金额
* 根据预估输入输出tokens和模型价格计算冻结金额 , 并应用扣费系数
*
* @param accountFrozenDto 冻结单DTO
* @return 冻结金额 ( 单位 : 积分 )
* /
private BigDecimal calculateTokenFrozenAmount ( AccountFrozenDto accountFrozenDto ) {
if ( accountFrozenDto . getEstimatedInputTokens ( ) = = null | |
accountFrozenDto . getEstimatedOutputTokens ( ) = = null | |
accountFrozenDto . getModelName ( ) = = null ) {
return accountFrozenDto . getFrozenAmount ( ) ;
}
/ / 总费用 ( 分 )
/ / 注意 : 因为1分 = 1积分 , 所以totalFee直接就是积分数量
long totalFee = inputFee + outputFee ;
/ / 转换为积分 ( 1分 = 1积分 , 无需转换 )
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 ) ;
/ / 应用扣费系数
finalFrozenAmount = baseAmount . multiply ( accountDeductionProperties . getCoefficient ( ) ) ;
}
}
return baseAmount . multiply ( accountDeductionProperties . getCoefficient ( ) ) ;
}
/ / 5 . 检查余额是否足够 ( 账户总余额 - 已冻结金额 > = 本次冻结金额 )
/ * *
* 标准化模型名称
* 将前端传入的模型名称转换为数据库中对应的名称
*
* @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,19 +369,56 @@ 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 ( ) ) ;
}
@ -172,153 +426,211 @@ public class AccountFrozenServiceImpl implements AccountFrozenService {
throw new BizException ( ResultCode . FROZEN_ID_EMPTY . getCode ( ) , ResultCode . FROZEN_ID_EMPTY . getMessage ( ) ) ;
}
/ / 2 . 查询冻结单
AccountFrozen accountFrozen = accountFrozenMapper . selectByPrimaryKey ( accountReleaseDto . getFrozenId ( ) ) ;
/ / 注 : 释放冻结单时的frozenType需要从冻结单中获取 , 所以这里不做类型相关的校验
/ / 具体的类型相关校验会在calculateReleaseAmount方法中处理
}
/ * *
* 通过冻结单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 ( ) ) ;
}
/ / 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 ;
/ * *
* 根据冻结类型计算最终释放金额
*
* @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 ;
}
long outputFee = accountReleaseDto . getUsageOutputTokens ( ) / outputModelPrice . getOutputPerCent ( ) ;
if ( accountReleaseDto . getUsageOutputTokens ( ) % outputModelPrice . getOutputPerCent ( ) > 0 ) {
outputFee + = 1 ;
/ * *
* 计算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 ;
}
/ / 总费用 ( 分 )
/ / 注意 : 因为1分 = 1积分 , 所以totalFee直接就是积分数量
long totalFee = inputFee + outputFee ;
/ / 转换为积分 ( 1分 = 1积分 , 无需转换 )
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 ) ;
/ / 应用扣费系数
finalAmount = baseAmount . multiply ( accountDeductionProperties . getCoefficient ( ) ) ;
}
}
}
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 ) ) ;
balance = calculateNewBalance ( balance , finalAmount , accountFrozen . getFrozenAmount ( ) ) ;
account . setBalance ( balance ) ;
}
} else {
/ / 实际扣减小于等于预扣减 , 正常扣减
if ( balance . compareTo ( finalAmount ) < 0 ) {
account . setBalance ( BigDecimal . ZERO ) ;
} else {
account . setBalance ( balance . subtract ( finalAmount ) ) ;
}
}
}
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 ( ) ) ;
/ * *
* 计算释放后的新余额
* 比较实际扣减金额和预扣减金额 , 取较大值进行扣减
*
* @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 ;
}
transaction . setModelName ( accountFrozen . getModelName ( ) ) ;
return balance . subtract ( amountToDeduct ) ;
}
transaction . setCreateTime ( new Date ( ) ) ;
transaction . setUpdateTime ( new Date ( ) ) ;
/ * *
* 创建交易流水记录
*
* @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 ) ;
}
/ / 9 . 更新冻结单状态 ( 单位 : 积分 )
/ * *
* 构建交易流水对象
*
* @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 冻结单信息
* /