package com.liquidnet.service.kylin.controller;

import com.github.pagehelper.PageInfo;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.constant.LnsEnum;
import com.liquidnet.commons.lang.constant.LnsRegex;
import com.liquidnet.commons.lang.core.JwtValidator;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.kylin.dto.param.CheckPerformanceRelationParam;
import com.liquidnet.service.kylin.dto.param.KylinStationCheckOrderParam;
import com.liquidnet.service.kylin.dto.param.KylinStationUploadParam;
import com.liquidnet.service.kylin.dto.vo.middle.KylinTicketTimesVo;
import com.liquidnet.service.kylin.dto.vo.middle.KylinTicketVo;
import com.liquidnet.service.kylin.dto.vo.mongo.*;
import com.liquidnet.service.kylin.dto.vo.returns.*;
import com.liquidnet.service.kylin.service.IKylinOrderTicketEntitiesService;
import com.liquidnet.service.kylin.utils.DataUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Api(tags = "验票")
@Slf4j
@Validated
@RestController
@RequestMapping("station")
public class KylinStationController {
    @Autowired
    Environment environment;
    @Autowired
    JwtValidator jwtValidator;
    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    DataUtils dataUtils;
    @Autowired
    IKylinOrderTicketEntitiesService kylinOrderTicketEntitiesService;

