فهرست منبع

fix API兑换码重试获取

zoujiajian 4 روز پیش
والد
کامیت
34100e8d57

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

@@ -142,6 +142,9 @@ public interface GroupRelationFrontService {
 
 	ReceiveGptActivityTicketResp receiveGptActivityTicket(long userId, Long relationId) throws Exception;
 
+	/** 重试处于 error 或 processing 状态的 GPT API 额度兑换码发放。 */
+	String retryGptActivityRedeemCode(long userId, Long relationId) throws Exception;
+
 	/** 获取指定商品的 GPT API 额度领取状态。 */
 	GptApiQuotaTagResp getGptApiQuotaTagStatus(long userId, Long goodsId);
 

+ 104 - 0
netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationFrontServiceImpl.java

@@ -3059,6 +3059,110 @@ public class GroupRelationFrontServiceImpl implements GroupRelationFrontService
 		}
 	}
 
+
+    @Override
+    public String retryGptActivityRedeemCode(long userId, Long relationId) throws Exception {
+        List<Long> userIds = userBindRelationService.getRelationUserIdList(userId, null);
+        if (CollUtil.isEmpty(userIds)) {
+            userIds = List.of(userId);
+        }
+        String lockKey = String.format("%s:%s", RedisKey.GPT_API_QUOTA_KEY, Collections.min(userIds));
+        String lockToken = UUID.randomUUID().toString();
+        if (!redisService.setNx(lockKey, lockToken, 60L)) {
+            throw BusinessRuntimeException.getInstance("兑换码正在发放,请稍后再试");
+        }
+        try {
+            TicketRedemptionCodeRecord record = ticketRedemptionCodeRecordMapper.selectOne(Wrappers.lambdaQuery(TicketRedemptionCodeRecord.class)
+                    .eq(TicketRedemptionCodeRecord::getRelationId, relationId)
+                    .in(TicketRedemptionCodeRecord::getUserId, userIds)
+                    .eq(TicketRedemptionCodeRecord::getSkuId, Constant.GPT_ACTIVITY_GIFT_SKU_ID)
+                    .in(TicketRedemptionCodeRecord::getStatus, List.of("error", "processing"))
+                    .isNotNull(TicketRedemptionCodeRecord::getErrorMsg)
+                    .ne(TicketRedemptionCodeRecord::getErrorMsg, "")
+                    .orderByDesc(TicketRedemptionCodeRecord::getId)
+                    .last("limit 1"));
+            if (record == null) {
+                throw BusinessRuntimeException.getInstance("兑换码不存在,请联系客服");
+            }
+
+            try {
+                // 老记录未落 quota/redeemName 时,按原领取规则还原参数。
+                String redeemName = resolveRetryRedeemName(record);
+                Integer quota = resolveRetryGptApiQuota(record);
+                GptActivityRedeemResult redeemResult = requestGptActivityRedeemCode(
+                        Constant.AI_API_SVC_DOMAIN,
+                        redeemName,
+                        quota);
+                record.setRedeemCode(redeemResult.getRedeemCode());
+                record.setQuota(redeemResult.getQuota());
+                record.setRedeemCount(0);
+                record.setExpiredTime(redeemResult.getExpiredTime());
+                record.setApiResponse(redeemResult.getApiResponse());
+                record.setStatus("success");
+                record.setErrorMsg(null);
+                if (ticketRedemptionCodeRecordMapper.updateById(record) != 1) {
+                    throw BusinessRuntimeException.getInstance("额度发放结果待确认,请联系客服,勿重复领取");
+                }
+                return redeemResult.getRedeemCode();
+            } catch (Exception e) {
+                // 远端调用可能已成功但响应丢失,继续保留 processing;仅有错误信息时允许下次查询重试。
+                record.setStatus("processing");
+                record.setErrorMsg(StringUtil.getErrorText(e));
+                try {
+                    ticketRedemptionCodeRecordMapper.updateById(record);
+                } catch (Exception persistenceError) {
+                    e.addSuppressed(persistenceError);
+                    log.error("GPT API quota retry result persistence failed, recordId={}", record.getId(), persistenceError);
+                }
+                throw e;
+            }
+        } finally {
+            redisService.deleteIfValueEquals(lockKey, lockToken);
+        }
+    }
+
+
+
+    private Integer resolveRetryGptApiQuota(TicketRedemptionCodeRecord record) {
+        if (record.getQuota() != null && record.getQuota() > 0) {
+            return record.getQuota();
+        }
+        //学生认证
+        if (RedisKey.STUDENT_CERT_GPT_API_QUOTA_KEY.equals(record.getSourceType())) {
+            return DEFAULT_API_QUOTA;
+        }
+        //为null 检查手动兑换
+        throw BusinessRuntimeException.getInstance("兑换码不存在,请联系客服");
+
+//        Long userId = record.getUserId();
+//        Long relationId = record.getRelationId();
+//        GroupsRelation relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+//                .eq(GroupsRelation::getId, relationId)
+//                .eq(GroupsRelation::getUserId, userId)
+//                .in(GroupsRelation::getStatus, List.of(GroupsRelation.Status.validity.name(), GroupsRelation.Status.outside.name()))
+//                .last("limit 1"));
+//        if (relation == null) {
+//            throw BusinessRuntimeException.getInstance("车票不存在");
+//        }
+//        GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
+//        GoodsDonSku goodsDonSku = goodsDonSkuMapper.selectById(groupsTrips.getSkuId());
+//        boolean yearQuota = isYearRechargeSku(goodsDonSku.getGoodsId(), goodsDonSku.getMonths(), goodsDonSku.getNum());
+//        return yearQuota ? YEAR_RECHARGE_API_QUOTA : DEFAULT_API_QUOTA;
+    }
+
+    private String resolveRetryRedeemName(TicketRedemptionCodeRecord record) {
+        if (StrUtil.isNotBlank(record.getRedeemName())) {
+            return record.getRedeemName();
+        }
+        if (RedisKey.GPT_API_QUOTA_KEY.equals(record.getSourceType())) {
+            return GPT_ACTIVITY_REDEEM_NAME;
+        }
+        if (RedisKey.STUDENT_CERT_GPT_API_QUOTA_KEY.equals(record.getSourceType())) {
+            return STUDENT_CERT_API_QUOTA_REDEEM_NAME;
+        }
+        throw BusinessRuntimeException.getInstance("兑换码不存在,请联系客服");
+    }
+
 	@Override
 	public ReceiveGptActivityTicketResp receiveStudentCertGptApiQuotaTicket(long userId, Integer quota) throws Exception {
 		List<Long> userIds = userBindRelationService.getRelationUserIdList(userId, null);

+ 6 - 4
netflix-web/src/main/java/com/cyksj/web/controller/group/GroupRelationController.java

@@ -247,7 +247,7 @@ public class GroupRelationController {
      * 获取车票api额度兑换码
      */
     @GetMapping("/get/redemption/code/{relationId}")
-    public Result<String> getRedemptionCode(@PathVariable Long relationId) {
+    public Result<String> getRedemptionCode(@PathVariable Long relationId) throws Exception {
         long userId = StpUserUtil.getLoginIdAsLong();
         List<Long> userIds = userBindRelationService.getRelationUserIdList(userId, null);
         TicketRedemptionCodeRecord record = ticketRedemptionCodeRecordMapper.selectOne(Wrappers.lambdaQuery(TicketRedemptionCodeRecord.class)
@@ -255,10 +255,12 @@ public class GroupRelationController {
                 .in(TicketRedemptionCodeRecord::getUserId, userIds)
                 .eq(TicketRedemptionCodeRecord::getStatus, "success")
                 .last("limit 1"));
-        if (record == null || StrUtil.isBlank(record.getRedeemCode())) {
-            throw BusinessRuntimeException.getInstance("兑换码不存在");
+        if (record != null && StrUtil.isNotBlank(record.getRedeemCode())) {
+            return GatewayResponse.SUCCESS.newBuilder().toResult(record.getRedeemCode());
         }
-        return GatewayResponse.SUCCESS.newBuilder().toResult(record.getRedeemCode());
+        // 仅当 error/processing 记录已写入 errorMsg 时才重试,避免对正常处理中记录重复发码。
+        String redeemCode = groupRelationFrontService.retryGptActivityRedeemCode(userId, relationId);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(redeemCode);
     }
 
     /**