zoujiajian 1 месяц назад
Родитель
Сommit
bd3e69d96f

+ 21 - 0
netflix-dao/src/main/java/com/cyksj/model/response/GptApiQuotaTagResp.java

@@ -0,0 +1,21 @@
+package com.cyksj.model.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 指定商品的 GPT API 额度领取状态。
+ */
+@Getter
+@Setter
+public class GptApiQuotaTagResp {
+
+    /** 查询的商品 ID。 */
+    private Long goodsId;
+
+    /** 是否已领取该商品的 5 元 API 额度。 */
+    private Boolean received5ApiQuota;
+
+    /** 是否已领取该商品的 50 元 API 额度。 */
+    private Boolean received50ApiQuota;
+}

+ 4 - 0
netflix-service/src/main/java/com/cyksj/service/relation/GroupRelationFrontService.java

@@ -14,6 +14,7 @@ import com.cyksj.model.response.NetflixErrorAccountStatus;
 import com.cyksj.model.response.NetflixRecoverResp;
 import com.cyksj.model.response.NetflixRefundInfoResp;
 import com.cyksj.model.response.ReceiveGptActivityTicketResp;
+import com.cyksj.model.response.GptApiQuotaTagResp;
 import com.cyksj.model.views.*;
 import com.ejlchina.searcher.SearchResult;
 
@@ -141,5 +142,8 @@ public interface GroupRelationFrontService {
 
 	ReceiveGptActivityTicketResp receiveGptActivityTicket(long userId, Long relationId) throws Exception;
 
+	/** 获取指定商品的 GPT API 额度领取状态。 */
+	GptApiQuotaTagResp getGptApiQuotaTagStatus(long userId, Long goodsId);
+
 	ReceiveGptActivityTicketResp receiveStudentCertGptApiQuotaTicket(long userId, Integer quota) throws Exception;
 }

+ 33 - 7
netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationFrontServiceImpl.java

@@ -48,6 +48,7 @@ import com.cyksj.model.response.NetflixErrorAccountStatus;
 import com.cyksj.model.response.NetflixRecoverResp;
 import com.cyksj.model.response.NetflixRefundInfoResp;
 import com.cyksj.model.response.ReceiveGptActivityTicketResp;
+import com.cyksj.model.response.GptApiQuotaTagResp;
 import com.cyksj.model.views.*;
 import com.cyksj.redis.RedisService;
 import com.cyksj.service.chatgpt.ChatGptAccountService;
@@ -604,7 +605,7 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
         // PLUS 仅 skuNum == 1 的独立规格可领;与实际领取接口共用资格校验。
         ticket.setCanReceiveGptApiQuota(isGptApiQuotaSourceTicket(sourceOrder, ticket.getGoodsId(), ticket.getSkuNum())
                 && !hasReceivedGptApiQuota(userIds, ticket.getGoodsId(), yearQuota)
-                && !hasPendingGptApiQuota(userIds, ticket.getGoodsId())
+                && !hasPendingGptApiQuota(userIds, ticket.getGoodsId(), yearQuota)
                 && (!yearQuota || hasNewGptApiQuotaPurchase(userIds, ticket.getGoodsId(), sourceOrder)));
 	}
 
@@ -620,19 +621,44 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
         if (yearQuota) {
             // 独立年付、代充年付按商品各限领一次50元;原5元不占用50元资格。
             query.ge(TicketRedemptionCodeRecord::getQuota, YEAR_RECHARGE_API_QUOTA);
+        } else {
+            // 5元与50元分开限领;兼容未记录quota的历史5元记录。
+            query.and(wrapper -> wrapper.lt(TicketRedemptionCodeRecord::getQuota, YEAR_RECHARGE_API_QUOTA)
+                    .or().isNull(TicketRedemptionCodeRecord::getQuota));
         }
-        // 普通规格仍按当前商品的任意已发额度限制一次。
         return ticketRedemptionCodeRecordMapper.selectCount(query) > 0;
     }
 
