ExchangeCodeServiceImpl.java 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. package com.cyksj.service.exchange.impl;
  2. import cn.hutool.core.date.DateField;
  3. import cn.hutool.core.date.DateTime;
  4. import cn.hutool.core.date.DateUtil;
  5. import cn.hutool.core.util.ObjectUtil;
  6. import cn.hutool.core.util.StrUtil;
  7. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  8. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  9. import com.cyksj.common.annotation.NoSubmit;
  10. import com.cyksj.common.constant.Constant;
  11. import com.cyksj.common.exception.BusinessRuntimeException;
  12. import com.cyksj.common.snowflake.Sequence;
  13. import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
  14. import com.cyksj.common.util.Jsons;
  15. import com.cyksj.common.util.StringUtil;
  16. import com.cyksj.dto.RedisKey;
  17. import com.cyksj.dto.Result;
  18. import com.cyksj.enums.GatewayResponse;
  19. import com.cyksj.mapper.*;
  20. import com.cyksj.mapper.manage.distribute.UserDistributeSharedMapper;
  21. import com.cyksj.mapper.manage.exchange.ExchangeCodeMapper;
  22. import com.cyksj.mapper.market.draw.LuckyDrawRecordMapper;
  23. import com.cyksj.model.entity.*;
  24. import com.cyksj.model.excel.ExcelExchangeCodeData;
  25. import com.cyksj.model.manage.views.GroupsRelationView;
  26. import com.cyksj.model.request.ExchangeCodeReq;
  27. import com.cyksj.model.request.ExchangeCodeTicket;
  28. import com.cyksj.redis.RedisService;
  29. import com.cyksj.service.claude.ClaudeCodeService;
  30. import com.cyksj.service.codex.CodexService;
  31. import com.cyksj.service.exchange.ExchangeCodeService;
  32. import com.cyksj.service.relation.GroupRelationFrontService;
  33. import com.cyksj.service.relation.GroupsRelationExpiryRecordService;
  34. import com.cyksj.service.shop.YhShopFrontService;
  35. import com.cyksj.service.user.UserBindRelationService;
  36. import com.ejlchina.searcher.BeanSearcher;
  37. import com.ejlchina.searcher.param.Operator;
  38. import com.ejlchina.searcher.util.MapUtils;
  39. import lombok.RequiredArgsConstructor;
  40. import lombok.extern.slf4j.Slf4j;
  41. import org.springframework.beans.BeanUtils;
  42. import org.springframework.dao.DuplicateKeyException;
  43. import org.springframework.stereotype.Service;
  44. import org.springframework.transaction.annotation.Transactional;
  45. import java.math.BigDecimal;
  46. import java.util.ArrayList;
  47. import java.util.Date;
  48. import java.util.List;
  49. import java.util.Optional;
  50. /*
  51. *项目名: netflix
  52. *文件名: ExchangeCodeServiceImpl
  53. *创建者: JavaZou
  54. *创建时间:2022/11/7 15:41
  55. */
  56. @Service
  57. @RequiredArgsConstructor
  58. @Slf4j
  59. public class ExchangeCodeServiceImpl extends ServiceImpl<ExchangeCodeMapper, ExchangeCode> implements ExchangeCodeService {
  60. private final GoodsDonMapper goodsDonMapper;
  61. private final GoodsDonSkuMapper goodsDonSkuMapper;
  62. private final ExchangeCodeMapper exchangeCodeMapper;
  63. private final GroupsRelationMapper groupsRelationMapper;
  64. private final GroupsMapper groupsMapper;
  65. private final RedisService redisService;
  66. private static final Sequence SEQUENCE = new Sequence(0);
  67. private final UserMapper userMapper;
  68. private final OrderDonMapper orderDonMapper;
  69. private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
  70. private final LuckyDrawRecordMapper luckyDrawRecordMapper;
  71. private final BeanSearcher beanSearcher;
  72. private final GroupRelationFrontService groupRelationFrontService;
  73. private final OrderDiscountPlusPurchaseSkuRelationMapper orderDiscountPlusPurchaseSkuRelationMapper;
  74. private final GroupsRelationExpiryRecordService groupsRelationExpiryRecordService;
  75. private final YhShopFrontService yhShopFrontService;
  76. private final UserDistributeSharedMapper userDistributeSharedMapper;
  77. private final UserDistributeExchangeCodeMapper userDistributeExchangeCodeMapper;
  78. private final UserDistributeExchangeCodeUseRecordMapper userDistributeExchangeCodeUseRecordMapper;
  79. private final UserBindRelationService userBindRelationService;
  80. private final ClaudeCodeService claudeCodeService;
  81. private final CodexService codexService;
  82. @Override
  83. @Transactional(rollbackFor = Throwable.class)
  84. public List<ExcelExchangeCodeData> createExchangeCode(ExchangeCodeReq exchangeCodeReq) throws Exception {
  85. String skuIds = exchangeCodeReq.getSkuIds();
  86. if (StrUtil.isEmpty(skuIds)) {
  87. throw BusinessRuntimeException.getInstance("请选择对应规格");
  88. }
  89. List<Long> skuList = Jsons.parseList(skuIds, Long.class);
  90. Integer skuCount = goodsDonSkuMapper.selectCount(Wrappers.lambdaQuery(GoodsDonSku.class)
  91. .in(GoodsDonSku::getId, skuList));
  92. if (skuCount != skuList.size()) {
  93. throw BusinessRuntimeException.getInstance("所选规格已被删除");
  94. }
  95. String goodsIds = goodsDonSkuMapper.selectGIdsBySkuIds(skuList).toString();
  96. List<ExcelExchangeCodeData> data = new ArrayList<>(exchangeCodeReq.getNumber());
  97. GoodsDonSku sku = goodsDonSkuMapper.selectById(skuList.get(0));
  98. for (int i = 0; i < exchangeCodeReq.getNumber(); i++) {
  99. StringBuilder sb = new StringBuilder(32);
  100. sb.append("YH");
  101. sb.append(getFillByGoodsId(sku.getGoodsId()));
  102. sb.append(getFillByMonth(sku.getMonths()));
  103. //随即补充12位随即数,组成20位兑换码
  104. StringUtil.getRandomStr(sb, 12);
  105. ExchangeCode exchangeCode = new ExchangeCode();
  106. exchangeCode.setSkuIds(exchangeCodeReq.getSkuIds());
  107. exchangeCode.setGoodsIds(goodsIds);
  108. exchangeCode.setCode(sb.toString());
  109. exchangeCode.setUseStatus(false);
  110. exchangeCode.setType(exchangeCodeReq.getType());
  111. exchangeCode.setOperator(exchangeCodeReq.getOperator());
  112. exchangeCode.setCodeType(exchangeCodeReq.getCodeType());
  113. exchangeCodeMapper.insert(exchangeCode);
  114. ExcelExchangeCodeData excelExchangeCodeData = new ExcelExchangeCodeData();
  115. BeanUtils.copyProperties(exchangeCode, excelExchangeCodeData);
  116. String title = goodsDonSkuMapper.selectExchangeTitle(skuList);
  117. excelExchangeCodeData.setTitle(title);
  118. excelExchangeCodeData.setUseStatusStr(exchangeCode.getUseStatus() ? "已使用" : "未使用");
  119. data.add(excelExchangeCodeData);
  120. }
  121. return data;
  122. }
  123. @Override
  124. @Transactional(rollbackFor = Throwable.class)
  125. @NoSubmit
  126. public Result<String> getTicketByExchangeCode(ExchangeCodeTicket exchangeCodeTicket, Long userId) throws Exception {
  127. if (StrUtil.isEmpty(exchangeCodeTicket.getCode())) {
  128. throw BusinessRuntimeException.getInstance("兑换码有误,请使用正确的兑换码");
  129. }
  130. //是渠道兑换码
  131. String code = exchangeCodeTicket.getCode();
  132. UserDistributeExchangeCode userDistributeExchangeCode = userDistributeExchangeCodeMapper.selectOne(Wrappers.lambdaQuery(UserDistributeExchangeCode.class)
  133. .eq(UserDistributeExchangeCode::getCode, code)
  134. .last("limit 1"));
  135. if (userDistributeExchangeCode != null) {
  136. //是否是该渠道下的用户
  137. Integer bindCount = userDistributeSharedMapper.selectCount(Wrappers.lambdaQuery(UserDistributeShared.class)
  138. .eq(UserDistributeShared::getSharedId, userDistributeExchangeCode.getUserId())
  139. .eq(UserDistributeShared::getUserId, userId));
  140. if (bindCount == 0) {
  141. throw BusinessRuntimeException.getInstance("您不是该渠道下的分销用户");
  142. }
  143. //是否已经兑换过
  144. Integer useCount = userDistributeExchangeCodeUseRecordMapper.selectCount(Wrappers.lambdaQuery(UserDistributeExchangeCodeUseRecord.class)
  145. .eq(UserDistributeExchangeCodeUseRecord::getUserId, userId)
  146. .eq(UserDistributeExchangeCodeUseRecord::getCode, code));
  147. if (useCount > 0) {
  148. throw BusinessRuntimeException.getInstance("您已使用该兑换码");
  149. }
  150. try {
  151. UserDistributeExchangeCodeUseRecord userDistributeExchangeCodeUseRecord = new UserDistributeExchangeCodeUseRecord();
  152. userDistributeExchangeCodeUseRecord.setUserId(userId);
  153. userDistributeExchangeCodeUseRecord.setCode(code);
  154. userDistributeExchangeCodeUseRecordMapper.insert(userDistributeExchangeCodeUseRecord);
  155. log.info("用户userId:{}兑换渠道兑换码:{}", userId, code);
  156. GoodsDonSku sku = goodsDonSkuMapper.selectById(userDistributeExchangeCode.getSkuId());
  157. GroupsRelation relation = getRelationNoOrder(sku, userId, 0l);
  158. //重新设置时间
  159. DateField dateField = DateField.MONTH;
  160. if (Constant.DAY_DATE.equals(userDistributeExchangeCode.getTimeType())) {
  161. dateField = DateField.DAY_OF_YEAR;
  162. } else if (Constant.HOUR_DAY_OF_DATE.equals(userDistributeExchangeCode.getTimeType())) {
  163. dateField = DateField.HOUR_OF_DAY;
  164. }
  165. relation.setAqType(2);
  166. relation.setExpiryTime(DateUtil.offset(relation.getStartTime(), dateField, userDistributeExchangeCode.getTime()));
  167. groupsRelationMapper.updateById(relation);
  168. userDistributeExchangeCodeUseRecord.setRelationId(relation.getId());
  169. userDistributeExchangeCodeUseRecordMapper.updateById(userDistributeExchangeCodeUseRecord);
  170. } catch (DuplicateKeyException e) {
  171. throw BusinessRuntimeException.getInstance("您已使用该兑换码");
  172. }
  173. return GatewayResponse.SUCCESS.newBuilder().toResult();
  174. }
  175. ExchangeCode exchangeCode = this.getOne(Wrappers.lambdaQuery(ExchangeCode.class).eq(ExchangeCode::getCode, exchangeCodeTicket.getCode()).last("limit 1"));
  176. if (exchangeCode == null) {
  177. throw BusinessRuntimeException.getInstance("兑换码有误,请使用正确的兑换码");
  178. }
  179. if (exchangeCode.getUseStatus()) {
  180. throw BusinessRuntimeException.getInstance("该兑换码已使用");
  181. }
  182. //店铺中 进行兑换码 兑换 标记订单 座位
  183. String customId = exchangeCodeTicket.getCustomId();
  184. Long yhsId = yhShopFrontService.checkShopCustomIdAndReturnShopId(customId);
  185. //兑换成功
  186. exchangeCode.setUseStatus(true);
  187. exchangeCode.setUserId(userId);
  188. exchangeCode.setUseTime(DateTime.now());
  189. int update = exchangeCodeMapper.update(exchangeCode, Wrappers.lambdaQuery(ExchangeCode.class).eq(ExchangeCode::getId, exchangeCode.getId()).eq(ExchangeCode::getUseStatus, false));
  190. if (update != 1) {
  191. throw BusinessRuntimeException.getInstance("该兑换码已使用");
  192. }
  193. //是否是推广用户未登录订单关联的兑换码
  194. OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
  195. .eq(OrderDon::getIsNoLogin, true)
  196. .eq(OrderDon::getExCode, exchangeCode.getCode())
  197. .notIn(OrderDon::getStatus, Constant.noOrderStatus)
  198. .last("limit 1"));
  199. // Long popularizeId = orderDon.getPopularizeId();
  200. //
  201. // //360 渠道兑换规则验证.
  202. // if(popularizeId != null && popularizeId != 0){
  203. // String channel = channelPopularizeMapper.selectChannelByPid(popularizeId);
  204. // if (Constant.CHANNEL_NAME_360.equals(channel)) {
  205. // //查询该用户之前是否存在订单.
  206. // Integer count = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class)
  207. // .eq(OrderDon::getUserId, userId)
  208. // .notIn(OrderDon::getStatus, Constant.noOrderStatus));
  209. // if (count > 0) {
  210. // throw new BusinessRuntimeException("您不符合360活动条件.");
  211. // }
  212. // }
  213. // }
  214. if (orderDon != null && orderDon.getStatus() == OrderDon.Status.refund) {
  215. throw BusinessRuntimeException.getInstance("您的订单已退款,无法使用该兑换码");
  216. }
  217. List<Long> skuIds = Jsons.parseList(exchangeCode.getSkuIds(), Long.class);
  218. skuIds.forEach(skuId -> {
  219. getGroupTripsTicket(yhsId, goodsDonSkuMapper.selectById(skuId), userId, orderDon, OrderDon.Type.EX_CODE, exchangeCodeTicket.getCode());
  220. });
  221. //生成车票订单
  222. log.info("用户:{}通过兑换码:{}兑换成功", userId, exchangeCodeTicket.getCode());
  223. if (exchangeCode.getType() == 1) {
  224. luckyDrawRecordMapper.updateExCodeStatus(userId, exchangeCode.getCode());
  225. }
  226. return GatewayResponse.SUCCESS.newBuilder().toResult("兑换成功");
  227. }
  228. @Override
  229. public String getExCode(Long goodsId, Long skuId, Integer type) {
  230. ExchangeCodeReq exchangeCodeReq = new ExchangeCodeReq();
  231. exchangeCodeReq.setNumber(1);
  232. // exchangeCodeReq.setGoodsId(goodsId);
  233. exchangeCodeReq.setSkuIds(List.of(skuId).toString());
  234. //兑换码
  235. exchangeCodeReq.setCodeType(1);
  236. if (type != null) {
  237. exchangeCodeReq.setType(type);
  238. }
  239. try {
  240. List<ExcelExchangeCodeData> exchangeCodes = this.createExchangeCode(exchangeCodeReq);
  241. return exchangeCodes.get(0).getCode();
  242. } catch (Exception e) {
  243. return null;
  244. }
  245. }
  246. /**
  247. * 根据平台id获取填充的字符串
  248. */
  249. private String getFillByGoodsId(Long goodsId) {
  250. String goodsIdStr = String.valueOf(goodsId);
  251. if (goodsIdStr.length() == 1) {
  252. return String.format("%s%s%s", 0, 0, goodsId);
  253. }
  254. if (goodsIdStr.length() == 2) {
  255. return String.format("%s%s", 0, goodsId);
  256. }
  257. //最大不超过999
  258. if (goodsIdStr.length() > 3) {
  259. return goodsIdStr.substring(0, 3);
  260. }
  261. return goodsIdStr;
  262. }
  263. /**
  264. * 根据月份长度获取填充的字符串
  265. */
  266. private String getFillByMonth(Integer skuMonth) {
  267. String goodsIdStr = String.valueOf(skuMonth);
  268. if (goodsIdStr.length() == 1) {
  269. return String.format("%s%s%s", 0, 0, skuMonth);
  270. }
  271. return String.format("%s%s", 0, skuMonth);
  272. }
  273. /**
  274. * 获取车位
  275. */
  276. @Override
  277. public GroupsRelation getGroupTripsTicket(Long yhsId, GoodsDonSku sku, Long userId, OrderDon exCodeOrderDon, OrderDon.Type orderDonTye, String exCode) {
  278. if (sku == null) {
  279. throw BusinessRuntimeException.getInstance("规格不存在,请联系客服");
  280. }
  281. GoodsDon goodsDon = goodsDonMapper.selectById(sku.getGoodsId());
  282. if (goodsDon == null) {
  283. throw BusinessRuntimeException.getInstance("平台不存在,请联系客服");
  284. }
  285. User user = userMapper.selectById(userId);
  286. if (user == null) {
  287. throw BusinessRuntimeException.getInstance("用户不存在");
  288. }
  289. GroupsRelation relation = getRelationNoOrder(sku, userId, yhsId);
  290. Long relationId = relation.getId();
  291. OrderDon orderDon;
  292. if (exCodeOrderDon != null) {
  293. orderDon = exCodeOrderDon;
  294. orderDon.setRelationId(relationId);
  295. orderDon.setUserId(userId);
  296. orderDon.setStatus(goodsDon.getType() == 1 && goodsDon.getSecondType() == 2 ? OrderDon.Status.complete : OrderDon.Status.hasPayment);
  297. orderDon.setYhsId(yhsId);
  298. orderDonMapper.updateById(orderDon);
  299. if (orderDon.getIsDp()) {
  300. //是否已经发送优惠加购车票
  301. OrderDiscountPlusPurchaseSkuRelation purchaseSkuRelation = orderDiscountPlusPurchaseSkuRelationMapper.selectOne(Wrappers.lambdaQuery(OrderDiscountPlusPurchaseSkuRelation.class)
  302. .eq(OrderDiscountPlusPurchaseSkuRelation::getOrderId, orderDon.getId()));
  303. if (purchaseSkuRelation != null) {
  304. String plusSkuIdsStr = purchaseSkuRelation.getPlusSkuIds();
  305. String relationIdsStr = purchaseSkuRelation.getRelationIds();
  306. if (StrUtil.isNotEmpty(plusSkuIdsStr) && StrUtil.isEmpty(relationIdsStr)) {
  307. try {
  308. List<Long> plusSkuIds = Jsons.parseList(plusSkuIdsStr, Long.class);
  309. //发送优惠加购 规格车票
  310. List<Long> dis_relation_ids = new ArrayList<>();
  311. plusSkuIds.forEach(plus_skuId -> {
  312. GoodsDonSku plus_sku = goodsDonSkuMapper.selectById(plus_skuId);
  313. if (plus_sku == null) {
  314. log.error("订单id:{}优惠加购规格已被删除 skuId:{},车票未发送", orderDon.getId(), plus_skuId);
  315. return;
  316. }
  317. GroupsRelation relationNoOrder = getRelationNoOrder(plus_sku, orderDon.getUserId(), orderDon.getYhsId());
  318. dis_relation_ids.add(relationNoOrder.getId());
  319. });
  320. //保存订单关联优惠加购车票
  321. orderDiscountPlusPurchaseSkuRelationMapper.update(null, Wrappers.lambdaUpdate(OrderDiscountPlusPurchaseSkuRelation.class)
  322. .set(OrderDiscountPlusPurchaseSkuRelation::getRelationIds, dis_relation_ids.toString())
  323. .eq(OrderDiscountPlusPurchaseSkuRelation::getOrderId, orderDon.getId())
  324. .isNull(OrderDiscountPlusPurchaseSkuRelation::getRelationIds));
  325. } catch (Exception e) {
  326. }
  327. }
  328. }
  329. }
  330. } else {
  331. /* 生成订单 */
  332. String donNo = SEQUENCE.nextId().toString();
  333. orderDon = new OrderDon();
  334. orderDon.setOrderNo(donNo);
  335. orderDon.setOpenId(user.getOpenId());
  336. orderDon.setMoney(BigDecimal.ZERO);
  337. orderDon.setSkuId(sku.getId());
  338. orderDon.setRelationId(relationId);
  339. orderDon.setYhsId(yhsId);
  340. //默认数量为1
  341. orderDon.setNum(1);
  342. orderDon.setGoodsId(sku.getGoodsId());
  343. orderDon.setExCode(exCode);
  344. orderDon.setStatus(goodsDon.getType() == 1 && goodsDon.getSecondType() == 2 ? OrderDon.Status.complete : OrderDon.Status.hasPayment);
  345. if (relation.getStartTime() == null || relation.getExpiryTime() == null) {
  346. orderDon.setStatus(OrderDon.Status.hasPayment);
  347. }
  348. if (relation != null && relation.getStatus() == GroupsRelation.Status.outside) {
  349. orderDon.setIsOt(true);
  350. }
  351. //兑换码兑换
  352. orderDon.setType(orderDonTye);
  353. orderDon.setUserId(user.getId());
  354. StringBuilder payTitle = new StringBuilder();
  355. payTitle.append(goodsDon.getTitle());
  356. payTitle.append(sku.getSpecVal());
  357. orderDon.setPayTitle(payTitle.toString());
  358. orderDonMapper.insert(orderDon);
  359. log.info("用户{}兑换规格:{}车位成功", user.getId(), orderDon.getSkuId());
  360. if (relation.getIsRenew() != null && relation.getIsRenew()) {
  361. //claude code/codex续费
  362. orderDon.setOrderType(2);
  363. orderDonMapper.updateById(orderDon);
  364. //claude code续费且升级
  365. if (relation.getIsClaudeCodeUpgrade() != null && relation.getIsClaudeCodeUpgrade()) {
  366. claudeCodeService.handleClaudeCodeUserPackageSku(relation, sku, orderDon.getId());
  367. }
  368. //codex 续费且升级
  369. if (relation.getIsCodexUpgrade() != null && relation.getIsCodexUpgrade()) {
  370. codexService.handleClaudeCodeUserPackageSku(relation, sku, orderDon.getId());
  371. }
  372. }
  373. }
  374. if (orderDonTye == OrderDon.Type.EX_CODE) {
  375. //同步车票是否重购 记录
  376. TASK_POOL.execute(() -> {
  377. groupsRelationExpiryRecordService.syncGroupsRelationExpiryRecord(orderDon, goodsDon, sku);
  378. });
  379. }
  380. relation.setOrderId(orderDon.getId());
  381. //邀请制代充平台
  382. if ((goodsDon.getSpecialType() != null && goodsDon.getSpecialType() == GoodsDon.SpecialType.recharge) || Constant.RECHARGE_INVITE_GOODS_IDS.contains(goodsDon.getId())) {
  383. relation.setStartTime(null);
  384. relation.setExpiryTime(null);
  385. relation.setRechargeStatus(GroupsRelation.RechargeStatus.waiting);
  386. relation.setSubmitTime(DateTime.now());
  387. //GPT代充
  388. if (goodsDon.getId() == Constant.GPT_RECHARGE_GOODS_ID) {
  389. //代充次数
  390. relation.setRechargeNum(sku.getMonths());
  391. relation.setRechargeRemainNum(relation.getRechargeNum());
  392. }
  393. groupsRelationMapper.updateById(relation);
  394. }
  395. return relation;
  396. }
  397. @Override
  398. public GroupsRelation getRelationNoOrder(GoodsDonSku sku, Long userId, Long yhsId) {
  399. //claude code商品兑换
  400. if (sku.getGoodsId() == Constant.CLAUDE_CODE_GOODS_ID) {
  401. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  402. GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getUserId, userIdList).op(Operator.InList).field(GroupsRelationView::getGoodsId, Constant.CLAUDE_CODE_GOODS_ID).field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan).build());
  403. if (groupsRelationView != null) {
  404. GoodsDonSku relationSku = goodsDonSkuMapper.selectById(groupsRelationView.getSkuId());
  405. //兑换的规格 低于当前规格 不让兑换
  406. if (relationSku.getClaudeDailyLimit() > sku.getClaudeDailyLimit()) {
  407. throw BusinessRuntimeException.getInstance("请在claude code当前版本过期后再兑换");
  408. }
  409. Date expiryTime = groupsRelationView.getExpiryTime();
  410. if (sku.getDays() != null) {
  411. expiryTime = DateUtil.offsetDay(expiryTime, sku.getDays());
  412. } else {
  413. expiryTime = DateUtil.offsetMonth(expiryTime, sku.getMonths());
  414. }
  415. GroupsRelation relation = groupsRelationMapper.selectById(groupsRelationView.getId());
  416. relation.setExpiryTime(expiryTime);
  417. groupsRelationMapper.updateById(relation);
  418. //同规格只加时长
  419. relation.setIsRenew(Boolean.TRUE);
  420. if (relationSku.getClaudeDailyLimit() != sku.getClaudeDailyLimit()) {
  421. relation.setIsClaudeCodeUpgrade(true);
  422. }
  423. return relation;
  424. }
  425. }
  426. //codex 商品兑换
  427. if (sku.getGoodsId() == Constant.CODEX_GOODS_ID) {
  428. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  429. 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());
  430. if (groupsRelationView != null) {
  431. GoodsDonSku relationSku = goodsDonSkuMapper.selectById(groupsRelationView.getSkuId());
  432. //兑换的规格 低于当前规格 不让兑换
  433. if (relationSku.getOpenaiDailyLimit() > sku.getOpenaiDailyLimit()) {
  434. throw BusinessRuntimeException.getInstance("请在codex当前版本过期后再兑换");
  435. }
  436. Date expiryTime = groupsRelationView.getExpiryTime();
  437. if (sku.getDays() != null) {
  438. expiryTime = DateUtil.offsetDay(expiryTime, sku.getDays());
  439. } else {
  440. expiryTime = DateUtil.offsetMonth(expiryTime, sku.getMonths());
  441. }
  442. GroupsRelation relation = groupsRelationMapper.selectById(groupsRelationView.getId());
  443. relation.setExpiryTime(expiryTime);
  444. groupsRelationMapper.updateById(relation);
  445. //同规格只加时长
  446. relation.setIsRenew(Boolean.TRUE);
  447. if (relationSku.getOpenaiDailyLimit() != sku.getOpenaiDailyLimit()) {
  448. relation.setIsCodexUpgrade(true);
  449. }
  450. return relation;
  451. }
  452. }
  453. Integer retryNum = 1;
  454. List<Long> errorsRelationIds = new ArrayList<>();
  455. GroupsRelation relation = getFinalRelation(sku, userId, retryNum, yhsId, errorsRelationIds);
  456. return relation;
  457. }
  458. public GroupsRelation getFinalRelation(GoodsDonSku sku, Long userId, Integer retryNum, Long yhsId, List<Long> errorsRelationIds) {
  459. //获取车票车位
  460. GroupsRelation relation = groupRelationFrontService.getRelation(null, userId, sku, null);
  461. try {
  462. //锁定座位
  463. setRelation(relation);
  464. relation.setUserId(userId);
  465. GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
  466. if (GroupsTrips.Status.validity.equals(groupsTrips.getStatus())) {
  467. //如果续费增加时间,如果首次则设置当前时间为初始时间
  468. Date expiryTime = Optional.ofNullable(relation.getExpiryTime()).orElse(new Date());
  469. Date newDate;
  470. if (sku.getDays() != null && sku.getDays() > 0) {
  471. newDate = DateUtil.offsetDay(expiryTime, sku.getDays() * 1);
  472. } else {
  473. //奈飞月付 按30天计算
  474. if (sku.getId() == 4) {
  475. newDate = DateUtil.offsetDay(expiryTime, 30);
  476. } else {
  477. newDate = DateUtil.offset(expiryTime, DateField.MONTH, sku.getMonths() * 1);
  478. }
  479. }
  480. relation.setExpiryTime(newDate);
  481. relation.setStartTime(relation.getStartTime() == null ? new Date() : null);
  482. }
  483. if (relation.getStatus() != GroupsRelation.Status.outside) {
  484. relation.setStatus(GroupsRelation.Status.validity);
  485. }
  486. relation.setYhsId(yhsId);
  487. if (yhsId != null && yhsId != 0) {
  488. relation.setCmt(1);
  489. }
  490. groupsRelationMapper.updateById(relation);
  491. Long skuId = sku.getId();
  492. Long groupsId = relation.getGroupsId();
  493. //校验指定平台规格的 目前车队的可停车位数
  494. groupRelationFrontService.checkGroupsAvailParkingNum(skuId, groupsId, relation.getGroupsAvailParkingNum(), relation.getMaxNum());
  495. groupRelationFrontService.adjustGroupsParkingTime(relation.getId(), sku);
  496. } catch (Exception e) {
  497. //删除座位缓存
  498. delRelationKey(relation.getId(), userId);
  499. errorsRelationIds.add(relation.getId());
  500. if (retryNum == 15) {
  501. log.error("用户:{}获取车票:{}异常", userId, relation.getId());
  502. throw BusinessRuntimeException.getInstance("获取车票异常,请联系客服");
  503. }
  504. //失败重新给他分配车位
  505. return getFinalRelation(sku, userId, ++retryNum, yhsId, errorsRelationIds);
  506. }
  507. return relation;
  508. }
  509. public void setRelation(GroupsRelation relation) {
  510. Long relationId = relation.getId();
  511. Long userId = relation.getUserId();
  512. Long groupsId = relation.getGroupsId();
  513. GroupsRelation.Status status = relation.getStatus();
  514. String relation_num_key = RedisKey.GROUPS_RELATION_NUM_KEY + relationId;
  515. if (!redisService.setNx(relation_num_key, userId, 60 * 5L)) {
  516. GroupsRelation dbRelation = groupsRelationMapper.selectById(relationId);
  517. if (dbRelation.getUserId() != 0) {
  518. log.info("=====>用户:{}未抢到座位:{}", userId, relationId);
  519. throw new BusinessRuntimeException("系统繁忙,请重试...");
  520. }
  521. }
  522. if (status == GroupsRelation.Status.outside) {
  523. return;
  524. }
  525. int count = groupsMapper.decrAvailableNum(groupsId);
  526. if (count == 0) {
  527. log.error("车位异常 groupId:{}", groupsId);
  528. groupsMapper.updateAvailableNum(groupsId);
  529. //删除座位缓存
  530. delRelationKey(relationId, userId);
  531. throw new BusinessRuntimeException("车位异常");
  532. }
  533. }
  534. /**
  535. * 清除座位缓存key
  536. */
  537. public void delRelationKey(Long relationId, Long userId) {
  538. String relation_num_key = RedisKey.GROUPS_RELATION_NUM_KEY + relationId;
  539. Object object = redisService.get(relation_num_key);
  540. if (object != null) {
  541. try {
  542. Long keyUserId = Long.parseLong(object.toString());
  543. //清除车票key
  544. if (ObjectUtil.equal(userId, keyUserId)) {
  545. redisService.del(relation_num_key);
  546. }
  547. } catch (Exception e) {
  548. log.error("删除缓存key错误:{}", StringUtil.getErrorText(e));
  549. }
  550. }
  551. }
  552. }