    @ApiOperation(value = "手机号密码登录")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", dataType = "String", name = "mobile", value = "手机号", example = "11122233344"),
            @ApiImplicitParam(type = "form", dataType = "String", name = "passwd", value = "密码", example = "123456"),
    })
    @PostMapping("login")
    public ResponseDto<KylinStationLoginVo> login(@Pattern(regexp = "\\d{11}", message = "手机号格式有误")
                                                  @RequestParam String mobile,
                                                  @NotBlank(message = "密码不能为空")
                                                  @RequestParam String passwd) {
        log.info("mobile:{},passwd:{}", mobile, passwd);

        return this.loginVerification(mobile, passwd, true);
    }

    @ApiOperation(value = "手机号验证码登录")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "body", required = true, dataType = "String", name = "mobile", value = "手机号", example = "11122233344"),
            @ApiImplicitParam(type = "body", required = true, dataType = "String", name = "code", value = "验证码", example = "111111"),
    })
    @PostMapping("login/sms")
    public ResponseDto<KylinStationLoginVo> loginBySms(@Pattern(regexp = "\\d{11}", message = "手机号格式有误")
                                                       @RequestParam String mobile,
                                                       @Pattern(regexp = "\\d{6}", message = "验证码格式有误")
                                                       @RequestParam String code) {
        log.info("mobile:{},code:{}", mobile, code);

        return this.loginVerification(mobile, code, false);
    }

    @ApiOperation(value = "登出")
    @PostMapping("out")
    public void out() {
        String checkUserId = CurrentUtil.getCurrentUid();

        String token = CurrentUtil.getToken();

        String ssoUidM5TokenKey = jwtValidator.getSsoRedisKey()
                .concat(checkUserId).concat(":").concat(DigestUtils.md5DigestAsHex(token.getBytes(StandardCharsets.UTF_8)));

        log.info("###logout:checkUserId:{}\nssoKey:{}\ntoken:{}", checkUserId, ssoUidM5TokenKey, token);

        redisUtil.set(ssoUidM5TokenKey, 0);
    }

    /* ------------------------------------------------------------------ */

    @ApiOperation(value = "演出列表")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "mod", value = "模块[recent-近期的,over-历史]", allowableValues = "recent,over"),
            @ApiImplicitParam(type = "form", required = false, dataType = "String", name = "match", value = "匹配字符[title|cityName|fieldName]"),
            @ApiImplicitParam(type = "form", required = true, dataType = "Integer", name = "pageNo", value = "页码", example = "1"),
            @ApiImplicitParam(type = "form", required = true, dataType = "Integer", name = "pageSize", value = "页记录数", example = "5"),
    })
    @GetMapping("performances")
    public ResponseDto<PageInfo<KylinStationPerformanceVo>> performances(@Pattern(regexp = "\\b(recent|over)\\b", message = "模块参数无效")
                                                                         @RequestParam String mod,
                                                                         @RequestParam(required = false) String match,
                                                                         @RequestParam(defaultValue = "1", required = false) int pageNo,
                                                                         @RequestParam(defaultValue = "5", required = false) int pageSize) {
        List<KylinStationPerformanceVo> voList = new ArrayList<>();
        String currentUid = CurrentUtil.getCurrentUid();
        long count = 0;
        try {
            // 查取当前用户下关联演出ID列表
            KylinCheckUserPerformanceVo checkUserRelationVo = dataUtils.getCheckUserRelationVo(currentUid);

            if (null != checkUserRelationVo) {
                List<CheckPerformanceRelationParam> performanceRelationList = checkUserRelationVo.getRelationParams();
                if (!CollectionUtils.isEmpty(performanceRelationList)) {
                    log.debug("performanceRelationList:{}", JsonUtils.toJson(performanceRelationList));
                    LocalDateTime tmpDt = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0, 0, 0));
                    String tmpDtStr = DateUtil.format(tmpDt, DateUtil.Formatter.yyyyMMddHHmmss);
                    Criteria criteria = Criteria.where("performancesId").in(
                            performanceRelationList.stream().map(CheckPerformanceRelationParam::getPerformanceId).toArray()
                    );
                    switch (mod) {
                        case "recent":
                            log.debug(":::performances/recent:{},match:{}", currentUid, match);
                            criteria.andOperator(Criteria.where("timeEnd").gt(tmpDtStr));
                            break;
//                        case "down":
//                            log.info(":::performances/down:{}", currentUid);
//                            break;
                        case "over":
                            log.info(":::performances/over:{}", currentUid);
                            criteria.andOperator(Criteria.where("timeEnd").lte(tmpDtStr));
                            break;
                        default:
                            log.info(":::performances/default:{}", currentUid);
                            return ResponseDto.success(new PageInfo<>());
                    }
                    if (StringUtils.isNotBlank(match)) {
                        java.util.regex.Pattern pattern =
                                java.util.regex.Pattern.compile("^.*" + match + ".*$", java.util.regex.Pattern.CASE_INSENSITIVE);
                        criteria.orOperator(
                                Criteria.where("title").regex(pattern),
                                Criteria.where("cityName").regex(pattern),
                                Criteria.where("fieldName").regex(pattern)
                        );
                    }
                    Query performancesVoQuery = Query.query(criteria);
                    count = mongoTemplate.count(performancesVoQuery, KylinPerformanceVo.class.getSimpleName());

                    if (count <= 0) return ResponseDto.success(new PageInfo<>());

                    performancesVoQuery.with(PageRequest.of(pageNo - 1, pageSize));
                    performancesVoQuery.with(Sort.by(Sort.Order.asc("timeEnd"), Sort.Order.desc("sort")));
                    voList = mongoTemplate.find(performancesVoQuery, KylinStationPerformanceVo.class, KylinPerformanceVo.class.getSimpleName());
                    log.debug("voList:{}", JsonUtils.toJson(voList));
                    // 查取演出对应的订单票明细
                    Query orderTicketEntitiesVoQuery = Query.query(Criteria.where("performanceId").in(
                            voList.stream().map(KylinStationPerformanceVo::getPerformancesId).toArray()
                    ).and("isPayment").is(1));
                    List<KylinOrderTicketEntitiesVo> oteVoList = mongoTemplate.find(orderTicketEntitiesVoQuery, KylinOrderTicketEntitiesVo.class, KylinOrderTicketEntitiesVo.class.getSimpleName());
                    log.debug("oteVoList:{}", JsonUtils.toJson(oteVoList));
                    // 转换订单票明细结构为Map<performanceId, List<KylinOrderTicketEntitiesVo>>
                    Map<String, List<KylinOrderTicketEntitiesVo>> oteVoMap = oteVoList.stream().collect(Collectors.groupingBy(KylinOrderTicketEntitiesVo::getPerformanceId));

//                    // 查取订单对应票种
//                    Query performanceTicketVoQuery = Query.query(Criteria.where("ticketsId").in(
//                            oteVoList.stream().filter(distinctByKey(KylinOrderTicketEntitiesVo::getTicketId)).map(KylinOrderTicketEntitiesVo::getTicketId).toArray()
//                    ));
//                    List<KylinTicketVo> performanceTicketVoList = mongoTemplate.find(performanceTicketVoQuery, KylinTicketVo.class, KylinPerformanceVo.class.getSimpleName());
//                    log.debug("performanceTicketVoList:{}", JsonUtils.toJson(performanceTicketVoList));
//                    // 转换票种信息结构为Map<ticketsId, ticketVo>
//                    Map<String, KylinTicketVo> performanceTicketMap = performanceTicketVoList.stream().collect(Collectors.toMap(KylinTicketVo::getTicketsId, Function.identity(), (k1, k2) -> k2));
//                    log.debug("performanceTicketMap:{}", JsonUtils.toJson(performanceTicketMap));

                    // 转换Map<performanceId, canDownTime>
                    Map<String, String> performanceRelationMap = performanceRelationList.stream().collect(Collectors.toMap(CheckPerformanceRelationParam::getPerformanceId, CheckPerformanceRelationParam::getCanDownTime));
                    log.debug("performanceRelationMap:{}", JsonUtils.toJson(performanceRelationMap));
                    // 整合演出列表票种统计
                    for (KylinStationPerformanceVo r : voList) {
                        r.setCanDownTime(performanceRelationMap.get(r.getPerformancesId()));
                        try {
                            // 演出的所有订单票明细
                            List<KylinOrderTicketEntitiesVo> performanceTicketEntitiesVoList = oteVoMap.get(r.getPerformancesId());

                            if (!CollectionUtils.isEmpty(performanceTicketEntitiesVoList)) {
                                // 提取演出对应票种信息
                                List<KylinTicketVo> performanceTicketVoList = new ArrayList<>();
                                List<KylinTicketTimesVo> ticketTimeList = r.getTicketTimeList();
                                ticketTimeList.forEach(tt -> {
                                    performanceTicketVoList.addAll(tt.getTicketList());
                                });
                                // 转换票种信息结构为Map<ticketsId, ticketVo>
                                Map<String, KylinTicketVo> performanceTicketMap = performanceTicketVoList.stream().collect(Collectors.toMap(KylinTicketVo::getTicketsId, Function.identity(), (k1, k2) -> k2));
                                log.debug("performanceTicketMap:{}", JsonUtils.toJson(performanceTicketMap));

                                // 订单票明细按票种分组
                                Map<String, List<KylinOrderTicketEntitiesVo>> performanceTicketEntitiesVoMap =
                                        performanceTicketEntitiesVoList.stream().collect(Collectors.groupingBy(KylinOrderTicketEntitiesVo::getTicketId));

                                List<KylinStationTicketVo> ticketVoList = new ArrayList<>();

                                BigDecimal priceSum = BigDecimal.ZERO;
                                int number = 0, checkedNum = 0, remainderNum = 0;
                                for (Map.Entry<String, List<KylinOrderTicketEntitiesVo>> entry : performanceTicketEntitiesVoMap.entrySet()) {
                                    KylinTicketVo ticketVo = performanceTicketMap.get(entry.getKey());

                                    KylinStationTicketVo stationTicketVo = KylinStationTicketVo.getNew();
                                    stationTicketVo.setTicketId(ticketVo.getTicketsId());
                                    stationTicketVo.setTitle(ticketVo.getTitle());
                                    stationTicketVo.setPrice(ticketVo.getPrice());
                                    stationTicketVo.setUseStart(ticketVo.getUseStart());
                                    stationTicketVo.setUseEnd(ticketVo.getUseEnd());

                                    List<KylinOrderTicketEntitiesVo> subPerformanceTicketEntitiesVoList = entry.getValue();
                                    // 订单票明细按出票状态分组
                                    Map<Integer, List<KylinOrderTicketEntitiesVo>> subStatusPerformanceTicketEntitiesVoMap
                                            = subPerformanceTicketEntitiesVoList.stream().collect(Collectors.groupingBy(KylinOrderTicketEntitiesVo::getStatus));

                                    stationTicketVo.setNumber(subPerformanceTicketEntitiesVoList.size());
                                    stationTicketVo.setPriceSum(stationTicketVo.getPrice().multiply(BigDecimal.valueOf(stationTicketVo.getNumber())));
                                    List<KylinOrderTicketEntitiesVo> checkedEntitiesVoList = subStatusPerformanceTicketEntitiesVoMap.get(1);
                                    stationTicketVo.setCheckedNum(CollectionUtils.isEmpty(checkedEntitiesVoList) ? 0 : checkedEntitiesVoList.size());
                                    List<KylinOrderTicketEntitiesVo> remainderEntitiesVoList = subStatusPerformanceTicketEntitiesVoMap.get(0);
                                    stationTicketVo.setRemainderNum(CollectionUtils.isEmpty(remainderEntitiesVoList) ? 0 : remainderEntitiesVoList.size());

                                    number += stationTicketVo.getNumber();
                                    checkedNum += stationTicketVo.getCheckedNum();
                                    remainderNum += stationTicketVo.getRemainderNum();
                                    priceSum = priceSum.add(stationTicketVo.getPriceSum());

                                    ticketVoList.add(stationTicketVo);
                                }
                                r.setPriceSum(priceSum);
                                r.setNumber(number);
                                r.setCheckedNum(checkedNum);
                                r.setRemainderNum(remainderNum);
                                r.setTicketVoList(ticketVoList);
                            }
                        } catch (Exception e) {
                            log.error("验票:整合票种统计异常[station/performances],performancesId:{}", r.getPerformancesId(), e);
                        }
                        r.setTicketTimeList(null);
                    }
                }
            }
        } catch (Exception e) {
            log.error("验票:查取演出列表异常[station/performances]", e);
        }
        PageInfo<KylinStationPerformanceVo> voPageInfo = PageInfo.of(voList);
        voPageInfo.setTotal(count);
        return ResponseDto.success(voPageInfo);
    }

    @ApiOperation(value = "下载验票数据")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "performanceId", value = "演出ID[64]"),
    })
    @GetMapping("download")
    public ResponseDto<KylinStationCheckRefreshVo> downloadTicketData(@NotBlank(message = "演出ID不能为空")
                                                                      @RequestParam String performanceId) {
        log.info("performanceId:{}", performanceId);
        String currentUid = CurrentUtil.getCurrentUid();

        // 查取当前用户下关联演出ID列表
        KylinCheckUserPerformanceVo checkUserRelationVo = dataUtils.getCheckUserRelationVo(currentUid);
        List<CheckPerformanceRelationParam> relationParams;
        if (null == checkUserRelationVo || CollectionUtils.isEmpty(relationParams = checkUserRelationVo.getRelationParams())) {
            return ResponseDto.failure(ErrorMapping.get("20606"));
        }
        List<CheckPerformanceRelationParam> performanceRelationParams =
                relationParams.stream().filter(r -> r.getPerformanceId().equals(performanceId)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(performanceRelationParams)) {
            return ResponseDto.failure(ErrorMapping.get("20607"));
        }
        CheckPerformanceRelationParam relationParam = performanceRelationParams.get(0);
        LocalDateTime canDownDt = DateUtil.Formatter.yyyyMMddHHmmss.parse(relationParam.getCanDownTime());
        if (canDownDt.isAfter(LocalDateTime.now())) {
            return ResponseDto.failure(ErrorMapping.get("20608"));
        }

        // 查取演出对应的订单票明细
        Query orderTicketEntitiesVoQuery = Query.query(Criteria.where("performanceId").is(performanceId).and("isPayment").is(1));
        List<KylinStationCheckOrderVo> checkOrderVos = mongoTemplate.find(orderTicketEntitiesVoQuery, KylinStationCheckOrderVo.class, KylinOrderTicketEntitiesVo.class.getSimpleName());

        // 查取订单信息(orderTicketsId,userMobile,userName,qrCode)
        Query query = Query.query(Criteria.where("orderTicketsId").in(checkOrderVos.stream().map(KylinStationCheckOrderVo::getOrderId).toArray()));
        query.fields().include("orderTicketsId").include("userMobile").include("userName").include("qrCode");
        List<KylinOrderTicketVo> orderTicketVoList = mongoTemplate.find(query, KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());

        for (KylinOrderTicketVo t : orderTicketVoList) {
            for (KylinStationCheckOrderVo r : checkOrderVos) {
                if (r.getOrderId().equals(t.getOrderTicketsId())) {
                    r.setQrCode(t.getQrCode());
                    r.setUserMobile(t.getUserMobile());
                    r.setUserName(t.getUserName());
                }
            }
        }
        KylinStationCheckRefreshVo vo = KylinStationCheckRefreshVo.getNew();
        vo.setCheckOrderVos(checkOrderVos);
        {
            // 查取演出信息
            KylinStationPerformanceVo performanceVo = mongoTemplate.findOne(Query.query(Criteria.where("performancesId").is(performanceId)),
                    KylinStationPerformanceVo.class, KylinPerformanceVo.class.getSimpleName());

//            // 查取订单对应票种
//            Query performanceTicketVoQuery = Query.query(Criteria.where("ticketsId").in(
//                    checkOrderVos.stream().filter(distinctByKey(KylinStationCheckOrderVo::getTicketId)).map(KylinStationCheckOrderVo::getTicketId).toArray()
//            ));
//            List<KylinTicketVo> performanceTicketVoList = mongoTemplate.find(performanceTicketVoQuery, KylinTicketVo.class, KylinTicketVo.class.getSimpleName());
            // 提取演出对应票种信息
            List<KylinTicketVo> performanceTicketVoList = new ArrayList<>();
            List<KylinTicketTimesVo> ticketTimeList = performanceVo.getTicketTimeList();
            ticketTimeList.forEach(tt -> {
                performanceTicketVoList.addAll(tt.getTicketList());
            });

            List<KylinStationTicketVo> ticketVoList = new ArrayList<>();
            for (KylinTicketVo r : performanceTicketVoList) {
                KylinStationTicketVo stationTicketVo = KylinStationTicketVo.getNew();
                stationTicketVo.setTicketId(r.getTicketsId());
                stationTicketVo.setTitle(r.getTitle());
                stationTicketVo.setPrice(r.getPrice());
                stationTicketVo.setUseStart(r.getUseStart());
                stationTicketVo.setUseEnd(r.getUseEnd());

                ticketVoList.add(stationTicketVo);
            }
            performanceVo.setTicketTimeList(null);
            performanceVo.setTicketVoList(ticketVoList);
            vo.setPerformanceVo(performanceVo);
        }
        return ResponseDto.success(vo);
    }

    @ApiOperation(value = "上载验票数据")
    @PostMapping("upload")
    public ResponseDto<Object> uploadTicketData(@RequestBody KylinStationUploadParam parameter) {
        log.debug("checkDataParams:{}", JsonUtils.toJson(parameter));

        HttpServletRequest request = ServletUtils.getRequest();
        String agent = request.getParameter("User-Agent");
        if (StringUtils.containsIgnoreCase(agent, "android")) {
            parameter.setCheckClient("ANDROID");
        } else if (StringUtils.containsIgnoreCase(agent, "iPhone")
                || StringUtils.containsIgnoreCase(agent, "iPod")
                || StringUtils.containsIgnoreCase(agent, "iPad")) {
            parameter.setCheckClient("IOS");
        } else {
            parameter.setCheckClient("APP");
        }

        List<KylinStationCheckOrderParam> checkOrderParamList = parameter.getCheckOrderParamList();
        if (!CollectionUtils.isEmpty(checkOrderParamList)) {
            kylinOrderTicketEntitiesService.updateByStation(parameter);
        }

        return ResponseDto.success();
    }

    @ApiOperation(value = "刷新验票数据")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "performanceId", value = "演出ID[64]"),
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "latestUpdateAt", value = "最近更新时间[yyyy-MM-dd HH:mm:ss]"),
    })
    @PostMapping("refresh")
    public ResponseDto<KylinStationCheckRefreshVo> refreshTicketData(@NotBlank(message = "演出ID不能为空")
                                                                     @RequestParam String performanceId,
                                                                     @Pattern(regexp = LnsRegex.Valid.DATETIME_FULL, message = "时间格式有误")
                                                                     @RequestParam String latestUpdateAt) {
        log.info("refresh performanceId:{},latestUpdateAt:{}", performanceId, latestUpdateAt);
        String currentUid = CurrentUtil.getCurrentUid();

        // 查取当前用户下关联演出ID列表
        KylinCheckUserPerformanceVo checkUserRelationVo = dataUtils.getCheckUserRelationVo(currentUid);
        List<CheckPerformanceRelationParam> relationParams;
        if (null == checkUserRelationVo || CollectionUtils.isEmpty(relationParams = checkUserRelationVo.getRelationParams())) {
            return ResponseDto.failure(ErrorMapping.get("20606"));
        }
        List<CheckPerformanceRelationParam> performanceRelationParamList = relationParams.stream().filter(r -> r.getPerformanceId().equals(performanceId)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(performanceRelationParamList)) {
            return ResponseDto.failure(ErrorMapping.get("20607"));
        }
        CheckPerformanceRelationParam relationParam = performanceRelationParamList.get(0);
        LocalDateTime canDownDt = DateUtil.Formatter.yyyyMMddHHmmss.parse(relationParam.getCanDownTime());
        if (canDownDt.isAfter(LocalDateTime.now())) {
            return ResponseDto.failure(ErrorMapping.get("20608"));
        }

        // 查取演出对应的订单票明细
        Query orderTicketEntitiesVoQuery = Query.query(Criteria.where("performanceId").is(performanceId).and("updatedAt").gte(latestUpdateAt));
        List<KylinStationCheckOrderVo> checkOrderVos = mongoTemplate.find(orderTicketEntitiesVoQuery, KylinStationCheckOrderVo.class, KylinOrderTicketEntitiesVo.class.getSimpleName());

        // 查取订单信息(orderTicketsId,userMobile,userName,qrCode)
        Query query = Query.query(Criteria.where("orderTicketsId").in(checkOrderVos.stream().map(KylinStationCheckOrderVo::getOrderId).toArray()));
        query.fields().include("orderTicketsId").include("userMobile").include("userName").include("qrCode");
        List<KylinOrderTicketVo> orderTicketVoList = mongoTemplate.find(query, KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());

        for (KylinOrderTicketVo t : orderTicketVoList) {
            for (KylinStationCheckOrderVo r : checkOrderVos) {
                if (r.getOrderId().equals(t.getOrderTicketsId())) {
                    r.setQrCode(t.getQrCode());
                    r.setUserMobile(t.getUserMobile());
                    r.setUserName(t.getUserName());
                }
            }
        }
        KylinStationCheckRefreshVo vo = KylinStationCheckRefreshVo.getNew();
        vo.setCheckOrderVos(checkOrderVos);
        {
            // 查取演出信息
            KylinStationPerformanceVo performanceVo = mongoTemplate.findOne(Query.query(Criteria.where("performancesId").is(performanceId)),
                    KylinStationPerformanceVo.class, KylinPerformanceVo.class.getSimpleName());

//            // 查取订单对应票种
//            Query performanceTicketVoQuery = Query.query(Criteria.where("ticketsId").in(
//                    checkOrderVos.stream().filter(distinctByKey(KylinStationCheckOrderVo::getTicketId)).map(KylinStationCheckOrderVo::getTicketId).toArray()
//            ));
//            List<KylinTicketVo> performanceTicketVoList = mongoTemplate.find(performanceTicketVoQuery, KylinTicketVo.class, KylinTicketVo.class.getSimpleName());
            // 提取演出对应票种信息
            List<KylinTicketVo> performanceTicketVoList = new ArrayList<>();
            List<KylinTicketTimesVo> ticketTimeList = performanceVo.getTicketTimeList();
            ticketTimeList.forEach(tt -> {
                performanceTicketVoList.addAll(tt.getTicketList());
            });

            List<KylinStationTicketVo> ticketVoList = new ArrayList<>();
            for (KylinTicketVo r : performanceTicketVoList) {
                KylinStationTicketVo stationTicketVo = KylinStationTicketVo.getNew();
                stationTicketVo.setTicketId(r.getTicketsId());
                stationTicketVo.setTitle(r.getTitle());
                stationTicketVo.setPrice(r.getPrice());
                stationTicketVo.setUseStart(r.getUseStart());
                stationTicketVo.setUseEnd(r.getUseEnd());

                ticketVoList.add(stationTicketVo);
            }
            performanceVo.setTicketTimeList(null);
            performanceVo.setTicketVoList(ticketVoList);
            vo.setPerformanceVo(performanceVo);
        }
        return ResponseDto.success(vo);
    }

    /* ------------------------------------------------------------------ */

    private ResponseDto<KylinStationLoginVo> loginVerification(String mobile, String identity, boolean isPasswd) {
        KylinCheckUserVo checkUserVo = mongoTemplate.findOne(Query.query(Criteria.where("mobile").is(mobile)),
                KylinCheckUserVo.class, KylinCheckUserVo.class.getSimpleName());

        if (null == checkUserVo) {
            return ResponseDto.failure(ErrorMapping.get("20601"));
        }

        if (isPasswd) {// 校验密码
            if (checkUserVo.getPwd().equals(DigestUtils.md5DigestAsHex(identity.getBytes()))) {
                return this.loginAuthentication(checkUserVo);
            }
            return ResponseDto.failure(ErrorMapping.get("20602"));
        } else {// 校验验证码
            if (Arrays.asList(LnsEnum.ENV.dev.name(), LnsEnum.ENV.test.name()).contains(environment.getProperty(CurrentUtil.CK_ENV_ACTIVE))) {
                if (CurrentUtil.GRAY_LOGIN_SMS_CODE.equals(identity)) {
                    return this.loginAuthentication(checkUserVo);
                }
                return ResponseDto.failure(ErrorMapping.get("20002"));
            }

            try {
                LinkedMultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
                paramsMap.add("mobile", mobile);
                paramsMap.add("code", identity);
                LinkedMultiValueMap<String, String> headersMap = new LinkedMultiValueMap<>();
                headersMap.add("token", null);

                // TODO: 2021/5/12
                String respStr = HttpUtil.get("https://service.zhengzai.tv/smsValidation", paramsMap, headersMap);
                log.info("###PHP.API.RESP:{}", respStr);

                Map respMap = JsonUtils.fromJson(respStr, Map.class);

                if (StringUtils.equalsIgnoreCase("OK", (String) respMap.get("message"))) {
                    return this.loginAuthentication(checkUserVo);
                }
                return ResponseDto.failure(ErrorMapping.get("20002"));
            } catch (Exception e) {
                log.error("验证码验证异常[mobile:{},code:{}]", mobile, identity, e);
                return ResponseDto.failure(ErrorMapping.get("20002"));
            }
        }
    }

    private ResponseDto<KylinStationLoginVo> loginAuthentication(KylinCheckUserVo checkUserVo) {
        Map<String, Object> claimsMap = new HashMap<>();
        claimsMap.put("sub", checkUserVo.getCheckUserId());
        claimsMap.put("mobile", checkUserVo.getMobile());
        claimsMap.put("nickname", checkUserVo.getName());

        String token = jwtValidator.create(claimsMap);

        String ssoUidM5TokenKey = jwtValidator.getSsoRedisKey()
                .concat(checkUserVo.getCheckUserId())
                .concat(":").concat(DigestUtils.md5DigestAsHex(token.getBytes(StandardCharsets.UTF_8)));

        redisUtil.set(ssoUidM5TokenKey, 1, jwtValidator.getExpireTtl() * 60);

        KylinStationLoginVo stationLoginVo = KylinStationLoginVo.getNew();
        stationLoginVo.setUid(checkUserVo.getCheckUserId());
        stationLoginVo.setMobile(SensitizeUtil.custom(checkUserVo.getMobile(), 3, 4));
        stationLoginVo.setName(SensitizeUtil.chineseName(checkUserVo.getName()));
        stationLoginVo.setToken(token);
        return ResponseDto.success(stationLoginVo);
    }

    private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}
