feat(speech): 为语音转文字接口增加VIP每日调用限制

This commit is contained in:
2026-02-04 20:01:25 +08:00
parent ca2cd87d89
commit e7437a39b2

View File

@@ -1,16 +1,31 @@
package com.yolo.keyborad.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.yolo.keyborad.common.BaseResponse;
import com.yolo.keyborad.common.ErrorCode;
import com.yolo.keyborad.common.ResultUtils;
import com.yolo.keyborad.config.AppConfig;
import com.yolo.keyborad.config.NacosAppConfigCenter;
import com.yolo.keyborad.exception.BusinessException;
import com.yolo.keyborad.model.entity.KeyboardUser;
import com.yolo.keyborad.model.vo.SpeechToTextVO;
import com.yolo.keyborad.service.DeepgramService;
import com.yolo.keyborad.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
/**
* 语音服务控制器
*
@@ -22,13 +37,71 @@ import org.springframework.web.multipart.MultipartFile;
@Tag(name = "语音服务", description = "语音相关功能接口")
public class SpeechController {
private static final String SPEECH_DAILY_LIMIT_PREFIX = "speech:daily:limit:";
@Resource
private DeepgramService deepgramService;
@Resource
private UserService userService;
@Resource
private StringRedisTemplate stringRedisTemplate;
private final NacosAppConfigCenter.DynamicAppConfig cfgHolder;
public SpeechController(NacosAppConfigCenter.DynamicAppConfig cfgHolder) {
this.cfgHolder = cfgHolder;
}
@PostMapping("/transcribe")
@Operation(summary = "语音转文字", description = "上传音频文件并转换为文本")
public BaseResponse<SpeechToTextVO> transcribe(@RequestPart("file") MultipartFile file) {
// 获取当前登录用户ID
Long userId = StpUtil.getLoginIdAsLong();
// 查询用户信息
KeyboardUser user = userService.getById(userId);
// 获取VIP等级null视为0
int vipLevel = user != null && user.getVipLevel() != null ? user.getVipLevel() : 0;
// vipLevel >= 2 不做限制,直接放行
if (vipLevel < 2) {
AppConfig appConfig = cfgHolder.getRef().get();
String redisKey = SPEECH_DAILY_LIMIT_PREFIX + userId;
Integer dailyLimit = appConfig.getUserRegisterProperties().getVipFreeTrialTalk();
// 获取当前使用次数
String countStr = stringRedisTemplate.opsForValue().get(redisKey);
int currentCount = countStr != null ? Integer.parseInt(countStr) : 0;
// 检查是否超出限制
if (currentCount >= dailyLimit) {
log.warn("用户 {} VIP等级 {} 已达到每日语音转文字次数限制 {}", userId, vipLevel, dailyLimit);
throw new BusinessException(ErrorCode.VIP_TRIAL_LIMIT_REACHED);
}
}
// 调用语音转文字服务
SpeechToTextVO result = deepgramService.transcribe(file);
// 成功后扣减次数(仅 vipLevel < 2 的用户)
if (vipLevel < 2) {
String redisKey = SPEECH_DAILY_LIMIT_PREFIX + userId;
Long newCount = stringRedisTemplate.opsForValue().increment(redisKey);
// 设置到午夜过期(只有第一次设置时需要设置过期时间)
if (newCount != null && newCount == 1) {
LocalDateTime now = LocalDateTime.now(ZoneId.of("America/New_York"));
LocalDateTime midnight = LocalDateTime.of(LocalDate.now(ZoneId.of("America/New_York")).plusDays(1), LocalTime.MIDNIGHT);
long secondsUntilMidnight = ChronoUnit.SECONDS.between(now, midnight);
stringRedisTemplate.expire(redisKey, secondsUntilMidnight, TimeUnit.SECONDS);
}
log.info("用户 {} VIP等级 {} 今日已使用语音转文字 {}/{} 次", userId, vipLevel, newCount,
cfgHolder.getRef().get().getUserRegisterProperties().getVipFreeTrialTalk());
}
return ResultUtils.success(result);
}
}