|
@@ -1,23 +1,53 @@
|
|
|
package com.cyksj.service.codex.impl;
|
|
package com.cyksj.service.codex.impl;
|
|
|
|
|
|
|
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
import cn.hutool.core.date.DateTime;
|
|
import cn.hutool.core.date.DateTime;
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
|
|
+import cn.hutool.core.lang.Assert;
|
|
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
import cn.hutool.http.HttpResponse;
|
|
import cn.hutool.http.HttpResponse;
|
|
|
import cn.hutool.http.HttpUtil;
|
|
import cn.hutool.http.HttpUtil;
|
|
|
import cn.hutool.http.Method;
|
|
import cn.hutool.http.Method;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.cyksj.common.constant.Constant;
|
|
import com.cyksj.common.constant.Constant;
|
|
|
import com.cyksj.common.exception.BusinessRuntimeException;
|
|
import com.cyksj.common.exception.BusinessRuntimeException;
|
|
|
|
|
+import com.cyksj.common.snowflake.Sequence;
|
|
|
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.mapper.*;
|
|
import com.cyksj.mapper.*;
|
|
|
|
|
+import com.cyksj.mapper.codex.CodexDeductRecordMapper;
|
|
|
|
|
+import com.cyksj.mapper.codex.CodexUserRenewExpiryRecordMapper;
|
|
|
|
|
+import com.cyksj.mapper.manage.coupon.CouponMapper;
|
|
|
|
|
+import com.cyksj.mapper.manage.coupon.CouponSkuMapper;
|
|
|
|
|
+import com.cyksj.mapper.manage.coupon.CouponUserMapper;
|
|
|
|
|
+import com.cyksj.mapper.manage.distribute.UserDistributeMapper;
|
|
|
|
|
+import com.cyksj.mapper.manage.distribute.UserDistributeSharedMapper;
|
|
|
|
|
+import com.cyksj.model.dto.CouponPopularizeDto;
|
|
|
import com.cyksj.model.entity.*;
|
|
import com.cyksj.model.entity.*;
|
|
|
|
|
+import com.cyksj.model.manage.views.GroupsRelationView;
|
|
|
|
|
+import com.cyksj.model.request.ClaudeCodeApiKeysReq;
|
|
|
|
|
+import com.cyksj.model.request.ClaudeCodeDelReq;
|
|
|
|
|
+import com.cyksj.model.request.CodexUpgradePayReq;
|
|
|
import com.cyksj.model.request.UpdateDailyLimitRequest;
|
|
import com.cyksj.model.request.UpdateDailyLimitRequest;
|
|
|
import com.cyksj.model.request.codex.CodexUserPackageReq;
|
|
import com.cyksj.model.request.codex.CodexUserPackageReq;
|
|
|
|
|
+import com.cyksj.model.response.CodexDeductResp;
|
|
|
import com.cyksj.model.response.claudecode.ClaudeCodeResp;
|
|
import com.cyksj.model.response.claudecode.ClaudeCodeResp;
|
|
|
|
|
+import com.cyksj.model.views.ClaudeCodeUserApiKeysView;
|
|
|
import com.cyksj.model.views.CodexUserInfoView;
|
|
import com.cyksj.model.views.CodexUserInfoView;
|
|
|
|
|
+import com.cyksj.model.views.CouponNewUserView;
|
|
|
|
|
+import com.cyksj.model.views.CouponUserView;
|
|
|
|
|
+import com.cyksj.redis.RedisService;
|
|
|
|
|
+import com.cyksj.service.api.ClaudeCodexApiService;
|
|
|
import com.cyksj.service.codex.CodexService;
|
|
import com.cyksj.service.codex.CodexService;
|
|
|
|
|
+import com.cyksj.service.coupon.CouponFontService;
|
|
|
|
|
+import com.cyksj.service.groups.GroupsFuncService;
|
|
|
import com.cyksj.service.user.UserBindRelationService;
|
|
import com.cyksj.service.user.UserBindRelationService;
|
|
|
|
|
+import com.ejlchina.searcher.BeanSearcher;
|
|
|
|
|
+import com.ejlchina.searcher.param.Operator;
|
|
|
|
|
+import com.ejlchina.searcher.util.MapBuilder;
|
|
|
|
|
+import com.ejlchina.searcher.util.MapUtils;
|
|
|
import com.github.rholder.retry.Retryer;
|
|
import com.github.rholder.retry.Retryer;
|
|
|
import com.github.rholder.retry.RetryerBuilder;
|
|
import com.github.rholder.retry.RetryerBuilder;
|
|
|
import com.github.rholder.retry.StopStrategies;
|
|
import com.github.rholder.retry.StopStrategies;
|
|
@@ -26,10 +56,14 @@ import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.dao.DuplicateKeyException;
|
|
import org.springframework.dao.DuplicateKeyException;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.math.RoundingMode;
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
import java.util.concurrent.ExecutionException;
|
|
import java.util.concurrent.ExecutionException;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 项目名: yhlxj11111111
|
|
* 项目名: yhlxj11111111
|
|
@@ -50,65 +84,41 @@ public class CodexServiceImpl implements CodexService {
|
|
|
|
|
|
|
|
private final GoodsDonSkuMapper skuMapper;
|
|
private final GoodsDonSkuMapper skuMapper;
|
|
|
|
|
|
|
|
|
|
+ private final GoodsDonMapper goodsDonMapper;
|
|
|
|
|
+
|
|
|
|
|
+ private final CouponMapper couponMapper;
|
|
|
|
|
+
|
|
|
private final GroupsRelationMapper relationMapper;
|
|
private final GroupsRelationMapper relationMapper;
|
|
|
|
|
|
|
|
private final GroupsMapper groupsMapper;
|
|
private final GroupsMapper groupsMapper;
|
|
|
|
|
|
|
|
- @Override
|
|
|
|
|
- public CodexUserInfoView getCodexUserInfo(Long userId) {
|
|
|
|
|
- List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
|
|
- CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"));
|
|
|
|
|
- // 查询Claude Code信息
|
|
|
|
|
- CodexUserInfoView.ClaudeCodeInfo claudeCodeInfo = getClaudeCodeInfo(userId);
|
|
|
|
|
|
|
+ private final CodexUserRenewExpiryRecordMapper codexUserRenewExpiryRecordMapper;
|
|
|
|
|
|
|
|
- if (codexUser == null) {
|
|
|
|
|
- //是否有claude code
|
|
|
|
|
- if (claudeCodeInfo != null && claudeCodeInfo.getCodePoints() != null) {
|
|
|
|
|
- GoodsDonSku sku = null;
|
|
|
|
|
- if (claudeCodeInfo.getRenewSkuId() != null) {
|
|
|
|
|
- sku = skuMapper.selectById(claudeCodeInfo.getRenewSkuId());
|
|
|
|
|
- }
|
|
|
|
|
- //续费升级规格不存在 或者 不赠送codex
|
|
|
|
|
- if (sku == null || !sku.getIsCodex()) {
|
|
|
|
|
- GroupsRelation relation = relationMapper.selectById(claudeCodeInfo.getRelationId());
|
|
|
|
|
- GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
|
|
|
|
|
- sku = skuMapper.selectById(groupsTrips.getSkuId());
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ private final BeanSearcher beanSearcher;
|
|
|
|
|
|
|
|
- //sku 存在并且开启赠送codex
|
|
|
|
|
- if (sku != null && sku.getIsCodex()) {
|
|
|
|
|
- CodexUserPackageReq codexPackageReq = CodexUserPackageReq.builder()
|
|
|
|
|
- .userId(claudeCodeInfo.getUserId())
|
|
|
|
|
- .planName(sku.getCodexPlanName())
|
|
|
|
|
- .openaiDailyLimit(sku.getOpenaiDailyLimit())
|
|
|
|
|
- .openaiQuota(sku.getOpenaiQuota())
|
|
|
|
|
- .expiryTime(claudeCodeInfo.getExpiryTime())
|
|
|
|
|
- .build();
|
|
|
|
|
- createOrUpdateUserPackage(codexPackageReq);
|
|
|
|
|
- codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (codexUser == null) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ private final OrderDonMapper orderDonMapper;
|
|
|
|
|
|
|
|
|
|
+ private final GroupsFuncService groupsFuncService;
|
|
|
|
|
|
|
|
- return CodexUserInfoView.builder().planName(codexUser.getPlanName()).openaiDailyLimit(codexUser.getOpenaiDailyLimit()).openaiQuota(codexUser.getOpenaiQuota()).relationId(codexUser.getRelationId()).claudeCodeInfo(claudeCodeInfo).build();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ private final RedisService redisService;
|
|
|
|
|
|
|
|
- private CodexUserInfoView.ClaudeCodeInfo getClaudeCodeInfo(Long userId) {
|
|
|
|
|
- // 获取用户关联的ID列表
|
|
|
|
|
- List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
|
|
|
|
+ private final CodexDeductRecordMapper codexDeductRecordMapper;
|
|
|
|
|
|
|
|
- ClaudeCodeUser claudeCodeUser = claudeCodeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUser.class).in(ClaudeCodeUser::getUserId, userIdList).last("limit 1"));
|
|
|
|
|
|
|
+ private final CouponSkuMapper couponSkuMapper;
|
|
|
|
|
|
|
|
- if (claudeCodeUser == null) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ private final CouponUserMapper couponUserMapper;
|
|
|
|
|
|
|
|
- return CodexUserInfoView.ClaudeCodeInfo.builder().userId(claudeCodeUser.getUserId()).planName(claudeCodeUser.getPlanName()).relationId(claudeCodeUser.getRelationId()).codePoints(claudeCodeUser.getCodePoints()).codeCreditRecovery(claudeCodeUser.getCodeCreditRecovery()).expiryTime(claudeCodeUser.getExpiryTime()).renewSkuId(claudeCodeUser.getRenewSkuId()).renewExpiryTime(claudeCodeUser.getRenewExpiryTime()).build();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ private final UserDistributeMapper userDistributeMapper;
|
|
|
|
|
+
|
|
|
|
|
+ private final UserDistributeSharedMapper userDistributeSharedMapper;
|
|
|
|
|
+
|
|
|
|
|
+ private final UserMapper userMapper;
|
|
|
|
|
+
|
|
|
|
|
+ private final CouponFontService couponFontService;
|
|
|
|
|
+
|
|
|
|
|
+ private static final Sequence SEQUENCE = new Sequence(0);
|
|
|
|
|
+
|
|
|
|
|
+ private final ClaudeCodexApiService claudeCodexApiService;
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public void createOrUpdateUserPackage(CodexUserPackageReq req) {
|
|
public void createOrUpdateUserPackage(CodexUserPackageReq req) {
|
|
@@ -143,31 +153,420 @@ public class CodexServiceImpl implements CodexService {
|
|
|
/**
|
|
/**
|
|
|
* 创建或更新Codex用户套餐
|
|
* 创建或更新Codex用户套餐
|
|
|
*/
|
|
*/
|
|
|
- public ClaudeCodeResp createOrUpdateCodexUserPackage(Long userId, String planName, Integer openaiDailyLimit, Integer openaiQuota, Date expiryTime) throws Exception {
|
|
|
|
|
- Map<String, Object> params = new HashMap<>();
|
|
|
|
|
- params.put("plan_name", planName.replaceAll(";", ""));
|
|
|
|
|
- params.put("openai_daily_limit", openaiDailyLimit);
|
|
|
|
|
- params.put("openai_quota", openaiQuota);
|
|
|
|
|
- params.put("end_date", DateUtil.format(expiryTime, "yyyy-MM-dd HH:mm:ss"));
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public ClaudeCodeResp createOrUpdateCodexUserPackageApi(Long userId, String planName, Integer openaiDailyLimit, Integer openaiQuota, Date expiryTime) throws Exception {
|
|
|
|
|
+ return claudeCodexApiService.createOrUpdateCodexUserPackageApi(userId, planName, openaiDailyLimit, openaiQuota, expiryTime);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public CodexDeductResp getDeductMoney(Long userId, Long relationId) {
|
|
|
|
|
+ List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
|
|
+ CodexDeductResp codexDeductResp = new CodexDeductResp();
|
|
|
|
|
+ List<Long> sendCodexSkuIds = skuMapper.selectList(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, Constant.CLAUDE_CODE_GOODS_ID).eq(GoodsDonSku::getIsCodex, Boolean.TRUE)).stream().map(GoodsDonSku::getId).collect(Collectors.toList());
|
|
|
|
|
+ if (CollUtil.isNotEmpty(sendCodexSkuIds)) {
|
|
|
|
|
+ GroupsRelationView ccRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getSkuId, sendCodexSkuIds).op(Operator.InList).field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan).field(GroupsRelationView::getUserId, userIdList).op(Operator.InList).field(GroupsRelationView::getGoodsId, Constant.CLAUDE_CODE_GOODS_ID).build());
|
|
|
|
|
+ if (ccRelationView != null) {
|
|
|
|
|
+ codexDeductResp.setIsDeduct(Boolean.FALSE);
|
|
|
|
|
+ return codexDeductResp;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ MapBuilder builder = MapUtils.builder();
|
|
|
|
|
+ if (relationId != null) {
|
|
|
|
|
+ builder.field(GroupsRelationView::getId, relationId);
|
|
|
|
|
+ }
|
|
|
|
|
+ GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, builder
|
|
|
|
|
+ .field(GroupsRelationView::getGoodsId, Constant.CODEX_GOODS_ID)
|
|
|
|
|
+ .field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
|
|
|
|
|
+ .field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
|
|
|
|
|
+ .build());
|
|
|
|
|
+ BigDecimal deductMoney;
|
|
|
|
|
+ if (groupsRelationView == null || groupsRelationView.getExpiryTime().before(DateTime.now())) {
|
|
|
|
|
+ deductMoney = BigDecimal.ZERO;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ relationId = groupsRelationView.getId();
|
|
|
|
|
+ CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).eq(CodexUser::getRelationId, relationId).last("limit 1"));
|
|
|
|
|
+ Assert.notNull(codexUser, "系统异常,请联系客服");
|
|
|
|
|
+ deductMoney = getFinalDeductMoney(relationId, groupsRelationView.getSkuId(), codexUser.getRenewSkuId(), codexUser.getRenewOrderId(), codexUser.getRenewExpiryTime(), groupsRelationView.getStartTime(), groupsRelationView.getExpiryTime(), DateTime.now(), userIdList);
|
|
|
|
|
+ }
|
|
|
|
|
+ codexDeductResp.setDeductMoney(deductMoney);
|
|
|
|
|
+ return codexDeductResp;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Throwable.class)
|
|
|
|
|
+ public OrderDon deductSubmitOrder(CodexUpgradePayReq payReq) throws Exception {
|
|
|
|
|
+ Long userId = payReq.getUserId();
|
|
|
|
|
+ //codex 车票id
|
|
|
|
|
+ Long relationId = payReq.getRelationId();
|
|
|
|
|
+ CodexDeductResp codexDeductResp = getDeductMoney(userId, relationId);
|
|
|
|
|
+ if (!codexDeductResp.getIsDeduct()) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("你存在claude code 无法抵扣");
|
|
|
|
|
+ }
|
|
|
|
|
+ //抵扣金额
|
|
|
|
|
+ BigDecimal deductMoney = codexDeductResp.getDeductMoney();
|
|
|
|
|
+ Long skuId = payReq.getSkuId();
|
|
|
|
|
+
|
|
|
|
|
+ GoodsDonSku sku = skuMapper.selectById(skuId);
|
|
|
|
|
+ if (sku == null || sku.getGoodsId() != Constant.CLAUDE_CODE_GOODS_ID || !sku.getIsCodex()) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("系统异常,请刷新页面重试");
|
|
|
|
|
+ }
|
|
|
|
|
+ List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
|
|
+ int noPayCount = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class).in(OrderDon::getUserId, userIdList).eq(OrderDon::getGoodsId, sku.getGoodsId())
|
|
|
|
|
+ .eq(OrderDon::getStatus, OrderDon.Status.noPayment.toString()));
|
|
|
|
|
+ if (noPayCount > 0) {
|
|
|
|
|
+ throw new BusinessRuntimeException("您有未支付订单.");
|
|
|
|
|
+ }
|
|
|
|
|
+ String cacheKye = "codex_deduct_cache_key:" + relationId;
|
|
|
|
|
+ boolean b = redisService.setNx(cacheKye, relationId, 10L);
|
|
|
|
|
+ if (!b) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("系统繁忙,请刷新页面重试");
|
|
|
|
|
+ }
|
|
|
|
|
+ CouponUser couponUser = null;
|
|
|
|
|
+ if (payReq.getCouponId() != null) {
|
|
|
|
|
+ CouponPopularizeDto couponPopularizeDto = checkCouponStatus(skuId, null, payReq.getCouponId(), userId, false);
|
|
|
|
|
+ //记录优惠券使用状态
|
|
|
|
|
+ couponUser = couponStatusRecord(null, payReq.getCouponId(), userId, couponPopularizeDto);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ OrderDon orderDon;
|
|
|
|
|
+ try {
|
|
|
|
|
+ orderDon = new OrderDon();
|
|
|
|
|
+ orderDon.setGoodsId(sku.getGoodsId());
|
|
|
|
|
+ orderDon.setSkuId(sku.getId());
|
|
|
|
|
+ orderDon.setUserId(userId);
|
|
|
|
|
+ //优惠 --> 订单金额
|
|
|
|
|
+ deductOrderMoney(orderDon, couponUser);
|
|
|
|
|
+ orderDon.setMoney(sku.getPrice().subtract(deductMoney));
|
|
|
|
|
+ orderDon.setType(OrderDon.Type.ALI_PAY);
|
|
|
|
|
+
|
|
|
|
|
+ /* 生成订单 */
|
|
|
|
|
+ String donNo = SEQUENCE.nextId().toString();
|
|
|
|
|
+ orderDon.setOrderNo(donNo);
|
|
|
|
|
+ orderDon.setStatus(OrderDon.Status.noPayment);
|
|
|
|
|
+
|
|
|
|
|
+ //锁一个cc的车票id
|
|
|
|
|
+ //是否存在无赠送的claude code 直接原规格上抵扣
|
|
|
|
|
+ List<Long> sendCodexSkuIds = skuMapper.selectList(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, Constant.CLAUDE_CODE_GOODS_ID).eq(GoodsDonSku::getIsCodex, Boolean.TRUE)).stream().map(GoodsDonSku::getId).collect(Collectors.toList());
|
|
|
|
|
+ //cc车票id
|
|
|
|
|
+ Long ccRelationId = null;
|
|
|
|
|
+ if (CollUtil.isEmpty(sendCodexSkuIds)) {
|
|
|
|
|
+ //是否存在cc
|
|
|
|
|
+ Integer selectCount = claudeCodeUserMapper.selectCount(Wrappers.lambdaQuery(ClaudeCodeUser.class).in(ClaudeCodeUser::getUserId, userIdList));
|
|
|
|
|
+ if (selectCount > 0) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("你存在claude code 无法抵扣");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder()
|
|
|
|
|
+ .field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
|
|
|
|
|
+ .field(GroupsRelationView::getSkuId, sendCodexSkuIds).op(Operator.NotIn)
|
|
|
|
|
+ .field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
|
|
|
|
|
+ .build());
|
|
|
|
|
+ if (groupsRelationView != null) {
|
|
|
|
|
+ ccRelationId = groupsRelationView.getId();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ GroupsRelation relation = getClaudeCodeRelation(userId, sku);
|
|
|
|
|
+ orderDon.setRelationId(relation.getId());
|
|
|
|
|
+ orderDonMapper.insert(orderDon);
|
|
|
|
|
+
|
|
|
|
|
+ //记录抵扣记录
|
|
|
|
|
+ CodexDeductRecord codexDeductRecord = new CodexDeductRecord();
|
|
|
|
|
+ codexDeductRecord.setCcRelationId(ccRelationId);
|
|
|
|
|
+ codexDeductRecord.setOrderId(orderDon.getId());
|
|
|
|
|
+ codexDeductRecord.setRelationId(relationId);
|
|
|
|
|
+ codexDeductRecordMapper.insert(codexDeductRecord);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ if (redisService.hasKey(cacheKye)) {
|
|
|
|
|
+ redisService.del(cacheKye);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return orderDon;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public Boolean getUserSubs(Long userId) {
|
|
|
|
|
+ List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
|
|
+ //是否有该车票
|
|
|
|
|
+ GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder()
|
|
|
|
|
+ .field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
|
|
|
|
|
+ .field(GroupsRelationView::getGoodsId, Constant.CODEX_GOODS_ID)
|
|
|
|
|
+ .field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
|
|
|
|
|
+ .build());
|
|
|
|
|
+ CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"));
|
|
|
|
|
+ //车票不存在
|
|
|
|
|
+ if (groupsRelationView == null) {
|
|
|
|
|
+ //套餐是否存在
|
|
|
|
|
+ if (codexUser != null) {
|
|
|
|
|
+ //清除用户套餐
|
|
|
|
|
+ if (delUserPackages(codexUser.getUserId())) {
|
|
|
|
|
+ codexUserMapper.deleteById(codexUser.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public CodexUserInfoView getUserSubsInfo(long userId) {
|
|
|
|
|
+ List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
|
|
+ //是否有该车票
|
|
|
|
|
+ GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder()
|
|
|
|
|
+ .field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
|
|
|
|
|
+ .field(GroupsRelationView::getGoodsId, Constant.CODEX_GOODS_ID)
|
|
|
|
|
+ .field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
|
|
|
|
|
+ .build());
|
|
|
|
|
+ CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"));
|
|
|
|
|
+ //车票不存在
|
|
|
|
|
+ if (groupsRelationView == null) {
|
|
|
|
|
+ //套餐是否存在
|
|
|
|
|
+ if (codexUser != null) {
|
|
|
|
|
+ //清除用户套餐
|
|
|
|
|
+ if (delUserPackages(codexUser.getUserId())) {
|
|
|
|
|
+ codexUserMapper.deleteById(codexUser.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- String planUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/plan", userId);
|
|
|
|
|
- ClaudeCodeResp codexResp = executeCodexPostApi(planUrl, Jsons.toJson(params));
|
|
|
|
|
|
|
+ GoodsDonSku sku = null;
|
|
|
|
|
+ if (codexUser == null) {
|
|
|
|
|
+ OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
|
|
|
|
|
+ .eq(OrderDon::getGoodsId, Constant.CODEX_GOODS_ID)
|
|
|
|
|
+ .eq(OrderDon::getRelationId, groupsRelationView.getId())
|
|
|
|
|
+ .in(OrderDon::getUserId, userIdList)
|
|
|
|
|
+ .notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
|
|
|
|
|
+ .orderByDesc(OrderDon::getId)
|
|
|
|
|
+ .last("limit 1"));
|
|
|
|
|
+ if (orderDon != null) {
|
|
|
|
|
+ sku = skuMapper.selectById(orderDon.getSkuId());
|
|
|
|
|
+ createOrUpdateCodexUserInfo(orderDon.getId(), orderDon.getRelationId(), orderDon.getUserId(), sku, orderDon.getOrderType());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ //试用车票
|
|
|
|
|
+ Long skuId = groupsRelationView.getSkuId();
|
|
|
|
|
+ sku = skuMapper.selectById(skuId);
|
|
|
|
|
+ createOrUpdateCodexUserInfo(null, groupsRelationView.getId(), groupsRelationView.getUserId(), sku, null);
|
|
|
|
|
+ }
|
|
|
|
|
+ codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ codexUser.setExpiryTime(groupsRelationView.getExpiryTime());
|
|
|
|
|
+ codexUserMapper.updateById(codexUser);
|
|
|
|
|
+ }
|
|
|
|
|
+ //续费升级
|
|
|
|
|
+ if (codexUser.getRenewSkuId() != null) {
|
|
|
|
|
+ sku = skuMapper.selectById(codexUser.getRenewSkuId());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (sku == null) {
|
|
|
|
|
+ sku = skuMapper.selectById(groupsRelationView.getSkuId());
|
|
|
|
|
+ }
|
|
|
|
|
+ //是否需要重试更新 cladueCode userInfo信息
|
|
|
|
|
+ if (codexUser.getIsRetry()) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ ClaudeCodeResp codeUserPackage = createOrUpdateCodexUserPackageApi(codexUser.getUserId(), codexUser.getPlanName(), codexUser.getOpenaiDailyLimit(), codexUser.getOpenaiQuota(), codexUser.getExpiryTime());
|
|
|
|
|
+ if (!Constant.SUCCESS.equals(codeUserPackage.getMessage())) {
|
|
|
|
|
+ codexUser.setIsRetry(Boolean.TRUE);
|
|
|
|
|
+ codexUserMapper.updateById(codexUser);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ codexUser.setIsRetry(Boolean.FALSE);
|
|
|
|
|
+ codexUserMapper.updateById(codexUser);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
|
|
|
- if (!"success".equals(codexResp.getMessage())) {
|
|
|
|
|
- log.info("用户codex用户:{}套餐:{}创建或更新失败,info:{}", userId, planName, codexResp.getMessage());
|
|
|
|
|
- throw BusinessRuntimeException.getInstance("调用codex 套餐接口失败");
|
|
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ return CodexUserInfoView.builder()
|
|
|
|
|
+ .planName(codexUser.getPlanName())
|
|
|
|
|
+ .relationId(codexUser.getRelationId())
|
|
|
|
|
+ .openaiDailyLimit(codexUser.getOpenaiDailyLimit())
|
|
|
|
|
+ .openaiQuota(codexUser.getOpenaiQuota())
|
|
|
|
|
+ .expiryTime(codexUser.getExpiryTime())
|
|
|
|
|
+ .renewSkuId(Optional.ofNullable(codexUser.getRenewSkuId()).orElse(groupsRelationView.getSkuId()))
|
|
|
|
|
+ .renewOrderId(codexUser.getRenewOrderId())
|
|
|
|
|
+ .renewExpiryTime(codexUser.getRenewExpiryTime())
|
|
|
|
|
+ .build();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public ClaudeCodeResp createApiKeys(ClaudeCodeApiKeysReq req) throws Exception {
|
|
|
|
|
+ Long userId = req.getUserId();
|
|
|
|
|
+ if (checkCodexUser(userId)) {
|
|
|
|
|
+ Long codexUserId = getCodexUserId(userId);
|
|
|
|
|
+ Assert.notNull(codexUserId, "您的codex账号已过期");
|
|
|
|
|
|
|
|
- return codexResp;
|
|
|
|
|
|
|
+ req.setUserId(codexUserId);
|
|
|
|
|
+ ClaudeCodeResp claudeCodeResp = claudeCodexApiService.createApiKeys(req);
|
|
|
|
|
+ return claudeCodeResp;
|
|
|
|
|
+ }
|
|
|
|
|
+ throw new BusinessRuntimeException("您还没有codex账号");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void deleteApiKeysById(ClaudeCodeDelReq delReq) throws Exception {
|
|
|
|
|
+ Long userId = delReq.getUserId();
|
|
|
|
|
+ Long keyId = delReq.getKeyId();
|
|
|
|
|
+ if (checkCodexUser(userId)) {
|
|
|
|
|
+ Long codexUserId = getCodexUserId(userId);
|
|
|
|
|
+ claudeCodexApiService.deleteApiKeysById(codexUserId, keyId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public List<ClaudeCodeUserApiKeysView> getUserApiKeys(long userId) throws Exception {
|
|
|
|
|
+ Long codexUserId = getCodexUserId(userId);
|
|
|
|
|
+ if (codexUserId != null) {
|
|
|
|
|
+ List<ClaudeCodeUserApiKeysView> keysViews = claudeCodexApiService.getUserApiKeys(codexUserId);
|
|
|
|
|
+ return keysViews;
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private GroupsRelation getClaudeCodeRelation(Long userId, GoodsDonSku sku) {
|
|
|
|
|
+ //获取车票车位
|
|
|
|
|
+ GroupsRelation relation = getRelation(sku);
|
|
|
|
|
+ //锁定座位
|
|
|
|
|
+ setRelation(relation);
|
|
|
|
|
+ //校验座位
|
|
|
|
|
+ int update = relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
|
|
|
|
|
+ .set(GroupsRelation::getUserId, userId)
|
|
|
|
|
+ .eq(GroupsRelation::getUserId, 0)
|
|
|
|
|
+ .eq(GroupsRelation::getId, relation.getId()));
|
|
|
|
|
+ if (update == 0) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("系统繁忙,请重试");
|
|
|
|
|
+ }
|
|
|
|
|
+ relation.setUserId(userId);
|
|
|
|
|
+ //重新置为锁定
|
|
|
|
|
+ relation.setStatus(GroupsRelation.Status.locking);
|
|
|
|
|
+ relationMapper.updateById(relation);
|
|
|
|
|
+ return relation;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private GroupsRelation getRelation(GoodsDonSku sku) {
|
|
|
|
|
+ GroupsRelation relation = null;
|
|
|
|
|
+ Integer maxNum = sku.getNum();
|
|
|
|
|
+ GroupsTrips groups = groupsMapper.selectTicketGroups(sku.getId(), GroupsTrips.Status.validity, maxNum, null, null);
|
|
|
|
|
+ if (groups == null) {
|
|
|
|
|
+ //待发车
|
|
|
|
|
+ groups = groupsMapper.selectTicketGroups(sku.getId(), GroupsTrips.Status.waiting, maxNum, null, null);
|
|
|
|
|
+ }
|
|
|
|
|
+ //新增空车队
|
|
|
|
|
+ if (groups == null) {
|
|
|
|
|
+ relation = groupsFuncService.createNewGroupsTrips(sku);
|
|
|
|
|
+ }
|
|
|
|
|
+ //获取车位
|
|
|
|
|
+ if (relation == null) {
|
|
|
|
|
+ //寻找快满编车队进行占座
|
|
|
|
|
+ relation = relationMapper.selectRandomOneRelationByGroupsId(groups.getId());
|
|
|
|
|
+ if (relation == null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("系统繁忙,请重试");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return relation;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public BigDecimal getFinalDeductMoney(Long relationId, Long relationSkuId, Long renewSkuId, Long renewOrderId, Date renewExpiryTime, Date relationStartTime, Date relationExpiryTime, DateTime now, List<Long> userIdList) {
|
|
|
|
|
+ BigDecimal deductMoney = BigDecimal.ZERO;
|
|
|
|
|
+ //一个月当30天算单价 往上取整
|
|
|
|
|
+ //续费规格
|
|
|
|
|
+ if (renewSkuId != null && renewExpiryTime.compareTo(now) > 0) {
|
|
|
|
|
+ OrderDon renewOrderDon = orderDonMapper.selectById(renewOrderId);
|
|
|
|
|
+ BigDecimal orderMoney = renewOrderDon.getMoney().add(Optional.ofNullable(renewOrderDon.getBalance()).orElse(BigDecimal.ZERO));
|
|
|
|
|
+ GoodsDonSku presentSku = skuMapper.selectById(renewSkuId);
|
|
|
|
|
+ if (orderMoney.compareTo(BigDecimal.ZERO) == 0) {
|
|
|
|
|
+ orderMoney = presentSku.getPrice();
|
|
|
|
|
+ }
|
|
|
|
|
+ Long betweenDay = DateUtil.betweenDay(now, renewExpiryTime, false) + 1;
|
|
|
|
|
+ //当天购买的按规格原价抵扣
|
|
|
|
|
+ BigDecimal renewDeductMoney;
|
|
|
|
|
+ if (now.toDateStr().equals(DateUtil.format(renewOrderDon.getCreatedTime(), "yyyy-MM-dd"))) {
|
|
|
|
|
+ renewDeductMoney = orderMoney;
|
|
|
|
|
+ deductMoney = deductMoney.add(renewDeductMoney);
|
|
|
|
|
+ if (presentSku.getDays() != null) {
|
|
|
|
|
+ relationExpiryTime = DateUtil.offsetDay(relationExpiryTime, -presentSku.getDays());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ relationExpiryTime = DateUtil.offsetMonth(relationExpiryTime, -presentSku.getMonths());
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ //此次续费规格单价
|
|
|
|
|
+ BigDecimal single = getPayOrderSingle(orderMoney, presentSku);
|
|
|
|
|
+ //该车票抵扣金额
|
|
|
|
|
+ renewDeductMoney = single.multiply(BigDecimal.valueOf(betweenDay));
|
|
|
|
|
+ log.info("codex车票id:{}目前续费升级规格可抵扣的金额为:{}", relationId, renewDeductMoney);
|
|
|
|
|
+ deductMoney = deductMoney.add(renewDeductMoney);
|
|
|
|
|
+ relationExpiryTime = DateUtil.offsetDay(relationExpiryTime, -betweenDay.intValue());
|
|
|
|
|
+ }
|
|
|
|
|
+ //上一个续费升级规格
|
|
|
|
|
+ List<OrderDon> orderDOns = codexUserRenewExpiryRecordMapper.getOtherRenewSkus(renewOrderId, relationId, userIdList);
|
|
|
|
|
+ OrderDon presentRenewOrderDon = renewOrderDon;
|
|
|
|
|
+ for (OrderDon thisRenewOrderDon : orderDOns) {
|
|
|
|
|
+ Long skuId = thisRenewOrderDon.getSkuId();
|
|
|
|
|
+ GoodsDonSku thisRenewSku = skuMapper.selectById(skuId);
|
|
|
|
|
+ BigDecimal thisOrderMoney = thisRenewOrderDon.getMoney().add(Optional.ofNullable(thisRenewOrderDon.getBalance()).orElse(BigDecimal.ZERO));
|
|
|
|
|
+ if (thisOrderMoney.compareTo(BigDecimal.ZERO) == 0) {
|
|
|
|
|
+ thisOrderMoney = thisRenewSku.getPrice();
|
|
|
|
|
+ }
|
|
|
|
|
+ //当天购买的按规格原价抵扣
|
|
|
|
|
+ BigDecimal thisRenewDeductMoney = BigDecimal.ZERO;
|
|
|
|
|
+ if (now.toDateStr().equals(DateUtil.format(thisRenewOrderDon.getCreatedTime(), "yyyy-MM-dd"))) {
|
|
|
|
|
+ thisRenewDeductMoney = thisOrderMoney;
|
|
|
|
|
+ deductMoney = deductMoney.add(thisRenewDeductMoney);
|
|
|
|
|
+ if (thisRenewSku.getDays() != null) {
|
|
|
|
|
+ relationExpiryTime = DateUtil.offsetDay(relationExpiryTime, -thisRenewSku.getDays());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ relationExpiryTime = DateUtil.offsetMonth(relationExpiryTime, -thisRenewSku.getMonths());
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ //此次续费订单过期时间
|
|
|
|
|
+ Date thisOrderExpiryTime;
|
|
|
|
|
+ if (thisRenewSku.getDays() != null) {
|
|
|
|
|
+ thisOrderExpiryTime = DateUtil.offsetDay(thisRenewOrderDon.getCreatedTime(), thisRenewSku.getDays());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ thisOrderExpiryTime = DateUtil.offsetMonth(thisRenewOrderDon.getCreatedTime(), thisRenewSku.getMonths());
|
|
|
|
|
+ }
|
|
|
|
|
+ //续费同规格不处理
|
|
|
|
|
+ if (!ObjectUtil.equal(thisRenewOrderDon.getSkuId(), presentRenewOrderDon.getSkuId())) {
|
|
|
|
|
+ //先扣除已使用的天数
|
|
|
|
|
+ //相差多少天
|
|
|
|
|
+ Long thisRenewBetDay = DateUtil.betweenDay(thisRenewOrderDon.getCreatedTime(), presentRenewOrderDon.getCreatedTime(), false);
|
|
|
|
|
+ //扣除已使用的
|
|
|
|
|
+ thisOrderExpiryTime = DateUtil.offsetDay(thisOrderExpiryTime, -thisRenewBetDay.intValue());
|
|
|
|
|
+ }
|
|
|
|
|
+ //此订单过期时间
|
|
|
|
|
+ if (thisOrderExpiryTime.compareTo(now) > 0) {
|
|
|
|
|
+ //距离过期还剩多少天
|
|
|
|
|
+ Long thisDeductBetweenDay = DateUtil.betweenDay(now, thisOrderExpiryTime, false) + 1;
|
|
|
|
|
+ if (thisDeductBetweenDay != 0) {
|
|
|
|
|
+ //此次续费规格单价
|
|
|
|
|
+ BigDecimal single = getPayOrderSingle(thisOrderMoney, thisRenewSku);
|
|
|
|
|
+ //该车票抵扣金额
|
|
|
|
|
+ thisRenewDeductMoney = single.multiply(BigDecimal.valueOf(thisDeductBetweenDay));
|
|
|
|
|
+ deductMoney = deductMoney.add(thisRenewDeductMoney);
|
|
|
|
|
+ }
|
|
|
|
|
+ //目前级别续费订单
|
|
|
|
|
+ presentRenewOrderDon = thisRenewOrderDon;
|
|
|
|
|
+ relationExpiryTime = DateUtil.offsetDay(relationExpiryTime, -thisDeductBetweenDay.intValue());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("codex车票id:{}目前次一级规格:{}可抵扣的金额为:{}", relationId, thisRenewSku.getSpecVal(), thisRenewDeductMoney);
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (relationExpiryTime.after(now)) {
|
|
|
|
|
+ //该车票抵扣金额
|
|
|
|
|
+ BigDecimal deductMoneyFromTicket = getDeductMoneyFromTicket(relationId, userIdList, relationSkuId, now, relationStartTime, relationExpiryTime);
|
|
|
|
|
+ deductMoney = deductMoney.add(deductMoneyFromTicket);
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("codex车票id:{}最终到期时间为:{}车票可返回 抵扣金额:{}", relationId, DateUtil.format(relationExpiryTime, "yyyy-MM-dd HH:mm:ss"), deductMoney);
|
|
|
|
|
+ return deductMoney;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
private Retryer<ClaudeCodeResp> getApiRetryer(int wait, int stop) {
|
|
private Retryer<ClaudeCodeResp> getApiRetryer(int wait, int stop) {
|
|
|
return RetryerBuilder.<ClaudeCodeResp>newBuilder().retryIfResult(result -> result == null).retryIfException().withWaitStrategy(WaitStrategies.fixedWait(wait, TimeUnit.SECONDS)).withStopStrategy(StopStrategies.stopAfterAttempt(stop)).build();
|
|
return RetryerBuilder.<ClaudeCodeResp>newBuilder().retryIfResult(result -> result == null).retryIfException().withWaitStrategy(WaitStrategies.fixedWait(wait, TimeUnit.SECONDS)).withStopStrategy(StopStrategies.stopAfterAttempt(stop)).build();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Codex POST请求 - 复制ClaudeCodeServiceImpl的实现模式
|
|
|
|
|
|
|
+ * Codex POST请求
|
|
|
*/
|
|
*/
|
|
|
public ClaudeCodeResp executeCodexPostApi(String url, String body) {
|
|
public ClaudeCodeResp executeCodexPostApi(String url, String body) {
|
|
|
Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
|
|
Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
|
|
@@ -210,10 +609,7 @@ public class CodexServiceImpl implements CodexService {
|
|
|
if (codexUser == null) {
|
|
if (codexUser == null) {
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
- // 调用Codex API获取实时使用统计
|
|
|
|
|
- String usageUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/openai/users/%s/usage?period=%s", codexUser.getUserId(), period);
|
|
|
|
|
- ClaudeCodeResp codexResp = executeCodexGetApi(usageUrl);
|
|
|
|
|
- return codexResp;
|
|
|
|
|
|
|
+ return claudeCodexApiService.getCodexUserOpenAIUsage(codexUser.getUserId(), period);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -267,42 +663,6 @@ public class CodexServiceImpl implements CodexService {
|
|
|
throw new BusinessRuntimeException("更新每日限额失败");
|
|
throw new BusinessRuntimeException("更新每日限额失败");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- @Override
|
|
|
|
|
- public ClaudeCodeResp getSystemMetrics(String period, String metric) {
|
|
|
|
|
- try {
|
|
|
|
|
- // 构建系统指标API URL
|
|
|
|
|
- String metricsUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/openai/system/metrics");
|
|
|
|
|
-
|
|
|
|
|
- // 添加查询参数
|
|
|
|
|
- List<String> queryParams = new ArrayList<>();
|
|
|
|
|
- if (period != null && !period.isEmpty()) {
|
|
|
|
|
- queryParams.add("period=" + period);
|
|
|
|
|
- }
|
|
|
|
|
- if (metric != null && !metric.isEmpty()) {
|
|
|
|
|
- queryParams.add("metric=" + metric);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (!queryParams.isEmpty()) {
|
|
|
|
|
- metricsUrl += "?" + String.join("&", queryParams);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 调用Codex API获取系统指标
|
|
|
|
|
- ClaudeCodeResp codexResp = executeCodexGetApi(metricsUrl);
|
|
|
|
|
-
|
|
|
|
|
- if (codexResp == null) {
|
|
|
|
|
- log.error("获取系统指标失败: API返回null");
|
|
|
|
|
- throw new BusinessRuntimeException("获取系统指标失败");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- log.info("成功获取系统指标, period={}, metric={}", period, metric);
|
|
|
|
|
- return codexResp;
|
|
|
|
|
-
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.error("获取系统指标异常, period={}, metric={}, error={}", period, metric, StringUtil.getErrorText(e));
|
|
|
|
|
- throw new BusinessRuntimeException("获取系统指标失败");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public ClaudeCodeResp getJavaUserDashboard(Long userId) {
|
|
public ClaudeCodeResp getJavaUserDashboard(Long userId) {
|
|
@@ -312,11 +672,7 @@ public class CodexServiceImpl implements CodexService {
|
|
|
if (codexUser == null) {
|
|
if (codexUser == null) {
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // 调用Codex API获取用户控制台数据
|
|
|
|
|
- String dashboardUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/openai/users/%s/dashboard", codexUser.getUserId());
|
|
|
|
|
- ClaudeCodeResp codexResp = executeCodexGetApi(dashboardUrl);
|
|
|
|
|
- return codexResp;
|
|
|
|
|
|
|
+ return claudeCodexApiService.getCodexUserDashboard(codexUser.getUserId());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -333,12 +689,7 @@ public class CodexServiceImpl implements CodexService {
|
|
|
start = DateUtil.offsetDay(now, -1).toString();
|
|
start = DateUtil.offsetDay(now, -1).toString();
|
|
|
end = now.toString();
|
|
end = now.toString();
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // 调用Codex API获取用户分析数据
|
|
|
|
|
- String analyticsUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/openai/users/%s/analytics?start=%s&end=%s&page=%s&limit=%s&order=%s", codexUser.getUserId(), start, end, page, limit, order);
|
|
|
|
|
- ClaudeCodeResp codexResp = executeCodexGetApi(analyticsUrl);
|
|
|
|
|
-
|
|
|
|
|
- return codexResp;
|
|
|
|
|
|
|
+ return claudeCodexApiService.getCodexUserAnalytics(codexUser.getUserId(), start, end, page, limit, order);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -350,6 +701,114 @@ public class CodexServiceImpl implements CodexService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void createOrUpdateCodexUserInfo(Long orderId, Long relationId, Long userId, GoodsDonSku sku, Integer orderType) {
|
|
|
|
|
+ //新增或续费升级
|
|
|
|
|
+ List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
|
|
+ GroupsRelation relation = relationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class).eq(GroupsRelation::getId, relationId).in(GroupsRelation::getUserId, userIdList));
|
|
|
|
|
+ GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
|
|
|
|
|
+ GoodsDonSku relationSku = skuMapper.selectById(groupsTrips.getSkuId());
|
|
|
|
|
+ Long thisCodeOriSkuId = relationSku.getId();
|
|
|
|
|
+ Long relationUserId = relation.getUserId();
|
|
|
|
|
+ //此时车票的套餐规格积分
|
|
|
|
|
+ Integer thisRelationDayilyLimit = null;
|
|
|
|
|
+ CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).eq(CodexUser::getRelationId, relationId).last("limit 1"));
|
|
|
|
|
+ if (codexUser != null) {
|
|
|
|
|
+ thisRelationDayilyLimit = codexUser.getOpenaiDailyLimit();
|
|
|
|
|
+ }
|
|
|
|
|
+ Date expiryTime = relation.getExpiryTime();
|
|
|
|
|
+ Integer openaiDailyLimit = sku.getOpenaiDailyLimit();
|
|
|
|
|
+ Integer openaiQuota = sku.getOpenaiQuota();
|
|
|
|
|
+ Long skuId = sku.getId();
|
|
|
|
|
+ Boolean isUpgrade = false;
|
|
|
|
|
+ Date renewExpiryTime = null;
|
|
|
|
|
+ //若是续费升级 记录此时续费升级的过期时间
|
|
|
|
|
+ if (!ObjectUtil.equal(thisCodeOriSkuId, skuId)) {
|
|
|
|
|
+ //非车票原规格id 续费升级记录
|
|
|
|
|
+ saveCodexRenewExpiryRecord(orderId, relationUserId, relationId, skuId);
|
|
|
|
|
+ //是否需要修改当前claude code user 套餐等级
|
|
|
|
|
+ if (thisRelationDayilyLimit != null && thisRelationDayilyLimit <= openaiDailyLimit) {
|
|
|
|
|
+ if (sku.getDays() != null) {
|
|
|
|
|
+ renewExpiryTime = DateUtil.offsetDay(DateTime.now(), sku.getDays());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ renewExpiryTime = DateUtil.offsetMonth(DateTime.now(), sku.getMonths());
|
|
|
|
|
+ }
|
|
|
|
|
+ isUpgrade = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ //更新db codexUser
|
|
|
|
|
+ generateOrUpdateCodexUser(relationUserId, relationId, openaiDailyLimit, openaiQuota, expiryTime, isUpgrade, orderId, skuId, renewExpiryTime, sku.getSpecVal(), userIdList, Boolean.TRUE);
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ //获取最新的claude code user
|
|
|
|
|
+ codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).eq(CodexUser::getRelationId, relationId).last("limit 1"));
|
|
|
|
|
+ //生成codex或更新用户套餐
|
|
|
|
|
+ ClaudeCodeResp codeUserPackage = createOrUpdateCodexUserPackageApi(relation.getUserId(), codexUser.getPlanName(), codexUser.getOpenaiDailyLimit(), codexUser.getOpenaiQuota(), codexUser.getExpiryTime());
|
|
|
|
|
+ if (!Constant.SUCCESS.equals(codeUserPackage.getMessage())) {
|
|
|
|
|
+ //重试更新code userInfo数据
|
|
|
|
|
+ setCodexUserRetryUpdateInfo(relationId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("生成或更新claude code用户:{}套餐skuId:{} 失败,error_info:{}", userId, skuId, StringUtil.getErrorText(e));
|
|
|
|
|
+ //重试更新code userInfo数据
|
|
|
|
|
+ setCodexUserRetryUpdateInfo(relationId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void updateCodexUserPackageInfo(CodexUser codexUser, Date expiryTime, GoodsDonSku finalSku) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ ClaudeCodeResp codeUserPackage = createOrUpdateCodexUserPackageApi(codexUser.getUserId(), codexUser.getPlanName(), codexUser.getOpenaiDailyLimit(), codexUser.getOpenaiQuota(), expiryTime);
|
|
|
|
|
+ if (!Constant.SUCCESS.equals(codeUserPackage.getMessage())) {
|
|
|
|
|
+ //重试标记
|
|
|
|
|
+ codexUser.setIsRetry(Boolean.TRUE);
|
|
|
|
|
+ codexUserMapper.updateById(codexUser);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ codexUser.setIsRetry(Boolean.TRUE);
|
|
|
|
|
+ codexUserMapper.updateById(codexUser);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void generateOrUpdateCodexUser(Long relationUserId, Long relationId, Integer openaiDailyLimit, Integer openaiQuota, Date expiryTime, Boolean isUpgrade, Long orderId, Long renewSkuId, Date renewExpiryTime, String planName, List<Long> userIdList, Boolean isPay) {
|
|
|
|
|
+ CodexUser codexUser = Optional.ofNullable(codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).eq(CodexUser::getRelationId, relationId).last("limit 1"))).orElse(new CodexUser());
|
|
|
|
|
+ if (isUpgrade) {
|
|
|
|
|
+ codexUser.setRenewOrderId(orderId);
|
|
|
|
|
+ codexUser.setRenewSkuId(renewSkuId);
|
|
|
|
|
+ codexUser.setRenewExpiryTime(renewExpiryTime);
|
|
|
|
|
+ codexUser.setPlanName(planName);
|
|
|
|
|
+ codexUser.setOpenaiDailyLimit(openaiDailyLimit);
|
|
|
|
|
+ codexUser.setOpenaiQuota(openaiQuota);
|
|
|
|
|
+ }
|
|
|
|
|
+ codexUser.setIsPay(isPay);
|
|
|
|
|
+ codexUser.setUserId(relationUserId);
|
|
|
|
|
+ codexUser.setRelationId(relationId);
|
|
|
|
|
+ codexUser.setExpiryTime(expiryTime);
|
|
|
|
|
+ if (codexUser.getId() == null) {
|
|
|
|
|
+ codexUser.setPlanName(planName);
|
|
|
|
|
+ codexUser.setOpenaiDailyLimit(openaiDailyLimit);
|
|
|
|
|
+ codexUser.setOpenaiQuota(openaiQuota);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (codexUser.getId() == null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ codexUserMapper.insert(codexUser);
|
|
|
|
|
+ } catch (DuplicateKeyException e) {
|
|
|
|
|
+ log.info("插入codex user 重复");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else codexUserMapper.updateById(codexUser);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void saveCodexRenewExpiryRecord(Long orderId, Long relationUserId, Long relationId, Long skuId) {
|
|
|
|
|
+ CodexUserRenewExpiryRecord renewExpiryRecord = new CodexUserRenewExpiryRecord();
|
|
|
|
|
+ renewExpiryRecord.setOrderId(orderId);
|
|
|
|
|
+ renewExpiryRecord.setUserId(relationUserId);
|
|
|
|
|
+ renewExpiryRecord.setRelationId(relationId);
|
|
|
|
|
+ renewExpiryRecord.setRenewSkuId(skuId);
|
|
|
|
|
+ codexUserRenewExpiryRecordMapper.insert(renewExpiryRecord);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Codex DELETE请求
|
|
* Codex DELETE请求
|
|
|
*/
|
|
*/
|
|
@@ -379,30 +838,360 @@ public class CodexServiceImpl implements CodexService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Codex GET请求
|
|
|
|
|
|
|
+ * 获取订单单价
|
|
|
*/
|
|
*/
|
|
|
- public ClaudeCodeResp executeCodexGetApi(String url) {
|
|
|
|
|
- Retryer<ClaudeCodeResp> build = getApiRetryer(1, 3);
|
|
|
|
|
- try {
|
|
|
|
|
- return build.call(() -> {
|
|
|
|
|
- try {
|
|
|
|
|
- HttpResponse execute = HttpUtil.createGet(url)
|
|
|
|
|
- .header(Constant.CLAUDE_CODE_HEARD_KEY, Constant.CLAUDE_CODE_API_ADMIN_KEY)
|
|
|
|
|
- .setConnectionTimeout(Constant.CONNECT_MILLISECONDS)
|
|
|
|
|
- .execute();
|
|
|
|
|
- ClaudeCodeResp resp = Jsons.parseObject(execute.body(), ClaudeCodeResp.class);
|
|
|
|
|
- if (!"success".equals(resp.getMessage())) {
|
|
|
|
|
- log.error("codex GET URL:{}接口返回msg:{}", url, resp.getMessage());
|
|
|
|
|
|
|
+ public BigDecimal getPayOrderSingle(BigDecimal orderMoney, GoodsDonSku sku) {
|
|
|
|
|
+ //此次续费规格单价
|
|
|
|
|
+ BigDecimal single;
|
|
|
|
|
+ //区分天/月
|
|
|
|
|
+ if (sku.getDays() != null) {
|
|
|
|
|
+ single = orderMoney.divide(BigDecimal.valueOf(sku.getDays()), 0, RoundingMode.HALF_UP);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ single = orderMoney.divide(BigDecimal.valueOf(sku.getMonths()).multiply(BigDecimal.valueOf(30)), 0, RoundingMode.HALF_UP);
|
|
|
|
|
+ }
|
|
|
|
|
+ return single;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取车票可抵扣金额
|
|
|
|
|
+ */
|
|
|
|
|
+ public BigDecimal getDeductMoneyFromTicket(Long relationId, List<Long> userIdList, Long skuId, DateTime now, Date relationStartTime, Date relationExpiryTime) {
|
|
|
|
|
+ List<OrderDon> orderDonList = orderDonMapper.selectList(Wrappers.lambdaQuery(OrderDon.class)
|
|
|
|
|
+ .eq(OrderDon::getRelationId, relationId)
|
|
|
|
|
+ .in(OrderDon::getUserId, userIdList)
|
|
|
|
|
+ .eq(OrderDon::getSkuId, skuId)
|
|
|
|
|
+ .notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
|
|
|
|
|
+ .orderByDesc(OrderDon::getId));
|
|
|
|
|
+ //部分退款
|
|
|
|
|
+ if (orderDonList.isEmpty()) {
|
|
|
|
|
+ orderDonList = orderDonMapper.selectList(Wrappers.lambdaQuery(OrderDon.class)
|
|
|
|
|
+ .eq(OrderDon::getRelationId, relationId)
|
|
|
|
|
+ .in(OrderDon::getUserId, userIdList)
|
|
|
|
|
+ .eq(OrderDon::getSkuId, skuId)
|
|
|
|
|
+ .notIn(OrderDon::getStatus, Constant.noOrderStatus)
|
|
|
|
|
+ .orderByDesc(OrderDon::getId));
|
|
|
|
|
+ }
|
|
|
|
|
+ //原车票订单总实付金额
|
|
|
|
|
+ BigDecimal totalOrderMoney = BigDecimal.ZERO;
|
|
|
|
|
+ //原车票订单总天数
|
|
|
|
|
+ Integer totalDays = 0;
|
|
|
|
|
+ for (OrderDon orderDon : orderDonList) {
|
|
|
|
|
+ GoodsDonSku sku = skuMapper.selectById(skuId);
|
|
|
|
|
+ BigDecimal thisOrderMoney = orderDon.getMoney().add(Optional.ofNullable(orderDon.getBalance()).orElse(BigDecimal.ZERO)).subtract(Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO));
|
|
|
|
|
+ if (thisOrderMoney.compareTo(BigDecimal.ZERO) == 0) {
|
|
|
|
|
+ thisOrderMoney = sku.getPrice();
|
|
|
|
|
+ }
|
|
|
|
|
+ totalOrderMoney = totalOrderMoney.add(thisOrderMoney);
|
|
|
|
|
+ //总时间
|
|
|
|
|
+ if (sku.getDays() != null) {
|
|
|
|
|
+ totalDays += sku.getDays();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ totalDays += sku.getMonths() * 30;
|
|
|
|
|
+ }
|
|
|
|
|
+ //原订单数
|
|
|
|
|
+ if (orderDonList.size() == 1) {
|
|
|
|
|
+ //此次订单过期时间
|
|
|
|
|
+ Date thisOrderExpiryTime;
|
|
|
|
|
+ if (sku.getDays() != null) {
|
|
|
|
|
+ thisOrderExpiryTime = DateUtil.offsetDay(orderDon.getCreatedTime(), sku.getDays());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ thisOrderExpiryTime = DateUtil.offsetMonth(orderDon.getCreatedTime(), sku.getMonths());
|
|
|
|
|
+ }
|
|
|
|
|
+ //当天购买的按规格原价抵扣
|
|
|
|
|
+ if (now.toDateStr().equals(DateUtil.format(relationStartTime, "yyyy-MM-dd")) && (DateUtil.betweenDay(thisOrderExpiryTime, relationExpiryTime, true) == 0)) {
|
|
|
|
|
+ return thisOrderMoney;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ Long thisDeductBetweenDay = DateUtil.betweenDay(now, relationExpiryTime, false) + 1;
|
|
|
|
|
+ BigDecimal single = totalOrderMoney.divide(BigDecimal.valueOf(totalDays), 0, RoundingMode.HALF_UP);
|
|
|
|
|
+ //该车票抵扣金额
|
|
|
|
|
+ BigDecimal deductMoney = single.multiply(BigDecimal.valueOf(thisDeductBetweenDay));
|
|
|
|
|
+ return deductMoney;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void setRelation(GroupsRelation relation) {
|
|
|
|
|
+ Long relationId = relation.getId();
|
|
|
|
|
+ Long userId = relation.getUserId();
|
|
|
|
|
+ Long groupsId = relation.getGroupsId();
|
|
|
|
|
+ GroupsRelation.Status status = relation.getStatus();
|
|
|
|
|
+ String relation_num_key = RedisKey.GROUPS_RELATION_NUM_KEY + relationId;
|
|
|
|
|
+ if (!redisService.setNx(relation_num_key, userId, 60 * 5L)) {
|
|
|
|
|
+ GroupsRelation dbRelation = relationMapper.selectById(relationId);
|
|
|
|
|
+ if (dbRelation.getUserId() != 0) {
|
|
|
|
|
+ log.info("=====>用户:{}未抢到座位:{}", userId, relationId);
|
|
|
|
|
+ throw new BusinessRuntimeException("系统繁忙,请重试...");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (status == GroupsRelation.Status.outside) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ int count = groupsMapper.decrAvailableNum(groupsId);
|
|
|
|
|
+ if (count == 0) {
|
|
|
|
|
+ log.error("车位异常 groupId:{}", groupsId);
|
|
|
|
|
+ groupsMapper.updateAvailableNum(groupsId);
|
|
|
|
|
+ //清除座位缓存key
|
|
|
|
|
+ delRelationKey(relationId, userId);
|
|
|
|
|
+ throw new BusinessRuntimeException("车位异常");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 清除座位缓存key
|
|
|
|
|
+ */
|
|
|
|
|
+ public void delRelationKey(Long relationId, Long userId) {
|
|
|
|
|
+ String relation_num_key = RedisKey.GROUPS_RELATION_NUM_KEY + relationId;
|
|
|
|
|
+ Object object = redisService.get(relation_num_key);
|
|
|
|
|
+ if (object != null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Long keyUserId = Long.parseLong(object.toString());
|
|
|
|
|
+ //清除车票key
|
|
|
|
|
+ if (ObjectUtil.equal(userId, keyUserId)) {
|
|
|
|
|
+ redisService.del(relation_num_key);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("删除缓存key错误:{}", StringUtil.getErrorText(e));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public CouponPopularizeDto checkCouponStatus(Long skuId, String couponExCode, Long couponId, Long userId, Boolean isRenew) throws Exception {
|
|
|
|
|
+ log.info("开始校验兑换码:{},优惠券id:{}状态", couponExCode, couponId);
|
|
|
|
|
+ //实物还是虚物商品
|
|
|
|
|
+ GoodsDonSku goodsDonSku = skuMapper.selectById(skuId);
|
|
|
|
|
+ GoodsDon goodsDon = goodsDonMapper.selectById(goodsDonSku.getGoodsId());
|
|
|
|
|
+ if (StrUtil.isNotBlank(couponExCode) && couponId != null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("优惠券与优惠码不可同时使用..");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StrUtil.isNotBlank(couponExCode)) {
|
|
|
|
|
+ CouponPopularizeDto exists = couponMapper.checkExCode(couponExCode);
|
|
|
|
|
+ if (exists == null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠码不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ //推广码
|
|
|
|
|
+ if (exists.getIsGeneral() != null) {
|
|
|
|
|
+ if (exists.getIsGeneral() && userId.equals(exists.getUserId())) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("不可以使用自己的优惠码");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!exists.getIsGeneral()) {
|
|
|
|
|
+ UserDistribute userDistribute = userDistributeMapper.selectById(exists.getDistributeId());
|
|
|
|
|
+ if (userDistribute != null && userDistribute.getUserId().equals(userId)) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("不可以使用自己的优惠码");
|
|
|
}
|
|
}
|
|
|
- return resp;
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.error("codex GET请求异常: url={}, error={}", url, StringUtil.getErrorText(e));
|
|
|
|
|
- return null;
|
|
|
|
|
}
|
|
}
|
|
|
- });
|
|
|
|
|
- } catch (ExecutionException | com.github.rholder.retry.RetryException e) {
|
|
|
|
|
- log.error("重试调用codex GET请求url->{}失败,msg->{}", url, StringUtil.getErrorText(e));
|
|
|
|
|
- throw BusinessRuntimeException.getInstance("获取codex使用统计失败");
|
|
|
|
|
|
|
+ //是否有上下级关系
|
|
|
|
|
+ if (exists.getIsGeneral()) {
|
|
|
|
|
+ UserDistributeShared userDistributeShared = userDistributeSharedMapper.selectOne(Wrappers.lambdaQuery(UserDistributeShared.class)
|
|
|
|
|
+ .eq(UserDistributeShared::getUserId, userId).last("limit 1"));
|
|
|
|
|
+ if (userDistributeShared == null) {
|
|
|
|
|
+ //是否是 当天注册新用户 且下过单
|
|
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
|
|
+ if (user.getCreatedTime().before(DateUtil.beginOfDay(DateTime.now()))) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠码只适用于新用户");
|
|
|
|
|
+ }
|
|
|
|
|
+ CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
|
|
|
|
|
+ if (couponNewUserView != null)
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠码只适用于新用户");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (userDistributeShared != null && !userDistributeShared.getSharedId().equals(exists.getUserId())) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("您已有推广,无法使用其他人推广码");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ checkCouponGoods(goodsDon, skuId, exists.getCouponId(), true);
|
|
|
|
|
+ //检验是优惠券使用范围
|
|
|
|
|
+ if (isRenew && exists.getUseScope() == Coupon.UseScope.orders) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠码续费时不可使用");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!isRenew && exists.getUseScope() == Coupon.UseScope.renew) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠码下单时不可使用");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (exists.getScope() == Coupon.Scope.newUser) {
|
|
|
|
|
+ CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
|
|
|
|
|
+ if (couponNewUserView != null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该新用户优惠码有且仅可使用一次");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (exists.getIsValidTime() && exists.getValidEndTime().before(DateTime.now())) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠码已失效");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (exists.getIsValidTime() && exists.getValidStartTime().after(DateTime.now())) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠码待生效");
|
|
|
|
|
+ }
|
|
|
|
|
+ return exists;
|
|
|
|
|
+ } else if (couponId != null) {
|
|
|
|
|
+ CouponUserView couponUserView = beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder()
|
|
|
|
|
+ .field(CouponUserView::getCouponId, couponId).op(Operator.Equal)
|
|
|
|
|
+ .field(CouponUserView::getUserId, userId).op(Operator.Equal)
|
|
|
|
|
+ .build());
|
|
|
|
|
+ if (couponUserView == null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠券不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (couponUserView.getCouponUserStatus() == CouponUser.Status.used) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠券已使用");
|
|
|
|
|
+ }
|
|
|
|
|
+ couponFontService.setCouponUserStatus(couponUserView, DateTime.now());
|
|
|
|
|
+ if (couponUserView.getCouponUserStatus() != CouponUser.Status.unused) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance(couponUserView.getReason());
|
|
|
|
|
+ }
|
|
|
|
|
+ checkCouponGoods(goodsDon, skuId, couponUserView.getCouponId(), false);
|
|
|
|
|
+ //检验是优惠券使用范围
|
|
|
|
|
+ if (isRenew && couponUserView.getUseScope() == Coupon.UseScope.orders) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠券续费时不可使用");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!isRenew && couponUserView.getUseScope() == Coupon.UseScope.renew) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠券下单时不可使用");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ private CouponUser couponStatusRecord(String couponExCode, Long couponId, Long userId, CouponPopularizeDto exists) {
|
|
|
|
|
+ log.info("兑换码:{},优惠券id:{},更新其优惠券状态", couponExCode, couponId);
|
|
|
|
|
+ CouponUser couponUser = null;
|
|
|
|
|
+ if (StrUtil.isNotBlank(couponExCode)) {
|
|
|
|
|
+ if (exists == null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠码不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ couponUser = new CouponUser();
|
|
|
|
|
+ couponUser.setCouponId(exists.getCouponId());
|
|
|
|
|
+ if (exists.getPopularizeId() != null) {
|
|
|
|
|
+ couponUser.setCouponId(exists.getPopularizeCouponId());
|
|
|
|
|
+ couponUser.setPopularizeId(exists.getPopularizeId());
|
|
|
|
|
+ }
|
|
|
|
|
+ //续费优惠码可重复使用
|
|
|
|
|
+ if (exists.getUseScope() != Coupon.UseScope.renew) {
|
|
|
|
|
+ //非渠道推广 推广优惠码只可使用一次
|
|
|
|
|
+ if ((exists.getPopularizeId() != null && exists.getIsGeneral()) || exists.getPopularizeId() == null) {
|
|
|
|
|
+ CouponUser received = couponUserMapper.selectOne(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, couponUser.getCouponId()).last("limit 1"));
|
|
|
|
|
+ if (received != null && received.getStatus() != CouponUser.Status.unused) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("您已使用该优惠码所对应的优惠券");
|
|
|
|
|
+ }
|
|
|
|
|
+ couponUser.setUserId(userId);
|
|
|
|
|
+ couponUser.setStatus(CouponUser.Status.used);
|
|
|
|
|
+ couponUser.setChannel(CouponUser.Channel.exCode);
|
|
|
|
|
+ couponFontService.updateCouponUserValidTime(couponUser.getCouponId(), couponUser);
|
|
|
|
|
+ try {
|
|
|
|
|
+ couponUserMapper.insert(couponUser);
|
|
|
|
|
+ } catch (DuplicateKeyException e) {
|
|
|
|
|
+ log.info("用户已领取该{}优惠券", couponUser.getCouponId());
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("您已领取该优惠码对应的优惠券,请使用优惠券");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ couponUser = couponUserMapper.selectOne(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, couponId).last("limit 1"));
|
|
|
|
|
+ if (couponUser == null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("该优惠券不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ couponUser.setStatus(CouponUser.Status.used);
|
|
|
|
|
+ couponUserMapper.updateById(couponUser);
|
|
|
|
|
+ }
|
|
|
|
|
+ return couponUser;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void checkCouponGoods(GoodsDon goodsDon, Long skuId, Long couponId, Boolean isCouponCode) throws Exception {
|
|
|
|
|
+ if (goodsDon.getType() == 1) {
|
|
|
|
|
+ CouponSku couponSku = couponSkuMapper.selectOne(Wrappers.lambdaQuery(CouponSku.class)
|
|
|
|
|
+ .eq(CouponSku::getSkuId, skuId)
|
|
|
|
|
+ .eq(CouponSku::getCouponId, couponId)
|
|
|
|
|
+ .select(CouponSku::getId).last("limit 1"));
|
|
|
|
|
+ if (couponSku == null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance(String.format("该%s不适用于该规格", isCouponCode ? "优惠码" : "优惠券"));
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ //实物、潮玩 优惠码优惠券 针对平台
|
|
|
|
|
+ Long gid = goodsDon.getId();
|
|
|
|
|
+ CouponSku couponSku = couponSkuMapper.selectOne(Wrappers.lambdaQuery(CouponSku.class)
|
|
|
|
|
+ .eq(CouponSku::getGoodsId, gid)
|
|
|
|
|
+ .eq(CouponSku::getCouponId, couponId)
|
|
|
|
|
+ .select(CouponSku::getId).last("limit 1"));
|
|
|
|
|
+ if (couponSku == null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance(String.format("该%s不可用于该商品", isCouponCode ? "优惠码" : "优惠券"));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 使用优惠营销 订单金额抵扣
|
|
|
|
|
+ */
|
|
|
|
|
+ public void deductOrderMoney(OrderDon orderDon, CouponUser couponUser) throws Exception {
|
|
|
|
|
+ BigDecimal first_money = orderDon.getMoney();
|
|
|
|
|
+ //存在优惠券
|
|
|
|
|
+ if (couponUser != null) {
|
|
|
|
|
+ Long couponId = couponUser.getCouponId();
|
|
|
|
|
+ Coupon coupon = couponMapper.getCouponById(couponId);
|
|
|
|
|
+ BigDecimal couponMoney = null;
|
|
|
|
|
+ BigDecimal payMoney = orderDon.getMoney();
|
|
|
|
|
+ BigDecimal needDeductMoney = payMoney;
|
|
|
|
|
+ if (orderDon.getOriMoney() == null) {
|
|
|
|
|
+ orderDon.setOriMoney(payMoney);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (orderDon.getIsDp() != null && orderDon.getIsDp()) {
|
|
|
|
|
+ needDeductMoney = needDeductMoney.subtract(orderDon.getDpMoney());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (coupon.getType() == Coupon.Type.discount) {
|
|
|
|
|
+ couponMoney = needDeductMoney.multiply(BigDecimal.valueOf(1).subtract(coupon.getDiscount()));
|
|
|
|
|
+ //需支付金额
|
|
|
|
|
+ orderDon.setMoney(payMoney.subtract(couponMoney));
|
|
|
|
|
+
|
|
|
|
|
+ orderDon.setOriMoney(orderDon.getOriMoney().multiply(coupon.getDiscount()));
|
|
|
|
|
+ //记录折扣 用于分销
|
|
|
|
|
+ couponUser.setDiscount(coupon.getDiscount());
|
|
|
|
|
+ couponUserMapper.updateById(couponUser);
|
|
|
|
|
+ } else if (coupon.getType() == Coupon.Type.reduce) {
|
|
|
|
|
+ BigDecimal subtractMoney = payMoney.subtract(coupon.getReduceMoney());
|
|
|
|
|
+ orderDon.setMoney(subtractMoney);
|
|
|
|
|
+ couponMoney = coupon.getReduceMoney();
|
|
|
|
|
+
|
|
|
|
|
+ orderDon.setOriMoney(orderDon.getOriMoney().subtract(coupon.getReduceMoney()));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (couponMoney != null && couponMoney.compareTo(first_money) >= 0) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("支付金额不能小于优惠金额");
|
|
|
|
|
+ }
|
|
|
|
|
+ orderDon.setCouponMoney(couponMoney);
|
|
|
|
|
+ orderDon.setCouponUserId(couponUser.getId());
|
|
|
|
|
+ orderDon.setCouponId(couponUser.getCouponId());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean delUserPackages(Long userId) {
|
|
|
|
|
+ return claudeCodexApiService.delCodexUserPackages(userId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void handleClaudeCodeUserPackageSku(GroupsRelation relation, GoodsDonSku sku, Long orderId) {
|
|
|
|
|
+ //更新套餐
|
|
|
|
|
+ createOrUpdateCodexUserInfo(orderId, relation.getId(), relation.getUserId(), sku, 2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 校验是否是codex用户
|
|
|
|
|
+ * @param userId
|
|
|
|
|
+ * @return
|
|
|
|
|
+ */
|
|
|
|
|
+ public Boolean checkCodexUser(Long userId) {
|
|
|
|
|
+ List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
|
|
+ Integer count = relationMapper.selectCountByGoodsId(userIdList, Constant.CODEX_GOODS_ID, DateTime.now());
|
|
|
|
|
+ return count > 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取codex用户id
|
|
|
|
|
+ */
|
|
|
|
|
+ public Long getCodexUserId(Long userId) {
|
|
|
|
|
+ List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
|
|
|
|
|
+ GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder()
|
|
|
|
|
+ .field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
|
|
|
|
|
+ .field(GroupsRelationView::getGoodsId, Constant.CODEX_GOODS_ID)
|
|
|
|
|
+ .field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
|
|
|
|
|
+ .build());
|
|
|
|
|
+ if (groupsRelationView != null) {
|
|
|
|
|
+ return groupsRelationView.getUserId();
|
|
|
}
|
|
}
|
|
|
|
|
+ return null;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|