fix(chat): 为低VIP用户增加每日体验次数限制

- 在ChatServiceImpl.message()中新增VIP等级检查逻辑
- 使用Redis计数器实现每日额度控制,午夜自动重置
- 新增错误码VIP_TRIAL_LIMIT_REACHED(50030)
- 同步更新.gitignore忽略.omc目录
This commit is contained in:
2026-01-26 22:02:44 +08:00
parent 1decf0ac58
commit f18217ba93
4 changed files with 49 additions and 3 deletions

1
.gitignore vendored
View File

@@ -46,3 +46,4 @@ build/
/voice-optimization-plan.md /voice-optimization-plan.md
/docs/websocket-api.md /docs/websocket-api.md
/src/main/resources/static/ws-test.html /src/main/resources/static/ws-test.html
/.omc/

View File

@@ -2,6 +2,6 @@
"active": true, "active": true,
"started_at": "2026-01-26T13:01:18.447Z", "started_at": "2026-01-26T13:01:18.447Z",
"original_prompt": "刚刚回滚了代码现在AI陪聊角色评论需要使用KeyboardAiCompanionCommentLikeService添加一个评论点赞接口用来记录点赞和取消点赞。 ulw", "original_prompt": "刚刚回滚了代码现在AI陪聊角色评论需要使用KeyboardAiCompanionCommentLikeService添加一个评论点赞接口用来记录点赞和取消点赞。 ulw",
"reinforcement_count": 2, "reinforcement_count": 4,
"last_checked_at": "2026-01-26T13:21:19.022Z" "last_checked_at": "2026-01-26T13:55:34.306Z"
} }

View File

@@ -69,7 +69,8 @@ public enum ErrorCode {
INVITE_CODE_USED_UP(50026, "邀请码使用次数已达上限"), INVITE_CODE_USED_UP(50026, "邀请码使用次数已达上限"),
INVITE_CODE_ALREADY_BOUND(50028, "您已绑定过邀请码,无法重复绑定"), INVITE_CODE_ALREADY_BOUND(50028, "您已绑定过邀请码,无法重复绑定"),
INVITE_CODE_CANNOT_BIND_SELF(50029, "不能绑定自己的邀请码"), INVITE_CODE_CANNOT_BIND_SELF(50029, "不能绑定自己的邀请码"),
RECEIPT_ALREADY_PROCESSED(50027, "收据已处理"); RECEIPT_ALREADY_PROCESSED(50027, "收据已处理"),
VIP_TRIAL_LIMIT_REACHED(50030, "今日体验次数已达上限,请开通会员");
/** /**
* 状态码 * 状态码

View File

@@ -39,6 +39,11 @@ import reactor.core.scheduler.Schedulers;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.Date; import java.util.Date;
@@ -96,6 +101,8 @@ public class ChatServiceImpl implements ChatService {
private static final String AUDIO_TASK_PREFIX = "audio:task:"; private static final String AUDIO_TASK_PREFIX = "audio:task:";
private static final long AUDIO_TASK_EXPIRE_SECONDS = 3600; // 1小时过期 private static final long AUDIO_TASK_EXPIRE_SECONDS = 3600; // 1小时过期
private static final String CHAT_DAILY_LIMIT_PREFIX = "chat:daily:limit:";
private final NacosAppConfigCenter.DynamicAppConfig cfgHolder; private final NacosAppConfigCenter.DynamicAppConfig cfgHolder;
public ChatServiceImpl(NacosAppConfigCenter.DynamicAppConfig cfgHolder) { public ChatServiceImpl(NacosAppConfigCenter.DynamicAppConfig cfgHolder) {
@@ -369,6 +376,43 @@ public class ChatServiceImpl implements ChatService {
public ChatMessageVO message(String content, String userId, Long companionId) { public ChatMessageVO message(String content, String userId, Long companionId) {
log.info("同步对话请求, userId: {}, companionId: {}, content: {}", userId, companionId, content); log.info("同步对话请求, userId: {}, companionId: {}, content: {}", userId, companionId, content);
// ============ VIP等级检查 ============
AppConfig appConfig = cfgHolder.getRef().get();
KeyboardUser user = userService.getById(Long.parseLong(userId));
// 获取VIP等级null视为0
int vipLevel = user != null && user.getVipLevel() != null ? user.getVipLevel() : 0;
// 如果VIP等级 <= 1需要限制每日体验次数
if (vipLevel <= 1) {
Integer dailyLimit = appConfig.getUserRegisterProperties().getVipFreeTrialTalk();
String redisKey = CHAT_DAILY_LIMIT_PREFIX + userId;
// 获取当前使用次数
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);
}
// 增加使用次数
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, dailyLimit);
}
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 获取AI人设的系统提示词 // 获取AI人设的系统提示词