From f18217ba93c0ecdaae98f6a62384969cb9297ffc Mon Sep 17 00:00:00 2001 From: ziin Date: Mon, 26 Jan 2026 22:02:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(chat):=20=E4=B8=BA=E4=BD=8EVIP=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=A2=9E=E5=8A=A0=E6=AF=8F=E6=97=A5=E4=BD=93=E9=AA=8C?= =?UTF-8?q?=E6=AC=A1=E6=95=B0=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在ChatServiceImpl.message()中新增VIP等级检查逻辑 - 使用Redis计数器实现每日额度控制,午夜自动重置 - 新增错误码VIP_TRIAL_LIMIT_REACHED(50030) - 同步更新.gitignore忽略.omc目录 --- .gitignore | 1 + .omc/ultrawork-state.json | 4 +- .../com/yolo/keyborad/common/ErrorCode.java | 3 +- .../service/impl/ChatServiceImpl.java | 44 +++++++++++++++++++ 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 094988c..8854d7d 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ build/ /voice-optimization-plan.md /docs/websocket-api.md /src/main/resources/static/ws-test.html +/.omc/ diff --git a/.omc/ultrawork-state.json b/.omc/ultrawork-state.json index 2bd7f53..198f9fc 100644 --- a/.omc/ultrawork-state.json +++ b/.omc/ultrawork-state.json @@ -2,6 +2,6 @@ "active": true, "started_at": "2026-01-26T13:01:18.447Z", "original_prompt": "刚刚回滚了代码,现在AI陪聊角色评论需要使用KeyboardAiCompanionCommentLikeService添加一个评论点赞接口,用来记录点赞和取消点赞。 ulw", - "reinforcement_count": 2, - "last_checked_at": "2026-01-26T13:21:19.022Z" + "reinforcement_count": 4, + "last_checked_at": "2026-01-26T13:55:34.306Z" } \ No newline at end of file diff --git a/src/main/java/com/yolo/keyborad/common/ErrorCode.java b/src/main/java/com/yolo/keyborad/common/ErrorCode.java index 7fb4c55..59d4f4e 100644 --- a/src/main/java/com/yolo/keyborad/common/ErrorCode.java +++ b/src/main/java/com/yolo/keyborad/common/ErrorCode.java @@ -69,7 +69,8 @@ public enum ErrorCode { INVITE_CODE_USED_UP(50026, "邀请码使用次数已达上限"), INVITE_CODE_ALREADY_BOUND(50028, "您已绑定过邀请码,无法重复绑定"), INVITE_CODE_CANNOT_BIND_SELF(50029, "不能绑定自己的邀请码"), - RECEIPT_ALREADY_PROCESSED(50027, "收据已处理"); + RECEIPT_ALREADY_PROCESSED(50027, "收据已处理"), + VIP_TRIAL_LIMIT_REACHED(50030, "今日体验次数已达上限,请开通会员"); /** * 状态码 diff --git a/src/main/java/com/yolo/keyborad/service/impl/ChatServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/ChatServiceImpl.java index 948c9da..ca240fa 100644 --- a/src/main/java/com/yolo/keyborad/service/impl/ChatServiceImpl.java +++ b/src/main/java/com/yolo/keyborad/service/impl/ChatServiceImpl.java @@ -39,6 +39,11 @@ import reactor.core.scheduler.Schedulers; import java.math.BigDecimal; 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.Base64; 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 long AUDIO_TASK_EXPIRE_SECONDS = 3600; // 1小时过期 + private static final String CHAT_DAILY_LIMIT_PREFIX = "chat:daily:limit:"; + private final 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) { 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(); // 获取AI人设的系统提示词