package com.cyksj.service.chatgpt.impl; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.UUID; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import cn.hutool.http.Method; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.cyksj.common.exception.BusinessRuntimeException; import com.cyksj.common.task.GlobalThreadPoolTaskExecutor; import com.cyksj.common.util.StringUtil; import com.cyksj.mapper.*; import com.cyksj.mapper.station.CollegeStudentStationUseTicketRecordMapper; import com.cyksj.model.entity.*; import com.cyksj.model.request.gpt.ConversationRequest; import com.cyksj.model.response.ConversationLimitResponse; import com.cyksj.model.views.ChatGptUserConversationRecordHistoryView; import com.cyksj.model.views.ChatGptUserView; import com.cyksj.model.views.ChatgptCarInfoView; import com.cyksj.redis.RedisService; import com.cyksj.service.chatgpt.ChatGptAccountService; import com.cyksj.service.user.UserBindRelationService; import com.ejlchina.searcher.BeanSearcher; import com.ejlchina.searcher.SearchResult; import com.ejlchina.searcher.util.MapUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DuplicateKeyException; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.imageio.stream.FileImageOutputStream; import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; /** * @author chan * @date 2024/3/19 11:01 */ @Service @Slf4j @RequiredArgsConstructor public class ChatGptAccountServiceImpl implements ChatGptAccountService { @Value("${chatgpt.domain}") private String GPT_DOMAIN; @Value("${chatgpt.car.domain}") private String CAR_GPT_DOMAIN; 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 static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance(); private final RedisService redisService; private final ChatgptUserMapper chatgptUserMapper; private final ChatgptSessionMapper chatgptSessionMapper; private final GroupsRelationMapper groupsRelationMapper; private final GroupsMapper groupsMapper; private final GoodsDonSkuMapper skuMapper; private final UserMapper userMapper; private final UserBindRelationService userBindRelationService; private final ChatgptUserConversationRecordMapper chatgptUserConversationRecordMapper; private final ChatgptUserConversationMapper chatgptUserConversationMapper; private final AccountMapper accountMapper; private final BeanSearcher beanSearcher; private final ChatgptUserTokenPrepareMapper chatgptUserTokenPrepareMapper; private final ChatgptUserCarUsedRecordMapper chatgptUserCarUsedRecordMapper; private final CollegeStudentStationUseTicketRecordMapper collegeStudentStationUseTicketRecordMapper; @Override public String addAccount(Account account) { if (isAccountExists(account)) { throw BusinessRuntimeException.getInstance("镜像服务该账号已存在."); } String refreshToken = ""; String officialSession = ""; try { JSONObject loginResult = getLoginResult(account); if (StringUtils.isBlank(loginResult.getStr("accessToken"))) { throw BusinessRuntimeException.getInstance(loginResult.getStr("detail")); } refreshToken = loginResult.getStr("refresh_token"); officialSession = loginResult.toString(); } catch (Exception e) { throw BusinessRuntimeException.getInstance("登录获取token错误 error:" + e.getMessage()); } createChatgptSession(account, officialSession); return refreshToken; } @Override public String getGptSession(String account, String password) { String officialSession = ""; try { Account at = new Account(); at.setAccount(account); at.setPassword(password); JSONObject loginResult = getLoginResult(at); if (StringUtils.isBlank(loginResult.getStr("accessToken"))) { throw BusinessRuntimeException.getInstance(loginResult.getStr("detail")); } officialSession = loginResult.toString(); } catch (Exception e) { throw BusinessRuntimeException.getInstance("登录获取token错误 error:" + e.getMessage()); } return officialSession; } /** * 判断账号是否存在 * * @param account 账号 * @return 是否存在 */ private boolean isAccountExists(Account account) { Integer count = chatgptSessionMapper.selectCount(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getEmail, account.getAccount())); return count > 0; } /** * 获取登录结果 * * @param account * @return * @throws Exception */ private JSONObject getLoginResult(Account account) throws Exception { HttpRequest request = new HttpRequest(GPT_PROXY + "/getsession"); request.form("username", account.getAccount()); request.form("password", account.getPassword()); request.header("Content-Type", "application/x-www-form-urlencoded"); HttpResponse execute = request.method(Method.POST).execute(); return new JSONObject(execute.body()); } /** * 创建chatgptSession * * @param account * @param officialSession */ private void createChatgptSession(Account account, String officialSession) { ChatgptSession chatgptSession = new ChatgptSession(); chatgptSession.setOfficialSession(officialSession); chatgptSession.setEmail(account.getAccount()); chatgptSession.setPassword(account.getPassword()); chatgptSession.setIsPlus(1); chatgptSession.setAccountId(account.getId()); chatgptSession.setStatus(1); chatgptSessionMapper.insert(chatgptSession); } /** * 更新账号 * * @param account */ @Override public void upAccount(Account account) { ChatgptSession chatgptSession = chatgptSessionMapper.selectOne(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getEmail, account.getAccount())); if (chatgptSession != null) { if (StringUtils.isNotBlank(account.getGptRefreshToken())) { chatgptSession.setOfficialSession(account.getGptRefreshToken()); } chatgptSession.setEmail(account.getAccount()); chatgptSession.setPassword(account.getPassword()); chatgptSession.setIsPlus(1); chatgptSession.setAccountId(account.getId()); chatgptSession.setStatus(1); chatgptSessionMapper.updateById(chatgptSession); } } /** * 获取登录url * * @param userId * @param relationId * @return */ @Override public String getLoginUrl(Long userId, Long relationId) { GroupsRelation groupsRelation = getGroupsRelation(userId, relationId); GroupsTrips groupsTrips = getGroupsTrips(groupsRelation); GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId()); log.info("用户:{},所在车次:{},座位id:{},在{}获取跳转GPT镜像的登录url", userId, groupsTrips.getId(), relationId, DateTime.now()); if (goodsDonSku != null && goodsDonSku.getIsMirror()) { ChatgptUser chatgptUser = getChatgptUser(userId, relationId, groupsRelation, groupsTrips, goodsDonSku); if (214610L == relationId) { return "https://cdn.galaxydvd.com/login_token?access_token=" + chatgptUser.getUserToken(); } return GPT_DOMAIN + "/login_token?access_token=" + chatgptUser.getUserToken(); } else { throw BusinessRuntimeException.getInstance("服务器出了点问题"); } } /** * 获取车队登录url * * @param userId * @param carId * @return */ @Override public String getCarLoginUrl(Long userId, Long relationId, String carId) { GroupsRelation groupsRelation = getGroupsRelation(userId, relationId); GroupsTrips groupsTrips = getGroupsTrips(groupsRelation); GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId()); if (goodsDonSku != null && goodsDonSku.getIsMirror() && goodsDonSku.getIsCar()) { ChatgptUser chatgptUser = getChatgptCarUser(userId, relationId, groupsRelation, goodsDonSku); String DOMAIN = "https://chat.galaxydvd.com"; log.info("domain:{},用户:{},所在车次:{},座位id:{},在{}获取跳转GPT镜像的登录url", DOMAIN, userId, groupsTrips.getId(), relationId, DateTime.now()); return DOMAIN + "/auth/logintoken?carid=" + carId + "&usertoken=" + chatgptUser.getUserToken(); } else { throw BusinessRuntimeException.getInstance("服务器出了点问题"); } } /** * 根据userToken获取车队登录url */ @Override public String getCarLoginUrlWithToken(String userToken, String carId) { ChatgptUser chatgptUser = getCarChatGptUserWithToken(userToken); if (chatgptUser != null) { String DOMAIN = "https://chat.galaxydvd.com"; log.info("domain:{},渠道用户:{},在{}获取跳转GPT镜像的登录url", DOMAIN, userToken, DateTime.now()); return DOMAIN + "/auth/logintoken?carid=" + carId + "&usertoken=" + chatgptUser.getUserToken(); } else { throw BusinessRuntimeException.getInstance("服务出了点问题"); } } /** * 获取车位信息 * * @param userId * @param relationId * @return */ private GroupsRelation getGroupsRelation(Long userId, Long relationId) { List userIdList = userBindRelationService.getRelationUserIdList(userId, null); GroupsRelation groupsRelation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class).in(GroupsRelation::getUserId, userIdList).eq(GroupsRelation::getId, relationId)); if (groupsRelation == null) { throw BusinessRuntimeException.getInstance("车票不存在"); } return groupsRelation; } /** * 获取车队信息 * * @param groupsRelation * @return */ private GroupsTrips getGroupsTrips(GroupsRelation groupsRelation) { GroupsTrips groupsTrips = groupsMapper.selectById(groupsRelation.getGroupsId()); if (groupsTrips == null) { throw BusinessRuntimeException.getInstance("车队异常"); } return groupsTrips; } private ChatgptUser getChatgptUser(Long userId, Long relationId, GroupsRelation groupsRelation, GroupsTrips groupsTrips, GoodsDonSku goodsDonSku) { ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getRelationId, relationId)); User user = userMapper.selectById(userId); if (chatgptUser == null) { chatgptUser = createChatgptUser(groupsRelation, groupsTrips, user, goodsDonSku); } else { updateChatgptUser(groupsRelation, user, chatgptUser); } return chatgptUser; } private ChatgptUser getChatgptCarUser(Long userId, Long relationId, GroupsRelation groupsRelation, GoodsDonSku goodsDonSku) { ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getRelationId, relationId)); User user = userMapper.selectById(userId); if (chatgptUser == null) { chatgptUser = createChatgptUser(groupsRelation, null, user, goodsDonSku); } else { updateChatgptUser(groupsRelation, user, chatgptUser); } return chatgptUser; } /** * 创建chatgptUser */ private synchronized ChatgptUser createChatgptUser(GroupsRelation groupsRelation, GroupsTrips groupsTrips, User user, GoodsDonSku goodsDonSku) { ChatgptUser chatgptUser = new ChatgptUser(); if (!goodsDonSku.getIsCar()) { ChatgptSession chatgptSession = chatgptSessionMapper.selectOne(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getAccountId, groupsTrips.getAccountId())); if (chatgptSession == null) { Account account = accountMapper.selectById(groupsTrips.getAccountId()); String officialSession = ""; try { JSONObject loginResult = getLoginResult(account); if (StringUtils.isBlank(loginResult.getStr("accessToken"))) { throw BusinessRuntimeException.getInstance(loginResult.getStr("detail")); } officialSession = loginResult.toString(); } catch (Exception e) { throw BusinessRuntimeException.getInstance("配置账号错误 msg: " + e.getMessage()); } createChatgptSession(account, officialSession); chatgptSession = chatgptSessionMapper.selectOne(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getAccountId, groupsTrips.getAccountId())); } chatgptUser.setSessionId(chatgptSession.getId()); } if (goodsDonSku.getIsCar()) { chatgptUser.setIsCar(true); chatgptUser.setLimitNum(goodsDonSku.getGptLimitNum()); chatgptUser.setLimitTime(goodsDonSku.getGptLimitTime()); } try { chatgptUser.setExpireTime(groupsRelation.getExpiryTime()); chatgptUser.setIsPlus(1); chatgptUser.setName(user.getNickname()); chatgptUser.setImg(getWxImg(user.getHeadimgurl(), user)); chatgptUser.setRelationId(groupsRelation.getId()); chatgptUser.setUserToken(UUID.randomUUID().toString()); chatgptUserMapper.insert(chatgptUser); //是否是大学生限时车队 Date expiryTime = groupsRelation.getExpiryTime(); Date startTime = groupsRelation.getStartTime(); //大学生站 1天免费试用 if (DateUtil.between(startTime, expiryTime, DateUnit.DAY) <= 1) { CollegeStudentStationUseTicketRecord useTicketRecord = new CollegeStudentStationUseTicketRecord(); useTicketRecord.setUserId(user.getId()); useTicketRecord.setUserToken(chatgptUser.getUserToken()); collegeStudentStationUseTicketRecordMapper.insert(useTicketRecord); } } catch (DuplicateKeyException e) { log.error("重复插入镜像用户:{}token,errmsg:{}", user.getNickname(), StringUtil.getErrorText(e)); } return chatgptUser; } /** * 创建chatgptUser 无需车队 */ public ChatgptUser generateChatGptUserUnderPrepare(ChatgptUserTokenPrepare prepare) { ChatgptUser chatgptUser = new ChatgptUser(); GoodsDonSku goodsDonSku = skuMapper.selectById(prepare.getSkuId()); if (goodsDonSku == null) { throw BusinessRuntimeException.getInstance("车队规格不存在,请联系客服.."); } if (goodsDonSku.getIsCar()) { chatgptUser.setIsCar(true); chatgptUser.setLimitNum(goodsDonSku.getGptLimitNum()); chatgptUser.setLimitTime(goodsDonSku.getGptLimitTime()); } try { chatgptUser.setExpireTime(DateUtil.offsetMonth(DateTime.now(), goodsDonSku.getMonths())); chatgptUser.setIsPlus(1); chatgptUser.setName(prepare.getName()); chatgptUser.setImg(prepare.getImg()); chatgptUser.setUserToken(prepare.getUserToken()); chatgptUserMapper.insert(chatgptUser); prepare.setStatus(true); chatgptUserTokenPrepareMapper.updateById(prepare); log.info("用户激活userToken:{}成功", prepare.getUserToken()); } catch (DuplicateKeyException e) { log.error("重复插入镜像车队用户:{}token,errmsg:{}" + chatgptUser.getId(), StringUtil.getErrorText(e)); } return chatgptUser; } private void updateChatgptUser(GroupsRelation groupsRelation, User user, ChatgptUser chatgptUser) { chatgptUser.setName(user.getNickname()); chatgptUser.setImg(getWxImg(user.getHeadimgurl(), user)); chatgptUser.setExpireTime(groupsRelation.getExpiryTime()); chatgptUserMapper.updateById(chatgptUser); } /** * 更换wx头像至oss */ private String getWxImg(String headimgurl, User user) { if (headimgurl.contains("thirdwx.qlogo.cn")) { try { byte[] body = HttpUtil.downloadBytes(headimgurl); Path tempFile = Files.createTempFile("wxheadimg-" + user.getId(), ".jpeg"); try (FileImageOutputStream imageOutput = new FileImageOutputStream(tempFile.toFile())) { imageOutput.write(body, 0, body.length); } Map paramMap = new HashMap<>(); paramMap.put("file", tempFile.toFile()); JSONObject result = JSONUtil.parseObj(HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap)); return result.getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl"); } catch (Exception ex) { log.error("上传微信头像错误!msg:{}", StringUtil.getErrorText(ex)); return "./avatars.png"; } } else { return user.getHeadimgurl(); } } @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 void saveConversationRecord(String userToken, ConversationRequest conversationRequest) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(ChatgptSession.class); ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getUserToken, userToken)); if (StringUtils.isNotBlank(conversationRequest.getCarId())) { wrapper.eq(ChatgptSession::getCarId, conversationRequest.getCarId()); } else { if (chatgptUser.getSessionId() != null) { wrapper.eq(ChatgptSession::getId, chatgptUser.getSessionId()); } } ChatgptSession chatgptSession = chatgptSessionMapper.selectOne(wrapper); ChatgptUserConversationRecord chatgptUserConversationRecord = new ChatgptUserConversationRecord(); chatgptUserConversationRecord.setUserToken(userToken); chatgptUserConversationRecord.setCarId(chatgptSession.getCarId()); chatgptUserConversationRecord.setCarName(chatgptSession.getCarName()); 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); if(!"text-davinci-002-render-sha".equals(conversationRequest.getModel())){ String key = RedisService.key.CHATGPT_CONVERSATION_LIMIT.getName() + ":" + userToken; long currentTimeMillis = System.currentTimeMillis(); redisService.del(); if(redisService.hasKey("chatgpt:team:clears_in:" + chatgptSession.getCarId())){ redisService.del("chatgpt:team:clears_in:" + chatgptSession.getCarId()); } if (redisService.hasKey("chatgpt:clears_in:" + chatgptSession.getCarId())) { redisService.del("chatgpt:clears_in:" + chatgptSession.getCarId()); } // 如果未超过限制,记录当前请求的时间戳 redisService.zAdd(key, currentTimeMillis, currentTimeMillis); // 设置ZSet的过期时间,窗口大小加上一段冗余时间 redisService.expire(key, (chatgptUser.getLimitTime() * 60 * 60) + 20); } if (StringUtils.isNotBlank(chatgptSession.getCarId())) { updateExperienceAndScore(chatgptSession.getCarId(), !"text-davinci-002-render-sha".equals(conversationRequest.getModel()), System.currentTimeMillis()); } } @Override public ConversationLimitResponse conversationLimit(String userToken, String model, String carId, ChatgptUser chatgptUser, Boolean isCar) { ConversationLimitResponse conversationLimitResponse = new ConversationLimitResponse(); conversationLimitResponse.setLimited(false); if (!"text-davinci-002-render-sha".equals(model)) { //如果不为车队 直接返回 if (chatgptUser.getIsCar()) { conversationLimitResponse = isConversationAllowed(userToken, chatgptUser.getLimitNum(), chatgptUser.getLimitTime()); } } return conversationLimitResponse; } /** * 检查是否允许进行进行提问 * * @param userToken 用户token * @return true 如果允许请求,false 如果请求被限制 */ public ConversationLimitResponse 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; ConversationLimitResponse conversationLimitResponse = new ConversationLimitResponse(); // 清除时间窗口之前的请求记录 redisService.zRemoveRangeByScore(key, 0, windowStartMillis); Long currentSize = redisService.zCard(key); if (currentSize != null && currentSize >= limit) { // 如果当前请求次数超过限制,则拒绝请求 Set times = redisService.zRangeByScore(key, 0, currentTimeMillis, 0, 1); Long oldestTime = (Long) times.stream().findFirst().orElse(null); if (oldestTime != null) { // 下一次可用时间是最早请求时间之后的3小时 long nextAvailableTime = oldestTime + (limitTime * 60 * 60 * 1000); conversationLimitResponse.setNextAvailableTime(nextAvailableTime); } conversationLimitResponse.setLimited(true); //用户次数使用限制 ChatgptUserCarUsedRecord chatgptUserCarUsedRecord = new ChatgptUserCarUsedRecord(); chatgptUserCarUsedRecord.setUserToken(userToken); chatgptUserCarUsedRecord.setIsUserLimit(true); chatgptUserCarUsedRecordMapper.insert(chatgptUserCarUsedRecord); return conversationLimitResponse; } else { conversationLimitResponse.setLimited(false); return conversationLimitResponse; } } /** * 获取指定用户ID在滑动窗口内的提问次数 * * @param userToken 用户token * @return 滑动窗口内的请求次数 */ @Override 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; } /** * 获取指定车队ID在滑动窗口内的提问次数 * * @param carId 车队ID * @return 滑动窗口内的请求次数 */ private Long getCarConversationCount(String carId, Long limitTime) { // 定义键名 String key = RedisService.key.CHATGPT_CAR_HIGH_CHAT.getName() + carId; long timestamp = System.currentTimeMillis(); long windowStartMillis = timestamp - (limitTime * 60 * 60 * 1000); // 清除时间窗口之前的请求记录(可选,根据需要决定是否在此处清理过期记录) redisService.zRemoveRangeByScore(key, 0, windowStartMillis); // 获取当前窗口内的请求次数 Long currentSize = redisService.zCard(key); return currentSize != null ? currentSize : 0L; } @Override public ChatgptSession checkSession(String carId) { ChatgptSession chatgptSession = chatgptSessionMapper.selectOne(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getCarId, carId).eq(ChatgptSession::getIsCar, true).last(" limit 1")); if (chatgptSession == null) { throw BusinessRuntimeException.getInstance("车队不存在"); } return chatgptSession; } private Long carOaiLimit(String carId, Boolean isTeam) { if(isTeam && redisService.hasKey("chatgpt:team:clears_in:" + carId)){ return redisService.getExpire("chatgpt:team:clears_in:" + carId); } if (redisService.hasKey("chatgpt:clears_in:" + carId)) { return redisService.getExpire("chatgpt:clears_in:" + carId); } return 0L; } @Override public Boolean checkCarAccount(long userId, Long relationId) { GroupsRelation groupsRelation = getGroupsRelation(userId, relationId); GroupsTrips groupsTrips = getGroupsTrips(groupsRelation); GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId()); if (goodsDonSku != null && goodsDonSku.getIsMirror() && goodsDonSku.getIsCar()) { ChatgptUser chatgptUser = getChatgptCarUser(userId, relationId, groupsRelation, goodsDonSku); return true; } else { return false; } } @Override public Boolean checkCarAccountWithToken(String userToken) { ChatgptUser chatgptUser = getCarChatGptUserWithToken(userToken); return chatgptUser != null; } @Override public ChatgptUser getCarChatGptUser(long userId, Long relationId) { GroupsRelation groupsRelation = getGroupsRelation(userId, relationId); GroupsTrips groupsTrips = getGroupsTrips(groupsRelation); GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId()); if (goodsDonSku != null && goodsDonSku.getIsMirror() && goodsDonSku.getIsCar()) { return getChatgptCarUser(userId, relationId, groupsRelation, goodsDonSku); } return null; } @Override public ChatgptUser getCarChatGptUserWithToken(String userToken) { ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getUserToken, userToken).last(" limit 1")); if (chatgptUser == null) { //是否是用户token预备账号 ChatgptUserTokenPrepare chatgptUserTokenPrepare = chatgptUserTokenPrepareMapper.selectOne(Wrappers.lambdaQuery(ChatgptUserTokenPrepare.class) .eq(ChatgptUserTokenPrepare::getUserToken, userToken).last("limit 1")); if (chatgptUserTokenPrepare != null) { chatgptUser = generateChatGptUserUnderPrepare(chatgptUserTokenPrepare); } } if (chatgptUser != null) { if(!chatgptUser.getIsCar()){ throw BusinessRuntimeException.getInstance("您非车队用户,请购买车队套餐"); } if (chatgptUser.getRelationId() != null) { throw BusinessRuntimeException.getInstance("非渠道用户,请通过官网登录!"); } return chatgptUser; } return null; } /** * 获取车队信息 */ @Override public Set getCarInfoList(String userToken, Integer limit, Boolean isPlus) { SearchResult search = beanSearcher.search(ChatGptUserConversationRecordHistoryView.class, MapUtils.builder().field("userToken", userToken).limit(0, 2).build()); Set res = search.getDataList().stream() .map((historyView) -> { ChatgptCarInfoView chatgptCarInfoView = buildChatgptCarInfoView(historyView.getCarId(), historyView.getCarName(), historyView.getIsPLus(), redisService.zScore(RedisService.key.CHATGPT_CAR_SCORES.getName(), historyView.getCarId())); chatgptCarInfoView.setIsHistory(true); chatgptCarInfoView.setScore(Math.min(redisService.zScore(RedisService.key.CHATGPT_CAR_SCORES.getName(), historyView.getCarId()), 200)); return chatgptCarInfoView; }).collect(Collectors.toCollection(LinkedHashSet::new)); Set lowestScoreFleets = getLowestScoreFleets(limit, isPlus); res.addAll(lowestScoreFleets); return res; } // 更新体验并计算评分 public void updateExperienceAndScore(String carId, boolean isHighLevel, long timestamp) { // 定义键名 String key = isHighLevel ? RedisService.key.CHATGPT_CAR_HIGH_CHAT.getName() + carId : RedisService.key.CHATGPT_CAR_LOW_CHAT.getName() + carId; double scoreToAdd = isHighLevel ? 2.0 : 1.0; // 分数更新规则 // 更新体验数据 redisService.zAdd(key, timestamp, String.valueOf(timestamp)); // 清理旧数据(可选)和重新计算评分(根据需要实现) cleanupOldExperiencesAndRecalculateScore(carId, timestamp); } // 清理旧数据和重新计算评分 public void cleanupOldExperiencesAndRecalculateScore(String carId, long currentTimestamp) { long threeHoursAgo = currentTimestamp - (3 * 60 * 60 * 1000); // 3小时前的时间戳 // 清理高级体验旧数据 redisService.zRemoveRangeByScore(RedisService.key.CHATGPT_CAR_HIGH_CHAT.getName() + carId, 0, threeHoursAgo); // 清理低级体验旧数据 redisService.zRemoveRangeByScore(RedisService.key.CHATGPT_CAR_LOW_CHAT.getName() + carId, 0, threeHoursAgo); // 重新计算评分 recalculateScore(carId, currentTimestamp); } // 重新计算指定车队的评分 private void recalculateScore(String carId, long currentTimestamp) { // 实际应用中,你需要根据高级体验和低级体验的数量重新计算得分 Double highExperienceScore = (double) redisService.zCount(RedisService.key.CHATGPT_CAR_HIGH_CHAT.getName() + carId, currentTimestamp - (3 * 60 * 60 * 1000), currentTimestamp) * 2; Double lowExperienceScore = (double) redisService.zCount(RedisService.key.CHATGPT_CAR_LOW_CHAT.getName() + carId, currentTimestamp - (3 * 60 * 60 * 1000), currentTimestamp); double newScore = highExperienceScore + lowExperienceScore; if(carId.contains("T")){ newScore = BigDecimal.valueOf(newScore).divide(BigDecimal.valueOf(5), 2, BigDecimal.ROUND_HALF_UP).doubleValue(); } redisService.zAdd(RedisService.key.CHATGPT_CAR_SCORES.getName(), newScore, carId); } // 获取得分最低的N个车队的方法 public Set getLowestScoreFleets(int count, Boolean isPlus) { Set> lowestScorecarIds; Long fleetsSize = redisService.zCard(RedisService.key.CHATGPT_CAR_SCORES.getName()); // 构建ChatgptCarInfoView集合 if (fleetsSize != null && fleetsSize > 10) { TASK_EXECUTOR.execute(() -> { initFleets(true); }); } else { initFleets(false); } lowestScorecarIds = redisService.zRangeWithScores(RedisService.key.CHATGPT_CAR_SCORES.getName(), 0L, count - 1); Map collect = lowestScorecarIds.stream().collect(Collectors.toMap(ZSetOperations.TypedTuple::getValue, ZSetOperations.TypedTuple::getScore)); List chatgptSessions = chatgptSessionMapper.selectList(Wrappers.lambdaQuery(ChatgptSession.class).in(ChatgptSession::getCarId, collect.keySet()).eq(isPlus !=null && isPlus, ChatgptSession::getIsPlus, true)); return chatgptSessions.stream().map((chatgptSession)-> buildChatgptCarInfoView(chatgptSession.getCarId(),chatgptSession.getCarName(), chatgptSession.getIsPlus(), collect.get(chatgptSession.getCarId()))) .sorted(Comparator.comparing(ChatgptCarInfoView::getScore)).collect(Collectors.toCollection(LinkedHashSet::new)); } public void initFleets(Boolean flag) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(ChatgptSession.class); if (flag) { Set> fleets = redisService.zRangeWithScores(RedisService.key.CHATGPT_CAR_SCORES.getName(), 0L, -1); List collect = fleets.stream().map(ZSetOperations.TypedTuple::getValue).collect(Collectors.toList()); wrapper.notIn(collect.size() > 0, ChatgptSession::getCarId, collect); } //如果存在未存在redis中的车辆则同步进 chatgpt:car:scores List chatgptSessions = chatgptSessionMapper.selectList(wrapper.eq(ChatgptSession::getIsCar, true)); chatgptSessions.forEach((chatgptSession) -> { if (!redisService.checkValueExistsInZSet(RedisService.key.CHATGPT_CAR_SCORES.getName(), chatgptSession.getCarId())) { redisService.zAdd(RedisService.key.CHATGPT_CAR_SCORES.getName(), 0, chatgptSession.getCarId()); } }); } // 构建ChatgptCarInfoView对象 private ChatgptCarInfoView buildChatgptCarInfoView(String carId, String carName, Integer isPlus, Double score) { // 在这里根据carId获取相关信息并填充到ChatgptCarInfoView对象中 ChatgptCarInfoView view = new ChatgptCarInfoView(); view.setCarId(carId); // 假设以下方法从Redis或其他服务获取数据 view.setScore(Math.min(score, 200)); view.setStatus(score >= 50 ? "繁忙" : "空闲"); // 或“繁忙” if(carName.contains("T")){ view.setType("Team"); Long team = carOaiLimit(carId, true); if (team != 0L) { view.setTeamExpTime(new DateTime(System.currentTimeMillis() + team * 1000)); } Long aLong = carOaiLimit(carId, false); if (aLong != 0L) { view.setExpTime(new DateTime(System.currentTimeMillis() + aLong * 1000)); } if (view.getExpTime() != null && view.getTeamExpTime() != null) { view.setStatus("全部停运"); }else if(view.getExpTime() != null || view.getTeamExpTime() != null) { view.setStatus("部分停运"); } }else { view.setType(isPlus == 1 ? "Plus" : "3.5"); Long aLong = carOaiLimit(carId, false); if (aLong != 0L) { view.setStatus("停运"); view.setExpTime(new DateTime(System.currentTimeMillis() + aLong * 1000)); } } int use = getCarConversationCount(carId, 3L).intValue(); view.setUse(use); view.setCarName(carName); return view; } @Override public ChatGptUserView getUserInfo(long userId, Long relationId) { if (checkCarAccount(userId, relationId)) { GroupsRelation groupsRelation = getGroupsRelation(userId, relationId); GroupsTrips groupsTrips = getGroupsTrips(groupsRelation); GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId()); ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getRelationId, relationId)); return ChatGptUserView.builder() .skuName(goodsDonSku.getSubTitle()) .expireTime(chatgptUser.getExpireTime()) .limitNum(chatgptUser.getLimitNum()) .limitTime(chatgptUser.getLimitTime()) .use(Math.min(chatgptUser.getLimitNum(),getConversationCount(chatgptUser.getUserToken(), chatgptUser.getLimitTime()))) .build(); } else { throw BusinessRuntimeException.getInstance("您还未购买该车票"); } } @Override public ChatGptUserView getUserInfoWithToken(String userToken) { if (checkCarAccountWithToken(userToken)) { ChatgptUser chatgptUser = getCarChatGptUserWithToken(userToken); return ChatGptUserView.builder() .skuName("") .expireTime(chatgptUser.getExpireTime()) .limitNum(chatgptUser.getLimitNum()) .limitTime(chatgptUser.getLimitTime()) .use(getConversationCount(chatgptUser.getUserToken(), chatgptUser.getLimitTime())) .build(); } else { throw BusinessRuntimeException.getInstance("您还未购买该车票"); } } @Override public void genTitleSync(String conversationId, String carid, String userToken) { //同步对话 if (!StringUtils.isAnyBlank(conversationId, carid, userToken)) { ChatgptUserConversationRecord chatgptUserConversationRecord = chatgptUserConversationRecordMapper.selectOne(Wrappers.lambdaQuery(ChatgptUserConversationRecord.class) .isNull(ChatgptUserConversationRecord::getConversationId) .eq(ChatgptUserConversationRecord::getCarId, carid) .eq(ChatgptUserConversationRecord::getUserToken, userToken) .orderByDesc(ChatgptUserConversationRecord::getId) .last(" limit 1")); if (chatgptUserConversationRecord != null) { chatgptUserConversationRecord.setConversationId(conversationId); chatgptUserConversationRecordMapper.updateById(chatgptUserConversationRecord); } } } @Override public void carLimited(String carId, String userToken, Long expTime, Boolean isTeam) { if (carId == null) { if (StringUtils.isNotBlank(userToken)) { ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getUserToken, userToken)); if (chatgptUser != null && chatgptUser.getSessionId() != null) { ChatgptSession chatgptSession = chatgptSessionMapper.selectById(chatgptUser.getSessionId()); if (chatgptSession != null) { carId = chatgptSession.getCarId(); } } } } if(isTeam){ redisService.set("chatgpt:team:clears_in:" + carId, expTime, expTime); }else { redisService.set("chatgpt:clears_in:" + carId, expTime, expTime); } try { //记录用户触发限制 ChatgptUserCarUsedRecord chatgptUserCarUsedRecord = new ChatgptUserCarUsedRecord(); chatgptUserCarUsedRecord.setUserToken(userToken); chatgptUserCarUsedRecord.setCarId(carId); String highCarChatKey = RedisService.key.CHATGPT_CAR_HIGH_CHAT.getName() + carId; String lowCarChatKey = RedisService.key.CHATGPT_CAR_LOW_CHAT.getName() + carId; Long highCarChatNum = redisService.zCard(highCarChatKey); if (highCarChatNum != null) { chatgptUserCarUsedRecord.setGptFour(Integer.parseInt(highCarChatNum.toString())); } Long lowCarChatNum = redisService.zCard(lowCarChatKey); if (lowCarChatNum != null) { chatgptUserCarUsedRecord.setGpt(Integer.parseInt(lowCarChatNum.toString())); } chatgptUserCarUsedRecordMapper.insert(chatgptUserCarUsedRecord); log.info("记录userToken:{}访问对应镜像车队carId:{}操作成功", userToken, carId); } catch (Exception e) { log.info("记录userToken:{}访问对应镜像车队carId:{}操作失败e:{}", userToken, carId, StringUtil.getErrorText(e)); } } @PostConstruct public void init() { //如果存在未存在redis中的车辆则同步进 chatgpt:car:scores TASK_EXECUTOR.execute(() -> { Set> lowestScorecarIds = redisService.zRangeWithScores(RedisService.key.CHATGPT_CAR_SCORES.getName(), 0L, -1); List collect = lowestScorecarIds.stream().map(ZSetOperations.TypedTuple::getValue).collect(Collectors.toList()); List chatgptSessions = chatgptSessionMapper.selectList(Wrappers.lambdaQuery(ChatgptSession.class).notIn(collect.size() > 0, ChatgptSession::getCarId, collect).eq(ChatgptSession::getIsPlus, 1).eq(ChatgptSession::getIsCar, true)); chatgptSessions.forEach((chatgptSession) -> { if (!redisService.checkValueExistsInZSet(RedisService.key.CHATGPT_CAR_SCORES.getName(), chatgptSession.getCarId())) { redisService.zAdd(RedisService.key.CHATGPT_CAR_SCORES.getName(), 0, chatgptSession.getCarId()); } }); }); } }