ClaudeCodeServiceImpl.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. package com.cyksj.service.claude.impl;
  2. import cn.hutool.core.date.DateTime;
  3. import cn.hutool.core.date.DateUtil;
  4. import cn.hutool.core.lang.Assert;
  5. import cn.hutool.core.util.ObjectUtil;
  6. import cn.hutool.http.HttpException;
  7. import cn.hutool.http.HttpResponse;
  8. import cn.hutool.http.HttpUtil;
  9. import cn.hutool.http.Method;
  10. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  11. import com.cyksj.common.constant.Constant;
  12. import com.cyksj.common.exception.BusinessRuntimeException;
  13. import com.cyksj.common.util.Jsons;
  14. import com.cyksj.common.util.StringUtil;
  15. import com.cyksj.mapper.*;
  16. import com.cyksj.model.entity.*;
  17. import com.cyksj.model.manage.views.GroupsRelationView;
  18. import com.cyksj.model.request.ClaudeCodeApiKeysReq;
  19. import com.cyksj.model.request.ClaudeCodeDelReq;
  20. import com.cyksj.model.response.ClaudeCodePointsHistoryResp;
  21. import com.cyksj.model.response.claudecode.ClaudeCodeResp;
  22. import com.cyksj.model.views.ClaudeCodeUserApiKeysView;
  23. import com.cyksj.model.views.ClaudeCodeUserInfoView;
  24. import com.cyksj.service.claude.ClaudeCodeService;
  25. import com.cyksj.service.user.UserBindRelationService;
  26. import com.ejlchina.searcher.BeanSearcher;
  27. import com.ejlchina.searcher.param.Operator;
  28. import com.ejlchina.searcher.util.MapUtils;
  29. import com.github.rholder.retry.*;
  30. import lombok.RequiredArgsConstructor;
  31. import lombok.extern.slf4j.Slf4j;
  32. import org.springframework.dao.DuplicateKeyException;
  33. import org.springframework.stereotype.Service;
  34. import java.util.Date;
  35. import java.util.HashMap;
  36. import java.util.List;
  37. import java.util.Map;
  38. import java.util.concurrent.ExecutionException;
  39. import java.util.concurrent.TimeUnit;
  40. /**
  41. * 项目名: yhlxj11111111
  42. * 文件名: ClaudeCodeServiceImpl
  43. * 创建者: JavaZou
  44. * 创建时间:2025/8/8 10:55
  45. */
  46. @Service
  47. @RequiredArgsConstructor
  48. @Slf4j
  49. public class ClaudeCodeServiceImpl implements ClaudeCodeService {
  50. private final UserBindRelationService userBindRelationService;
  51. private final GroupsRelationMapper relationMapper;
  52. private final BeanSearcher beanSearcher;
  53. private final ClaudeCodeUserRenewExpiryRecordMapper claudeCodeUserRenewExpiryRecordMapper;
  54. private final ClaudeCodeUserMapper claudeCodeUserMapper;
  55. private final GoodsDonSkuMapper skuMapper;
  56. private final GroupsMapper groupsMapper;
  57. private final OrderDonMapper orderDonMapper;
  58. private final static String CLAUDE_CODE_API_PREFIX = "https://relay01.yhlxj.com/";
  59. public static final String CLAUDE_CODE_API_ADMIN_KEY = "claudecodeyhlxjclaude";
  60. //连接超时时间 毫秒
  61. public static final int CONNECT_MILLISECONDS = 10000;
  62. @Override
  63. public void generateOrUpdateClaudeCodeUserInfo(Long orderId, Long relationId, Long userId, GoodsDonSku sku, Integer orderType) {
  64. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  65. GroupsRelation relation = relationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class).eq(GroupsRelation::getId, relationId).in(GroupsRelation::getUserId, userIdList));
  66. GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
  67. GoodsDonSku relationSku = skuMapper.selectById(groupsTrips.getSkuId());
  68. Long thisCodeOriSkuId = relationSku.getId();
  69. Long relationUserId = relation.getUserId();
  70. //此时车票的套餐规格积分
  71. Integer thisRelationCodePoints = null;
  72. ClaudeCodeUser claudeCodeUser = claudeCodeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUser.class).eq(ClaudeCodeUser::getRelationId, relationId).last("limit 1"));
  73. if (claudeCodeUser != null) {
  74. thisRelationCodePoints = claudeCodeUser.getCodePoints();
  75. }
  76. Date expiryTime = relation.getExpiryTime();
  77. Integer codePoints = sku.getCodePoints();
  78. Integer codeCreditRecovery = sku.getCodeCreditRecovery();
  79. Long skuId = sku.getId();
  80. Boolean isUpgrade = false;
  81. Date renewExpiryTime = null;
  82. //若是续费升级 记录此时续费升级的过期时间
  83. if (thisRelationCodePoints != null && thisRelationCodePoints <= codePoints && !ObjectUtil.equal(thisCodeOriSkuId, skuId)) {
  84. if (sku.getDays() != null) {
  85. renewExpiryTime = DateUtil.offsetDay(DateTime.now(), sku.getDays());
  86. } else {
  87. renewExpiryTime = DateUtil.offsetMonth(DateTime.now(), sku.getMonths());
  88. }
  89. isUpgrade = true;
  90. saveClaudeCodeRenewExpiryRecord(orderId, relationUserId, relationId, renewExpiryTime, skuId);
  91. }
  92. generateOrUpdateClaudeCodeUser(relationUserId, relationId, codePoints, codeCreditRecovery, expiryTime, isUpgrade, orderId, skuId, renewExpiryTime, sku.getSpecVal());
  93. try {
  94. //生成claude code或更新用户套餐
  95. ClaudeCodeResp codeUserPackage = createOrUpdateClaudeCodeUserPackage(relation.getUserId(), sku.getSpecVal(), codePoints, codeCreditRecovery, expiryTime);
  96. if (!Constant.SUCCESS.equals(codeUserPackage.getMessage())) {
  97. //重试更新code userInfo数据
  98. setClaudeCodeUserRetryUpdateInfo(relationId);
  99. return;
  100. }
  101. //清缓存
  102. if (orderType == 2) {
  103. invalidateUserCache(relationUserId);
  104. }
  105. } catch (Exception e) {
  106. log.error("生成或更新claude code用户:{}套餐skuId:{} 失败,error_info:{}", userId, skuId, StringUtil.getErrorText(e));
  107. //重试更新code userInfo数据
  108. setClaudeCodeUserRetryUpdateInfo(relationId);
  109. }
  110. }
  111. private void generateOrUpdateClaudeCodeUser(Long relationUserId, Long relationId, Integer codePoints, Integer codeCreditRecovery, Date expiryTime, Boolean isUpgrade, Long orderId, Long renewSkuId, Date renewExpiryTime, String planName) {
  112. ClaudeCodeUser claudeCodeUser = claudeCodeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUser.class).eq(ClaudeCodeUser::getRelationId, relationId).last("limit 1"));
  113. if (claudeCodeUser == null) {
  114. claudeCodeUser = new ClaudeCodeUser();
  115. }
  116. if (isUpgrade) {
  117. claudeCodeUser.setRenewOrderId(orderId);
  118. claudeCodeUser.setRenewSkuId(renewSkuId);
  119. claudeCodeUser.setRenewExpiryTime(renewExpiryTime);
  120. }
  121. claudeCodeUser.setUserId(relationUserId);
  122. claudeCodeUser.setPlanName(planName);
  123. claudeCodeUser.setRelationId(relationId);
  124. claudeCodeUser.setExpiryTime(expiryTime);
  125. claudeCodeUser.setCodePoints(codePoints);
  126. claudeCodeUser.setCodeCreditRecovery(codeCreditRecovery);
  127. if (claudeCodeUser.getId() == null) {
  128. try {
  129. claudeCodeUserMapper.insert(claudeCodeUser);
  130. } catch (DuplicateKeyException e) {
  131. log.info("插入claude code user 重复");
  132. }
  133. } else claudeCodeUserMapper.updateById(claudeCodeUser);
  134. }
  135. @Override
  136. public ClaudeCodeResp createApiKeys(ClaudeCodeApiKeysReq req) throws Exception {
  137. Long userId = req.getUserId();
  138. if (checkClaudeCodeUser(userId)) {
  139. Long claudeCodeUserId = getClaudeCodeUserId(userId);
  140. Assert.notNull(claudeCodeUserId, "您的claude code账号已过期");
  141. String apiKeyUrl = String.format(CLAUDE_CODE_API_PREFIX + "api/users/%s/keys", claudeCodeUserId);
  142. Map<String, Object> parmas = new HashMap<>();
  143. parmas.put("name", req.getName());
  144. Integer expiresDays = req.getExpiresDays();
  145. if (expiresDays != null) {
  146. parmas.put("expires_days", expiresDays);
  147. }
  148. ClaudeCodeResp claudeCodeResp = executeClaudeCodePostApi(apiKeyUrl, Jsons.toJson(parmas));
  149. if (!Constant.SUCCESS.equals(claudeCodeResp.getMessage())) {
  150. throw BusinessRuntimeException.getInstance("创建失败");
  151. }
  152. return claudeCodeResp;
  153. }
  154. throw new BusinessRuntimeException("您还没有claudecode账号");
  155. }
  156. @Override
  157. public void deleteApiKeysById(ClaudeCodeDelReq delReq) throws Exception {
  158. Long userId = delReq.getUserId();
  159. Long keyId = delReq.getKeyId();
  160. if (checkClaudeCodeUser(userId)) {
  161. Long claudeCodeUserId = getClaudeCodeUserId(userId);
  162. String url = String.format(CLAUDE_CODE_API_PREFIX + "api/keys/%s/%s", claudeCodeUserId, keyId);
  163. HttpResponse execute = HttpUtil.createRequest(Method.DELETE, url).header("X-Admin-Key", CLAUDE_CODE_API_ADMIN_KEY).setConnectionTimeout(CONNECT_MILLISECONDS).execute();
  164. String responseBody = execute.body();
  165. ClaudeCodeResp claudeCodeResp = Jsons.parseObject(responseBody, ClaudeCodeResp.class);
  166. if (!Constant.SUCCESS.equals(claudeCodeResp.getMessage())) {
  167. log.error("删除key失败:{}", claudeCodeResp.getMessage());
  168. throw BusinessRuntimeException.getInstance("删除失败");
  169. }
  170. }
  171. }
  172. @Override
  173. public List<ClaudeCodeUserApiKeysView> getUserApiKeys(long userId) throws Exception {
  174. Long claudeCodeUserId = getClaudeCodeUserId(userId);
  175. if (claudeCodeUserId != null) {
  176. String url = String.format(CLAUDE_CODE_API_PREFIX + "api/users/%s/keys", claudeCodeUserId);
  177. ClaudeCodeResp claudeCodeResp = executeClaudeCodeGetApi(url);
  178. return Jsons.parseList(claudeCodeResp.getData(), ClaudeCodeUserApiKeysView.class);
  179. }
  180. return null;
  181. }
  182. @Override
  183. public ClaudeCodePointsHistoryResp getUserPointsDetail(Long userId, String type, Integer page, Integer limit) throws Exception {
  184. Long claudeCodeUserId = getClaudeCodeUserId(userId);
  185. if (claudeCodeUserId != null) {
  186. String url = String.format(CLAUDE_CODE_API_PREFIX + "api/users/%s/credits/history?page=%s&limit=%s&type=%s", claudeCodeUserId, page, limit, type);
  187. ClaudeCodeResp claudeCodeResp = executeClaudeCodeGetApi(url);
  188. ClaudeCodePointsHistoryResp historyResp = Jsons.parseObject(claudeCodeResp.getData(), ClaudeCodePointsHistoryResp.class);
  189. return historyResp;
  190. }
  191. return null;
  192. }
  193. @Override
  194. public ClaudeCodeResp getUserDashboard(Long userId) throws Exception {
  195. Long claudeCodeUserId = getClaudeCodeUserId(userId);
  196. if (claudeCodeUserId != null) {
  197. String url = String.format(CLAUDE_CODE_API_PREFIX + "api/users/%s/dashboard", claudeCodeUserId);
  198. ClaudeCodeResp claudeCodeResp = executeClaudeCodeGetApi(url);
  199. return claudeCodeResp;
  200. }
  201. return null;
  202. }
  203. @Override
  204. public ClaudeCodeResp getUserBalance(Long userId) throws Exception {
  205. Long claudeCodeUserId = getClaudeCodeUserId(userId);
  206. if (claudeCodeUserId != null) {
  207. String url = String.format(CLAUDE_CODE_API_PREFIX + "api/users/%s/balance", claudeCodeUserId);
  208. ClaudeCodeResp claudeCodeResp = executeClaudeCodeGetApi(url);
  209. return claudeCodeResp;
  210. }
  211. return null;
  212. }
  213. @Override
  214. public ClaudeCodeResp getCreditsAnalytics(Long userId, String start, String end, String tz, Integer page, Integer limit, String order, String type) throws Exception {
  215. Long claudeCodeUserId = getClaudeCodeUserId(userId);
  216. if (start == null) {
  217. DateTime now = DateTime.now();
  218. start = DateUtil.offsetDay(now, -1).toString();
  219. end = now.toString();
  220. }
  221. if (claudeCodeUserId != null) {
  222. String url = String.format(CLAUDE_CODE_API_PREFIX + "api/users/%s/credits/analytics?start=%s&end=%s&tz=%s&page=%s&limit=%s&order=%s&type=%s", claudeCodeUserId, start, end, tz, page, limit, order, type);
  223. ClaudeCodeResp claudeCodeResp = executeClaudeCodeGetApi(url);
  224. return claudeCodeResp;
  225. }
  226. return null;
  227. }
  228. /**
  229. * 用户缓存失效
  230. */
  231. @Override
  232. public void invalidateUserCache(Long userId) {
  233. String url = String.format(CLAUDE_CODE_API_PREFIX + "api/cache/invalidate/user/%s", userId);
  234. try {
  235. executeClaudeCodePostApi(url, null);
  236. } catch (Exception e) {
  237. }
  238. }
  239. @Override
  240. public ClaudeCodeUserInfoView getClaudeCodeUserSubsInfo(Long userId) {
  241. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  242. //是否有该车票
  243. GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder()
  244. .field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
  245. .field(GroupsRelationView::getGoodsId, Constant.CLAUDE_CODE_GOODS_ID)
  246. .field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
  247. .build());
  248. ClaudeCodeUser claudeCodeUser = claudeCodeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUser.class).in(ClaudeCodeUser::getUserId, userIdList).last("limit 1"));
  249. //车票不存在
  250. if (groupsRelationView == null) {
  251. //套餐是否存在
  252. if (claudeCodeUser != null) {
  253. //清除用户套餐
  254. if (delUserPackages(claudeCodeUser.getUserId())) {
  255. claudeCodeUserMapper.deleteById(claudeCodeUser.getId());
  256. }
  257. }
  258. return null;
  259. }
  260. if (claudeCodeUser == null) {
  261. OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
  262. .eq(OrderDon::getGoodsId, Constant.CLAUDE_CODE_GOODS_ID)
  263. .eq(OrderDon::getRelationId, groupsRelationView.getId())
  264. .in(OrderDon::getUserId, userIdList)
  265. .notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
  266. .orderByDesc(OrderDon::getId)
  267. .last("limit 1"));
  268. if (orderDon != null) {
  269. GoodsDonSku sku = skuMapper.selectById(orderDon.getSkuId());
  270. generateOrUpdateClaudeCodeUserInfo(orderDon.getId(), orderDon.getRelationId(), orderDon.getUserId(), sku, orderDon.getOrderType());
  271. claudeCodeUser = claudeCodeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUser.class).in(ClaudeCodeUser::getUserId, userIdList).last("limit 1"));
  272. }
  273. } else {
  274. claudeCodeUser.setExpiryTime(groupsRelationView.getExpiryTime());
  275. claudeCodeUserMapper.updateById(claudeCodeUser);
  276. }
  277. //是否需要重试更新 cladueCode userInfo信息
  278. if (claudeCodeUser.getIsRetry()) {
  279. try {
  280. ClaudeCodeResp codeUserPackage = createOrUpdateClaudeCodeUserPackage(claudeCodeUser.getUserId(), claudeCodeUser.getPlanName(), claudeCodeUser.getCodePoints(), claudeCodeUser.getCodeCreditRecovery(), claudeCodeUser.getExpiryTime());
  281. if (!Constant.SUCCESS.equals(codeUserPackage.getMessage())) {
  282. claudeCodeUser.setIsRetry(Boolean.TRUE);
  283. claudeCodeUserMapper.updateById(claudeCodeUser);
  284. } else {
  285. claudeCodeUser.setIsRetry(Boolean.FALSE);
  286. claudeCodeUserMapper.updateById(claudeCodeUser);
  287. }
  288. } catch (Exception e) {
  289. }
  290. }
  291. return ClaudeCodeUserInfoView.builder()
  292. .planName(claudeCodeUser.getPlanName())
  293. .relationId(claudeCodeUser.getRelationId())
  294. .codePoints(claudeCodeUser.getCodePoints())
  295. .codeCreditRecovery(claudeCodeUser.getCodeCreditRecovery())
  296. .expiryTime(claudeCodeUser.getExpiryTime())
  297. .renewSkuId(claudeCodeUser.getRenewSkuId())
  298. .renewOrderId(claudeCodeUser.getRenewOrderId())
  299. .renewExpiryTime(claudeCodeUser.getRenewExpiryTime())
  300. .build();
  301. }
  302. public ClaudeCodeResp createOrUpdateClaudeCodeUserPackage(Long userId, String planName, Integer codePoints, Integer codeCreditRecovery, Date expiryTime) throws Exception {
  303. Map<String, Object> parmas = new HashMap<>();
  304. parmas.put("plan_name", planName.replaceAll(";", ""));
  305. parmas.put("credit_limit", codePoints);
  306. parmas.put("credit_recovery", codeCreditRecovery);
  307. parmas.put("expire_time", DateUtil.format(expiryTime,"yyyy-MM-dd HH:mm:ss"));
  308. String planUrl = String.format(CLAUDE_CODE_API_PREFIX + "api/users/%s/plan", userId);
  309. ClaudeCodeResp claudeCodeResp = executeClaudeCodePostApi(planUrl, Jsons.toJson(parmas));
  310. if (!Constant.SUCCESS.equals(claudeCodeResp.getMessage())) {
  311. log.info("用户claude code用户:{}套餐:{}创建或更新失败,info:{}", userId, planName, claudeCodeResp.getMessage());
  312. }
  313. return claudeCodeResp;
  314. }
  315. /**
  316. * 校验是否是claude code用户
  317. * @param userId
  318. * @return
  319. */
  320. @Override
  321. public Boolean checkClaudeCodeUser(Long userId) {
  322. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  323. Integer count = relationMapper.selectCountByGoodsId(userIdList, Constant.CLAUDE_CODE_GOODS_ID, DateTime.now());
  324. return count > 0;
  325. }
  326. @Override
  327. public void updateClaudeCodeUserPackageInfo(ClaudeCodeUser claudeCodeUser, Date expiryTime) {
  328. try {
  329. ClaudeCodeResp codeUserPackage = createOrUpdateClaudeCodeUserPackage(claudeCodeUser.getUserId(), claudeCodeUser.getPlanName(), claudeCodeUser.getCodePoints(), claudeCodeUser.getCodeCreditRecovery(), expiryTime);
  330. if (!Constant.SUCCESS.equals(codeUserPackage.getMessage())) {
  331. //重试标记
  332. claudeCodeUser.setIsRetry(Boolean.TRUE);
  333. claudeCodeUserMapper.updateById(claudeCodeUser);
  334. return;
  335. }
  336. //刷新缓存
  337. invalidateUserCache(claudeCodeUser.getUserId());
  338. } catch (Exception e) {
  339. claudeCodeUser.setIsRetry(Boolean.TRUE);
  340. claudeCodeUserMapper.updateById(claudeCodeUser);
  341. }
  342. }
  343. @Override
  344. public Boolean delUserPackages(Long userId) {
  345. String url = String.format(CLAUDE_CODE_API_PREFIX + "api/users/%s/plan", userId);
  346. try {
  347. ClaudeCodeResp claudeCodeResp = executeClaudeCodeDeletedApi(url);
  348. if (!Constant.SUCCESS.equals(claudeCodeResp.getMessage())) {
  349. log.error("删除用户:{} claude code套餐失败:{}", userId, claudeCodeResp.getMessage());
  350. return Boolean.FALSE;
  351. }
  352. return Boolean.TRUE;
  353. } catch (Exception e) {
  354. log.error("删除用户:{} claude code套餐报错:{}", userId, StringUtil.getErrorText(e));
  355. }
  356. return Boolean.FALSE;
  357. }
  358. @Override
  359. public void handleClaudeCodeUserPackageSku(GroupsRelation relation, GoodsDonSku sku, Long orderId) {
  360. Long groupsId = relation.getGroupsId();
  361. GroupsTrips groupsTrips = groupsMapper.selectById(groupsId);
  362. GoodsDonSku oriRelationSku = skuMapper.selectById(groupsTrips.getSkuId());
  363. Integer oriRelationSkuCodePoints = oriRelationSku.getCodePoints();
  364. Integer presentCodePoints = sku.getCodePoints();
  365. Long oriRelationSkuId = oriRelationSku.getId();
  366. Long toUseSkuId = sku.getId();
  367. //若是续费升级 记录此时续费升级的过期时间
  368. Boolean isUpgrade = false;
  369. Date renewExpiryTime = null;
  370. if (oriRelationSkuCodePoints <= presentCodePoints && !ObjectUtil.equal(oriRelationSkuId, toUseSkuId)) {
  371. if (sku.getDays() != null) {
  372. renewExpiryTime = DateUtil.offsetDay(DateTime.now(), sku.getDays());
  373. } else {
  374. renewExpiryTime = DateUtil.offsetMonth(DateTime.now(), sku.getMonths());
  375. }
  376. isUpgrade = true;
  377. saveClaudeCodeRenewExpiryRecord(orderId, relation.getUserId(), relation.getId(), renewExpiryTime, sku.getId());
  378. }
  379. }
  380. /**
  381. * 获取claude code用户id
  382. */
  383. public Long getClaudeCodeUserId(Long userId) {
  384. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  385. GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder()
  386. .field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
  387. .field(GroupsRelationView::getGoodsId, Constant.CLAUDE_CODE_GOODS_ID)
  388. .field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
  389. .build());
  390. if (groupsRelationView != null) {
  391. return groupsRelationView.getUserId();
  392. }
  393. return null;
  394. }
  395. /**
  396. * claude code GET请求
  397. */
  398. public ClaudeCodeResp executeClaudeCodeGetApi(String url) {
  399. Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
  400. try {
  401. return build.call(() -> {
  402. try {
  403. HttpResponse execute = HttpUtil.createGet(url)
  404. .header("X-Admin-Key", CLAUDE_CODE_API_ADMIN_KEY)
  405. .setConnectionTimeout(CONNECT_MILLISECONDS)
  406. .execute();
  407. ClaudeCodeResp resp = Jsons.parseObject(execute.body(), ClaudeCodeResp.class);
  408. if (!Constant.SUCCESS.equals(resp.getMessage())) {
  409. log.info("claude code GET URL:{}接口返回msg:{}", url, resp.getMessage());
  410. }
  411. return resp;
  412. } catch (HttpException e) {
  413. return null;
  414. }
  415. });
  416. } catch (ExecutionException | RetryException e) {
  417. log.info("重试调用claude code GET请求url->{}失败,msg->{}", url, StringUtil.getErrorText(e));
  418. throw BusinessRuntimeException.getInstance("接口请求失败");
  419. }
  420. }
  421. /**
  422. * claude code Post请求
  423. */
  424. public ClaudeCodeResp executeClaudeCodePostApi(String url, String body) {
  425. Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
  426. try {
  427. return build.call(() -> {
  428. try {
  429. HttpResponse execute = HttpUtil.createPost(url)
  430. .header("X-Admin-Key", CLAUDE_CODE_API_ADMIN_KEY)
  431. .setConnectionTimeout(CONNECT_MILLISECONDS)
  432. .body(body)
  433. .execute();
  434. ClaudeCodeResp resp = Jsons.parseObject(execute.body(), ClaudeCodeResp.class);
  435. if (!Constant.SUCCESS.equals(resp.getMessage())) {
  436. log.info("claude code POST URL:{}接口返回msg:{}", url, resp.getMessage());
  437. }
  438. return resp;
  439. } catch (Exception e) {
  440. return null;
  441. }
  442. });
  443. } catch (ExecutionException | RetryException e) {
  444. log.info("重试调用claude code POST请求 url->{}失败,msg->{}", url, StringUtil.getErrorText(e));
  445. throw BusinessRuntimeException.getInstance("接口请求失败");
  446. }
  447. }
  448. /**
  449. * claude code DELETE请求
  450. */
  451. public ClaudeCodeResp executeClaudeCodeDeletedApi(String url) {
  452. Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
  453. try {
  454. return build.call(() -> {
  455. try {
  456. HttpResponse execute = HttpUtil.createRequest(Method.DELETE, url).header("X-Admin-Key", CLAUDE_CODE_API_ADMIN_KEY).setConnectionTimeout(CONNECT_MILLISECONDS).execute();
  457. ClaudeCodeResp resp = Jsons.parseObject(execute.body(), ClaudeCodeResp.class);
  458. if (!Constant.SUCCESS.equals(resp.getMessage())) {
  459. log.info("claude code DELETE URL:{}接口返回msg:{}", url, resp.getMessage());
  460. }
  461. return resp;
  462. } catch (HttpException e) {
  463. return null;
  464. }
  465. });
  466. } catch (ExecutionException | RetryException e) {
  467. log.info("重试调用claude code DELETE请求url->{}失败,msg->{}", url, StringUtil.getErrorText(e));
  468. throw BusinessRuntimeException.getInstance("接口请求失败");
  469. }
  470. }
  471. /**
  472. * 记录claude code 续费升级的过期时间
  473. */
  474. private void saveClaudeCodeRenewExpiryRecord(Long orderId, Long relationUserId, Long relationId, Date expiryTime, Long skuId) {
  475. ClaudeCodeUserRenewExpiryRecord renewExpiryRecord = new ClaudeCodeUserRenewExpiryRecord();
  476. renewExpiryRecord.setOrderId(orderId);
  477. renewExpiryRecord.setUserId(relationUserId);
  478. renewExpiryRecord.setRelationId(relationId);
  479. renewExpiryRecord.setRenewSkuId(skuId);
  480. renewExpiryRecord.setExpiryTime(expiryTime);
  481. claudeCodeUserRenewExpiryRecordMapper.insert(renewExpiryRecord);
  482. }
  483. /**
  484. * claude code用户信息重试更新信息
  485. */
  486. public void setClaudeCodeUserRetryUpdateInfo(Long relationId) {
  487. ClaudeCodeUser claudeCodeUser = claudeCodeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUser.class).eq(ClaudeCodeUser::getRelationId, relationId).last("limit 1"));
  488. if (claudeCodeUser != null) {
  489. claudeCodeUser.setIsRetry(Boolean.TRUE);
  490. claudeCodeUserMapper.updateById(claudeCodeUser);
  491. }
  492. }
  493. public Retryer getApiRetryer(Integer sleepSecond, Integer attemptNum) {
  494. Retryer build = RetryerBuilder.newBuilder()
  495. .retryIfException()
  496. //运行时异常重试
  497. .retryIfRuntimeException()
  498. //false重试
  499. .retryIfResult(res -> res == null)
  500. //1s 间隔
  501. .withWaitStrategy(WaitStrategies.fixedWait(sleepSecond, TimeUnit.SECONDS))
  502. //停止策略 : 尝试请求3次
  503. .withStopStrategy(StopStrategies.stopAfterAttempt(attemptNum)).build();
  504. return build;
  505. }
  506. }