VipFrontServiceImpl.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. package com.cyksj.service.vip.impl;
  2. import cn.hutool.core.date.DateTime;
  3. import cn.hutool.core.date.DateUtil;
  4. import cn.hutool.core.util.ObjectUtil;
  5. import cn.hutool.core.util.StrUtil;
  6. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  7. import com.cyksj.common.constant.Constant;
  8. import com.cyksj.common.exception.BusinessRuntimeException;
  9. import com.cyksj.mapper.*;
  10. import com.cyksj.mapper.sys.SysConfigMapper;
  11. import com.cyksj.mapper.vip.VipGoodsSkuMapper;
  12. import com.cyksj.model.dto.VipDeductDto;
  13. import com.cyksj.model.entity.*;
  14. import com.cyksj.model.manage.views.GroupsRelationView;
  15. import com.cyksj.model.response.VipUserPageResp;
  16. import com.cyksj.service.chatgpt.ChatGptAccountService;
  17. import com.cyksj.service.claude.ClaudeService;
  18. import com.cyksj.service.user.UserBindRelationService;
  19. import com.cyksj.service.vip.VipFrontService;
  20. import com.ejlchina.searcher.BeanSearcher;
  21. import com.ejlchina.searcher.param.Operator;
  22. import com.ejlchina.searcher.util.MapUtils;
  23. import lombok.RequiredArgsConstructor;
  24. import lombok.extern.slf4j.Slf4j;
  25. import org.springframework.stereotype.Service;
  26. import java.math.BigDecimal;
  27. import java.math.RoundingMode;
  28. import java.util.*;
  29. import java.util.stream.Collectors;
  30. /**
  31. * 项目名: yhlxj2
  32. * 文件名: VipFrontServiceImpl
  33. * 创建者: JavaZou
  34. * 创建时间:2025/5/15 16:00
  35. */
  36. @Service
  37. @RequiredArgsConstructor
  38. @Slf4j
  39. public class VipFrontServiceImpl implements VipFrontService {
  40. private final SysConfigMapper sysConfigMapper;
  41. private final VipGoodsSkuMapper vipGoodsSkuMapper;
  42. private final UserBindRelationService userBindRelationService;
  43. private final BeanSearcher beanSearcher;
  44. private final GoodsDonSkuMapper skuMapper;
  45. private final ChatgptUserMapper chatgptUserMapper;
  46. private final OrderDonRenewRecordMapper orderDonRenewRecordMapper;
  47. private final ClaudeUserMapper claudeUserMapper;
  48. private final OrderDonMapper orderDonMapper;
  49. private final GoodsDonSkuMapper goodsDonSkuMapper;
  50. private final ChatGptAccountService chatGptAccountService;
  51. private final ClaudeService claudeService;
  52. @Override
  53. public void fillVipInfo(Long userId, VipUserPageResp vipUserPageResp) {
  54. SysConfig sysConfig = sysConfigMapper.selectOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, Constant.VIP_FEE_KEY).last("limit 1"));
  55. if (sysConfig != null && StrUtil.isNotBlank(sysConfig.getSysValue())) {
  56. vipUserPageResp.setVipFee(BigDecimal.valueOf(Integer.parseInt(sysConfig.getSysValue())));
  57. }
  58. if (userId == null) {
  59. return;
  60. }
  61. //可抵扣价格
  62. BigDecimal deductMoney = BigDecimal.ZERO;
  63. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  64. List<Long> vipSkuIds = vipGoodsSkuMapper.selectList(null).stream().map(VipGoodsSku::getSkuId).collect(Collectors.toList());
  65. //可有抵扣车票
  66. DateTime now = DateTime.now();
  67. //会员规格
  68. Set<Long> goodsIds = goodsDonSkuMapper.selectList(Wrappers.lambdaQuery(GoodsDonSku.class)
  69. .in(GoodsDonSku::getId, vipSkuIds)).stream().map(GoodsDonSku::getGoodsId).collect(Collectors.toSet());
  70. //todo 临时处理 nano先不参与抵扣
  71. goodsIds.remove(Constant.NANO_GOODS_ID);
  72. Number count = beanSearcher.searchCount(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getUserId, userIdList).op(Operator.InList).field(GroupsRelationView::getGoodsId, goodsIds).op(Operator.InList).field(GroupsRelationView::getIsMirror, Boolean.TRUE).field(GroupsRelationView::getExpiryTime, now).op(Operator.GreaterThan).build());
  73. //可抵扣车票
  74. if (count.intValue() > 0) {
  75. Integer months = vipUserPageResp.getMonths();
  76. //处理vip抵扣金额
  77. VipDeductDto vipDeductDto = commonHandleVipDeductMoney(userIdList, goodsIds, months);
  78. vipUserPageResp.setRelationUserViews(vipDeductDto.getRelationUserViews());
  79. vipUserPageResp.setExtendsDays(vipDeductDto.getExtendsDays());
  80. if (months != 12) {
  81. //取出最大可抵扣info
  82. vipDeductDto = commonHandleVipDeductMoney(userIdList, goodsIds, 12);
  83. }
  84. deductMoney = vipDeductDto.getDeductMoney();
  85. }
  86. vipUserPageResp.setDeductMoney(deductMoney);
  87. }
  88. /**
  89. * 检查用户并获取订单抵扣金额
  90. */
  91. @Override
  92. public VipDeductDto checkAndGetOrderDeductMoney(Long userId, Integer months) {
  93. List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
  94. List<Long> vipSkuIds = vipGoodsSkuMapper.selectList(null).stream().map(VipGoodsSku::getSkuId).collect(Collectors.toList());
  95. //会员规格
  96. Set<Long> goodsIds = goodsDonSkuMapper.selectList(Wrappers.lambdaQuery(GoodsDonSku.class)
  97. .in(GoodsDonSku::getId, vipSkuIds)).stream().map(GoodsDonSku::getGoodsId).collect(Collectors.toSet());
  98. Number count = beanSearcher.searchCount(GroupsRelationView.class, MapUtils.builder()
  99. .field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
  100. .field(GroupsRelationView::getGoodsId, goodsIds).op(Operator.InList)
  101. .field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
  102. .field(GroupsRelationView::getIsVip, false)
  103. .build());
  104. if (count.intValue() == 0) {
  105. throw BusinessRuntimeException.getInstance("暂无可抵扣AI车票");
  106. }
  107. VipDeductDto vipDeductDto = commonHandleVipDeductMoney(userIdList, goodsIds, months);
  108. return vipDeductDto;
  109. }
  110. private BigDecimal getVipMoney(Integer months) {
  111. SysConfig sysConfig = sysConfigMapper.selectOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, Constant.VIP_FEE_KEY).last("limit 1"));
  112. if (sysConfig == null || StrUtil.isEmpty(sysConfig.getSysValue())) {
  113. throw BusinessRuntimeException.getInstance("系统异常");
  114. }
  115. BigDecimal vipFee = BigDecimal.valueOf(Long.valueOf(sysConfig.getSysValue()));
  116. return vipFee.multiply(BigDecimal.valueOf(months));
  117. }
  118. /**
  119. * 统一处理vip抵扣金额
  120. */
  121. public VipDeductDto commonHandleVipDeductMoney(List<Long> userIdList, Set<Long> goodsIds, Integer months) {
  122. VipDeductDto vipDeductDto = new VipDeductDto();
  123. DateTime now = DateTime.now();
  124. List<Long> deductRelationIds = new ArrayList<>();
  125. BigDecimal deductMoney = BigDecimal.ZERO;
  126. BigDecimal vipMoney = getVipMoney(months);
  127. List<GroupsRelationView> relations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder()
  128. .field(GroupsRelationView::getGoodsId, goodsIds).op(Operator.InList)
  129. .field(GroupsRelationView::getIsMirror, Boolean.TRUE)
  130. .field(GroupsRelationView::getUserId, userIdList).op(Operator.InList)
  131. .field(GroupsRelationView::getIsVip, false)
  132. .field(GroupsRelationView::getExpiryTime, DateTime.now()).op(Operator.GreaterThan)
  133. .field(GroupsRelationView::getAqType, 1)
  134. .orderBy(GroupsRelationView::getExpiryTime).desc()
  135. .build());
  136. Set<Long> deductGoodsIds = null;
  137. List<GroupsRelationView> deductRelations = null;
  138. for (GroupsRelationView relation : relations) {
  139. if (deductGoodsIds == null) {
  140. deductGoodsIds = new HashSet<>();
  141. }
  142. if (deductRelations == null) {
  143. deductRelations = new ArrayList<>();
  144. }
  145. Long goodsId = relation.getGoodsId();
  146. if (deductGoodsIds.contains(goodsId)) {
  147. continue;
  148. }
  149. Long skuId = relation.getSkuId();
  150. deductGoodsIds.add(goodsId);
  151. Long relationId = relation.getId();
  152. Date relationExpiryTime = relation.getExpiryTime();
  153. //GPT
  154. if (goodsId == Constant.GPT_PLUS_GID) {
  155. ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getRelationId, relationId).last("limit 1"));
  156. if (chatgptUser == null) {
  157. chatGptAccountService.getUserInfo(relation.getUserId(), relation.getId());
  158. chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getRelationId, relationId).last("limit 1"));
  159. }
  160. if (chatgptUser != null) {
  161. //获取抵扣金额
  162. deductMoney = deductMoney.add(getFinalDeductMoney(relationId, relation.getSkuId(), chatgptUser.getRenewSkuId(), chatgptUser.getRenewOrderId(), chatgptUser.getRenewExpiryTime(), relation.getStartTime(), relationExpiryTime, now, userIdList));
  163. }
  164. } else if (goodsId == Constant.CLAUDE_PRO_GID) {
  165. ClaudeUser claudeUser = claudeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeUser.class).eq(ClaudeUser::getRelationId, relationId).last("limit 1"));
  166. if (claudeUser == null) {
  167. claudeService.getUserInfo(relation.getUserId(), relation.getId());
  168. claudeUser = claudeUserMapper.selectOne(Wrappers.lambdaQuery(ClaudeUser.class).eq(ClaudeUser::getRelationId, relationId).last("limit 1"));
  169. }
  170. if (claudeUser != null) {
  171. //获取抵扣金额
  172. deductMoney = deductMoney.add(getFinalDeductMoney(relationId, relation.getSkuId(), claudeUser.getRenewSkuId(), claudeUser.getRenewOrderId(), claudeUser.getRenewExpiryTime(), relation.getStartTime(), relationExpiryTime, now, userIdList));
  173. }
  174. } else {
  175. //该车票抵扣金额
  176. deductMoney = deductMoney.add(getDeductMoneyFromTicket(relationId, userIdList, skuId, now, relation.getStartTime(), relationExpiryTime));
  177. }
  178. deductRelationIds.add(relationId);
  179. //可抵扣车票
  180. deductRelations.add(relation);
  181. }
  182. //实际可抵扣金额
  183. vipDeductDto.setActuallyDeDuctMoney(deductMoney);
  184. vipDeductDto.setDeductMoney(deductMoney);
  185. //超出可抵扣的金额 直接返回
  186. if (deductMoney.compareTo(vipMoney) >= 0) {
  187. BigDecimal overMoney = deductMoney.subtract(vipMoney);
  188. //超过抵扣金额可换延长会员多少天
  189. //每天单价
  190. BigDecimal daySingle = vipMoney.divide(BigDecimal.valueOf(months)).divide(BigDecimal.valueOf(30), 0, RoundingMode.DOWN);
  191. //超出部分可换取多少天 低于每天单价为0天
  192. BigDecimal extendsDays = overMoney.divide(daySingle, 0, RoundingMode.UP);
  193. vipDeductDto.setExtendsDays(extendsDays.intValue());
  194. vipDeductDto.setDeductMoney(vipMoney);
  195. }
  196. vipDeductDto.setDeductRelationIds(deductRelationIds);
  197. vipDeductDto.setRelationUserViews(deductRelations);
  198. return vipDeductDto;
  199. }
  200. /**
  201. * 获取最终抵扣金额
  202. */
  203. public BigDecimal getFinalDeductMoney(Long relationId, Long relationSkuId, Long renewSkuId, Long renewOrderId, Date renewExpiryTime, Date relationStartTime, Date relationExpiryTime, DateTime now, List<Long> userIdList) {
  204. BigDecimal deductMoney = BigDecimal.ZERO;
  205. //一个月当30天算单价 往上取整
  206. //续费规格
  207. if (renewSkuId != null && renewExpiryTime.compareTo(now) > 0) {
  208. OrderDon renewOrderDon = orderDonMapper.selectById(renewOrderId);
  209. BigDecimal orderMoney = renewOrderDon.getMoney().add(Optional.ofNullable(renewOrderDon.getBalance()).orElse(BigDecimal.ZERO));
  210. GoodsDonSku presentSku = skuMapper.selectById(renewSkuId);
  211. if (orderMoney.compareTo(BigDecimal.ZERO) == 0) {
  212. orderMoney = presentSku.getPrice();
  213. }
  214. Long betweenDay = DateUtil.betweenDay(now, renewExpiryTime, false) + 1;
  215. //当天购买的按规格原价抵扣
  216. BigDecimal renewDeductMoney;
  217. if (now.toDateStr().equals(DateUtil.format(renewOrderDon.getCreatedTime(), "yyyy-MM-dd"))) {
  218. renewDeductMoney = orderMoney;
  219. deductMoney = deductMoney.add(renewDeductMoney);
  220. if (presentSku.getDays() != null) {
  221. relationExpiryTime = DateUtil.offsetDay(relationExpiryTime, -presentSku.getDays());
  222. } else {
  223. relationExpiryTime = DateUtil.offsetMonth(relationExpiryTime, -presentSku.getMonths());
  224. }
  225. } else {
  226. //此次续费规格单价
  227. BigDecimal single = getPayOrderSingle(orderMoney, presentSku);
  228. //该车票抵扣金额
  229. renewDeductMoney = single.multiply(BigDecimal.valueOf(betweenDay));
  230. log.info("车票id:{}目前续费升级规格可抵扣的金额为:{}", relationId, renewDeductMoney);
  231. deductMoney = deductMoney.add(renewDeductMoney);
  232. relationExpiryTime = DateUtil.offsetDay(relationExpiryTime, -betweenDay.intValue());
  233. }
  234. //上一个续费升级规格
  235. List<OrderDon> orderDOns = orderDonRenewRecordMapper.getOtherRenewSkus(renewOrderId, relationId, userIdList);
  236. OrderDon presentRenewOrderDon = renewOrderDon;
  237. for (OrderDon thisRenewOrderDon : orderDOns) {
  238. Long skuId = thisRenewOrderDon.getSkuId();
  239. GoodsDonSku thisRenewSku = skuMapper.selectById(skuId);
  240. BigDecimal thisOrderMoney = thisRenewOrderDon.getMoney().add(Optional.ofNullable(thisRenewOrderDon.getBalance()).orElse(BigDecimal.ZERO));
  241. if (thisOrderMoney.compareTo(BigDecimal.ZERO) == 0) {
  242. thisOrderMoney = thisRenewSku.getPrice();
  243. }
  244. //当天购买的按规格原价抵扣
  245. BigDecimal thisRenewDeductMoney = BigDecimal.ZERO;
  246. if (now.toDateStr().equals(DateUtil.format(thisRenewOrderDon.getCreatedTime(), "yyyy-MM-dd"))) {
  247. thisRenewDeductMoney = thisOrderMoney;
  248. deductMoney = deductMoney.add(thisRenewDeductMoney);
  249. if (thisRenewSku.getDays() != null) {
  250. relationExpiryTime = DateUtil.offsetDay(relationExpiryTime, -thisRenewSku.getDays());
  251. } else {
  252. relationExpiryTime = DateUtil.offsetMonth(relationExpiryTime, -thisRenewSku.getMonths());
  253. }
  254. } else {
  255. //此次续费订单过期时间
  256. Date thisOrderExpiryTime;
  257. if (thisRenewSku.getDays() != null) {
  258. thisOrderExpiryTime = DateUtil.offsetDay(thisRenewOrderDon.getCreatedTime(), thisRenewSku.getDays());
  259. } else {
  260. thisOrderExpiryTime = DateUtil.offsetMonth(thisRenewOrderDon.getCreatedTime(), thisRenewSku.getMonths());
  261. }
  262. //续费同规格不处理
  263. if (!ObjectUtil.equal(thisRenewOrderDon.getSkuId(), presentRenewOrderDon.getSkuId())) {
  264. //先扣除已使用的天数
  265. //相差多少天
  266. Long thisRenewBetDay = DateUtil.betweenDay(thisRenewOrderDon.getCreatedTime(), presentRenewOrderDon.getCreatedTime(), false);
  267. //扣除已使用的
  268. thisOrderExpiryTime = DateUtil.offsetDay(thisOrderExpiryTime, -thisRenewBetDay.intValue());
  269. }
  270. //此订单过期时间
  271. if (thisOrderExpiryTime.compareTo(now) > 0) {
  272. //距离过期还剩多少天
  273. Long thisDeductBetweenDay = DateUtil.betweenDay(now, thisOrderExpiryTime, false) + 1;
  274. if (thisDeductBetweenDay != 0) {
  275. //此次续费规格单价
  276. BigDecimal single = getPayOrderSingle(thisOrderMoney, thisRenewSku);
  277. //该车票抵扣金额
  278. thisRenewDeductMoney = single.multiply(BigDecimal.valueOf(thisDeductBetweenDay));
  279. deductMoney = deductMoney.add(thisRenewDeductMoney);
  280. }
  281. //目前级别续费订单
  282. presentRenewOrderDon = thisRenewOrderDon;
  283. relationExpiryTime = DateUtil.offsetDay(relationExpiryTime, -thisDeductBetweenDay.intValue());
  284. }
  285. }
  286. log.info("车票id:{}目前次一级规格:{}可抵扣的金额为:{}", relationId, thisRenewSku.getSpecVal(), thisRenewDeductMoney);
  287. }
  288. }
  289. if (relationExpiryTime.after(now)) {
  290. //该车票抵扣金额
  291. BigDecimal deductMoneyFromTicket = getDeductMoneyFromTicket(relationId, userIdList, relationSkuId, now, relationStartTime, relationExpiryTime);
  292. deductMoney = deductMoney.add(deductMoneyFromTicket);
  293. }
  294. log.info("车票id:{}最终到期时间为:{}车票可返回 抵扣金额:{}", relationId, DateUtil.format(relationExpiryTime, "yyyy-MM-dd HH:mm:ss"), deductMoney);
  295. return deductMoney;
  296. }
  297. /**
  298. * 获取车票可抵扣金额
  299. */
  300. public BigDecimal getDeductMoneyFromTicket(Long relationId, List<Long> userIdList, Long skuId, DateTime now, Date relationStartTime, Date relationExpiryTime) {
  301. List<OrderDon> orderDonList = orderDonMapper.selectList(Wrappers.lambdaQuery(OrderDon.class)
  302. .eq(OrderDon::getRelationId, relationId)
  303. .in(OrderDon::getUserId, userIdList)
  304. .eq(OrderDon::getSkuId, skuId)
  305. .notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
  306. .orderByDesc(OrderDon::getId));
  307. //部分退款
  308. if (orderDonList.isEmpty()) {
  309. orderDonList = orderDonMapper.selectList(Wrappers.lambdaQuery(OrderDon.class)
  310. .eq(OrderDon::getRelationId, relationId)
  311. .in(OrderDon::getUserId, userIdList)
  312. .eq(OrderDon::getSkuId, skuId)
  313. .notIn(OrderDon::getStatus, Constant.noOrderStatus)
  314. .orderByDesc(OrderDon::getId));
  315. }
  316. //原车票订单总实付金额
  317. BigDecimal totalOrderMoney = BigDecimal.ZERO;
  318. //原车票订单总天数
  319. Integer totalDays = 0;
  320. for (OrderDon orderDon : orderDonList) {
  321. GoodsDonSku sku = skuMapper.selectById(skuId);
  322. BigDecimal thisOrderMoney = orderDon.getMoney().add(Optional.ofNullable(orderDon.getBalance()).orElse(BigDecimal.ZERO)).subtract(Optional.ofNullable(orderDon.getRefundMoney()).orElse(BigDecimal.ZERO));
  323. if (thisOrderMoney.compareTo(BigDecimal.ZERO) == 0) {
  324. thisOrderMoney = sku.getPrice();
  325. }
  326. totalOrderMoney = totalOrderMoney.add(thisOrderMoney);
  327. //总时间
  328. if (sku.getDays() != null) {
  329. totalDays += sku.getDays();
  330. } else {
  331. totalDays += sku.getMonths() * 30;
  332. }
  333. //原订单数
  334. if (orderDonList.size() == 1) {
  335. //此次订单过期时间
  336. Date thisOrderExpiryTime;
  337. if (sku.getDays() != null) {
  338. thisOrderExpiryTime = DateUtil.offsetDay(orderDon.getCreatedTime(), sku.getDays());
  339. } else {
  340. thisOrderExpiryTime = DateUtil.offsetMonth(orderDon.getCreatedTime(), sku.getMonths());
  341. }
  342. //当天购买的按规格原价抵扣
  343. if (now.toDateStr().equals(DateUtil.format(relationStartTime, "yyyy-MM-dd")) && (DateUtil.betweenDay(thisOrderExpiryTime, relationExpiryTime, true) == 0)) {
  344. return thisOrderMoney;
  345. }
  346. }
  347. }
  348. Long thisDeductBetweenDay = DateUtil.betweenDay(now, relationExpiryTime, false) + 1;
  349. BigDecimal single = totalOrderMoney.divide(BigDecimal.valueOf(totalDays), 0, RoundingMode.HALF_UP);
  350. //该车票抵扣金额
  351. BigDecimal deductMoney = single.multiply(BigDecimal.valueOf(thisDeductBetweenDay));
  352. return deductMoney;
  353. }
  354. /**
  355. * 获取订单单价
  356. */
  357. public BigDecimal getPayOrderSingle(BigDecimal orderMoney, GoodsDonSku sku) {
  358. //此次续费规格单价
  359. BigDecimal single;
  360. //区分天/月
  361. if (sku.getDays() != null) {
  362. single = orderMoney.divide(BigDecimal.valueOf(sku.getDays()), 0, RoundingMode.HALF_UP);
  363. } else {
  364. single = orderMoney.divide(BigDecimal.valueOf(sku.getMonths()).multiply(BigDecimal.valueOf(30)), 0, RoundingMode.HALF_UP);
  365. }
  366. return single;
  367. }
  368. }