package com.cyksj.service.codex.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateTime; 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.HttpUtil; import cn.hutool.http.Method; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.cyksj.common.constant.Constant; import com.cyksj.common.exception.BusinessRuntimeException; import com.cyksj.common.snowflake.Sequence; import com.cyksj.common.util.Jsons; import com.cyksj.common.util.StringUtil; import com.cyksj.dto.RedisKey; 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.manage.views.GroupsRelationView; import com.cyksj.model.request.CodexUpgradePayReq; import com.cyksj.model.request.UpdateDailyLimitRequest; import com.cyksj.model.request.codex.CodexUserPackageReq; import com.cyksj.model.response.CodexDeductResp; import com.cyksj.model.response.claudecode.ClaudeCodeResp; 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.codex.CodexService; import com.cyksj.service.coupon.CouponFontService; import com.cyksj.service.groups.GroupsFuncService; 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.RetryerBuilder; import com.github.rholder.retry.StopStrategies; import com.github.rholder.retry.WaitStrategies; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DuplicateKeyException; 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.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * 项目名: yhlxj11111111 * 文件名: CodexServiceImpl * 创建者: Claude * 创建时间:2025/9/15 */ @Slf4j @Service @RequiredArgsConstructor public class CodexServiceImpl implements CodexService { private final CodexUserMapper codexUserMapper; private final ClaudeCodeUserMapper claudeCodeUserMapper; private final UserBindRelationService userBindRelationService; private final GoodsDonSkuMapper skuMapper; private final GoodsDonMapper goodsDonMapper; private final CouponMapper couponMapper; private final GroupsRelationMapper relationMapper; private final GroupsMapper groupsMapper; private final CodexUserRenewExpiryRecordMapper codexUserRenewExpiryRecordMapper; private final BeanSearcher beanSearcher; private final OrderDonMapper orderDonMapper; private final GroupsFuncService groupsFuncService; private final RedisService redisService; private final CodexDeductRecordMapper codexDeductRecordMapper; private final CouponSkuMapper couponSkuMapper; private final CouponUserMapper couponUserMapper; private final UserDistributeMapper userDistributeMapper; private final UserDistributeSharedMapper userDistributeSharedMapper; private final UserMapper userMapper; private final CouponFontService couponFontService; private static final Sequence SEQUENCE = new Sequence(0); @Override public CodexUserInfoView getCodexUserInfo(Long userId) { List 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); if (codexUser == null) { //是否有claude code if (claudeCodeInfo != null && claudeCodeInfo.getClaudeDailyLimit() != 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()); } //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; } } return CodexUserInfoView.builder().planName(codexUser.getPlanName()).openaiDailyLimit(codexUser.getOpenaiDailyLimit()).openaiQuota(codexUser.getOpenaiQuota()).relationId(codexUser.getRelationId()).claudeCodeInfo(claudeCodeInfo).build(); } private CodexUserInfoView.ClaudeCodeInfo getClaudeCodeInfo(Long userId) { // 获取用户关联的ID列表 List userIdList = userBindRelationService.getRelationUserIdList(userId, null); ClaudeCodeUser claudeCodeUser = claudeCodeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeCodeUser.class).in(ClaudeCodeUser::getUserId, userIdList).last("limit 1")); if (claudeCodeUser == null) { return null; } return CodexUserInfoView.ClaudeCodeInfo.builder() .userId(claudeCodeUser.getUserId()) .planName(claudeCodeUser.getPlanName()) .relationId(claudeCodeUser.getRelationId()) .claudeDailyLimit(claudeCodeUser.getClaudeDailyLimit()) .claudeQuota(claudeCodeUser.getClaudeQuota()) .expiryTime(claudeCodeUser.getExpiryTime()) .renewSkuId(claudeCodeUser.getRenewSkuId()) .renewExpiryTime(claudeCodeUser.getRenewExpiryTime()).build(); } @Override public void createOrUpdateUserPackage(CodexUserPackageReq req) { Long userId = req.getUserId(); // 构建完整的套餐参数 String planName = req.getPlanName(); Date expiryTime = req.getExpiryTime(); Integer openaiDailyLimit = req.getOpenaiDailyLimit(); Integer openaiQuota = req.getOpenaiQuota(); Long relationId = req.getRelationId(); List userIdList = userBindRelationService.getRelationUserIdList(userId, null); CodexUser existingCodexUser = Optional.ofNullable(codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1"))).orElse(new CodexUser()); existingCodexUser.setPlanName(planName); existingCodexUser.setRelationId(relationId); existingCodexUser.setUserId(userId); existingCodexUser.setOpenaiDailyLimit(openaiDailyLimit); existingCodexUser.setOpenaiQuota(openaiQuota); existingCodexUser.setExpiryTime(expiryTime); if (existingCodexUser.getId() == null) { try { codexUserMapper.insert(existingCodexUser); } catch (DuplicateKeyException e) { } return; } codexUserMapper.updateById(existingCodexUser); } /** * 创建或更新Codex用户套餐 */ @Override public ClaudeCodeResp createOrUpdateCodexUserPackageApi(Long userId, String planName, Integer openaiDailyLimit, Integer openaiQuota, Date expiryTime) throws Exception { Map params = new HashMap<>(); params.put("plan_name", planName.replaceAll(";", "")); params.put("openai_daily_limit", openaiDailyLimit); params.put("openai_quota", openaiQuota); params.put("expire_time", DateUtil.format(expiryTime, "yyyy-MM-dd HH:mm:ss")); String planUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/plan", userId); ClaudeCodeResp codexResp = executeCodexPostApi(planUrl, Jsons.toJson(params)); if (!"success".equals(codexResp.getMessage())) { log.info("用户codex用户:{}套餐:{}创建或更新失败,info:{}", userId, planName, codexResp.getMessage()); throw BusinessRuntimeException.getInstance("调用codex 套餐接口失败"); } return codexResp; } @Override public CodexDeductResp getDeductMoney(Long userId, Long relationId) { List userIdList = userBindRelationService.getRelationUserIdList(userId, null); CodexDeductResp codexDeductResp = new CodexDeductResp(); List 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 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)); /* 生成订单 */ String donNo = SEQUENCE.nextId().toString(); orderDon.setOrderNo(donNo); //锁一个cc的车票id //是否存在无赠送的claude code 直接原规格上抵扣 List 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.InList).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; } 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("系统繁忙,请重试"); } 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 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 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 getApiRetryer(int wait, int stop) { return RetryerBuilder.newBuilder().retryIfResult(result -> result == null).retryIfException().withWaitStrategy(WaitStrategies.fixedWait(wait, TimeUnit.SECONDS)).withStopStrategy(StopStrategies.stopAfterAttempt(stop)).build(); } /** * Codex POST请求 */ public ClaudeCodeResp executeCodexPostApi(String url, String body) { Retryer build = getApiRetryer(1, 3); try { return build.call(() -> { try { HttpResponse execute = HttpUtil.createPost(url).header(Constant.CLAUDE_CODE_HEARD_KEY, Constant.CLAUDE_CODE_API_ADMIN_KEY).setConnectionTimeout(Constant.CONNECT_MILLISECONDS).body(body).execute(); ClaudeCodeResp resp = Jsons.parseObject(execute.body(), ClaudeCodeResp.class); if (!"success".equals(resp.getMessage())) { log.error("codex POST URL:{}接口返回msg:{}", url, resp.getMessage()); } return resp; } catch (Exception e) { log.error("codex POST请求异常: url={}, error={}", url, StringUtil.getErrorText(e)); return null; } }); } catch (ExecutionException | com.github.rholder.retry.RetryException e) { log.info("重试调用codex POST请求 url->{}失败,msg->{}", url, StringUtil.getErrorText(e)); throw BusinessRuntimeException.getInstance("创建或更新codex user失败"); } } /** * codex用户信息重试更新信息 */ public void setCodexUserRetryUpdateInfo(Long relationId) { CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).eq(CodexUser::getRelationId, relationId).last("limit 1")); if (codexUser != null) { codexUser.setIsRetry(Boolean.TRUE); codexUserMapper.updateById(codexUser); } } @Override public ClaudeCodeResp getUserOpenAIUsage(Long userId, String period) { List userIdList = userBindRelationService.getRelationUserIdList(userId, null); CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1")); if (codexUser == 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; } @Override public ClaudeCodeResp updateUserOpenAIDailyLimit(Long userId, UpdateDailyLimitRequest request) { List userIdList = userBindRelationService.getRelationUserIdList(userId, null); CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1")); if (codexUser == null) { throw new BusinessRuntimeException("用户未开通Codex服务"); } Integer dailyLimit = request.getDailyLimit(); Integer totalQuota = request.getTotalQuota(); // 验证每日限额参数 if (dailyLimit == null || dailyLimit <= 0) { throw new BusinessRuntimeException("每日限额必须大于0"); } // 保存原始值用于回滚 Integer originalDailyLimit = codexUser.getOpenaiDailyLimit(); // 更新数据库中的每日限额 codexUser.setOpenaiDailyLimit(dailyLimit); codexUserMapper.updateById(codexUser); try { // 调用Codex API更新每日限额 String updateUrl = String.format(Constant.CLAUDE_CODE_API_PREFIX + "api/users/%s/limit", codexUser.getUserId()); Map params = new HashMap<>(); params.put("daily_limit", dailyLimit); params.put("total_quota", totalQuota); ClaudeCodeResp codexResp = executeCodexPostApi(updateUrl, Jsons.toJson(params)); if (!"success".equals(codexResp.getMessage())) { log.error("更新用户{}每日限额{}失败: {}", userId, dailyLimit, codexResp.getMessage()); // API调用失败时回滚数据库更新 codexUser.setOpenaiDailyLimit(originalDailyLimit); codexUserMapper.updateById(codexUser); throw new BusinessRuntimeException("更新每日限额失败"); } log.info("成功更新用户{}每日限额为{}", userId, dailyLimit); return codexResp; } catch (Exception e) { log.error("更新用户{}每日限额{}异常: {}", userId, dailyLimit, StringUtil.getErrorText(e)); // 异常时回滚数据库更新 codexUser.setOpenaiDailyLimit(originalDailyLimit); codexUserMapper.updateById(codexUser); 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 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 public ClaudeCodeResp getJavaUserDashboard(Long userId) { List userIdList = userBindRelationService.getRelationUserIdList(userId, null); CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1")); if (codexUser == 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; } @Override public ClaudeCodeResp getJavaUserAnalytics(Long userId, String start, String end, Integer page, Integer limit, String order) { List userIdList = userBindRelationService.getRelationUserIdList(userId, null); CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).last("limit 1")); if (codexUser == null) { return null; } if (start == null) { DateTime now = DateTime.now(); start = DateUtil.offsetDay(now, -1).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; } @Override public void delCodexUser(Long userId, Long relationId) { List userIdList = userBindRelationService.getRelationUserIdList(userId, null); CodexUser codexUser = codexUserMapper.selectOne(Wrappers.lambdaQuery(CodexUser.class).in(CodexUser::getUserId, userIdList).eq(CodexUser::getRelationId, relationId).last("limit 1")); if (codexUser != null) { codexUserMapper.deleteById(codexUser.getId()); } } @Override public void createOrUpdateCodexUserInfo(Long orderId, Long relationId, Long userId, GoodsDonSku sku, Integer orderType) { //新增或续费升级 List 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 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请求 */ public ClaudeCodeResp executeCodexDeleteApi(String url) { Retryer build = getApiRetryer(1, 3); try { return build.call(() -> { try { HttpResponse execute = HttpUtil.createRequest(Method.DELETE, 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 DELETE URL:{}接口返回msg:{}", url, resp.getMessage()); } return resp; } catch (Exception e) { log.error("codex DELETE请求异常: url={}, error={}", url, StringUtil.getErrorText(e)); return null; } }); } catch (ExecutionException | com.github.rholder.retry.RetryException e) { log.error("重试调用codex DELETE请求url->{}失败,msg->{}", url, StringUtil.getErrorText(e)); throw BusinessRuntimeException.getInstance("删除操作失败"); } } /** * Codex GET请求 */ public ClaudeCodeResp executeCodexGetApi(String url) { Retryer 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()); } 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使用统计失败"); } } /** * 获取订单单价 */ 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 userIdList, Long skuId, DateTime now, Date relationStartTime, Date relationExpiryTime) { List 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("不可以使用自己的优惠码"); } } //是否有上下级关系 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()); } } }