feat(chat): 新增免费额度与VIP校验逻辑

在聊天接口中增加用户免费次数及VIP身份校验,未通过时返回新错误码50022;
调用成功后若使用免费额度则自动扣减,保障额度体系闭环。
This commit is contained in:
2025-12-17 15:51:46 +08:00
parent 2621321dea
commit 323baa876f
2 changed files with 35 additions and 4 deletions

View File

@@ -56,7 +56,8 @@ public enum ErrorCode {
PRODUCT_QUOTA_NOT_SET(50018, "商品额度未配置"), PRODUCT_QUOTA_NOT_SET(50018, "商品额度未配置"),
LACK_ORIGIN_TRANSACTION_ID_ERROR(50019, "缺少原始交易id"), LACK_ORIGIN_TRANSACTION_ID_ERROR(50019, "缺少原始交易id"),
UNKNOWN_PRODUCT_TYPE(50020, "未知商品类型"), UNKNOWN_PRODUCT_TYPE(50020, "未知商品类型"),
PRODUCT_NOT_FOUND(50021, "商品不存在"); PRODUCT_NOT_FOUND(50021, "商品不存在"),
NO_QUOTA_AND_NOT_VIP(50022, "免费次数已用完请开通VIP");
/** /**
* 状态码 * 状态码

View File

@@ -11,10 +11,10 @@ import com.yolo.keyborad.exception.BusinessException;
import com.yolo.keyborad.model.dto.chat.ChatReq; import com.yolo.keyborad.model.dto.chat.ChatReq;
import com.yolo.keyborad.model.dto.chat.ChatStreamMessage; import com.yolo.keyborad.model.dto.chat.ChatStreamMessage;
import com.yolo.keyborad.model.entity.KeyboardCharacter; import com.yolo.keyborad.model.entity.KeyboardCharacter;
import com.yolo.keyborad.model.entity.KeyboardUser;
import com.yolo.keyborad.model.entity.KeyboardUserCallLog; import com.yolo.keyborad.model.entity.KeyboardUserCallLog;
import com.yolo.keyborad.service.ChatService; import com.yolo.keyborad.model.entity.KeyboardUserQuotaTotal;
import com.yolo.keyborad.service.KeyboardCharacterService; import com.yolo.keyborad.service.*;
import com.yolo.keyborad.service.KeyboardUserCallLogService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.ChatClient;
@@ -55,6 +55,12 @@ public class ChatServiceImpl implements ChatService {
@Resource @Resource
private KeyboardUserCallLogService callLogService; private KeyboardUserCallLogService callLogService;
@Resource
private KeyboardUserQuotaTotalService quotaTotalService;
@Resource
private UserService userService;
private final NacosAppConfigCenter.DynamicAppConfig cfgHolder; private final NacosAppConfigCenter.DynamicAppConfig cfgHolder;
public ChatServiceImpl(NacosAppConfigCenter.DynamicAppConfig cfgHolder) { public ChatServiceImpl(NacosAppConfigCenter.DynamicAppConfig cfgHolder) {
@@ -105,6 +111,22 @@ public class ChatServiceImpl implements ChatService {
throw new BusinessException(ErrorCode.CHAT_CHARACTER_NOT_FOUND); throw new BusinessException(ErrorCode.CHAT_CHARACTER_NOT_FOUND);
} }
// ============ 3. 校验用户免费次数和VIP ============
Long userId = StpUtil.getLoginIdAsLong();
KeyboardUserQuotaTotal quota = quotaTotalService.getById(userId);
boolean hasFreeQuota = quota != null && quota.getUsedQuota() < quota.getTotalQuota();
AtomicReference<Boolean> usedFreeQuota = new AtomicReference<>(hasFreeQuota);
if (!hasFreeQuota) {
KeyboardUser user = userService.getById(userId);
boolean isValidVip = user != null && user.getIsVip() != null && user.getIsVip()
&& (user.getVipExpiry() == null || user.getVipExpiry().after(new Date()));
if (!isValidVip) {
log.error("用户无免费次数且非VIP用户ID: {}", userId);
throw new BusinessException(ErrorCode.NO_QUOTA_AND_NOT_VIP);
}
}
// 获取应用配置 // 获取应用配置
// ============ 初始化调用日志相关变量 ============ // ============ 初始化调用日志相关变量 ============
@@ -260,6 +282,14 @@ public class ChatServiceImpl implements ChatService {
callLog.setCreatedAt(new Date()); callLog.setCreatedAt(new Date());
// 保存日志到数据库 // 保存日志到数据库
callLogService.save(callLog); callLogService.save(callLog);
// 如果使用了免费次数且调用成功,扣减免费次数
if (usedFreeQuota.get() && callLog.getSuccess()) {
KeyboardUserQuotaTotal updateQuota = new KeyboardUserQuotaTotal();
updateQuota.setUserId(callLog.getUserId());
updateQuota.setUsedQuota(quota.getUsedQuota() + 1);
quotaTotalService.updateById(updateQuota);
}
} catch (Exception e) { } catch (Exception e) {
log.error("保存调用日志失败", e); log.error("保存调用日志失败", e);
} }