用户发送消息 → 预检查余额 → 保存用户消息 → 发布计费事件 → 异步扣费 → 保存账单记录

添加了billingType计费类型字段消息保存的时候写入进去
This commit is contained in:
Administrator
2025-08-27 15:30:59 +08:00
parent 1c721981db
commit 9f7f00e50c
6 changed files with 83 additions and 25 deletions

View File

@@ -69,5 +69,10 @@ public class ChatMessage extends BaseEntity {
*/
private String remark;
/**
* 计费类型1-token计费2-次数计费null-普通消息)
*/
private String billingType;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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() : "系统计费";
}
/**
* 从用户余额中扣除费用
*

View File

@@ -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;

View File

@@ -0,0 +1,4 @@
-- 为 chat_message 表添加 billing_type 字段
ALTER TABLE chat_message
ADD COLUMN billing_type char NULL COMMENT '计费类型1-token计费2-次数计费null-普通消息)';