|
|
@@ -12,6 +12,8 @@ import com.cyksj.common.exception.BusinessRuntimeException;
|
|
|
import com.cyksj.common.util.StringUtil;
|
|
|
import com.cyksj.mapper.*;
|
|
|
import com.cyksj.model.entity.*;
|
|
|
+import com.cyksj.model.request.gpt.ConversationRequest;
|
|
|
+import com.cyksj.redis.RedisService;
|
|
|
import com.cyksj.service.chatgpt.ChatGptAccountService;
|
|
|
import com.cyksj.service.user.UserBindRelationService;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
@@ -23,6 +25,7 @@ import org.springframework.stereotype.Service;
|
|
|
import javax.imageio.stream.FileImageOutputStream;
|
|
|
import java.nio.file.Files;
|
|
|
import java.nio.file.Path;
|
|
|
+import java.time.LocalDateTime;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
@@ -41,6 +44,12 @@ public class ChatGptAccountServiceImpl implements ChatGptAccountService {
|
|
|
|
|
|
private static final String GPT_PROXY = "https://chat-chan-87jztgkf257d.xyhelper.net";
|
|
|
|
|
|
+ private static final int MAX_REQUESTS = 40; // 3小时内最大请求次数
|
|
|
+
|
|
|
+ private static final long WINDOW_SIZE = 3 * 60 * 60; // 3小时窗口的秒数
|
|
|
+
|
|
|
+ private final RedisService redisService;
|
|
|
+
|
|
|
private final ChatgptUserMapper chatgptUserMapper;
|
|
|
|
|
|
private final ChatgptSessionMapper chatgptSessionMapper;
|
|
|
@@ -55,6 +64,7 @@ public class ChatGptAccountServiceImpl implements ChatGptAccountService {
|
|
|
|
|
|
private final UserBindRelationService userBindRelationService;
|
|
|
|
|
|
+ private final ChatgptUserConversationRecordMapper chatgptUserConversationRecordMapper;
|
|
|
|
|
|
@Override
|
|
|
public String addAccount(Account account) {
|
|
|
@@ -255,5 +265,121 @@ public class ChatGptAccountServiceImpl implements ChatGptAccountService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public ChatgptUser findByUserTokenAndExpireTimeAfter(String userToken, LocalDateTime now) {
|
|
|
+ return chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getUserToken, userToken).gt(ChatgptUser::getExpireTime, now));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean conversationLimit(String userToken, String model) {
|
|
|
+ boolean limit = true;
|
|
|
+ if("gpt-4".equals(model)){
|
|
|
+ ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getUserToken, userToken));
|
|
|
+ if (chatgptUser == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ //如果不为车队 直接返回
|
|
|
+ if(!chatgptUser.getIsCar()){
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ limit = isConversationAllowed(userToken, chatgptUser.getLimit(), chatgptUser.getLimitTime());
|
|
|
+ //车队次数记录
|
|
|
+
|
|
|
+ }
|
|
|
+ return limit;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void saveConversationRecord(String userToken, ConversationRequest conversationRequest) {
|
|
|
+ ChatgptSession chatgptSession = chatgptSessionMapper.selectOne(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getCarId, conversationRequest.getCardId()));
|
|
|
+ ChatgptUserConversationRecord chatgptUserConversationRecord = new ChatgptUserConversationRecord();
|
|
|
+ chatgptUserConversationRecord.setUserToken(userToken);
|
|
|
+ chatgptUserConversationRecord.setCarId(chatgptSession.getCarId());
|
|
|
+ chatgptUserConversationRecord.setCarName(chatgptSession.getCardName());
|
|
|
+ if(StringUtils.isBlank(conversationRequest.getConversation_id())){
|
|
|
+ chatgptUserConversationRecord.setMessageId(conversationRequest.getMessages().get(0).getId());
|
|
|
+ }else {
|
|
|
+ chatgptUserConversationRecord.setConversationId(conversationRequest.getConversation_id());
|
|
|
+ }
|
|
|
+
|
|
|
+ chatgptUserConversationRecord.setModel(conversationRequest.getModel());
|
|
|
+ chatgptUserConversationRecordMapper.insert(chatgptUserConversationRecord);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查是否允许进行进行提问
|
|
|
+ *
|
|
|
+ * @param userToken 用户token
|
|
|
+ * @return true 如果允许请求,false 如果请求被限制
|
|
|
+ */
|
|
|
+ public boolean isConversationAllowed(String userToken, int limit, Long limitTime) {
|
|
|
+ String key = RedisService.key.CHATGPT_CONVERSATION_LIMIT.getName() + ":" + userToken;
|
|
|
+ long currentTimeMillis = System.currentTimeMillis();
|
|
|
+ long windowStartMillis = currentTimeMillis - (limitTime * 60 * 60) * 1000;
|
|
|
+
|
|
|
+ // 清除时间窗口之前的请求记录
|
|
|
+ redisService.zRemoveRangeByScore(key, 0, windowStartMillis);
|
|
|
+
|
|
|
+ Long currentSize = redisService.zCard(key);
|
|
|
+ if (currentSize != null && currentSize >= limit) {
|
|
|
+ // 如果当前请求次数超过限制,则拒绝请求
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ // 如果未超过限制,记录当前请求的时间戳
|
|
|
+ redisService.zAdd(key, currentTimeMillis, currentTimeMillis);
|
|
|
+ // 设置ZSet的过期时间,窗口大小加上一段冗余时间
|
|
|
+ redisService.expire(key, (limitTime * 60 * 60) + 20);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 记录车队的提问次数
|
|
|
+ * @param userToken
|
|
|
+ * @param limit
|
|
|
+ * @param limitTime
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public boolean cardConversationRecord(String cardid) {
|
|
|
+ String key = RedisService.key.CHATGPT_CAR_CONVERSATION_LIMIT.getName() + ":" + cardid;
|
|
|
+ long currentTimeMillis = System.currentTimeMillis();
|
|
|
+ long windowStartMillis = currentTimeMillis - (WINDOW_SIZE) * 1000;
|
|
|
+
|
|
|
+ // 清除时间窗口之前的请求记录
|
|
|
+ redisService.zRemoveRangeByScore(key, 0, windowStartMillis);
|
|
|
+
|
|
|
+ Long currentSize = redisService.zCard(key);
|
|
|
+ if (currentSize != null && currentSize >= MAX_REQUESTS) {
|
|
|
+ // 如果当前请求次数超过限制,则拒绝请求
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ // 如果未超过限制,记录当前请求的时间戳
|
|
|
+ redisService.zAdd(key, currentTimeMillis, currentTimeMillis);
|
|
|
+ // 设置ZSet的过期时间,窗口大小加上一段冗余时间
|
|
|
+ redisService.expire(key, (WINDOW_SIZE * 60 * 60) + 20);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取指定用户ID在滑动窗口内的提问次数
|
|
|
+ *
|
|
|
+ * @param userToken 用户token
|
|
|
+ * @return 滑动窗口内的请求次数
|
|
|
+ */
|
|
|
+ public Long getConversationCount(String userToken, Long limitTime) {
|
|
|
+ String key = RedisService.key.CHATGPT_CONVERSATION_LIMIT.getName() + ":" + userToken;
|
|
|
+ long currentTimeMillis = System.currentTimeMillis();
|
|
|
+ long windowStartMillis = currentTimeMillis - (limitTime * 60 * 60) * 1000;
|
|
|
+
|
|
|
+ // 清除时间窗口之前的请求记录(可选,根据需要决定是否在此处清理过期记录)
|
|
|
+ redisService.zRemoveRangeByScore(key, 0, windowStartMillis);
|
|
|
+
|
|
|
+ // 获取当前窗口内的请求次数
|
|
|
+ Long currentSize = redisService.zCard(key);
|
|
|
+ return currentSize != null ? currentSize : 0L;
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|