RedeemCloudRechargeServiceImpl.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. package com.cyksj.service.recharge.impl;
  2. import cn.hutool.core.date.DateTime;
  3. import cn.hutool.core.date.DateUtil;
  4. import cn.hutool.core.util.StrUtil;
  5. import cn.hutool.http.HttpRequest;
  6. import cn.hutool.http.HttpResponse;
  7. import cn.hutool.json.JSONUtil;
  8. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  9. import com.cyksj.common.exception.BusinessRuntimeException;
  10. import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
  11. import com.cyksj.common.util.Jsons;
  12. import com.cyksj.mapper.*;
  13. import com.cyksj.model.entity.*;
  14. import com.cyksj.model.response.recharge.RedeemCloudCdkQueryResponse;
  15. import com.cyksj.model.response.recharge.RedeemCloudExchangeResponse;
  16. import com.cyksj.model.response.recharge.RedeemCloudTaskResultResponse;
  17. import com.cyksj.redis.RedisService;
  18. import com.cyksj.server.recharge.dto.TaskResult;
  19. import com.cyksj.service.chatgpt.GptUserRechargeRecordService;
  20. import com.cyksj.service.recharge.GptRechargeCardKeyChannelService;
  21. import com.cyksj.service.recharge.RedeemCloudRechargeService;
  22. import lombok.RequiredArgsConstructor;
  23. import lombok.extern.slf4j.Slf4j;
  24. import org.springframework.stereotype.Service;
  25. import java.util.Date;
  26. @Service
  27. @RequiredArgsConstructor
  28. @Slf4j
  29. public class RedeemCloudRechargeServiceImpl implements RedeemCloudRechargeService {
  30. private final GptRechargeCardKeyChannelService gptRechargeCardKeyChannelService;
  31. private final GroupsRelationMapper relationMapper;
  32. private final GroupsMapper groupsMapper;
  33. private final GoodsDonSkuMapper skuMapper;
  34. private final RedisService redisService;
  35. private final OrderDonMapper orderDonMapper;
  36. private final GptRechargeCardKeyMapper gptRechargeCardKeyMapper;
  37. private final GptUserRechargeRecordService gptUserRechargeRecordService;
  38. private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
  39. private static final int SUCCESS_CODE = 200;
  40. private static final int UNUSED_STATUS = 0;
  41. @Override
  42. public RedeemCloudCdkQueryResponse validateCardKey(String cardKey) throws Exception {
  43. String baseUrl = gptRechargeCardKeyChannelService.getCardKeyDomain(cardKey);
  44. String url = baseUrl + "/sys-cdk/query?cdkCode=" + cardKey;
  45. HttpResponse response = HttpRequest.get(url)
  46. .setConnectionTimeout(10000)
  47. .execute();
  48. String body = response.body();
  49. log.info("redeemcloud验证卡密结果: {}", body);
  50. return Jsons.parseObject(body, RedeemCloudCdkQueryResponse.class);
  51. }
  52. @Override
  53. public String confirmRecharge(GroupsRelation relation, String gptCardKey, CmsUser cmsUser) {
  54. Long relationId = relation.getId();
  55. RedeemCloudCdkQueryResponse validateResponse;
  56. try {
  57. validateResponse = validateCardKey(gptCardKey);
  58. } catch (Exception e) {
  59. if (cmsUser == null) {
  60. throw BusinessRuntimeException.getInstance("系统繁忙,请重试");
  61. }
  62. throw BusinessRuntimeException.getInstance("卡密:{0}错误", gptCardKey);
  63. }
  64. if (!isCardKeyAvailable(validateResponse)) {
  65. if (cmsUser == null) {
  66. throw BusinessRuntimeException.getInstance("系统繁忙,请重试");
  67. }
  68. throw BusinessRuntimeException.getInstance(getValidateErrorMessage(validateResponse, gptCardKey));
  69. }
  70. String accessToken = relation.getGptToken();
  71. String rechargeAccount = relation.getAccount();
  72. String taskId;
  73. try {
  74. taskId = submitTask(gptCardKey, accessToken);
  75. } catch (Exception e) {
  76. setRechargeRelationStatusAutoError(relation);
  77. if (cmsUser == null) {
  78. throw BusinessRuntimeException.getInstance("充值失败,请联系客服开通");
  79. }
  80. throw BusinessRuntimeException.getInstance("充值失败,{0}", e.getMessage());
  81. }
  82. String operator = cmsUser != null ? cmsUser.getNickname() : null;
  83. String password = relation.getPassword();
  84. relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
  85. .set(GroupsRelation::getAccount, rechargeAccount)
  86. .set(GroupsRelation::getOperator, operator)
  87. .set(StrUtil.isNotBlank(password), GroupsRelation::getPassword, password)
  88. .set(GroupsRelation::getRechargeStatus, GroupsRelation.RechargeStatus.recharging)
  89. .set(GroupsRelation::getCardKey, gptCardKey)
  90. .set(GroupsRelation::getGptToken, accessToken)
  91. .set(GroupsRelation::getGptTaskId, taskId)
  92. .eq(GroupsRelation::getId, relationId));
  93. relation.setRechargeStatus(GroupsRelation.RechargeStatus.recharging);
  94. relation.setCardKey(gptCardKey);
  95. relation.setOperator(operator);
  96. return taskId;
  97. }
  98. private boolean isCardKeyAvailable(RedeemCloudCdkQueryResponse validateResponse) {
  99. return validateResponse != null
  100. && validateResponse.getCode() != null
  101. && validateResponse.getCode() == SUCCESS_CODE
  102. && validateResponse.getData() != null
  103. && validateResponse.getData().getUseStatus() != null
  104. && validateResponse.getData().getUseStatus() == UNUSED_STATUS
  105. && Boolean.TRUE.equals(validateResponse.getData().getChannelEnabled());
  106. }
  107. private String getValidateErrorMessage(RedeemCloudCdkQueryResponse validateResponse, String cardKey) {
  108. if (validateResponse == null) {
  109. return "卡密错误:" + cardKey;
  110. }
  111. if (StrUtil.isNotBlank(validateResponse.getMessage())) {
  112. return validateResponse.getMessage();
  113. }
  114. return "卡密错误:" + cardKey;
  115. }
  116. private String submitTask(String cardKey, String rechargePayload) {
  117. log.info("开始提交redeemcloud任务 - 卡密: {}", cardKey);
  118. try {
  119. String baseUrl = gptRechargeCardKeyChannelService.getCardKeyDomain(cardKey);
  120. String url = baseUrl + "/sys-cdk/exchange?cdkCode=" + cardKey;
  121. String requestBody = normalizeRechargePayload(rechargePayload);
  122. HttpResponse response = HttpRequest.post(url)
  123. .setConnectionTimeout(60000)
  124. .header("accept", "*/*")
  125. .header("accept-language", "zh-CN")
  126. .header("content-type", "application/json")
  127. .header("origin", "https://redeemcloud.com")
  128. .header("referer", "https://redeemcloud.com/zh")
  129. .header("sec-ch-ua", "\"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"150\", \"Microsoft Edge\";v=\"150\"")
  130. .header("sec-ch-ua-mobile", "?0")
  131. .header("sec-ch-ua-platform", "\"Windows\"")
  132. .header("sec-fetch-dest", "empty")
  133. .header("sec-fetch-mode", "cors")
  134. .header("sec-fetch-site", "same-origin")
  135. .header("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0")
  136. .header("x-product-code", "GPT")
  137. .body(requestBody)
  138. .execute();
  139. String body = response.body();
  140. log.info("redeemcloud任务提交响应: {}", body);
  141. RedeemCloudExchangeResponse exchangeResponse = Jsons.parseObject(body, RedeemCloudExchangeResponse.class);
  142. if (exchangeResponse != null && exchangeResponse.getCode() != null && exchangeResponse.getCode() == SUCCESS_CODE && StrUtil.isNotBlank(exchangeResponse.getData())) {
  143. return exchangeResponse.getData();
  144. }
  145. String errorMessage = exchangeResponse != null && StrUtil.isNotBlank(exchangeResponse.getMessage()) ? exchangeResponse.getMessage() : "提交任务失败";
  146. throw BusinessRuntimeException.getInstance(errorMessage);
  147. } catch (Exception e) {
  148. log.error("提交redeemcloud任务失败: {}", e.getMessage(), e);
  149. throw new RuntimeException("提交任务失败: " + e.getMessage(), e);
  150. }
  151. }
  152. private String normalizeRechargePayload(String rechargePayload) {
  153. if (StrUtil.isBlank(rechargePayload) || !StrUtil.startWith(rechargePayload.trim(), "{")) {
  154. throw BusinessRuntimeException.getInstance("redeemcloud提交任务需要完整充值参数JSON");
  155. }
  156. return JSONUtil.parseObj(rechargePayload).toString();
  157. }
  158. @Override
  159. public TaskResult getTaskResult(GroupsRelation relation) {
  160. String cardKey = relation.getCardKey();
  161. String taskId = relation.getGptTaskId();
  162. try {
  163. RedeemCloudTaskResultResponse taskResultResponse = queryTaskResult(cardKey, taskId);
  164. TaskResult taskResult = convertTaskResult(taskResultResponse);
  165. resetGroupsRelationRechargeStatus(relation, taskResult.getStatus(), taskResult.getError());
  166. return taskResult;
  167. } catch (Exception e) {
  168. log.error("查询redeemcloud任务结果失败: {}", e.getMessage(), e);
  169. throw new RuntimeException("获取任务结果失败: " + e.getMessage(), e);
  170. }
  171. }
  172. private RedeemCloudTaskResultResponse queryTaskResult(String cardKey, String taskId) throws Exception {
  173. String baseUrl = gptRechargeCardKeyChannelService.getCardKeyDomain(cardKey);
  174. String url = baseUrl + "/sys-cdk/exchange/result?taskId=" + taskId;
  175. HttpResponse response = HttpRequest.get(url)
  176. .setConnectionTimeout(10000)
  177. .execute();
  178. String body = response.body();
  179. log.info("redeemcloud任务查询响应: {}", body);
  180. return Jsons.parseObject(body, RedeemCloudTaskResultResponse.class);
  181. }
  182. private TaskResult convertTaskResult(RedeemCloudTaskResultResponse taskResultResponse) {
  183. TaskResult result = new TaskResult();
  184. if (taskResultResponse == null || taskResultResponse.getCode() == null || taskResultResponse.getCode() != SUCCESS_CODE) {
  185. result.setStatus("failed");
  186. result.setError(taskResultResponse != null ? taskResultResponse.getMessage() : "任务查询失败");
  187. return result;
  188. }
  189. if (taskResultResponse.getData() == null) {
  190. result.setStatus("recharging");
  191. result.setResult(taskResultResponse.getMessage());
  192. return result;
  193. }
  194. String status = taskResultResponse.getData().getStatus();
  195. if ("COMPLETED".equalsIgnoreCase(status)) {
  196. result.setStatus("completed");
  197. } else if ("FAILED".equalsIgnoreCase(status) || "ERROR".equalsIgnoreCase(status)) {
  198. result.setStatus("failed");
  199. } else if (taskResultResponse.getData().getUseStatus() != null && taskResultResponse.getData().getUseStatus() != UNUSED_STATUS) {
  200. result.setStatus("completed");
  201. } else if (StrUtil.isNotBlank(taskResultResponse.getData().getEmail()) || StrUtil.isNotBlank(taskResultResponse.getData().getAccountId())) {
  202. result.setStatus("completed");
  203. } else {
  204. result.setStatus("recharging");
  205. }
  206. result.setResult(status);
  207. result.setError(taskResultResponse.getData().getErrorMessage());
  208. return result;
  209. }
  210. public void setRechargeRelationStatusAutoError(GroupsRelation relation) {
  211. TASK_EXECUTOR.execute(() -> relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
  212. .set(GroupsRelation::getAccount, relation.getAccount())
  213. .set(GroupsRelation::getGptToken, relation.getGptToken())
  214. .set(GroupsRelation::getRechargeStatus, GroupsRelation.RechargeStatus.recharge_error)
  215. .eq(GroupsRelation::getId, relation.getId())));
  216. }
  217. private void resetGroupsRelationRechargeStatus(GroupsRelation relation, String status, String result) {
  218. if (!"completed".equals(status) && !"failed".equals(status) && !"unknown".equals(status)) {
  219. return;
  220. }
  221. relation = relationMapper.selectById(relation.getId());
  222. if (relation == null || relation.getRechargeStatus() != GroupsRelation.RechargeStatus.recharging) {
  223. return;
  224. }
  225. String taskId = relation.getGptTaskId();
  226. Integer rechargeRemainNum = relation.getRechargeRemainNum();
  227. GroupsRelation.RechargeStatus rechargeStatus = "completed".equals(status) ? GroupsRelation.RechargeStatus.complete : GroupsRelation.RechargeStatus.recharge_error;
  228. String cardKey = relation.getCardKey();
  229. GptRechargeCardKey gptRechargeCardKey = gptRechargeCardKeyMapper.selectOne(Wrappers.lambdaQuery(GptRechargeCardKey.class)
  230. .eq(GptRechargeCardKey::getCardKey, cardKey)
  231. .last("limit 1"));
  232. if (rechargeStatus == GroupsRelation.RechargeStatus.recharge_error && gptRechargeCardKey != null) {
  233. gptRechargeCardKeyMapper.update(null, Wrappers.lambdaUpdate(GptRechargeCardKey.class)
  234. .set(GptRechargeCardKey::getStatus, Boolean.FALSE)
  235. .set(GptRechargeCardKey::getOrderId, null)
  236. .set(GptRechargeCardKey::getOrderNo, null)
  237. .eq(GptRechargeCardKey::getId, gptRechargeCardKey.getId())
  238. .eq(GptRechargeCardKey::getStatus, Boolean.TRUE));
  239. }
  240. if (rechargeStatus == GroupsRelation.RechargeStatus.recharge_error
  241. && !redisService.setNx(RedisService.key.GPT_RECHARGE_RECOVER_KEY.getName() + taskId, taskId, RedisService.key.GPT_RECHARGE_RECOVER_KEY.getTimeout())) {
  242. return;
  243. }
  244. Date startTime = relation.getStartTime();
  245. DateTime expiryTime = null;
  246. if (rechargeStatus == GroupsRelation.RechargeStatus.complete && startTime == null) {
  247. GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
  248. if (groupsTrips != null) {
  249. startTime = DateTime.now();
  250. GoodsDonSku sku = skuMapper.selectById(groupsTrips.getSkuId());
  251. if (sku.getDays() != null && sku.getDays() > 0) {
  252. expiryTime = DateUtil.offsetDay(startTime, sku.getDays());
  253. } else {
  254. expiryTime = DateUtil.offsetMonth(startTime, sku.getMonths());
  255. }
  256. }
  257. }
  258. String rechargeErrorInfo = rechargeStatus == GroupsRelation.RechargeStatus.recharge_error ? result : null;
  259. int update = relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
  260. .set(rechargeStatus == GroupsRelation.RechargeStatus.recharge_error, GroupsRelation::getRechargeRemainNum, rechargeRemainNum + 1)
  261. .set(GroupsRelation::getRechargeStatus, rechargeStatus)
  262. .set(expiryTime != null, GroupsRelation::getStartTime, startTime)
  263. .set(expiryTime != null, GroupsRelation::getExpiryTime, expiryTime)
  264. .set(StrUtil.isNotEmpty(rechargeErrorInfo), GroupsRelation::getRechargeErrorInfo, rechargeErrorInfo)
  265. .eq(GroupsRelation::getId, relation.getId())
  266. .le(GroupsRelation::getRechargeRemainNum, relation.getRechargeNum())
  267. .eq(GroupsRelation::getRechargeStatus, GroupsRelation.RechargeStatus.recharging));
  268. if (update == 0) {
  269. return;
  270. }
  271. if (rechargeStatus == GroupsRelation.RechargeStatus.complete) {
  272. String remark = StrUtil.isNotBlank(relation.getOperator()) ? "后台自动代充" : "自动代充";
  273. gptUserRechargeRecordService.recordGptUserRechargeNum(relation.getCardKey(), relation.getUserId(), relation.getId(), relation.getAccount(), -1, relation.getOperator(), remark);
  274. if (gptRechargeCardKey != null) {
  275. Long orderId = gptRechargeCardKey.getOrderId();
  276. OrderDon orderDon = orderDonMapper.selectById(orderId);
  277. if (orderDon != null && orderDon.getStatus() == OrderDon.Status.hasPayment) {
  278. orderDon.setStatus(OrderDon.Status.complete);
  279. orderDonMapper.updateById(orderDon);
  280. }
  281. }
  282. }
  283. }
  284. }