|
@@ -9,6 +9,7 @@ import cn.hutool.core.lang.Assert;
|
|
|
import cn.hutool.core.thread.ThreadUtil;
|
|
import cn.hutool.core.thread.ThreadUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import cn.hutool.extra.servlet.ServletUtil;
|
|
import cn.hutool.extra.servlet.ServletUtil;
|
|
|
|
|
+import cn.hutool.http.HttpUtil;
|
|
|
import cn.hutool.json.JSONObject;
|
|
import cn.hutool.json.JSONObject;
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
@@ -21,6 +22,7 @@ import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
|
|
|
import com.cyksj.common.util.GoogleGenerator;
|
|
import com.cyksj.common.util.GoogleGenerator;
|
|
|
import com.cyksj.common.util.Jsons;
|
|
import com.cyksj.common.util.Jsons;
|
|
|
import com.cyksj.common.util.StringUtil;
|
|
import com.cyksj.common.util.StringUtil;
|
|
|
|
|
+import com.cyksj.dto.RedisKey;
|
|
|
import com.cyksj.dto.Result;
|
|
import com.cyksj.dto.Result;
|
|
|
import com.cyksj.enums.GatewayResponse;
|
|
import com.cyksj.enums.GatewayResponse;
|
|
|
import com.cyksj.mapper.*;
|
|
import com.cyksj.mapper.*;
|
|
@@ -37,9 +39,11 @@ import com.cyksj.model.response.GptStandbyResp;
|
|
|
import com.cyksj.model.response.NetflixErrorAccountStatus;
|
|
import com.cyksj.model.response.NetflixErrorAccountStatus;
|
|
|
import com.cyksj.model.response.NetflixRecoverResp;
|
|
import com.cyksj.model.response.NetflixRecoverResp;
|
|
|
import com.cyksj.model.response.NetflixRefundInfoResp;
|
|
import com.cyksj.model.response.NetflixRefundInfoResp;
|
|
|
|
|
+import com.cyksj.model.response.ReceiveGptActivityTicketResp;
|
|
|
import com.cyksj.model.views.*;
|
|
import com.cyksj.model.views.*;
|
|
|
import com.cyksj.redis.RedisService;
|
|
import com.cyksj.redis.RedisService;
|
|
|
import com.cyksj.service.chatgpt.ChatGptAuthService;
|
|
import com.cyksj.service.chatgpt.ChatGptAuthService;
|
|
|
|
|
+import com.cyksj.service.exchange.ExchangeCodeService;
|
|
|
import com.cyksj.service.mange.CmsOrderDonService;
|
|
import com.cyksj.service.mange.CmsOrderDonService;
|
|
|
import com.cyksj.service.mange.coupon.CouponCommonService;
|
|
import com.cyksj.service.mange.coupon.CouponCommonService;
|
|
|
import com.cyksj.service.mange.ticket.TicketAdvertiseConfigClickRecordService;
|
|
import com.cyksj.service.mange.ticket.TicketAdvertiseConfigClickRecordService;
|
|
@@ -83,11 +87,14 @@ import java.util.stream.Collectors;
|
|
|
@RestController
|
|
@RestController
|
|
|
@RequestMapping("/applets/group/relation")
|
|
@RequestMapping("/applets/group/relation")
|
|
|
public class GroupRelationController {
|
|
public class GroupRelationController {
|
|
|
|
|
+ private static final Date GPT_ACTIVITY_PAY_TIME_BEGIN = DateUtil.parse("2026-05-03 00:00:00");
|
|
|
|
|
|
|
|
private final BeanSearcher beanSearcher;
|
|
private final BeanSearcher beanSearcher;
|
|
|
|
|
|
|
|
private final OrderDonService orderDonService;
|
|
private final OrderDonService orderDonService;
|
|
|
|
|
|
|
|
|
|
+ private final ExchangeCodeService exchangeCodeService;
|
|
|
|
|
+
|
|
|
private final GroupsRelationMapper relationMapper;
|
|
private final GroupsRelationMapper relationMapper;
|
|
|
|
|
|
|
|
private final TicketRedemptionCodeRecordMapper ticketRedemptionCodeRecordMapper;
|
|
private final TicketRedemptionCodeRecordMapper ticketRedemptionCodeRecordMapper;
|
|
@@ -265,6 +272,106 @@ public class GroupRelationController {
|
|
|
return GatewayResponse.SUCCESS.newBuilder().toResult(record.getRedeemCode());
|
|
return GatewayResponse.SUCCESS.newBuilder().toResult(record.getRedeemCode());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 购买GPT活动API兑换码额度
|
|
|
|
|
+ */
|
|
|
|
|
+ @PostMapping("/receive/code/quota")
|
|
|
|
|
+ @NoSubmit
|
|
|
|
|
+ public Result<ReceiveGptActivityTicketResp> receiveGptActivityTicket(@RequestBody @Validated ReceiveGptActivityTicketReq req) throws Exception {
|
|
|
|
|
+ long userId = StpUserUtil.getLoginIdAsLong();
|
|
|
|
|
+ List<Long> userIds = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
|
|
+ if (CollUtil.isEmpty(userIds)) {
|
|
|
|
|
+ userIds = List.of(userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ Long sourceRelationId = req.getRelationId();
|
|
|
|
|
+ String lockKey = String.format("%s:%s", RedisKey.GPT_API_QUOTA_KEY, Collections.min(userIds));
|
|
|
|
|
+ if (!redisService.setNx(lockKey, userId, 60L)) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("领取中,请稍后再试");
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+
|
|
|
|
|
+ GroupsRelationView sourceRelation = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder()
|
|
|
|
|
+ .field(GroupsRelationView::getId, sourceRelationId)
|
|
|
|
|
+ .field(GroupsRelationView::getUserId, userIds).op(Operator.InList)
|
|
|
|
|
+ .field(GroupsRelationView::getStatus, List.of(GroupsRelation.Status.validity, GroupsRelation.Status.outside)).op(Operator.InList)
|
|
|
|
|
+ .build());
|
|
|
|
|
+ if (sourceRelation == null || (sourceRelation.getExpiryTime() != null && sourceRelation.getExpiryTime().before(DateTime.now()))) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("车票不存在或已过期");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ GoodsDonSku sourceSku = goodsDonSkuMapper.selectById(sourceRelation.getSkuId());
|
|
|
|
|
+ if (sourceSku == null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("车票规格不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!Constant.GPT_RECHARGE_GOODS_ID.equals(sourceRelation.getGoodsId())
|
|
|
|
|
+ && !(Constant.GPT_PLUS_GID.equals(sourceRelation.getGoodsId()) && Objects.equals(sourceSku.getNum(), 1))) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("当前车票不符合领取条件");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ OrderDon sourceOrder = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
|
|
|
|
|
+ .eq(OrderDon::getRelationId, sourceRelationId)
|
|
|
|
|
+ .in(OrderDon::getUserId, userIds)
|
|
|
|
|
+ .notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
|
|
|
|
|
+ .and(wrapper -> wrapper.ge(OrderDon::getPayTime, GPT_ACTIVITY_PAY_TIME_BEGIN)
|
|
|
|
|
+ .or(sub -> sub.isNull(OrderDon::getPayTime).ge(OrderDon::getCreatedTime, GPT_ACTIVITY_PAY_TIME_BEGIN)))
|
|
|
|
|
+ .orderByDesc(OrderDon::getId)
|
|
|
|
|
+ .last("limit 1"));
|
|
|
|
|
+ if (sourceOrder == null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("仅限 2026-05-03 后购买的用户领取");
|
|
|
|
|
+ }
|
|
|
|
|
+ Integer receivedCount = ticketRedemptionCodeRecordMapper.selectCount(Wrappers.lambdaQuery(TicketRedemptionCodeRecord.class)
|
|
|
|
|
+ .in(TicketRedemptionCodeRecord::getUserId, userIds)
|
|
|
|
|
+ .eq(TicketRedemptionCodeRecord::getSkuId, Constant.GPT_ACTIVITY_GIFT_SKU_ID)
|
|
|
|
|
+ .eq(TicketRedemptionCodeRecord::getSourceType, RedisKey.GPT_API_QUOTA_KEY));
|
|
|
|
|
+ if (receivedCount > 0) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("您已领取过该权益");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TicketRedemptionCodeRecord record = new TicketRedemptionCodeRecord();
|
|
|
|
|
+ record.setUserId(userId);
|
|
|
|
|
+ record.setRelationId(sourceRelationId);
|
|
|
|
|
+ record.setGoodsId(sourceRelation.getGoodsId());
|
|
|
|
|
+ record.setSkuId(Constant.GPT_ACTIVITY_GIFT_SKU_ID);
|
|
|
|
|
+ record.setSourceType(RedisKey.GPT_API_QUOTA_KEY);
|
|
|
|
|
+ record.setRedeemName("GPT代充/独立赠送");
|
|
|
|
|
+ record.setStatus("processing");
|
|
|
|
|
+ record.setRelationExpiryTime(sourceRelation.getExpiryTime());
|
|
|
|
|
+ ticketRedemptionCodeRecordMapper.insert(record);
|
|
|
|
|
+ //兑换码API车票规格ID
|
|
|
|
|
+ GoodsDonSku giftSku = goodsDonSkuMapper.selectById(Constant.GPT_ACTIVITY_GIFT_SKU_ID);
|
|
|
|
|
+
|
|
|
|
|
+ GroupsRelation giftRelation = null;
|
|
|
|
|
+ try {
|
|
|
|
|
+ giftRelation = exchangeCodeService.getGroupTripsTicket(null, giftSku, userId, null, OrderDon.Type.EX_CODE, null);
|
|
|
|
|
+ GptActivityRedeemResult redeemResult = requestGptActivityRedeemCode(Constant.AI_API_SVC_DOMAIN, userId, sourceRelationId, giftRelation, giftSku);
|
|
|
|
|
+ record.setRelationId(giftRelation.getId());
|
|
|
|
|
+ record.setGoodsId(giftSku.getGoodsId());
|
|
|
|
|
+ record.setRedeemCode(redeemResult.getRedeemCode());
|
|
|
|
|
+ record.setStatus("success");
|
|
|
|
|
+ record.setRelationExpiryTime(giftRelation.getExpiryTime());
|
|
|
|
|
+ record.setApiResponse(redeemResult.getApiResponse());
|
|
|
|
|
+ ticketRedemptionCodeRecordMapper.updateById(record);
|
|
|
|
|
+
|
|
|
|
|
+ ReceiveGptActivityTicketResp resp = new ReceiveGptActivityTicketResp();
|
|
|
|
|
+ resp.setRelationId(giftRelation.getId());
|
|
|
|
|
+ resp.setRedeemCode(redeemResult.getRedeemCode());
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult(resp);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ record.setGoodsId(giftSku.getGoodsId());
|
|
|
|
|
+ record.setStatus("error");
|
|
|
|
|
+ record.setErrorMsg(StringUtil.getErrorText(e));
|
|
|
|
|
+ if (giftRelation != null) {
|
|
|
|
|
+ record.setRelationId(giftRelation.getId());
|
|
|
|
|
+ record.setRelationExpiryTime(giftRelation.getExpiryTime());
|
|
|
|
|
+ }
|
|
|
|
|
+ ticketRedemptionCodeRecordMapper.updateById(record);
|
|
|
|
|
+ throw e;
|
|
|
|
|
+ }
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ redisService.del(lockKey);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 记录查看ChatGPT车票密码
|
|
* 记录查看ChatGPT车票密码
|
|
|
*/
|
|
*/
|
|
@@ -1655,4 +1762,53 @@ public class GroupRelationController {
|
|
|
OrderRefundDto orderRefundDto = groupRelationFrontService.getYoutubeRefundInfo(userId, relationId);
|
|
OrderRefundDto orderRefundDto = groupRelationFrontService.getYoutubeRefundInfo(userId, relationId);
|
|
|
return GatewayResponse.SUCCESS.newBuilder().toResult(orderRefundDto);
|
|
return GatewayResponse.SUCCESS.newBuilder().toResult(orderRefundDto);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ private GptActivityRedeemResult requestGptActivityRedeemCode(String apiUrl, Long userId, Long sourceRelationId, GroupsRelation giftRelation, GoodsDonSku giftSku) throws Exception {
|
|
|
|
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
|
|
+ params.put("userId", userId);
|
|
|
|
|
+ params.put("sourceRelationId", sourceRelationId);
|
|
|
|
|
+ params.put("relationId", giftRelation.getId());
|
|
|
|
|
+ params.put("goodsId", giftSku.getGoodsId());
|
|
|
|
|
+ params.put("skuId", giftSku.getId());
|
|
|
|
|
+ String response = HttpUtil.post(apiUrl, params, 15000);
|
|
|
|
|
+ if (StrUtil.isBlank(response)) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("第三方兑换码接口返回为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<String, Object> responseMap = Jsons.toMap(response);
|
|
|
|
|
+ Object success = responseMap.get("success");
|
|
|
|
|
+ Object flag = responseMap.get("flag");
|
|
|
|
|
+ Object code = responseMap.get("code");
|
|
|
|
|
+ boolean failed = Boolean.FALSE.equals(success)
|
|
|
|
|
+ || Boolean.FALSE.equals(flag)
|
|
|
|
|
+ || (code != null && !"0".equals(code.toString()) && !"200".equals(code.toString()));
|
|
|
|
|
+ if (failed) {
|
|
|
|
|
+ Object msg = Optional.ofNullable(responseMap.get("msg")).orElse(responseMap.get("message"));
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance(msg == null ? "第三方兑换码接口调用失败" : msg.toString());
|
|
|
|
|
+ }
|
|
|
|
|
+ Object redeemCode = Optional.ofNullable(responseMap.get("redeemCode"))
|
|
|
|
|
+ .orElse(Optional.ofNullable(responseMap.get("redeem_code")).orElse(responseMap.get("redemptionCode")));
|
|
|
|
|
+ Object data = responseMap.get("data");
|
|
|
|
|
+ if (redeemCode == null && data instanceof Map) {
|
|
|
|
|
+ Map<?, ?> dataMap = (Map<?, ?>) data;
|
|
|
|
|
+ redeemCode = Optional.ofNullable(dataMap.get("redeemCode"))
|
|
|
|
|
+ .orElse(Optional.ofNullable(dataMap.get("redeem_code"))
|
|
|
|
|
+ .orElse(Optional.ofNullable(dataMap.get("redemptionCode")).orElse(dataMap.get("code"))));
|
|
|
|
|
+ } else if (redeemCode == null && data instanceof String) {
|
|
|
|
|
+ redeemCode = data;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (redeemCode == null || StrUtil.isBlank(redeemCode.toString())) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("第三方兑换码接口未返回兑换码");
|
|
|
|
|
+ }
|
|
|
|
|
+ GptActivityRedeemResult result = new GptActivityRedeemResult();
|
|
|
|
|
+ result.setRedeemCode(redeemCode.toString());
|
|
|
|
|
+ result.setApiResponse(response);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @lombok.Getter
|
|
|
|
|
+ @lombok.Setter
|
|
|
|
|
+ private static class GptActivityRedeemResult {
|
|
|
|
|
+ private String redeemCode;
|
|
|
|
|
+ private String apiResponse;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|