package com.cyksj.service.relation.impl; import cn.hutool.core.date.BetweenFormater; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpStatus; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.cyksj.common.constant.Constant; import com.cyksj.common.exception.BusinessRuntimeException; import com.cyksj.common.task.GlobalThreadPoolTaskExecutor; import com.cyksj.common.util.J11HttpC; import com.cyksj.common.util.Jsons; import com.cyksj.enums.GatewayApiCode; import com.cyksj.mapper.*; import com.cyksj.model.entity.*; import com.cyksj.model.request.SpecialGoodsTicketReq; import com.cyksj.model.views.*; import com.cyksj.redis.RedisService; import com.cyksj.service.chatgpt.ChatGptAuthService; import com.cyksj.service.groups.GroupsFuncService; import com.cyksj.service.mail.MailService; import com.cyksj.service.mange.coupon.CouponCommonService; import com.cyksj.service.relation.GroupRelationFrontService; import com.cyksj.service.shop.YhShopFrontService; 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 lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletRequest; import java.net.http.HttpResponse; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Optional; import static com.cyksj.enums.GatewayApiCode.GROUP_INDEX_ALREADY_BE_USER; import static com.cyksj.enums.GatewayApiCode.GROUP_INDEX_NOT_FIND; /* *项目名: netflix *文件名: GroupRelationFrontServiceImpl *创建者: JavaZou *创建时间:2022/12/29 15:42 */ @Service @RequiredArgsConstructor @Slf4j public class GroupRelationFrontServiceImpl implements GroupRelationFrontService { private final UserBindRelationService userBindRelationService; private final UserMapper userMapper; private final BeanSearcher beanSearcher; private final CouponCommonService couponCommonService; private final GroupsRelationMapper groupsRelationMapper; private final GroupsMapper groupsMapper; private final ChatGptAuthService chatGptAuthService; private final RedisService redisService; private final MailService mailService; private final OrderDonMapper orderDonMapper; private final YhShopFrontService yhShopFrontService; private final HttpServletRequest request; private final GroupsFuncService groupsFuncService; private final OrderDiscountPlusPurchaseSkuRelationMapper orderDiscountPlusPurchaseSkuRelationMapper; private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance(); private final static String VERIFY_CODE_SECRET = "q1w2e3"; @Override public List getMyTicket(long userId, Boolean isLoginPopularize, String customId) { User u = userMapper.selectById(userId); if (u == null) throw BusinessRuntimeException.getGatewayApiCode(GatewayApiCode.CMS_TOKEN_VALID); if (u.getIsBlack()) { return new ArrayList<>(); } List userIds = userBindRelationService.getRelationUserIdList(userId, u); MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap()); Long yhsId = yhShopFrontService.getYhsIdByCustomId(customId); List list = beanSearcher.searchAll(RenewalView.class, builder .field(RenewalView::getUserId, 0).op(Operator.GreaterThan) .field(RenewalView::getUserId, userIds).op(Operator.InList) .field(RenewalView::getYhsId, yhsId) .field(RenewalView::getStatus, List.of("validity", "overdue", "outside")).op(Operator.InList) .orderBy(RenewalView::getExpiryTime).asc() .build()); //过滤邀请制、已失效的车位 DateTime now = DateTime.now(); list.stream().forEach(ticket -> { if (ticket.getGoodsId() == 18 && ticket.getSkuNum() > 1) { ticket.setPassword(null); } ticket.setIsMulti(false); if (ticket.getSkuNum() > 1) { ticket.setIsMulti(true); } //chatGPT直连 if (ticket.getGoodsId().equals(42l)) { if (StrUtil.isNotBlank(ticket.getAccount())) { String accessToken; try { accessToken = chatGptAuthService.getAccessToken(ticket.getAccount()); ticket.setGptRefreshToken(accessToken); } catch (Exception e) { redisService.set(RedisService.key.CHAT_GPT_TOKEN_EXCEPTION_KEY.getName() + ticket.getAccount(), ticket.getAccount()); } } ticket.setAccount(null); ticket.setPassword(null); } else { ticket.setGptRefreshToken(null); } if (ticket.getSecondType() == 1) { ticket.setAccount(null); ticket.setPassword(null); } if (isLoginPopularize != null && isLoginPopularize) { GoodsDonViews views = beanSearcher.searchFirst(GoodsDonViews.class, MapUtils.builder().field(GoodsDonViews::getId, ticket.getGoodsId()).build()); views.setSpecs(beanSearcher.searchFirst(GoodsDonSpecFrontView.class, MapUtils.builder().field(GoodsDonSpecFrontView::getGoodsId, views.getId()).build())); views.setSkuList(beanSearcher.searchAll(GoodsDonSkuFrontView.class, MapUtils.builder().field(GoodsDonSkuFrontView::getGoodsId, views.getId()).field(GoodsDonSkuFrontView::getStatus, true).build())); ticket.setGoodsDonViews(views); } if (ticket.getMonths() != null && ticket.getMonths() >= 300) { ticket.setExpiryTime(null); } if (ticket.getExpiryTime() != null && ticket.getGoodsType() == 1 && ticket.getSecondType() == 2) { String remainDays = DateUtil.formatBetween(now, ticket.getExpiryTime(), BetweenFormater.Level.DAY); if ("0天".equals(remainDays)) { remainDays = "小于1天"; } else { //当天也算 int days = Integer.parseInt(remainDays.substring(0, remainDays.indexOf("天"))); remainDays = days + 1 + "天"; } ticket.setRemainDays(remainDays); } if (list.size() > 1 && ticket.getGoodsId() != 18) { OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class) .eq(OrderDon::getRelationId, ticket.getRelationId()) .eq(OrderDon::getUserId, ticket.getUserId()) .orderByDesc(OrderDon::getId) .last("limit 1")); Date payTime = null; if (orderDon != null) { payTime = Optional.ofNullable(orderDon.getPayTime()).orElse(orderDon.getCreatedTime()); } //加购订单 if (orderDon == null) { List plusPurchaseSkuRelations = orderDiscountPlusPurchaseSkuRelationMapper.selectPlusRelationByIdAndUserId(ticket.getRelationId(), ticket.getUserId()); for (OrderDiscountPlusPurchaseSkuRelation data : plusPurchaseSkuRelations) { String relationIds = data.getRelationIds(); if (StrUtil.isEmpty(relationIds)) { return; } try { List relationList = Jsons.parseList(relationIds, Long.class); if (relationList.contains(ticket.getRelationId())) { OrderDon plusOrder = orderDonMapper.selectById(data.getOrderId()); if (plusOrder != null) { payTime = Optional.ofNullable(plusOrder.getPayTime()).orElse(plusOrder.getCreatedTime()); } break; } } catch (Exception e) { } } } ticket.setPayTime(payTime); } List relationUserViews = groupsRelationMapper.getUserRelationUserViews(ticket.getGroupId(), ticket.getRelationId(), ticket.getUserId(), List.of(GroupsRelation.Status.validity, GroupsRelation.Status.outside)); ticket.setRelationViews(relationUserViews); if (ticket.getExpiryTime() != null) { ticket.setExpiryTimeStr(DateUtil.format(ticket.getExpiryTime(), "yyyy-MM-dd")); } }); return list; } @Override public Boolean isJoinCorpWx(Long userId, Boolean isJoin, Boolean noCoupon) { if (!isJoin) { User user = userMapper.selectById(userId); if (user == null || StrUtil.isEmpty(user.getUnionid())) { return true; } // CorpUser corpUser = corpUserMapper.selectOne(Wrappers.lambdaQuery(CorpUser.class) // .ne(CorpUser::getUnionid, "") // .eq(CorpUser::getUnionid, user.getUnionid()) // .last("limit 1") // .select(CorpUser::getId) // ); CorpUserFollowView corpUserFollowView = beanSearcher.searchFirst(CorpUserFollowView.class, MapUtils.builder() .field(CorpUserFollowView::getUnionId, user.getUnionid()) .field(CorpUserFollowView::getStatus, 1).build()); if (corpUserFollowView == null) { return false; } } //用户是否买了奈飞车票 Number number = beanSearcher.searchCount(RenewalView.class, MapUtils.builder() .field(RenewalView::getUserId, userId) .field(RenewalView::getGoodsId, 1) .field(RenewalView::getStatus, List.of("validity", "overdue")).op(Operator.InList) .build()); if (number.intValue() > 0 && !noCoupon) { couponCommonService.sendCouponToUser(userId, 34l, 0l, 0l, 0l, CouponUser.Channel.wxCorp); } return true; } @Override public GroupsRelation getSimilarExpiredGroupRelation(GoodsDonSku sku, List errorsRelationIds) { Long skuId = sku.getId(); Integer days = sku.getDays(); Integer months = sku.getMonths(); Integer maxNum = sku.getNum(); DateTime expiryTime = null; if (days != null && days > 0) { expiryTime = DateUtil.offsetDay(DateTime.now(), days); } else { expiryTime = DateUtil.offsetMonth(DateTime.now(), months); } Integer extraNum = 5; if (sku.getId() == 178l) { extraNum = 2; } maxNum += extraNum; DateTime minExpiryTime = DateUtil.offsetDay(expiryTime, -5); Long groupsId = groupsRelationMapper.selectSimilarExpiredRelation(skuId, minExpiryTime, maxNum); if (groupsId == null) { log.info("暂无相似过期时间车队"); return null; } GroupsRelation relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class) .notIn(errorsRelationIds != null && errorsRelationIds.size() > 0, GroupsRelation::getId, errorsRelationIds) .eq(GroupsRelation::getGroupsId, groupsId) .eq(GroupsRelation::getStatus, GroupsRelation.Status.none) .eq(GroupsRelation::getUserId, 0) .orderByAsc(GroupsRelation::getNum) .last("limit 1")); if (relation != null) { log.info("用户userId:{},获取相似车队的车位relationId:{}", relation.getId()); } return relation; } @Override public Page getRecommendGoodsViews(Boolean isPayNf, List filter_goods_id, Integer start, Integer limit) { return groupsRelationMapper.getRecommendGoodsViews(new Page<>(start, limit),filter_goods_id, isPayNf); } @Override @Transactional(rollbackFor = Throwable.class) public void sendCourseWare(SpecialGoodsTicketReq request) throws Exception { Long userId = request.getUserId(); Long relationId = request.getRelationId(); String email = request.getEmail(); List userIdList = userBindRelationService.getRelationUserIdList(userId, null); RenewalView relation = beanSearcher.searchFirst(RenewalView.class, MapUtils.builder().field(RenewalView::getRelationId, relationId).build()); Assert.notNull(relation, "您的车票不存在"); Long relationUserId = relation.getUserId(); if (!userIdList.contains(relationUserId)) { throw BusinessRuntimeException.getInstance("您的车票不存在"); } if (relation.getSpecialType() != GoodsDon.SpecialType.courseware) { throw BusinessRuntimeException.getInstance(""); } //是否已发送 if (StrUtil.isNotBlank(relation.getGrAccount())) { throw BusinessRuntimeException.getInstance("已领取课件,请前往邮箱查看"); } if (!Validator.isEmail(email)) { throw BusinessRuntimeException.getInstance("请输入正确的邮箱"); } int update = groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class) .set(GroupsRelation::getAccount, email) .and(qr -> qr.isNull(GroupsRelation::getAccount).or().eq(GroupsRelation::getAccount, StrUtil.EMPTY)) .eq(GroupsRelation::getId, relationId)); if (update > 0) { orderDonMapper.update(null, Wrappers.lambdaUpdate(OrderDon.class) .set(OrderDon::getStatus, OrderDon.Status.complete) .eq(OrderDon::getRelationId, relationId) .eq(OrderDon::getUserId, userId) .eq(OrderDon::getStatus, OrderDon.Status.hasPayment)); mailService.sendCoursewareByGoodsId(relation.getGoodsId(), relation.getRelationId(), relation.getUserId(), relation.getTitle(), email); } } @Override public GroupsRelation getRelation(Long payRelationId, Long userId, GoodsDonSku sku) { GroupsRelation relation = null; if (payRelationId != null && payRelationId > 0) { relation = groupsRelationMapper.selectById(payRelationId); if (relation == null) { throw new BusinessRuntimeException(GROUP_INDEX_NOT_FIND.getCode(), GROUP_INDEX_NOT_FIND.getMsg()); } if (relation.getUserId() != null && relation.getUserId() > 0L) { throw new BusinessRuntimeException(GROUP_INDEX_ALREADY_BE_USER.getCode(), GROUP_INDEX_ALREADY_BE_USER.getMsg()); } } else { GroupsTrips groups = null; //ChatGpt Plus、Midjourney非独立规格 上车先获取硬塞座位 硬塞5个座位 //主账号过期时间25天以外 Integer maxNum = sku.getNum(); if (sku.getNum() > 1 && Constant.AI_goodsIds.contains(sku.getGoodsId())) { Integer extraNum = 5; if (sku.getId() == 178l) { extraNum = 2; } maxNum += extraNum; DateTime time = DateUtil.offsetDay(DateUtil.beginOfDay(DateTime.now()), 25); //不塞存在该用户的车队 Long groupsId = groupsRelationMapper.getOutSideGroupsTripsRelationByGoodsId(sku.getId(), time, extraNum, userId, sku.getNum(), maxNum); if (groupsId != null) { relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class) .eq(GroupsRelation::getGroupsId, groupsId) .eq(GroupsRelation::getUserId, 0) .eq(GroupsRelation::getStatus, GroupsRelation.Status.outside) .last("limit 1")); if (relation == null) { relation = new GroupsRelation(); relation.setStatus(GroupsRelation.Status.outside); relation.setUserId(userId); relation.setGroupsId(groupsId); GroupsRelation maxNumRelation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class).eq(GroupsRelation::getGroupsId, groupsId).orderByDesc(GroupsRelation::getNum).last("limit 1")); Integer nextNum = maxNumRelation.getNum() + 1; if (redisService.setNx(RedisService.key.GROUPS_TRIPS_OUTSIDE_RELATION.getNameFormat(groupsId, nextNum), nextNum, RedisService.key.GROUPS_TRIPS_OUTSIDE_RELATION.getTimeout())) { relation.setNum(nextNum); groupsRelationMapper.insert(relation); } else { throw BusinessRuntimeException.getInstance("系统繁忙,请重试..."); } } groups = groupsMapper.selectById(groupsId); } } if (groups == null) { //寻找差不多过期时间规格的车位 relation = getSimilarExpiredGroupRelation(sku,null); if (relation == null) { //TODO 如果没指定座位,等待逻辑确认 groups = groupsMapper.selectOne(Wrappers.lambdaQuery(GroupsTrips.class) .eq(GroupsTrips::getSkuId, sku.getId()) .gt(GroupsTrips::getAvailableNum, 0).last("limit 1") .apply(String.format("(available_parking_num = 0 or available_parking_num > (select count(*) from groups_relation gr where gr.groups_id = groups_trips.id and gr.user_id != 0 and gr.num < %s))", maxNum)) .eq(GroupsTrips::getStatus, GroupsTrips.Status.validity) .orderByAsc(GroupsTrips::getAvailableNum)); if (groups == null) { //待发车 groups = groupsMapper.selectOne(Wrappers.lambdaQuery(GroupsTrips.class) .eq(GroupsTrips::getSkuId, sku.getId()) .gt(GroupsTrips::getAvailableNum, 0).last("limit 1") .eq(GroupsTrips::getStatus, GroupsTrips.Status.waiting) .apply(String.format("(available_parking_num = 0 or available_parking_num > (select count(*) from groups_relation gr where gr.groups_id = groups_trips.id and gr.user_id != 0 and gr.num < %s))", maxNum)) .orderByAsc(GroupsTrips::getAvailableNum)); } //新增空车队 if (groups == null) { relation = groupsFuncService.createNewGroupsTrips(sku); } //获取车位 if (relation == null) { //寻找快满编车队进行占座 relation = groupsRelationMapper.selectRandomOneRelationByGroupsId(groups.getId()); if (relation == null) { Long groupsId = groups.getId(); TASK_EXECUTOR.execute(() -> groupsMapper.updateAvailableNum(groupsId)); throw BusinessRuntimeException.getInstance("系统繁忙,请重试..."); } } } } if (groups == null) { groups = groupsMapper.selectById(relation.getGroupsId()); } //车队目前可用停车数 relation.setGroupsAvailParkingNum(groups.getAvailableParkingNum()); } return relation; } @Override public String getAiVerifyCode(Long userId, Long relationId) throws Exception { List relationUserIdList = userBindRelationService.getRelationUserIdList(userId, null); RenewalView renewalView = beanSearcher.searchFirst(RenewalView.class, MapUtils.builder().field(RenewalView::getUserId, relationUserIdList).op(Operator.InList) .field(RenewalView::getRelationId, relationId).build()); if (renewalView == null) { throw BusinessRuntimeException.getInstance("您的车票不存在..."); } String account = renewalView.getAccount(); String password = renewalView.getPassword(); if (StrUtil.hasEmpty(account, password)) { throw BusinessRuntimeException.getInstance("您的车票账号密码异常,请联系客服..."); } String url = String.format("http://yinhelx.com/api/imap_yhcz.php?sc=&id=&yhm=%s&mm=%s&duan_kou=143&ip=127.0.0.1&mi_yao=%s", account, password, VERIFY_CODE_SECRET); HttpResponse send = J11HttpC.custom() .ofGet() .url(url) .send(HttpResponse.BodyHandlers.ofString()); if (send.statusCode() != HttpStatus.HTTP_OK) { throw BusinessRuntimeException.getInstance("获取验证码失败..."); } String code = StrUtil.subBetween(send.body(), "Doğrulama kodunuz:", "Bu istek sizin"); if (StrUtil.isEmpty(code)) { throw BusinessRuntimeException.getInstance("获取验证码失败..."); } return code.trim(); } @Override public void checkGroupsAvailParkingNum(Long skuId, Long groupsId, Integer availableParkingNum) { //GPT10人月付、4人月付 if (skuId == 161l || skuId == 178l) { //为0时,不参与限制 if (availableParkingNum == 0) { return; } //目前车队可用停车数 //目前上车人数 Integer currentParkingNum = groupsRelationMapper.selectCount(Wrappers.lambdaQuery(GroupsRelation.class) .eq(GroupsRelation::getGroupsId, groupsId) .ne(GroupsRelation::getUserId, 0)); if (currentParkingNum > availableParkingNum) { throw BusinessRuntimeException.getInstance("系统繁忙,请重试..."); } } } @Override public void adjustGroupsParkingTime(Long relationId, GoodsDonSku sku) { Long skuId = sku.getId(); //更新GPT Plus10人、4人月付车队状态 if (skuId == 161l || skuId == 178l) { GroupsRelation relation = groupsRelationMapper.selectById(relationId); Long groupsId = relation.getGroupsId(); GroupsTrips groupsTrips = groupsMapper.selectById(groupsId); Integer availableParkingNum = groupsTrips.getAvailableParkingNum(); //为0时 不参与限制 if (availableParkingNum == 0) { return; } Integer extraNum = 5; if (skuId == 178l) { extraNum = 2; } Integer skuNum = sku.getNum(); if (availableParkingNum >= skuNum + extraNum) { return; } //目前上车人数 Integer currentParkingNum = groupsRelationMapper.selectCount(Wrappers.lambdaQuery(GroupsRelation.class) .eq(GroupsRelation::getGroupsId, groupsId) .ne(GroupsRelation::getUserId, 0)); if (currentParkingNum >= availableParkingNum) { //锁住 DateTime parkingTime = DateUtil.offsetDay(DateTime.now(), 1); groupsMapper.update(null, Wrappers.lambdaUpdate(GroupsTrips.class) .eq(GroupsTrips::getId, groupsTrips.getId()) .set(GroupsTrips::getParkingTime, parkingTime) .isNull(GroupsTrips::getParkingTime)); } } } @Override public void setGroupsTripsParkingTimeEmpty(GoodsDonSku sku, Long relationId) { Long skuId = sku.getId(); if (skuId == 161l || skuId == 178l) { GroupsRelation relation = groupsRelationMapper.selectById(relationId); Long groupsId = relation.getGroupsId(); GroupsTrips groupsTrips = groupsMapper.selectById(groupsId); Integer availableParkingNum = groupsTrips.getAvailableParkingNum(); if (groupsTrips.getParkingTime() == null || availableParkingNum == 0) { return; } Integer extraNum = 5; if (skuId == 178l) { extraNum = 2; } Integer skuNum = sku.getNum(); if (availableParkingNum >= skuNum + extraNum) { return; } //目前上车人数 Integer currentParkingNum = groupsRelationMapper.selectCount(Wrappers.lambdaQuery(GroupsRelation.class) .eq(GroupsRelation::getGroupsId, groupsId) .ne(GroupsRelation::getUserId, 0)); if (currentParkingNum < availableParkingNum) { groupsMapper.update(null, Wrappers.lambdaUpdate(GroupsTrips.class) .eq(GroupsTrips::getId, groupsTrips.getId()) .set(GroupsTrips::getParkingTime, null) .isNotNull(GroupsTrips::getParkingTime)); } } } }