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.Validator; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpStatus; import cn.hutool.http.HttpUtil; 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.i18n.I18nMessageUtil; import com.cyksj.common.task.GlobalThreadPoolTaskExecutor; import com.cyksj.common.util.Jsons; import com.cyksj.common.util.StringUtil; import com.cyksj.common.util.TicketCodeCommonUtil; import com.cyksj.enums.GatewayApiCode; import com.cyksj.mapper.*; import com.cyksj.mapper.sys.SysEmailMapper; import com.cyksj.model.entity.*; import com.cyksj.model.request.TicketLoginReq; 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.CuiQiuMailService; import com.cyksj.service.mange.coupon.CouponCommonService; import com.cyksj.service.relation.GroupRelationFrontService; import com.cyksj.service.relation.GroupsRelationNetflixService; import com.cyksj.service.user.UserBindRelationService; import com.cyksj.service.user.UserLoginRecordService; import com.ejlchina.searcher.BeanSearcher; import com.ejlchina.searcher.SearchResult; 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 javax.servlet.http.HttpServletRequest; import java.util.*; 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 SysEmailMapper sysEmailMapper; private final CuiQiuMailService cuiQiuMailService; private final GroupsRelationLoginCodeRecordMapper groupsRelationLoginCodeRecordMapper; private final GroupsRelationNetflixService groupsRelationNetflixService; private final GroupsFuncService groupsFuncService; private final HttpServletRequest request; private final UserFuncPropertyMapper userFuncPropertyMapper; private final GoodsDonSkuMapper goodsDonSkuMapper; private final OrderDonMapper orderDonMapper; private final OrderDiscountPlusPurchaseSkuRelationMapper orderDiscountPlusPurchaseSkuRelationMapper; private final UserLoginRecordService userLoginRecordService; private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance(); //脆球邮箱后缀 private final static String CUI_QIU_EMAIL_SUFFIX = "@kindvd.com"; @Override public SearchResult getMyTicket(long userId, Boolean isLoginPopularize, String account) { User u = userMapper.selectById(userId); if (u == null) throw BusinessRuntimeException.getGatewayApiCode(GatewayApiCode.CMS_TOKEN_VALID); if (u.getIsBlack()) { return new SearchResult<>(); } List userIds = userBindRelationService.getRelationUserIdList(userId, u); MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap()); //Long yhsId = yhShopFrontService.getYhsIdByCustomId(customId); DateTime now = DateTime.now(); if (StrUtil.isNotBlank(account)) { builder.field(RenewalView::getAccount, account).op(Operator.Contain); } SearchResult list = beanSearcher.search(RenewalView.class, builder .field(RenewalView::getUserId, 0).op(Operator.GreaterThan) .field(RenewalView::getUserId, userIds).op(Operator.InList) //.field(RenewalView::getYhsId, yhsId) .put("condition", String.format("(gr.expiry_time is null or gr.expiry_time > '%s')", now)) .field(RenewalView::getStatus, List.of("validity", "overdue", "outside")).op(Operator.InList) .orderBy(RenewalView::getExpiryTime).asc() .build()); //过滤邀请制、已失效的车位 Integer size = list.getTotalCount().intValue(); list.getDataList().forEach(ticket -> { if (ticket.getSkuId() == null || ticket.getGoodsId() == null) { return; } 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 = I18nMessageUtil.getI18nMsg("less_than_one_day"); } else { //当天也算 int days = Integer.parseInt(remainDays.substring(0, remainDays.indexOf("天"))); remainDays = days + 1 + I18nMessageUtil.getI18nMsg("days"); } ticket.setRemainDays(remainDays); } OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class) .eq(OrderDon::getRelationId, ticket.getRelationId()) .eq(OrderDon::getUserId, ticket.getUserId()) .notIn(OrderDon::getStatus, Constant.noOrderAllStatus) .orderByDesc(OrderDon::getId) .last("limit 1")); if (orderDon != null) { ticket.setOrderNo(orderDon.getOrderNo()); ticket.setOrderId(orderDon.getId()); ticket.setTransactionId(orderDon.getTransactionId()); } if (size > 1 && ticket.getGoodsId() != 18) { 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")); } //查看双重验证码 if (StrUtil.isNotEmpty(ticket.getApiSecret())) { Boolean isHasVerifyCode = StrUtil.isNotBlank(ticket.getVerifyCode()) ? true : false; Integer codeNum = getUserCodeNumBySkuId(ticket.getUserId(), ticket.getSkuId(), isHasVerifyCode); ticket.setCodeNum(codeNum); } }); userLoginRecordService.saveUserLoginDayRecord(userId); 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, String ip) { 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, ip); 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 public String getAiVerifyCode(TicketLoginReq loginReq) throws Exception { Long userId = loginReq.getUserId(); Long relationId = loginReq.getRelationId(); Integer type = loginReq.getType(); Boolean sync = loginReq.getSync(); 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(I18nMessageUtil.getI18nMsg("sys_error")); } if (StrUtil.isEmpty(renewalView.getAccount())) { throw BusinessRuntimeException.getInstance(I18nMessageUtil.getI18nMsg("account_not_set")); } String account = renewalView.getAccount(); String password = renewalView.getPassword(); if (StrUtil.hasEmpty(account, password)) { throw BusinessRuntimeException.getInstance(I18nMessageUtil.getI18nMsg("sys_error")); } if (renewalView.getAccount().contains(CUI_QIU_EMAIL_SUFFIX)) { return cuiQiuMailService.getVerifyCode(renewalView.getAccount(), renewalView.getGoodsId(), type, sync, loginReq.getLinkType()); } //获取邮箱密码 SysEmail sysEmail = sysEmailMapper.selectOne(Wrappers.lambdaQuery(SysEmail.class) .eq(SysEmail::getEmail, account) .last("limit 1")); if (sysEmail == null) { log.info("获取账号:{}验证码异常,未配置对应系统邮箱", account); throw BusinessRuntimeException.getInstance(I18nMessageUtil.getI18nMsg("obtain_code_error")); } String url = "http://yinhelx.com/api/controller.php"; Map params = new HashMap<>(); params.put("imap_key", "imap_56568niua@@"); params.put("fun", "imap_read"); params.put("imap_ip", "yinhelx.com"); params.put("port", "143"); params.put("username", account); params.put("password", sysEmail.getPassword()); params.put("mbox_id", "0"); params.put("delete", "0"); HttpRequest post = HttpUtil.createPost(url); post.form(params); cn.hutool.http.HttpResponse execute = post.execute(); if (execute.getStatus() != HttpStatus.HTTP_OK) { throw BusinessRuntimeException.getInstance(I18nMessageUtil.getI18nMsg("obtain_code_error")); } String code = getDifferentGoodsCode(renewalView.getGoodsId(), execute.body(), type, sync, loginReq.getLinkType()); GroupsRelationLoginCodeRecord record = new GroupsRelationLoginCodeRecord(); record.setUserId(userId); record.setRelationId(relationId); record.setAccount(renewalView.getAccount()); record.setGoodsId(renewalView.getGoodsId()); record.setSkuId(renewalView.getSkuId()); groupsRelationLoginCodeRecordMapper.insert(record); return code; } @Override public GroupsRelation getRelation(Long payRelationId, Long userId, GoodsDonSku sku, String ip) { GroupsRelation relation = null; Integer maxNum = sku.getNum(); 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天以外 //23-12-06 MidJourney 不硬塞 if (sku.getNum() > 1 && Constant.AI_goodsIds.get(0) == sku.getGoodsId()) { Integer extraNum = 5; if (sku.getId() == 178l || sku.getId() == 423l) { 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(I18nMessageUtil.getI18nMsg("sys_busy_error")); } } groups = groupsMapper.selectById(groupsId); } } if (groups == null) { //寻找差不多过期时间规格的车位 relation = getSimilarExpiredGroupRelation(sku, null, ip); if (relation == null) { //TODO 如果没指定座位,等待逻辑确认 groups = groupsMapper.selectTicketGroups(sku.getId(), GroupsTrips.Status.validity, maxNum, ip); if (groups == null) { //待发车 groups = groupsMapper.selectTicketGroups(sku.getId(), GroupsTrips.Status.waiting, maxNum, ip); } //新增空车队 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(I18nMessageUtil.getI18nMsg("sys_busy_error")); } } } } if (groups == null) { groups = groupsMapper.selectById(relation.getGroupsId()); } //车队目前可用停车数 relation.setGroupsAvailParkingNum(groups.getAvailableParkingNum()); relation.setMaxNum(maxNum); } return relation; } public String getDifferentGoodsCode(Long goodsId, String body, Integer type, Boolean sync, Integer linkType) { String code = null; //奈飞 if (goodsId == 1) { if (type != null && type == 1) { if (body.contains("輸入此代碼登入") || body.contains("输入此代码登录")) { code = StrUtil.subBetween(body, "輸入此代碼登入", "請在裝置上輸入上"); if (StrUtil.isEmpty(code)) { code = StrUtil.subAfter(body, "输入此代码登录", true); } } if (StrUtil.isEmpty(code)) { throw BusinessRuntimeException.getInstance(I18nMessageUtil.getI18nMsg("ticket_login_code")); } } else { String urlPref; //更新装置 if (linkType == 1) { urlPref = "https://www.netflix.com/account/update-primary-location"; code = StrUtil.subBetween(body, "[" + urlPref, "]"); } else { //验证链接 urlPref = "https://www.netflix.com/account/travel/verify"; code = StrUtil.subBetween(body, "[" + urlPref, "]"); } if (StrUtil.isEmpty(code)) { throw BusinessRuntimeException.getInstance(I18nMessageUtil.getI18nMsg("ticket_verify_link_error")); } String url = urlPref + code; //非点击装置同步更新按钮 直接返回链接 if (linkType == 2) return url; //失败返回验证链接 if (!groupsRelationNetflixService.confirmUpdate(url)) { return url; } return StrUtil.EMPTY; } } //除奈飞 if (StrUtil.isEmpty(code)) { code = TicketCodeCommonUtil.getTicketCodeStr(body, goodsId); } int length = 4; if (goodsId == 33l) { length = 6; } code = StringUtil.getVerifyCodePattern(code, length); if (!Validator.isNumber(code)) { log.error("获取账号验证码失败:{}", StrUtil.subSufByLength(code, 512)); throw BusinessRuntimeException.getInstance(I18nMessageUtil.getI18nMsg("ticket_verify_link_error")); } return code.trim(); } @Override public Integer getUserCodeNumBySkuId(Long userId, Long skuId, Boolean isHasVerifyCode) { //查看双重验证码 UserFuncProperty userFuncProperty = userFuncPropertyMapper.selectOne(Wrappers.lambdaQuery(UserFuncProperty.class).eq(UserFuncProperty::getUserId, userId).last("limit 1")); //默认2次 int num = 2; //有安全码查看次数 置为0 if (isHasVerifyCode) { num = 0; return num; } //查看双重验证码次数 GoodsDonSku sku = goodsDonSkuMapper.selectById(skuId); if (sku.getCodeNum() > 0) { num = sku.getCodeNum(); } if (userFuncProperty != null) num = userFuncProperty.getVerifyCheckNum(); return num; } @Override public List getHasPayGoods(long userId) { User user = userMapper.selectById(userId); List userIds = userBindRelationService.getRelationUserIdList(userId, user); DateTime now = DateTime.now(); List list = groupsRelationMapper.getUserHasPayGoods(userIds, now); return list; } }