mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-14 20:33:40 +00:00
用户发送消息 → 预检查余额 → 保存用户消息 → 发布计费事件 → 异步扣费 → 保存账单记录
添加了billingType计费类型字段消息保存的时候写入进去
This commit is contained in:
@@ -69,5 +69,10 @@ public class ChatMessage extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String remark;
|
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 })
|
@NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||||
private String remark;
|
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 com.alibaba.excel.annotation.ExcelProperty;
|
||||||
import io.github.linpeilie.annotations.AutoMapper;
|
import io.github.linpeilie.annotations.AutoMapper;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.ruoyi.common.excel.annotation.ExcelDictFormat;
|
||||||
|
import org.ruoyi.common.excel.convert.ExcelDictConvert;
|
||||||
import org.ruoyi.domain.ChatMessage;
|
import org.ruoyi.domain.ChatMessage;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
@@ -73,6 +75,13 @@ public class ChatMessageVo implements Serializable {
|
|||||||
@ExcelProperty(value = "模型名称")
|
@ExcelProperty(value = "模型名称")
|
||||||
private String modelName;
|
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;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ public class ChatCostServiceImpl implements IChatCostService {
|
|||||||
log.debug("deductToken->扣费成功,更新余数: {}", remainder);
|
log.debug("deductToken->扣费成功,更新余数: {}", remainder);
|
||||||
|
|
||||||
// 记录账单消息
|
// 记录账单消息
|
||||||
saveBillingRecord(chatRequest, billable, numberCost.doubleValue(), "TOKEN_BILLING");
|
saveBillingRecord(chatRequest, billable, numberCost.doubleValue(), chatModelVo.getModelType());
|
||||||
} catch (ServiceException e) {
|
} catch (ServiceException e) {
|
||||||
// 余额不足时,不更新token累计,保持原有累计数
|
// 余额不足时,不更新token累计,保持原有累计数
|
||||||
log.warn("deductToken->余额不足,本次token累计保持不变: {}", totalTokens);
|
log.warn("deductToken->余额不足,本次token累计保持不变: {}", totalTokens);
|
||||||
@@ -167,6 +167,19 @@ public class ChatCostServiceImpl implements IChatCostService {
|
|||||||
// 普通消息不涉及扣费,deductCost保持null
|
// 普通消息不涉及扣费,deductCost保持null
|
||||||
chatMessageBo.setDeductCost(null);
|
chatMessageBo.setDeductCost(null);
|
||||||
chatMessageBo.setRemark("用户消息");
|
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 {
|
try {
|
||||||
chatMessageService.insertByBo(chatMessageBo);
|
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 {
|
try {
|
||||||
ChatMessageBo billingMessage = new ChatMessageBo();
|
ChatMessageBo billingMessage = new ChatMessageBo();
|
||||||
billingMessage.setUserId(chatRequest.getUserId());
|
billingMessage.setUserId(chatRequest.getUserId());
|
||||||
@@ -270,26 +283,47 @@ public class ChatCostServiceImpl implements IChatCostService {
|
|||||||
billingMessage.setModelName(chatRequest.getModel());
|
billingMessage.setModelName(chatRequest.getModel());
|
||||||
billingMessage.setTotalTokens(billedTokens);
|
billingMessage.setTotalTokens(billedTokens);
|
||||||
billingMessage.setDeductCost(cost);
|
billingMessage.setDeductCost(cost);
|
||||||
billingMessage.setRemark(billingType);
|
billingMessage.setRemark(getBillingTypeName(billingTypeCode));
|
||||||
|
|
||||||
// 构建账单消息内容
|
// 设置计费类型和构建消息内容
|
||||||
String content;
|
setBillingTypeAndContent(billingMessage, billingTypeCode, billedTokens, cost);
|
||||||
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);
|
|
||||||
|
|
||||||
chatMessageService.insertByBo(billingMessage);
|
chatMessageService.insertByBo(billingMessage);
|
||||||
log.debug("saveBillingRecord->保存账单记录成功,用户ID: {}, 计费类型: {}, 费用: {}",
|
log.debug("saveBillingRecord->保存账单记录成功,用户ID: {}, 计费类型: {}, 费用: {}",
|
||||||
chatRequest.getUserId(), billingType, cost);
|
chatRequest.getUserId(), billingTypeCode, cost);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("saveBillingRecord->保存账单记录失败", 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)
|
//待优化的地方 (这里请前端提交send的时候传递uuid进来或者sessionId)
|
||||||
//待优化的地方 (这里请前端提交send的时候传递uuid进来或者sessionId)
|
//待优化的地方 (这里请前端提交send的时候传递uuid进来或者sessionId)
|
||||||
{
|
// {
|
||||||
// 设置会话id
|
// // 设置会话id
|
||||||
if (chatRequest.getUuid() == null) {
|
// if (chatRequest.getUuid() == null) {
|
||||||
//暂时随机生成会话id
|
// //暂时随机生成会话id
|
||||||
chatRequest.setSessionId(System.currentTimeMillis());
|
// chatRequest.setSessionId(System.currentTimeMillis());
|
||||||
} else {
|
// } else {
|
||||||
//这里或许需要修改一下,这里应该用uuid 或者 前端传递 sessionId
|
// //这里或许需要修改一下,这里应该用uuid 或者 前端传递 sessionId
|
||||||
chatRequest.setSessionId(chatRequest.getUuid());
|
// chatRequest.setSessionId(chatRequest.getUuid());
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
// 先保存消息,再发布异步计费事件
|
// 先保存消息,再发布异步计费事件
|
||||||
chatCostService.saveMessage(chatRequest);
|
chatCostService.saveMessage(chatRequest);
|
||||||
chatCostService.publishBillingEvent(chatRequest);
|
|
||||||
chatRequest.setUserId(chatCostService.getUserId());
|
chatRequest.setUserId(chatCostService.getUserId());
|
||||||
if (chatRequest.getSessionId() == null) {
|
if (chatRequest.getSessionId() == null) {
|
||||||
ChatSessionBo chatSessionBo = new ChatSessionBo();
|
ChatSessionBo chatSessionBo = new ChatSessionBo();
|
||||||
@@ -131,7 +131,7 @@ public class SseServiceImpl implements ISseService {
|
|||||||
}
|
}
|
||||||
// 自动选择模型并获取对应的聊天服务
|
// 自动选择模型并获取对应的聊天服务
|
||||||
IChatService chatService = autoSelectModelAndGetService(chatRequest);
|
IChatService chatService = autoSelectModelAndGetService(chatRequest);
|
||||||
|
chatCostService.publishBillingEvent(chatRequest);
|
||||||
// 仅当 autoSelectModel = true 时,才启用重试与降级
|
// 仅当 autoSelectModel = true 时,才启用重试与降级
|
||||||
if (Boolean.TRUE.equals(chatRequest.getAutoSelectModel())) {
|
if (Boolean.TRUE.equals(chatRequest.getAutoSelectModel())) {
|
||||||
ChatModelVo currentModel = this.chatModelVo;
|
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