-    private boolean hasPendingGptApiQuota(List<Long> userIds, Long sourceGoodsId) {
-        // 同一商品发放中或结果不确定时禁止重发,不占用另一商品的领取资格。
-        return ticketRedemptionCodeRecordMapper.selectCount(Wrappers.lambdaQuery(TicketRedemptionCodeRecord.class)
+    private boolean hasPendingGptApiQuota(List<Long> userIds, Long sourceGoodsId, boolean yearQuota) {
+        // 只阻止同一商品、同一额度档的重复发放;旧processing金额未知时保守拦截。
+        LambdaQueryWrapper<TicketRedemptionCodeRecord> query = 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)
                 .eq(TicketRedemptionCodeRecord::getSourceGoodsId, sourceGoodsId)
-                .eq(TicketRedemptionCodeRecord::getStatus, "processing")) > 0;
+                .eq(TicketRedemptionCodeRecord::getStatus, "processing");
+        if (yearQuota) {
+            query.and(wrapper -> wrapper.ge(TicketRedemptionCodeRecord::getQuota, YEAR_RECHARGE_API_QUOTA)
+                    .or().isNull(TicketRedemptionCodeRecord::getQuota));
+        } else {
+            query.and(wrapper -> wrapper.lt(TicketRedemptionCodeRecord::getQuota, YEAR_RECHARGE_API_QUOTA)
+                    .or().isNull(TicketRedemptionCodeRecord::getQuota));
+        }
+        return ticketRedemptionCodeRecordMapper.selectCount(query) > 0;
+    }
+
+    @Override
+    public GptApiQuotaTagResp getGptApiQuotaTagStatus(long userId, Long goodsId) {
+        List<Long> userIds = userBindRelationService.getRelationUserIdList(userId, null);
+        if (CollUtil.isEmpty(userIds)) {
+            userIds = List.of(userId);
+        }
+        GptApiQuotaTagResp resp = new GptApiQuotaTagResp();
+        resp.setGoodsId(goodsId);
+        // 仅查询传入商品的记录,5 元和 50 元分开返回,另一商品不影响结果。
+        resp.setReceived5ApiQuota(hasReceivedGptApiQuota(userIds, goodsId, false));
+        resp.setReceived50ApiQuota(hasReceivedGptApiQuota(userIds, goodsId, true));
+        return resp;
     }
 
     private boolean hasNewGptApiQuotaPurchase(List<Long> userIds, Long sourceGoodsId, OrderDon sourceOrder) {
@@ -674,7 +700,7 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
             if (hasReceivedGptApiQuota(userIds, record.getSourceGoodsId(), yearQuota)) {
                 throw BusinessRuntimeException.getInstance(yearQuota ? "您已领取50元API额度" : "您已领取");
             }
-            if (hasPendingGptApiQuota(userIds, record.getSourceGoodsId())) {
+            if (hasPendingGptApiQuota(userIds, record.getSourceGoodsId(), yearQuota)) {
                 throw BusinessRuntimeException.getInstance("额度正在发放或结果待确认,请勿重复领取;长时间未到账请联系客服");
             }
             if (yearQuota && !hasNewGptApiQuotaPurchase(userIds, record.getSourceGoodsId(), sourceOrder)) {

+ 20 - 0
netflix-web/src/main/java/com/cyksj/web/controller/goods/GoodsDonController.java

@@ -25,11 +25,13 @@ import com.cyksj.mapper.sys.SysConfigMapper;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.request.ClickRecordReq;
 import com.cyksj.model.request.GoodsSubscribeAlertReq;
+import com.cyksj.model.response.GptApiQuotaTagResp;
 import com.cyksj.model.views.*;
 import com.cyksj.redis.RedisService;
 import com.cyksj.service.goods.GoodsDonFrontService;
 import com.cyksj.service.goods.UserSubscribeAlertGoodsRecordService;
 import com.cyksj.service.home.ClickRecordFrontService;
+import com.cyksj.service.relation.GroupRelationFrontService;
 import com.cyksj.service.shop.YhShopFrontService;
 import com.cyksj.service.translations.BackInfoTranslationsFrontService;
 import com.cyksj.service.user.UserBindRelationService;
@@ -100,6 +102,8 @@ public class GoodsDonController {
 
 	private final UserSubscribeAlertGoodsRecordService userSubscribeAlertGoodsRecordService;
 
+	private final GroupRelationFrontService groupRelationFrontService;
+
 	/**
      * 获取平台商品分类
      */
@@ -404,6 +408,22 @@ public class GoodsDonController {
     }
 
 
+	/**
+	 * 获取指定商品的 GPT API 额度领取状态。
+	 */
+	@GetMapping("/get/gptApiTag")
+	public Result<?> getGptApiQuotaTag(@RequestParam Long goodsId) {
+		Long userId = StpUserUtil.getUserIdAfterLogin();
+		if (userId == null) {
+			GptApiQuotaTagResp resp = new GptApiQuotaTagResp();
+			resp.setGoodsId(goodsId);
+			resp.setReceived5ApiQuota(false);
+			resp.setReceived50ApiQuota(false);
+			return GatewayResponse.SUCCESS.newBuilder().toResult(resp);
+		}
+		return GatewayResponse.SUCCESS.newBuilder().toResult(groupRelationFrontService.getGptApiQuotaTagStatus(userId, goodsId));
+	}
+
 	/**
 	 * 获取商品详情
 	 */