mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-03-13 20:53:42 +08:00
用户发送消息 → 预检查余额 → 保存用户消息 → 发布计费事件 → 异步扣费 → 保存账单记录
添加了billingType计费类型字段消息保存的时候写入进去
This commit is contained in:
@@ -69,5 +69,10 @@ public class ChatMessage extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 计费类型(1-token计费,2-次数计费,null-普通消息)
|
||||
*/
|
||||
private String billingType;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -75,5 +75,10 @@ public class ChatMessageBo extends BaseEntity {
|
||||
@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 计费类型(1-token计费,2-次数计费,null-普通消息)
|
||||
*/
|
||||
private String billingType;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.ruoyi.common.excel.annotation.ExcelDictFormat;
|
||||
import org.ruoyi.common.excel.convert.ExcelDictConvert;
|
||||
import org.ruoyi.domain.ChatMessage;
|
||||
|
||||
import java.io.Serial;
|
||||
@@ -73,6 +75,13 @@ public class ChatMessageVo implements Serializable {
|
||||
@ExcelProperty(value = "模型名称")
|
||||
private String modelName;
|
||||
|
||||
/**
|
||||
* 计费类型(1-token计费,2-次数计费)
|
||||
*/
|
||||
@ExcelProperty(value = "计费类型", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(dictType = "sys_model_billing")
|
||||
private String billingType;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@@ -87,4 +96,5 @@ public class ChatMessageVo implements Serializable {
|
||||
private Date createTime;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ public class ChatCostServiceImpl implements IChatCostService {
|
||||
}
|
||||
|
||||
// 记录账单消息
|
||||
saveBillingRecord(chatRequest, tokens, numberCost.doubleValue(), "TIMES_BILLING");
|
||||
saveBillingRecord(chatRequest, tokens, numberCost.doubleValue(), chatModelVo.getModelType());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ public class ChatCostServiceImpl implements IChatCostService {
|
||||
log.debug("deductToken->扣费成功,更新余数: {}", remainder);
|
||||
|
||||
// 记录账单消息
|
||||
saveBillingRecord(chatRequest, billable, numberCost.doubleValue(), "TOKEN_BILLING");
|
||||
saveBillingRecord(chatRequest, billable, numberCost.doubleValue(), chatModelVo.getModelType());
|
||||
} catch (ServiceException e) {
|
||||
// 余额不足时,不更新token累计,保持原有累计数
|
||||
log.warn("deductToken->余额不足,本次token累计保持不变: {}", totalTokens);
|
||||
@@ -167,6 +167,19 @@ public class ChatCostServiceImpl implements IChatCostService {
|
||||
// 普通消息不涉及扣费,deductCost保持null
|
||||
chatMessageBo.setDeductCost(null);
|
||||
chatMessageBo.setRemark("用户消息");
|
||||
|
||||
// 设置计费类型(根据模型配置获取计费类型)
|
||||
try {
|
||||
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
||||
if (chatModelVo != null) {
|
||||
chatMessageBo.setBillingType(chatModelVo.getModelType());
|
||||
} else {
|
||||
chatMessageBo.setBillingType(null); // 模型不存在时设为null
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("saveMessage->获取模型计费类型失败,设为null,模型: {}", chatRequest.getModel());
|
||||
chatMessageBo.setBillingType(null);
|
||||
}
|
||||
|
||||
try {
|
||||
chatMessageService.insertByBo(chatMessageBo);
|
||||
@@ -261,7 +274,7 @@ public class ChatCostServiceImpl implements IChatCostService {
|
||||
/**
|
||||
* 保存账单记录
|
||||
*/
|
||||
private void saveBillingRecord(ChatRequest chatRequest, int billedTokens, double cost, String billingType) {
|
||||
private void saveBillingRecord(ChatRequest chatRequest, int billedTokens, double cost, String billingTypeCode) {
|
||||
try {
|
||||
ChatMessageBo billingMessage = new ChatMessageBo();
|
||||
billingMessage.setUserId(chatRequest.getUserId());
|
||||
@@ -270,26 +283,47 @@ public class ChatCostServiceImpl implements IChatCostService {
|
||||
billingMessage.setModelName(chatRequest.getModel());
|
||||
billingMessage.setTotalTokens(billedTokens);
|
||||
billingMessage.setDeductCost(cost);
|
||||
billingMessage.setRemark(billingType);
|
||||
billingMessage.setRemark(getBillingTypeName(billingTypeCode));
|
||||
|
||||
// 构建账单消息内容
|
||||
String content;
|
||||
if ("TIMES_BILLING".equals(billingType)) {
|
||||
content = String.format("按次计费:消耗 %d tokens,扣费 %.2f 元", billedTokens, cost);
|
||||
} else {
|
||||
content = String.format("按量计费:结算 %d tokens,扣费 %.2f 元", billedTokens, cost);
|
||||
}
|
||||
billingMessage.setContent(content);
|
||||
// 设置计费类型和构建消息内容
|
||||
setBillingTypeAndContent(billingMessage, billingTypeCode, billedTokens, cost);
|
||||
|
||||
chatMessageService.insertByBo(billingMessage);
|
||||
log.debug("saveBillingRecord->保存账单记录成功,用户ID: {}, 计费类型: {}, 费用: {}",
|
||||
chatRequest.getUserId(), billingType, cost);
|
||||
chatRequest.getUserId(), billingTypeCode, cost);
|
||||
} catch (Exception e) {
|
||||
log.error("saveBillingRecord->保存账单记录失败", e);
|
||||
// 账单记录失败不影响主流程,只记录错误日志
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置计费类型和构建消息内容
|
||||
*/
|
||||
private void setBillingTypeAndContent(ChatMessageBo billingMessage, String billingTypeCode, int billedTokens, double cost) {
|
||||
billingMessage.setBillingType(billingTypeCode);
|
||||
|
||||
// 使用枚举获取计费类型并构建消息内容
|
||||
BillingType billingType = BillingType.fromCode(billingTypeCode);
|
||||
if (billingType != null) {
|
||||
String content = switch (billingType) {
|
||||
case TIMES -> String.format("%s:消耗 %d tokens,扣费 %.2f 元", billingType.getDescription(), billedTokens, cost);
|
||||
case TOKEN -> String.format("%s:结算 %d tokens,扣费 %.2f 元", billingType.getDescription(), billedTokens, cost);
|
||||
};
|
||||
billingMessage.setContent(content);
|
||||
} else {
|
||||
billingMessage.setContent(String.format("系统计费:处理 %d tokens,扣费 %.2f 元", billedTokens, cost));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取计费类型名称(用于remark字段)
|
||||
*/
|
||||
private String getBillingTypeName(String billingTypeCode) {
|
||||
BillingType billingType = BillingType.fromCode(billingTypeCode);
|
||||
return billingType != null ? billingType.getDescription() : "系统计费";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从用户余额中扣除费用
|
||||
*
|
||||
|
||||
@@ -104,21 +104,21 @@ public class SseServiceImpl implements ISseService {
|
||||
//待优化的地方 (这里请前端提交send的时候传递uuid进来或者sessionId)
|
||||
//待优化的地方 (这里请前端提交send的时候传递uuid进来或者sessionId)
|
||||
//待优化的地方 (这里请前端提交send的时候传递uuid进来或者sessionId)
|
||||
{
|
||||
// 设置会话id
|
||||
if (chatRequest.getUuid() == null) {
|
||||
//暂时随机生成会话id
|
||||
chatRequest.setSessionId(System.currentTimeMillis());
|
||||
} else {
|
||||
//这里或许需要修改一下,这里应该用uuid 或者 前端传递 sessionId
|
||||
chatRequest.setSessionId(chatRequest.getUuid());
|
||||
}
|
||||
}
|
||||
// {
|
||||
// // 设置会话id
|
||||
// if (chatRequest.getUuid() == null) {
|
||||
// //暂时随机生成会话id
|
||||
// chatRequest.setSessionId(System.currentTimeMillis());
|
||||
// } else {
|
||||
// //这里或许需要修改一下,这里应该用uuid 或者 前端传递 sessionId
|
||||
// chatRequest.setSessionId(chatRequest.getUuid());
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// 先保存消息,再发布异步计费事件
|
||||
chatCostService.saveMessage(chatRequest);
|
||||
chatCostService.publishBillingEvent(chatRequest);
|
||||
|
||||
chatRequest.setUserId(chatCostService.getUserId());
|
||||
if (chatRequest.getSessionId() == null) {
|
||||
ChatSessionBo chatSessionBo = new ChatSessionBo();
|
||||
@@ -131,7 +131,7 @@ public class SseServiceImpl implements ISseService {
|
||||
}
|
||||
// 自动选择模型并获取对应的聊天服务
|
||||
IChatService chatService = autoSelectModelAndGetService(chatRequest);
|
||||
|
||||
chatCostService.publishBillingEvent(chatRequest);
|
||||
// 仅当 autoSelectModel = true 时,才启用重试与降级
|
||||
if (Boolean.TRUE.equals(chatRequest.getAutoSelectModel())) {
|
||||
ChatModelVo currentModel = this.chatModelVo;
|
||||
|
||||
4
script/sql/update/chat-message-billing-type.sql
Normal file
4
script/sql/update/chat-message-billing-type.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
-- 为 chat_message 表添加 billing_type 字段
|
||||
ALTER TABLE chat_message
|
||||
ADD COLUMN billing_type char NULL COMMENT '计费类型(1-token计费,2-次数计费,null-普通消息)';
|
||||
|
||||
Reference in New Issue
Block a user