ChatGptAccountServiceImpl.java 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. package com.cyksj.service.chatgpt.impl;
  2. import cn.hutool.core.date.DateTime;
  3. import cn.hutool.core.lang.UUID;
  4. import cn.hutool.http.HttpRequest;
  5. import cn.hutool.http.HttpResponse;
  6. import cn.hutool.http.HttpUtil;
  7. import cn.hutool.http.Method;
  8. import cn.hutool.json.JSONArray;
  9. import cn.hutool.json.JSONObject;
  10. import cn.hutool.json.JSONUtil;
  11. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  12. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  13. import com.cyksj.common.exception.BusinessRuntimeException;
  14. import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
  15. import com.cyksj.common.util.StringUtil;
  16. import com.cyksj.mapper.*;
  17. import com.cyksj.model.entity.*;
  18. import com.cyksj.model.request.gpt.ConversationRequest;
  19. import com.cyksj.model.response.ConversationLimitResponse;
  20. import com.cyksj.model.views.ChatGptUserConversationRecordHistoryView;
  21. import com.cyksj.model.views.ChatGptUserView;
  22. import com.cyksj.model.views.ChatgptCarInfoView;
  23. import com.cyksj.redis.RedisService;
  24. import com.cyksj.service.chatgpt.ChatGptAccountService;
  25. import com.cyksj.service.user.UserBindRelationService;
  26. import com.ejlchina.searcher.BeanSearcher;
  27. import com.ejlchina.searcher.util.MapUtils;
  28. import lombok.RequiredArgsConstructor;
  29. import lombok.extern.slf4j.Slf4j;
  30. import org.apache.commons.lang3.StringUtils;
  31. import org.springframework.beans.factory.annotation.Value;
  32. import org.springframework.data.redis.core.ZSetOperations;
  33. import org.springframework.stereotype.Service;
  34. import javax.annotation.PostConstruct;
  35. import javax.imageio.stream.FileImageOutputStream;
  36. import java.nio.file.Files;
  37. import java.nio.file.Path;
  38. import java.time.LocalDateTime;
  39. import java.util.HashMap;
  40. import java.util.List;
  41. import java.util.Map;
  42. import java.util.Set;
  43. import java.util.stream.Collectors;
  44. /**
  45. * @author chan
  46. * @date 2024/3/19 11:01
  47. */
  48. @Service
  49. @Slf4j
  50. @RequiredArgsConstructor
  51. public class ChatGptAccountServiceImpl implements ChatGptAccountService {
  52. @Value("${chatgpt.domain}")
  53. private String GPT_DOMAIN;
  54. @Value("${chatgpt.car.domain}")
  55. private String CAR_GPT_DOMAIN;
  56. private static final String GPT_PROXY = "https://chat-chan-87jztgkf257d.xyhelper.net";
  57. private static final int MAX_REQUESTS = 40; // 3小时内最大请求次数
  58. private static final long WINDOW_SIZE = 3 * 60 * 60; // 3小时窗口的秒数
  59. private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
  60. private final RedisService redisService;
  61. private final ChatgptUserMapper chatgptUserMapper;
  62. private final ChatgptSessionMapper chatgptSessionMapper;
  63. private final GroupsRelationMapper groupsRelationMapper;
  64. private final GroupsMapper groupsMapper;
  65. private final GoodsDonSkuMapper skuMapper;
  66. private final UserMapper userMapper;
  67. private final UserBindRelationService userBindRelationService;
  68. private final ChatgptUserConversationRecordMapper chatgptUserConversationRecordMapper;
  69. private final ChatgptUserConversationMapper chatgptUserConversationMapper;
  70. private final AccountMapper accountMapper;
  71. private final BeanSearcher beanSearcher;
  72. @Override
  73. public String addAccount(Account account) {
  74. if (isAccountExists(account)) {
  75. throw BusinessRuntimeException.getInstance("镜像服务该账号已存在.");
  76. }
  77. String refreshToken = "";
  78. String officialSession = "";
  79. try {
  80. JSONObject loginResult = getLoginResult(account);
  81. if (StringUtils.isBlank(loginResult.getStr("accessToken"))) {
  82. throw BusinessRuntimeException.getInstance(loginResult.getStr("detail"));
  83. }
  84. refreshToken = loginResult.getStr("refresh_token");
  85. officialSession = loginResult.toString();
  86. } catch (Exception e) {
  87. throw BusinessRuntimeException.getInstance("登录获取token错误 error:" + e.getMessage());
  88. }
  89. createChatgptSession(account, officialSession);
  90. return refreshToken;
  91. }
  92. /**
  93. * 判断账号是否存在
  94. *
  95. * @param account 账号
  96. * @return 是否存在
  97. */
  98. private boolean isAccountExists(Account account) {
  99. Integer count = chatgptSessionMapper.selectCount(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getEmail, account.getAccount()));
  100. return count > 0;
  101. }
  102. /**
  103. * 获取登录结果
  104. *
  105. * @param account
  106. * @return
  107. * @throws Exception
  108. */
  109. private JSONObject getLoginResult(Account account) throws Exception {
  110. HttpRequest request = new HttpRequest(GPT_PROXY + "/getsession");
  111. request.form("username", account.getAccount());
  112. request.form("password", account.getPassword());
  113. request.header("Content-Type", "application/x-www-form-urlencoded");
  114. HttpResponse execute = request.method(Method.POST).execute();
  115. return new JSONObject(execute.body());
  116. }
  117. /**
  118. * 创建chatgptSession
  119. *
  120. * @param account
  121. * @param officialSession
  122. */
  123. private void createChatgptSession(Account account, String officialSession) {
  124. ChatgptSession chatgptSession = new ChatgptSession();
  125. chatgptSession.setOfficialSession(officialSession);
  126. chatgptSession.setEmail(account.getAccount());
  127. chatgptSession.setPassword(account.getPassword());
  128. chatgptSession.setIsPlus(1);
  129. chatgptSession.setAccountId(account.getId());
  130. chatgptSession.setStatus(1);
  131. chatgptSessionMapper.insert(chatgptSession);
  132. }
  133. /**
  134. * 更新账号
  135. *
  136. * @param account
  137. */
  138. @Override
  139. public void upAccount(Account account) {
  140. ChatgptSession chatgptSession = chatgptSessionMapper.selectOne(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getEmail, account.getAccount()));
  141. if (chatgptSession != null) {
  142. if (StringUtils.isNotBlank(account.getGptRefreshToken())) {
  143. chatgptSession.setOfficialSession(account.getGptRefreshToken());
  144. }
  145. chatgptSession.setEmail(account.getAccount());
  146. chatgptSession.setPassword(account.getPassword());
  147. chatgptSession.setIsPlus(1);
  148. chatgptSession.setAccountId(account.getId());
  149. chatgptSession.setStatus(1);
  150. chatgptSessionMapper.updateById(chatgptSession);
  151. }
  152. }
  153. /**
  154. * 获取登录url
  155. *
  156. * @param userId
  157. * @param relationId
  158. * @return
  159. */
  160. @Override
  161. public String getLoginUrl(Long userId, Long relationId) {
  162. GroupsRelation groupsRelation = getGroupsRelation(userId, relationId);
  163. GroupsTrips groupsTrips = getGroupsTrips(groupsRelation);
  164. GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId());
  165. if (goodsDonSku != null && goodsDonSku.getIsMirror()) {
  166. ChatgptUser chatgptUser = getChatgptUser(userId, relationId, groupsRelation, groupsTrips, goodsDonSku);
  167. return GPT_DOMAIN + "/login_token?access_token=" + chatgptUser.getUserToken();
  168. } else {
  169. throw BusinessRuntimeException.getInstance("服务器出了点问题");
  170. }
  171. }
  172. /**
  173. * 获取车队登录url
  174. *
  175. * @param userId
  176. * @param carId
  177. * @return
  178. */
  179. @Override
  180. public String getCarLoginUrl(Long userId, Long relationId, String carId) {
  181. GroupsRelation groupsRelation = getGroupsRelation(userId, relationId);
  182. GroupsTrips groupsTrips = getGroupsTrips(groupsRelation);
  183. GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId());
  184. if (goodsDonSku != null && goodsDonSku.getIsMirror() && goodsDonSku.getIsCar()) {
  185. ChatgptUser chatgptUser = getChatgptCarUser(userId, relationId, groupsRelation, goodsDonSku);
  186. return CAR_GPT_DOMAIN + "/auth/logintoken?carid=" + carId + "&usertoken=" + chatgptUser.getUserToken();
  187. } else {
  188. throw BusinessRuntimeException.getInstance("服务器出了点问题");
  189. }
  190. }
  191. /**
  192. * 获取车位信息
  193. *
  194. * @param userId
  195. * @param relationId
  196. * @return
  197. */
  198. private GroupsRelation getGroupsRelation(Long userId, Long relationId) {
  199. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  200. GroupsRelation groupsRelation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class).in(GroupsRelation::getUserId, userIdList).eq(GroupsRelation::getId, relationId));
  201. if (groupsRelation == null) {
  202. throw BusinessRuntimeException.getInstance("车票不存在");
  203. }
  204. return groupsRelation;
  205. }
  206. /**
  207. * 获取车队信息
  208. *
  209. * @param groupsRelation
  210. * @return
  211. */
  212. private GroupsTrips getGroupsTrips(GroupsRelation groupsRelation) {
  213. GroupsTrips groupsTrips = groupsMapper.selectById(groupsRelation.getGroupsId());
  214. if (groupsTrips == null) {
  215. throw BusinessRuntimeException.getInstance("车队异常");
  216. }
  217. return groupsTrips;
  218. }
  219. private ChatgptUser getChatgptUser(Long userId, Long relationId, GroupsRelation groupsRelation, GroupsTrips groupsTrips, GoodsDonSku goodsDonSku) {
  220. ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getRelationId, relationId));
  221. User user = userMapper.selectById(userId);
  222. if (chatgptUser == null) {
  223. chatgptUser = createChatgptUser(groupsRelation, groupsTrips, user, goodsDonSku);
  224. } else {
  225. updateChatgptUser(groupsRelation, user, chatgptUser);
  226. }
  227. return chatgptUser;
  228. }
  229. private ChatgptUser getChatgptCarUser(Long userId, Long relationId, GroupsRelation groupsRelation, GoodsDonSku goodsDonSku) {
  230. ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getRelationId, relationId));
  231. User user = userMapper.selectById(userId);
  232. if (chatgptUser == null) {
  233. chatgptUser = createChatgptUser(groupsRelation, null, user, goodsDonSku);
  234. } else {
  235. updateChatgptUser(groupsRelation, user, chatgptUser);
  236. }
  237. return chatgptUser;
  238. }
  239. /**
  240. * 创建chatgptUser
  241. */
  242. private ChatgptUser createChatgptUser(GroupsRelation groupsRelation, GroupsTrips groupsTrips, User user, GoodsDonSku goodsDonSku) {
  243. ChatgptUser chatgptUser = new ChatgptUser();
  244. if (!goodsDonSku.getIsCar()) {
  245. ChatgptSession chatgptSession = chatgptSessionMapper.selectOne(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getAccountId, groupsTrips.getAccountId()));
  246. if (chatgptSession == null) {
  247. Account account = accountMapper.selectById(groupsTrips.getAccountId());
  248. String officialSession = "";
  249. try {
  250. JSONObject loginResult = getLoginResult(account);
  251. if (StringUtils.isBlank(loginResult.getStr("accessToken"))) {
  252. throw BusinessRuntimeException.getInstance(loginResult.getStr("detail"));
  253. }
  254. officialSession = loginResult.toString();
  255. } catch (Exception e) {
  256. throw BusinessRuntimeException.getInstance("配置账号错误 msg: " + e.getMessage());
  257. }
  258. createChatgptSession(account, officialSession);
  259. chatgptSession = chatgptSessionMapper.selectOne(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getAccountId, groupsTrips.getAccountId()));
  260. }
  261. chatgptUser.setSessionId(chatgptSession.getId());
  262. }
  263. if (goodsDonSku.getIsCar()) {
  264. chatgptUser.setIsCar(true);
  265. chatgptUser.setLimitNum(goodsDonSku.getGptLimitNum());
  266. chatgptUser.setLimitTime(goodsDonSku.getGptLimitTime());
  267. }
  268. chatgptUser.setExpireTime(groupsRelation.getExpiryTime());
  269. chatgptUser.setIsPlus(1);
  270. chatgptUser.setName(user.getNickname());
  271. chatgptUser.setImg(getWxImg(user.getHeadimgurl(), user));
  272. chatgptUser.setRelationId(groupsRelation.getId());
  273. chatgptUser.setUserToken(UUID.randomUUID().toString());
  274. chatgptUserMapper.insert(chatgptUser);
  275. return chatgptUser;
  276. }
  277. private void updateChatgptUser(GroupsRelation groupsRelation, User user, ChatgptUser chatgptUser) {
  278. chatgptUser.setName(user.getNickname());
  279. chatgptUser.setImg(getWxImg(user.getHeadimgurl(), user));
  280. chatgptUser.setExpireTime(groupsRelation.getExpiryTime());
  281. chatgptUserMapper.updateById(chatgptUser);
  282. }
  283. /**
  284. * 更换wx头像至oss
  285. */
  286. private String getWxImg(String headimgurl, User user) {
  287. if (headimgurl.contains("thirdwx.qlogo.cn")) {
  288. try {
  289. byte[] body = HttpUtil.downloadBytes(headimgurl);
  290. Path tempFile = Files.createTempFile("wxheadimg-" + user.getId(), ".jpeg");
  291. try (FileImageOutputStream imageOutput = new FileImageOutputStream(tempFile.toFile())) {
  292. imageOutput.write(body, 0, body.length);
  293. }
  294. Map<String, Object> paramMap = new HashMap<>();
  295. paramMap.put("file", tempFile.toFile());
  296. JSONObject result = JSONUtil.parseObj(HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap));
  297. return result.getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl");
  298. } catch (Exception ex) {
  299. log.error("上传微信头像错误!msg:{}", StringUtil.getErrorText(ex));
  300. return "./avatars.png";
  301. }
  302. } else {
  303. return user.getHeadimgurl();
  304. }
  305. }
  306. @Override
  307. public ChatgptUser findByUserTokenAndExpireTimeAfter(String userToken, LocalDateTime now) {
  308. return chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getUserToken, userToken).gt(ChatgptUser::getExpireTime, now));
  309. }
  310. @Override
  311. public void saveConversationRecord(String userToken, ConversationRequest conversationRequest) {
  312. ChatgptSession chatgptSession = chatgptSessionMapper.selectOne(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getCarId, conversationRequest.getCarId()));
  313. ChatgptUserConversationRecord chatgptUserConversationRecord = new ChatgptUserConversationRecord();
  314. chatgptUserConversationRecord.setUserToken(userToken);
  315. chatgptUserConversationRecord.setCarId(chatgptSession.getCarId());
  316. chatgptUserConversationRecord.setCarName(chatgptSession.getCarName());
  317. if (StringUtils.isBlank(conversationRequest.getConversation_id())) {
  318. chatgptUserConversationRecord.setMessageId(conversationRequest.getMessages().get(0).getId());
  319. } else {
  320. chatgptUserConversationRecord.setConversationId(conversationRequest.getConversation_id());
  321. }
  322. chatgptUserConversationRecord.setModel(conversationRequest.getModel());
  323. chatgptUserConversationRecordMapper.insert(chatgptUserConversationRecord);
  324. updateExperienceAndScore(conversationRequest.getCarId(), !"text-davinci-002-render-sha".equals(conversationRequest.getModel()), System.currentTimeMillis());
  325. }
  326. @Override
  327. public ConversationLimitResponse conversationLimit(String userToken, String model, String carId) {
  328. ConversationLimitResponse conversationLimitResponse = new ConversationLimitResponse();
  329. conversationLimitResponse.setLimited(false);
  330. if ("gpt-4".equals(model)) {
  331. ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getUserToken, userToken));
  332. if (chatgptUser == null) {
  333. conversationLimitResponse.setLimited(true);
  334. }
  335. //如果不为车队 直接返回
  336. if (!chatgptUser.getIsCar()) {
  337. return conversationLimitResponse;
  338. }
  339. conversationLimitResponse = isConversationAllowed(userToken, chatgptUser.getLimitNum(), chatgptUser.getLimitTime());
  340. //车队次数记录
  341. cardConversationRecord(carId);
  342. }
  343. return conversationLimitResponse;
  344. }
  345. /**
  346. * 检查是否允许进行进行提问
  347. *
  348. * @param userToken 用户token
  349. * @return true 如果允许请求,false 如果请求被限制
  350. */
  351. public ConversationLimitResponse isConversationAllowed(String userToken, int limit, Long limitTime) {
  352. String key = RedisService.key.CHATGPT_CONVERSATION_LIMIT.getName() + ":" + userToken;
  353. long currentTimeMillis = System.currentTimeMillis();
  354. long windowStartMillis = currentTimeMillis - (limitTime * 60 * 60) * 1000;
  355. ConversationLimitResponse conversationLimitResponse = new ConversationLimitResponse();
  356. // 清除时间窗口之前的请求记录
  357. redisService.zRemoveRangeByScore(key, 0, windowStartMillis);
  358. Long currentSize = redisService.zCard(key);
  359. if (currentSize != null && currentSize >= limit) {
  360. // 如果当前请求次数超过限制,则拒绝请求
  361. Set<Object> times = redisService.zRangeByScore(key, 0, currentTimeMillis, 0, 1);
  362. Long oldestTime = (Long) times.stream().findFirst().orElse(null);
  363. if (oldestTime != null) {
  364. // 下一次可用时间是最早请求时间之后的3小时
  365. long nextAvailableTime = oldestTime + (limitTime * 60 * 60 * 1000);
  366. conversationLimitResponse.setNextAvailableTime(nextAvailableTime);
  367. }
  368. conversationLimitResponse.setLimited(true);
  369. return conversationLimitResponse;
  370. } else {
  371. // 如果未超过限制,记录当前请求的时间戳
  372. redisService.zAdd(key, currentTimeMillis, currentTimeMillis);
  373. // 设置ZSet的过期时间,窗口大小加上一段冗余时间
  374. redisService.expire(key, (limitTime * 60 * 60) + 20);
  375. conversationLimitResponse.setLimited(false);
  376. return conversationLimitResponse;
  377. }
  378. }
  379. /**
  380. * 记录车队的提问次数
  381. */
  382. public boolean cardConversationRecord(String carid) {
  383. String key = RedisService.key.CHATGPT_CAR_CONVERSATION_LIMIT.getName() + ":" + carid;
  384. long currentTimeMillis = System.currentTimeMillis();
  385. long windowStartMillis = currentTimeMillis - (WINDOW_SIZE) * 1000;
  386. // 清除时间窗口之前的请求记录
  387. redisService.zRemoveRangeByScore(key, 0, windowStartMillis);
  388. Long currentSize = redisService.zCard(key);
  389. if (currentSize != null && currentSize >= MAX_REQUESTS) {
  390. // 如果当前请求次数超过限制,则拒绝请求
  391. return false;
  392. } else {
  393. // 如果未超过限制,记录当前请求的时间戳
  394. redisService.zAdd(key, currentTimeMillis, currentTimeMillis);
  395. // 设置ZSet的过期时间,窗口大小加上一段冗余时间
  396. redisService.expire(key, (WINDOW_SIZE * 60 * 60) + 20);
  397. return true;
  398. }
  399. }
  400. /**
  401. * 获取指定用户ID在滑动窗口内的提问次数
  402. *
  403. * @param userToken 用户token
  404. * @return 滑动窗口内的请求次数
  405. */
  406. @Override
  407. public Long getConversationCount(String userToken, Long limitTime) {
  408. String key = RedisService.key.CHATGPT_CONVERSATION_LIMIT.getName() + ":" + userToken;
  409. long currentTimeMillis = System.currentTimeMillis();
  410. long windowStartMillis = currentTimeMillis - (limitTime * 60 * 60) * 1000;
  411. // 清除时间窗口之前的请求记录(可选,根据需要决定是否在此处清理过期记录)
  412. redisService.zRemoveRangeByScore(key, 0, windowStartMillis);
  413. // 获取当前窗口内的请求次数
  414. Long currentSize = redisService.zCard(key);
  415. return currentSize != null ? currentSize : 0L;
  416. }
  417. /**
  418. * 获取指定车队ID在滑动窗口内的提问次数
  419. *
  420. * @param carId 车队ID
  421. * @return 滑动窗口内的请求次数
  422. */
  423. private Long getCarConversationCount(String carId, Long limitTime) {
  424. String key = RedisService.key.CHATGPT_CAR_CONVERSATION_LIMIT.getName() + ":" + carId;
  425. long currentTimeMillis = System.currentTimeMillis();
  426. long windowStartMillis = currentTimeMillis - (limitTime * 60 * 60) * 1000;
  427. // 清除时间窗口之前的请求记录(可选,根据需要决定是否在此处清理过期记录)
  428. redisService.zRemoveRangeByScore(key, 0, windowStartMillis);
  429. // 获取当前窗口内的请求次数
  430. Long currentSize = redisService.zCard(key);
  431. return currentSize != null ? currentSize : 0L;
  432. }
  433. @Override
  434. public ChatgptSession checkSession(String carId) {
  435. ChatgptSession chatgptSession = chatgptSessionMapper.selectOne(Wrappers.lambdaQuery(ChatgptSession.class).eq(ChatgptSession::getCarId, carId).eq(ChatgptSession::getIsCar, true).last(" limit 1"));
  436. if (chatgptSession == null) {
  437. throw BusinessRuntimeException.getInstance("车队不存在");
  438. }
  439. return chatgptSession;
  440. }
  441. private Long carOaiLimit(String carId) {
  442. if (redisService.hasKey("clears_in:" + carId)) {
  443. return redisService.getExpire("clears_in:" + carId);
  444. }
  445. return 0L;
  446. }
  447. @Override
  448. public Boolean checkCarAccount(long userId, Long relationId) {
  449. GroupsRelation groupsRelation = getGroupsRelation(userId, relationId);
  450. GroupsTrips groupsTrips = getGroupsTrips(groupsRelation);
  451. GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId());
  452. if (goodsDonSku != null && goodsDonSku.getIsMirror() && goodsDonSku.getIsCar()) {
  453. ChatgptUser chatgptUser = getChatgptCarUser(userId, relationId, groupsRelation, goodsDonSku);
  454. return true;
  455. } else {
  456. return false;
  457. }
  458. }
  459. @Override
  460. public ChatgptUser getCarChatGptUser(long userId, Long relationId) {
  461. GroupsRelation groupsRelation = getGroupsRelation(userId, relationId);
  462. GroupsTrips groupsTrips = getGroupsTrips(groupsRelation);
  463. GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId());
  464. if (goodsDonSku != null && goodsDonSku.getIsMirror() && goodsDonSku.getIsCar()) {
  465. ChatgptUser chatgptUser = getChatgptCarUser(userId, relationId, groupsRelation, goodsDonSku);
  466. return chatgptUser;
  467. }
  468. return null;
  469. }
  470. /**
  471. * 获取车队信息
  472. */
  473. @Override
  474. public Set<ChatgptCarInfoView> getCarInfoList(String userToken) {
  475. List<ChatGptUserConversationRecordHistoryView> search = beanSearcher.searchAll(ChatGptUserConversationRecordHistoryView.class, MapUtils.builder().field("userToken", userToken).limit(0, 2).build());
  476. Set<ChatgptCarInfoView> res = search.stream()
  477. .map((historyView) -> {
  478. ChatgptCarInfoView chatgptCarInfoView = buildChatgptCarInfoView(historyView.getCarId(), redisService.zScore(RedisService.key.CHATGPT_CAR_SCORES.getName(), historyView.getCarId()));
  479. chatgptCarInfoView.setIsHistory(true);
  480. String sessionVar = redisService.getStr(RedisService.key.CHATGPT_CAR_SEESSION.getName() + historyView.getCarId());
  481. if(JSONUtil.isJson(sessionVar)){
  482. JSONObject sessionJson = new JSONObject(sessionVar);
  483. JSONArray models = sessionJson.getJSONArray("models");
  484. if(models.size() > 1){
  485. chatgptCarInfoView.setType("plus");
  486. }else {
  487. chatgptCarInfoView.setType("3.5");
  488. }
  489. }
  490. return chatgptCarInfoView;
  491. }).collect(Collectors.toSet());
  492. Set<ChatgptCarInfoView> lowestScoreFleets = getLowestScoreFleets(10);
  493. res.addAll(lowestScoreFleets);
  494. return res;
  495. }
  496. // 更新体验并计算评分
  497. public void updateExperienceAndScore(String carId, boolean isHighLevel, long timestamp) {
  498. // 定义键名
  499. String key = isHighLevel ? RedisService.key.CHATGPT_CAR_HIGH_CHAT.getName() + carId : RedisService.key.CHATGPT_CAR_LOW_CHAT.getName() + carId;
  500. double scoreToAdd = isHighLevel ? 2.0 : 1.0; // 分数更新规则
  501. // 更新体验数据
  502. redisService.zAdd(key, timestamp, String.valueOf(timestamp));
  503. // 更新车队评分
  504. redisService.zIncrementScore(RedisService.key.CHATGPT_CAR_SCORES.getName(), carId, scoreToAdd);
  505. // 清理旧数据(可选)和重新计算评分(根据需要实现)
  506. cleanupOldExperiencesAndRecalculateScore(carId, timestamp);
  507. }
  508. // 清理旧数据和重新计算评分
  509. public void cleanupOldExperiencesAndRecalculateScore(String carId, long currentTimestamp) {
  510. long threeHoursAgo = currentTimestamp - (3 * 60 * 60 * 1000); // 3小时前的时间戳
  511. // 清理高级体验旧数据
  512. redisService.zRemoveRangeByScore(RedisService.key.CHATGPT_CAR_HIGH_CHAT.getName() + carId, 0, threeHoursAgo);
  513. // 清理低级体验旧数据
  514. redisService.zRemoveRangeByScore(RedisService.key.CHATGPT_CAR_LOW_CHAT.getName() + carId, 0, threeHoursAgo);
  515. // 重新计算评分
  516. recalculateScore(carId, currentTimestamp);
  517. }
  518. // 重新计算指定车队的评分
  519. private void recalculateScore(String carId, long currentTimestamp) {
  520. // 实际应用中,你需要根据高级体验和低级体验的数量重新计算得分
  521. Double highExperienceScore = (double) redisService.zCount(RedisService.key.CHATGPT_CAR_HIGH_CHAT.getName() + carId, currentTimestamp - (3 * 60 * 60 * 1000), currentTimestamp) * 2;
  522. Double lowExperienceScore = (double) redisService.zCount(RedisService.key.CHATGPT_CAR_LOW_CHAT.getName() + carId, currentTimestamp - (3 * 60 * 60 * 1000), currentTimestamp);
  523. double newScore = highExperienceScore + lowExperienceScore;
  524. redisService.zAdd(RedisService.key.CHATGPT_CAR_SCORES.getName(), newScore, carId);
  525. }
  526. // 获取得分最低的N个车队的方法
  527. public Set<ChatgptCarInfoView> getLowestScoreFleets(int count) {
  528. Set<ZSetOperations.TypedTuple<Object>> lowestScorecarIds;
  529. Long fleetsSize = redisService.zCard(RedisService.key.CHATGPT_CAR_SCORES.getName());
  530. // 构建ChatgptCarInfoView集合
  531. if (fleetsSize != null && fleetsSize > 10) {
  532. TASK_EXECUTOR.execute(() -> {
  533. initFleets(true);
  534. });
  535. } else {
  536. initFleets(false);
  537. }
  538. lowestScorecarIds = redisService.zRangeWithScores(RedisService.key.CHATGPT_CAR_SCORES.getName(), 0L, count - 1);
  539. return lowestScorecarIds.stream()
  540. .map(typedTuple -> {
  541. String value = StringUtil.getString(typedTuple.getValue());
  542. Double score = typedTuple.getScore(); // 获取分数
  543. // 根据需要处理value和score
  544. return buildChatgptCarInfoView(value, score); // 假设这个方法现在也接收分数
  545. })
  546. .collect(Collectors.toSet());
  547. }
  548. public void initFleets(Boolean flag) {
  549. LambdaQueryWrapper<ChatgptSession> wrapper = Wrappers.lambdaQuery(ChatgptSession.class);
  550. if (flag) {
  551. Set<ZSetOperations.TypedTuple<Object>> fleets = redisService.zRangeWithScores(RedisService.key.CHATGPT_CAR_SCORES.getName(), 0L, -1);
  552. List<Object> collect = fleets.stream().map(ZSetOperations.TypedTuple::getValue).collect(Collectors.toList());
  553. wrapper.notIn(collect.size() > 0, ChatgptSession::getCarId, collect);
  554. }
  555. //如果存在未存在redis中的车辆则同步进 chatgpt:car:scores
  556. List<ChatgptSession> chatgptSessions = chatgptSessionMapper.selectList(wrapper.eq(ChatgptSession::getIsCar, true));
  557. chatgptSessions.forEach((chatgptSession) -> {
  558. if (!redisService.checkValueExistsInZSet(RedisService.key.CHATGPT_CAR_SCORES.getName(), chatgptSession.getCarId())) {
  559. redisService.zAdd(RedisService.key.CHATGPT_CAR_SCORES.getName(), 0, chatgptSession.getCarId());
  560. }
  561. });
  562. }
  563. // 构建ChatgptCarInfoView对象
  564. private ChatgptCarInfoView buildChatgptCarInfoView(String carId, Double score) {
  565. // 在这里根据carId获取相关信息并填充到ChatgptCarInfoView对象中
  566. ChatgptCarInfoView view = new ChatgptCarInfoView();
  567. view.setCarId(carId);
  568. // 假设以下方法从Redis或其他服务获取数据
  569. view.setStatus(score >= 50 ? "繁忙" : "空闲"); // 或“繁忙”
  570. String sessionVar = redisService.getStr(RedisService.key.CHATGPT_CAR_SEESSION.getName() + carId);
  571. if(JSONUtil.isJson(sessionVar)){
  572. JSONArray models = new JSONObject(sessionVar).getJSONArray("models");
  573. if(models!= null && models.size() > 1){
  574. view.setType("plus");
  575. }else {
  576. view.setType("3.5");
  577. }
  578. }
  579. view.setGptLimit(40); // 假设值
  580. int use = getCarConversationCount(carId, 3L).intValue();
  581. view.setUse(Math.min(use, 40));
  582. Long aLong = carOaiLimit(carId);
  583. if (aLong != 0L) {
  584. view.setStatus("停运");
  585. view.setExpTime(new DateTime(System.currentTimeMillis() + aLong * 1000));
  586. }
  587. // 设置其他必要的属性...
  588. return view;
  589. }
  590. @Override
  591. public ChatGptUserView getUserInfo(long userId, Long relationId) {
  592. if (checkCarAccount(userId, relationId)) {
  593. GroupsRelation groupsRelation = getGroupsRelation(userId, relationId);
  594. GroupsTrips groupsTrips = getGroupsTrips(groupsRelation);
  595. GoodsDonSku goodsDonSku = skuMapper.selectById(groupsTrips.getSkuId());
  596. ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getRelationId, relationId));
  597. return ChatGptUserView.builder()
  598. .skuName(goodsDonSku.getSubTitle())
  599. .expireTime(chatgptUser.getExpireTime())
  600. .limitNum(chatgptUser.getLimitNum())
  601. .limitTime(chatgptUser.getLimitTime())
  602. .use(getConversationCount(chatgptUser.getUserToken(), chatgptUser.getLimitTime()))
  603. .build();
  604. } else {
  605. throw BusinessRuntimeException.getInstance("您还未购买该车票");
  606. }
  607. }
  608. @Override
  609. public void genTitleSync(String messageId, String carid, String userToken) {
  610. //同步对话
  611. ChatgptUserConversationRecord chatgptUserConversationRecord = chatgptUserConversationRecordMapper.selectOne(Wrappers.lambdaQuery(ChatgptUserConversationRecord.class).eq(ChatgptUserConversationRecord::getMessageId, messageId));
  612. if (chatgptUserConversationRecord != null) {
  613. if (StringUtils.isBlank(chatgptUserConversationRecord.getConversationId())) {
  614. ChatgptUserConversation conversation = chatgptUserConversationMapper.selectOne(Wrappers.lambdaQuery(ChatgptUserConversation.class).eq(ChatgptUserConversation::getMessageId, messageId).last(" limit 1"));
  615. if (conversation != null) {
  616. chatgptUserConversationRecord.setConversationId(conversation.getConversationId());
  617. chatgptUserConversationRecordMapper.updateById(chatgptUserConversationRecord);
  618. }
  619. }
  620. }
  621. }
  622. @PostConstruct
  623. public void init() {
  624. //如果存在未存在redis中的车辆则同步进 chatgpt:car:scores
  625. TASK_EXECUTOR.execute(() -> {
  626. Set<ZSetOperations.TypedTuple<Object>> lowestScorecarIds = redisService.zRangeWithScores(RedisService.key.CHATGPT_CAR_SCORES.getName(), 0L, -1);
  627. List<Object> collect = lowestScorecarIds.stream().map(ZSetOperations.TypedTuple::getValue).collect(Collectors.toList());
  628. List<ChatgptSession> chatgptSessions = chatgptSessionMapper.selectList(Wrappers.lambdaQuery(ChatgptSession.class).notIn(collect.size() > 0, ChatgptSession::getCarId, collect).eq(ChatgptSession::getIsCar, true));
  629. chatgptSessions.forEach((chatgptSession) -> {
  630. if (!redisService.checkValueExistsInZSet(RedisService.key.CHATGPT_CAR_SCORES.getName(), chatgptSession.getCarId())) {
  631. redisService.zAdd(RedisService.key.CHATGPT_CAR_SCORES.getName(), 0, chatgptSession.getCarId());
  632. }
  633. });
  634. });
  635. }
  636. }