package com.liquidnet.service.kylin.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.PageInfo;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.common.cache.redisson.util.RedisLockUtil;
import com.liquidnet.common.mq.constant.MQConst;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.adam.dto.vo.AdamAddressesVo;
import com.liquidnet.service.adam.dto.vo.AdamEntersVo;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.feign.adam.api.FeignAdamBaseClient;
import com.liquidnet.service.kylin.constant.KylinRedisConst;
import com.liquidnet.service.kylin.dto.param.PayAgainParam;
import com.liquidnet.service.kylin.dto.param.PayOrderParam;
import com.liquidnet.service.kylin.dto.param.SyncOrderParam;
import com.liquidnet.service.kylin.dto.vo.*;
import com.liquidnet.service.kylin.entity.*;
import com.liquidnet.service.kylin.mapper.ExpressesMapper;
import com.liquidnet.service.kylin.mapper.KylinOrderTicketsMapper;
import com.liquidnet.service.kylin.service.IKylinOrderTicketsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.service.kylin.utils.DataUtils;
import com.mongodb.BasicDBObject;
import io.micrometer.core.instrument.util.TimeUtils;
import org.redisson.api.RLock;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * <p>
 * 订单 服务实现类
 * </p>
 *
 * @author liquidnet
 * @since 2021-05-20
 */
@Service
public class KylinOrderTicketsServiceImpl extends ServiceImpl<KylinOrderTicketsMapper, KylinOrderTickets> implements IKylinOrderTicketsService {

    @Value("${liquidnet.url-pay.pay}")
    private String payUrl;
    @Value("${liquidnet.url-pay.check}")
    private String checkUrl;
    @Value("${liquidnet.url-pay.localUrl}")
    private String synUrl;

    @Autowired
    private DataUtils dataUtils;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedisLockUtil redisLockUtil;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private MongoConverter mongoConverter;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private FeignAdamBaseClient feignAdamBaseClient;
    @Autowired
    private ExpressesMapper expressesMapper;

    @Override
    public ResponseDto<PayResultVo> checkCanOrder(PayOrderParam payOrderParam) {
        boolean isDownGeneral = false;
        String uid = CurrentUtil.getCurrentUid();
        if(!RedisLockUtil.tryLock("userId:" + uid,2,2)){
            return ResponseDto.failure("请求频繁");//参数错误
        }
        RLock lock = RedisLockUtil.lock("userId:" + uid, 2);
        try {
            KylinPerformanceVo performanceData = dataUtils.getPerformanceVo(payOrderParam.getPerformanceId());
            KylinTicketTimesVo ticketTimesData = dataUtils.getTicketTimesVo(payOrderParam.getTimeId());
            KylinTicketVo ticketData = dataUtils.getTicketVo(payOrderParam.getTicketId());
            Integer isStudent = ticketData.getIsStudent();
            if (performanceData == null || ticketTimesData == null || ticketData == null) {
                lock.unlock();
                return ResponseDto.failure("参数错误");//参数错误
            }
            if (!ticketData.getTimeId().equals(payOrderParam.getTimeId()) || !ticketTimesData.getPerformanceId().equals(payOrderParam.getPerformanceId())) {
                lock.unlock();
                return ResponseDto.failure("参数错误");//参数错误List<AdamEntersVo> entersVoList
            }
            //判断代理
            if (!checkAgent(payOrderParam.getAgentId(), ticketData)) {
                lock.unlock();
                return ResponseDto.failure("无权购买");
            }

            //会员时间获取
            String memberTimeStart = ticketData.getMemberTimeStart(); // 会员开售时间
            String timeStart = ticketData.getTimeStart(); // 普通开售时间
            String timeEnd = ticketData.getTimeEnd(); // 购票停售时间
            String timeExpressEnd = ticketData.getTimeEndExpress(); // 快递停售时间
            boolean isMember = feignAdamBaseClient.isMember(uid).getData();//获取是否是会员
            int memberType; //会员状态 不需要判断会员 1判断会员逻辑 2会员专属
            if (ticketData.getIsExclusive() == 1) {
                memberType = 2;
                if (!isMember) {
                    lock.unlock();
                    return ResponseDto.failure("非会员用户暂不可购买");//没有会员权限
                }
            } else {
                if (DateUtil.compareStrDay(DateUtil.getNowTime(), memberTimeStart) == 1 && DateUtil.compareStrDay(DateUtil.getNowTime(), timeStart) == -1 && ticketData.getIsMember() == 1) {
                    // 会员购买逻辑
                    memberType = 1;
                    if (!isMember) {
                        lock.unlock();
                        return ResponseDto.failure("非会员用户暂不可购买");//没有会员权限
                    }
                } else {
                    // 普通用户购买逻辑
                    memberType = 0;
                }
            }

            // 获取限购 实名
            int ticketLimit = ticketData.getLimitCount();//普通票种限购
            int ticketMemberLimit = ticketData.getLimitCountMember();//会员票种限购
            int performanceLimit = performanceData.getLimitCount();//普通演出限购
            int performanceMemberLimit = performanceData.getLimitCountMember();//会员演出限购
            int isTrueName = ticketData.getIsTrueName();//是否演出实名

            //通用判断时间
            if (isMember) {
                if (DateUtil.compareStrDay(DateUtil.getNowTime(), memberTimeStart) == -1) {
                    lock.unlock();
                    return ResponseDto.failure("未开始售卖");//未开始
                }
            } else {
                if (DateUtil.compareStrDay(DateUtil.getNowTime(), timeStart) == -1) {
                    lock.unlock();
                    return ResponseDto.failure("未开始售卖");//未开始
                }
            }
            if (DateUtil.compareStrDay(DateUtil.getNowTime(), timeEnd) == 1) {
                lock.unlock();
                return ResponseDto.failure("售卖已结束");//已结束
            }

            //快递票判断
            if(payOrderParam.getIsExpress()!=null) {
                if (payOrderParam.getIsExpress() == 1 && DateUtil.compareStrDay(DateUtil.getNowTime(), timeExpressEnd) == 1) {
                    lock.unlock();
                    return ResponseDto.failure("已超过快递票截止时间");//快递票不卖
                }
                if (payOrderParam.getIsExpress() == 1 && payOrderParam.getAddressId().isEmpty()) {
                    lock.unlock();
                    return ResponseDto.failure("快递票未填写收货地址");//快递票未填写收货地址
                }
            }
            //实名判断
            if (isTrueName == 1 && payOrderParam.getEnterIdList().size() <= 0) {
                lock.unlock();
                return ResponseDto.failure("入场人数量错误");//需要实名 未实名
            }
            if (isTrueName == 1 && payOrderParam.getEnterIdList().size() != payOrderParam.getNumber()) {
                lock.unlock();
                return ResponseDto.failure("入场人数量错误");//入场人数量错误
            }

            // 判断库存
            int surplusGeneral = dataUtils.changeSurplusGeneral(payOrderParam.getTicketId(), -payOrderParam.getNumber());
            if (surplusGeneral < 0) {//库存回滚
                dataUtils.changeSurplusGeneral(payOrderParam.getTicketId(), payOrderParam.getNumber());
                lock.unlock();
                return ResponseDto.failure("该票种已售罄");//没抢到
            } else {
                isDownGeneral = true;

                //学生票 判断
                List<AdamEntersVo> entersVoList = new ArrayList<>();
                if (isStudent == 1) {
                    for (String enterId : payOrderParam.getEnterIdList()) {
                        AdamEntersVo adamEnters = feignAdamBaseClient.queryEnters(enterId, uid).getData();
                        entersVoList.add(adamEnters);
                        int age = IDCard.getAgeByIdCard(adamEnters.getIdCard());
                        if (age > 25) {
                            lock.unlock();
                            return ResponseDto.failure("年龄不符合");//年龄超了
                        }
                    }
                }

                //限购判断 如果实名 则身份证维度限购 如果不实名则数量限购
                Query queryPerformance = new Query();
                Query queryTicket = new Query();
                queryPerformance.addCriteria(Criteria.where("performanceId").is(payOrderParam.getPerformanceId()).and("status").in(0, 1));
                queryTicket.addCriteria(Criteria.where("ticketId").is(payOrderParam.getTicketId()).and("status").in(0, 1));
                int performanceBuyCount = 0; //演出下所有票种购买数量
                int ticketBuyCount = 0;// 单一票种购买数量
                if (isTrueName == 1) {
                    //已购买数量 身份证
                    queryPerformance.fields().include("orderTicketsId");
                    queryTicket.fields().include("orderTicketsId");
                    KylinOrderTicketVo performanceOrderId = mongoTemplate.findOne(queryPerformance, KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());
                    KylinOrderTicketVo ticketOrderId = mongoTemplate.findOne(queryTicket, KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());
                    if (performanceOrderId != null && ticketOrderId != null) {
                        for (AdamEntersVo item : entersVoList) {
                            performanceBuyCount += mongoTemplate.count(Query.query(Criteria.where("enterIdCode").is(item.getIdCard()).and("performanceId").is(payOrderParam.getPerformanceId()).and("orderTicketsId").is(performanceOrderId.getOrderTicketsId())), KylinOrderTicketEntitiesVo.class, KylinOrderTicketEntitiesVo.class.getSimpleName())
                                    + payOrderParam.getNumber();
                            ticketBuyCount += mongoTemplate.count(Query.query(Criteria.where("enterIdCode").is(item.getIdCard()).and("ticketId").is(payOrderParam.getPerformanceId()).and("orderTicketsId").is(ticketOrderId.getOrderTicketsId())), KylinOrderTicketEntitiesVo.class, KylinOrderTicketEntitiesVo.class.getSimpleName())
                                    + payOrderParam.getNumber();
                            //会员区间
                            if (memberType == 1 || memberType == 2) {
                                if (performanceBuyCount >= performanceMemberLimit && performanceMemberLimit != 0) {
                                    lock.unlock();
                                    return ResponseDto.failure("该演出只能购买" + performanceMemberLimit + "张");//超过演出维度购买量
                                }
                                if (ticketBuyCount >= ticketMemberLimit && ticketMemberLimit != 0) {
                                    lock.unlock();
                                    return ResponseDto.failure("该票种只能购买" + ticketMemberLimit + "张");//超过票维度购买量
                                }
                            } else {//非会员区间
                                if (performanceBuyCount >= performanceLimit && performanceLimit != 0) {
                                    lock.unlock();
                                    return ResponseDto.failure("该演出只能购买" + performanceLimit + "张");//超过演出维度购买量
                                }
                                if (ticketBuyCount >= ticketLimit && ticketLimit != 0) {
                                    lock.unlock();
                                    return ResponseDto.failure("该票种只能购买" + ticketLimit + "张");//超过票维度购买量
                                }
                            }
                        }
                    }
                } else {
                    //已购买数量 数量
                    queryPerformance.fields().include("number");
                    queryTicket.fields().include("number");
                    List<KylinOrderTicketVo> performanceList = mongoTemplate.find(queryPerformance, KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());
                    List<KylinOrderTicketVo> ticketList = mongoTemplate.find(queryTicket, KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());

                    for (KylinOrderTicketVo number : performanceList) {
                        performanceBuyCount += number.getNumber();
                    }
                    for (KylinOrderTicketVo number : ticketList) {
                        ticketBuyCount += number.getNumber();
                    }
                    performanceBuyCount += payOrderParam.getNumber(); //演出下所有票种购买数量
                    ticketBuyCount += payOrderParam.getNumber(); // 单一票种购买数量
                    //会员区间
                    if (memberType == 1 || memberType == 2) {
                        if (performanceBuyCount >= performanceMemberLimit && performanceMemberLimit != 0) {
                            lock.unlock();
                            return ResponseDto.failure("该演出只能购买" + performanceMemberLimit + "张");//超过演出维度购买量
                        }
                        if (ticketBuyCount >= ticketMemberLimit && ticketMemberLimit != 0) {
                            lock.unlock();
                            return ResponseDto.failure("该票种只能购买" + ticketMemberLimit + "张");//超过票维度购买量
                        }
                    } else {//非会员区间
                        if (performanceBuyCount >= performanceLimit && performanceLimit != 0) {
                            lock.unlock();
                            return ResponseDto.failure("该演出只能购买" + performanceLimit + "张");//超过演出维度购买量
                        }
                        if (ticketBuyCount >= ticketLimit && ticketLimit != 0) {
                            lock.unlock();
                            return ResponseDto.failure("该票种只能购买" + ticketLimit + "张");//超过票维度购买量
                        }
                    }
                }
                lock.unlock();
                return order(payOrderParam, uid, isMember, isTrueName, performanceData, ticketData, entersVoList,isStudent);
            }
        } catch (Exception e) {
            if (isDownGeneral) {
                dataUtils.changeSurplusGeneral(payOrderParam.getTicketId(), payOrderParam.getNumber());
            }
            e.printStackTrace();
            lock.unlock();
            return ResponseDto.failure("下单失败");//乱七八糟异常
        }
    }

    private ResponseDto<PayResultVo> order(PayOrderParam payOrderParam, String uid, boolean isMember, int isTrueName, KylinPerformanceVo performanceData, KylinTicketVo ticketData, List<AdamEntersVo> entersVoList,Integer isStudent) {
        LinkedList<String> sqls = new LinkedList<>();
        LinkedList<Object[]> paramsList = new LinkedList<>();
        String source = CurrentUtil.getCliSource();
        String version = CurrentUtil.getCliVersion();
        //生成订单 order_ticket
        KylinOrderTickets orderTickets = new KylinOrderTickets();
        String orderTicketId = IDGenerator.nextSnowId().toString();
        orderTickets.setOrderTicketsId(orderTicketId);
        orderTickets.setUserId(uid);
        Map token = CurrentUtil.getTokenClaims();
        orderTickets.setUserName((String) token.get("nickname"));
        orderTickets.setUserMobile((String) token.get("mobile"));
        orderTickets.setPerformanceTitle(performanceData.getTitle());
        orderTickets.setOrderCode(IDGenerator.ticketOrderCode(orderTicketId));
        orderTickets.setPayCode("");
        orderTickets.setQrCode("");
        orderTickets.setOrderType(source);
        orderTickets.setOrderVersion(version);
        orderTickets.setNumber(payOrderParam.getNumber());
        orderTickets.setPrice(ticketData.getPrice());
        orderTickets.setPriceMember(ticketData.getMemberPrice());
        if (isMember) {
            orderTickets.setPriceTotal(ticketData.getMemberPrice().multiply(new BigDecimal(payOrderParam.getNumber())).add(payOrderParam.getIsExpress() == 1 ? ticketData.getPriceExpress() : new BigDecimal("0")));
        } else {
            orderTickets.setPriceTotal(ticketData.getPrice().multiply(new BigDecimal(payOrderParam.getNumber())).add(payOrderParam.getIsExpress() == 1 ? ticketData.getPriceExpress() : new BigDecimal("0")));
        }

        orderTickets.setPriceActual(orderTickets.getPriceTotal());
        orderTickets.setPriceVoucher(new BigDecimal("0.0"));
        orderTickets.setPriceExpress(ticketData.getPriceExpress());
        orderTickets.setPriceRefund(new BigDecimal("0.0"));
        orderTickets.setRefundNumber(0);
        orderTickets.setPayType(payOrderParam.getPayType());
        orderTickets.setPaymentType(null);
        orderTickets.setTimePay(null);
        if (ticketData.getIsExpress() == 1) {
            AdamAddressesVo addressesVo = feignAdamBaseClient.queryAddresses(payOrderParam.getAddressId(), uid).getData();
            orderTickets.setExpressContacts(addressesVo.getName());
            orderTickets.setExpressAddress(addressesVo.getAddress());
            orderTickets.setExpressPhone(addressesVo.getPhone());
            orderTickets.setGetTicketType("express");
        } else {
            orderTickets.setExpressContacts("");
            orderTickets.setExpressAddress("");
            orderTickets.setExpressPhone("");
            orderTickets.setGetTicketType("electronic");
        }
        orderTickets.setCouponType("no");
        orderTickets.setGetTicketDescribe("");
        orderTickets.setPayCountdownMinute(performanceData.getPayCountdownMinute());
        orderTickets.setCreatedAt(LocalDateTime.now());
        orderTickets.setUpdatedAt(null);
        sqls.add("kylin_order_ticket.add");
        paramsList.add(orderTickets.getAddObject());


        //生成订单 order_ticket_status
        KylinOrderTicketStatus orderTicketStatus = new KylinOrderTicketStatus();
        String orderTicketStatusId = IDGenerator.nextSnowId().toString();
        orderTicketStatus.setOrderTicketStatusId(orderTicketStatusId);
        orderTicketStatus.setOrderId(orderTicketId);
        orderTicketStatus.setExpressType(payOrderParam.getExpressType());
        orderTicketStatus.setTransferStatus(0);
        orderTicketStatus.setStatus(0);
        orderTicketStatus.setIsStudent(isStudent);
        orderTicketStatus.setPayStatus(0);
        orderTicketStatus.setCreatedAt(LocalDateTime.now());
        orderTicketStatus.setUpdatedAt(null);
        sqls.add("kylin_order_ticket_status.add");
        paramsList.add(orderTicketStatus.getAddObject());

        //生成订单 order_ticket_relation
        KylinOrderTicketRelations orderTicketRelations = new KylinOrderTicketRelations();
        String orderTicketRelationId = IDGenerator.nextSnowId().toString();
        orderTicketRelations.setOrderTicketRelationsId(orderTicketRelationId);
        orderTicketRelations.setOrderId(orderTicketId);
        orderTicketRelations.setTransferId("");
        orderTicketRelations.setLiveId("");
        orderTicketRelations.setAgentId(payOrderParam.getAgentId());
        orderTicketRelations.setIsMember(isMember ? 1 : 0);
        orderTicketRelations.setPerformanceId(payOrderParam.getPerformanceId());
        orderTicketRelations.setTimeId(payOrderParam.getTimeId());
        orderTicketRelations.setTicketId(payOrderParam.getTicketId());
        orderTicketRelations.setCreatedAt(LocalDateTime.now());
        orderTicketRelations.setUpdatedAt(null);
        sqls.add("kylin_order_ticket_relation.add");
        paramsList.add(orderTicketRelations.getAddObject());


        //生成票
        KylinOrderTicketEntities orderTicketEntities = new KylinOrderTicketEntities();
        String orderTicketEntitiesId = IDGenerator.nextSnowId().toString();
        if (isTrueName == 1) {
            for (AdamEntersVo enters : entersVoList) {
                orderTicketEntities.setOrderTicketEntitiesId(orderTicketEntitiesId);
                orderTicketEntities.setOrderId(orderTicketId);
                orderTicketEntities.setTicketId(payOrderParam.getTicketId());
                orderTicketEntities.setUserId(uid);
                orderTicketEntities.setPerformanceId(payOrderParam.getPerformanceId());
                orderTicketEntities.setTimeId(payOrderParam.getTimeId());
                orderTicketEntities.setEnterType(enters.getType());
                orderTicketEntities.setEnterName(enters.getName());
                orderTicketEntities.setEnterMobile(enters.getMobile());
                orderTicketEntities.setEnterIdCode(enters.getIdCard());
                orderTicketEntities.setStatus(0);
                orderTicketEntities.setSysDamai(ticketData.getSysDamai());
                orderTicketEntities.setCheckClient("");
                orderTicketEntities.setIsPayment(0);
                orderTicketEntities.setComment("");
                orderTicketEntities.setCreatedAt(LocalDateTime.now());
                orderTicketEntities.setUpdatedAt(null);
                sqls.add("kylin_order_ticket_entities.add");
                paramsList.add(orderTicketEntities.getAddObject());
                // 生成vo
                KylinOrderTicketEntitiesVo orderTicketEntitiesVo = new KylinOrderTicketEntitiesVo();
                BeanUtils.copyProperties(orderTicketEntities, orderTicketEntitiesVo);
                orderTicketEntitiesVo.setPerformanceTitle(performanceData.getTitle());
                orderTicketEntitiesVo.setTicketTitle(ticketData.getTitle());
                orderTicketEntitiesVo.setUseStart(ticketData.getUseStart());
                orderTicketEntitiesVo.setUseEnd(ticketData.getUseEnd());
                orderTicketEntitiesVo.setCreatedAt(orderTicketEntities.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                orderTicketEntitiesVo.setUpdatedAt(null);
                mongoTemplate.insert(orderTicketEntitiesVo, KylinOrderTicketEntitiesVo.class.getSimpleName());
            }
        } else {
            for (int i = 0; i < payOrderParam.getNumber(); i++) {
                orderTicketEntities.setOrderTicketEntitiesId(orderTicketEntitiesId);
                orderTicketEntities.setOrderId(orderTicketId);
                orderTicketEntities.setTicketId(payOrderParam.getTicketId());
                orderTicketEntities.setUserId(uid);
                orderTicketEntities.setPerformanceId(payOrderParam.getPerformanceId());
                orderTicketEntities.setTimeId(payOrderParam.getTimeId());
                orderTicketEntities.setEnterType(0);
                orderTicketEntities.setEnterName("");
                orderTicketEntities.setEnterMobile("");
                orderTicketEntities.setEnterIdCode("");
                orderTicketEntities.setStatus(0);
                orderTicketEntities.setSysDamai(ticketData.getSysDamai());
                orderTicketEntities.setCheckClient("");
                orderTicketEntities.setIsPayment(0);
                orderTicketEntities.setComment("");
                orderTicketEntities.setCreatedAt(LocalDateTime.now());
                orderTicketEntities.setUpdatedAt(null);
                sqls.add("kylin_order_ticket_entities.add");
                paramsList.add(orderTicketEntities.getAddObject());
                // 生成vo
                KylinOrderTicketEntitiesVo orderTicketEntitiesVo = new KylinOrderTicketEntitiesVo();
                BeanUtils.copyProperties(orderTicketEntities, orderTicketEntitiesVo);
                orderTicketEntitiesVo.setPerformanceTitle(performanceData.getTitle());
                orderTicketEntitiesVo.setTicketTitle(ticketData.getTitle());
                orderTicketEntitiesVo.setUseStart(ticketData.getUseStart());
                orderTicketEntitiesVo.setUseEnd(ticketData.getUseEnd());
                orderTicketEntitiesVo.setCreatedAt(orderTicketEntities.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                orderTicketEntitiesVo.setUpdatedAt(null);
                mongoTemplate.insert(orderTicketEntitiesVo, KylinOrderTicketEntitiesVo.class.getSimpleName());
            }
        }
        // 调用支付
        LinkedMultiValueMap<String, String> httpData = new LinkedMultiValueMap<String, String>();
        httpData.add("type", "TICKET");
        httpData.add("price", "0.01");
        httpData.add("name", ticketData.getUseStart() + "" + ticketData.getTitle());
        httpData.add("detail", performanceData.getTitle() + "-" + ticketData.getTitle() + "-" + ticketData.getUseStart());
        httpData.add("order_code", orderTickets.getOrderCode());
        httpData.add("client_ip", "127.0.0.1");
        httpData.add("notify_url", synUrl);
        httpData.add("create_date", orderTickets.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        httpData.add("expire_time", orderTickets.getPayCountdownMinute().toString());

        if (payOrderParam.getDeviceFrom().equals("js") || payOrderParam.getDeviceFrom().equals("applet")) {
            httpData.add("open_id", payOrderParam.getOpenId());
        }
        if (payOrderParam.getPayType().equals("alipay") && payOrderParam.getDeviceFrom().equals("wap")) {
            httpData.add("show_url", payOrderParam.getShowUrl());
            httpData.add("return_url", payOrderParam.getReturnUrl());
        }

        String returnData = HttpUtil.post(payUrl + payOrderParam.getDeviceFrom() + "/" + payOrderParam.getPayType(), httpData);
        PayResultVo payResultVo = JsonUtils.fromJson(returnData, PayResultVo.class);
        payResultVo.setOrder_id(orderTicketId);
        payResultVo.setPrice(orderTickets.getPriceActual());
        orderTickets.setPayCode(payResultVo.getCode());
        if (payOrderParam.getPayType().equals("alipay") && payOrderParam.getDeviceFrom().equals("wap")) {
            payResultVo.setShowUrl(payOrderParam.getShowUrl());
            payResultVo.setReturnUrl(payOrderParam.getReturnUrl());
        }
        // 生成vo
        KylinOrderTicketVo orderTicketVo = new KylinOrderTicketVo();
        orderTicketVo.setOrderTicket(orderTickets);
        orderTicketVo.setOrderTicketStatus(orderTicketStatus);
        orderTicketVo.setOrderTicketRelation(orderTicketRelations);
        orderTicketVo.setPerformanceImg(performanceData.getImgPoster());
        orderTicketVo.setTicketTitle(ticketData.getTitle());
        orderTicketVo.setUseStart(ticketData.getUseStart());
        orderTicketVo.setTimeStart(performanceData.getTimeStart());
        orderTicketVo.setOverdueAt(orderTickets.getCreatedAt().plusMinutes(performanceData.getPayCountdownMinute()).plusSeconds(15).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        orderTicketVo.setCreatedAt(orderTickets.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        mongoTemplate.insert(orderTicketVo, KylinOrderTicketVo.class.getSimpleName());
        // 执行sql
//        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
//                SqlMapping.get(sqls, paramsList));
        return ResponseDto.success(payResultVo);
    }

    @Override
    public ResponseDto<PayResultVo> payAgain(PayAgainParam payAgainParam) {
        try {
            String uid = CurrentUtil.getCurrentUid();
            checkOrderTime(uid);
            //检查订单时间 是否关闭
            KylinOrderTicketVo orderTicketData = dataUtils.getOrderTicketVo(payAgainParam.getOrderId());
            if (orderTicketData == null) {
                return ResponseDto.failure("订单不存在");
            }

            if (orderTicketData.getStatus() == 1) {
                return ResponseDto.failure("订单已支付");
            } else {
                if (orderTicketData.getStatus() != 0) {
                    return ResponseDto.failure("订单已关闭");
                }
            }
            String returnCheckData = HttpUtil.get(checkUrl + "?code=" + orderTicketData.getPayCode(), null);
            PayResultVo checkVo = JsonUtils.fromJson(returnCheckData, PayResultVo.class);
            if (checkVo.getStatus() == 1) {
                return ResponseDto.failure("'您已支付请刷新再试'");
            }
            if (!orderTicketData.getPayType().equals("no")) {

                KylinOrderTicketEntitiesVo entitiesDataOne = mongoTemplate.findOne(Query.query(Criteria.where("orderId").is(payAgainParam.getOrderId())), KylinOrderTicketEntitiesVo.class, KylinOrderTicketEntitiesVo.class.getSimpleName());
                if (entitiesDataOne == null) {
                    return ResponseDto.failure("参数错误");
                }
                KylinOrderTicketEntitiesVo entitiesData = dataUtils.getOrderTicketEntitiesVo(entitiesDataOne.getOrderTicketEntitiesId());

                LinkedMultiValueMap<String, String> httpData = new LinkedMultiValueMap<String, String>();
                httpData.add("type", "TICKET");
                httpData.add("price", "0.01");
                httpData.add("name", entitiesData.getUseStart() + "" + entitiesData.getPerformanceTitle());
                httpData.add("detail", entitiesData.getPerformanceTitle() + "-" + entitiesData.getTicketTitle() + "-" + entitiesData.getUseStart());
                httpData.add("order_code", orderTicketData.getOrderCode());
                httpData.add("client_ip", "127.0.0.1");
                httpData.add("notify_url", synUrl);
                httpData.add("create_date", orderTicketData.getCreatedAt());
                httpData.add("expire_time", orderTicketData.getPayCountdownMinute().toString());

                httpData.add("open_id", payAgainParam.getOpenId());

                if (payAgainParam.getPayType().equals("alipay") && payAgainParam.getDeviceFrom().equals("wap")) {
                    httpData.add("show_url", payAgainParam.getShowUrl());
                    httpData.add("return_url", payAgainParam.getReturnUrl());
                }

                String returnData = HttpUtil.post(payUrl + payAgainParam.getDeviceFrom() + "/" + payAgainParam.getPayType(), httpData);
                PayResultVo payResultVo = JsonUtils.fromJson(returnData, PayResultVo.class);
                payResultVo.setOrder_id(orderTicketData.getOrderTicketsId());
                payResultVo.setPrice(orderTicketData.getPriceActual());
                if (payAgainParam.getPayType().equals("alipay") && payAgainParam.getDeviceFrom().equals("wap")) {
                    payResultVo.setShowUrl(payAgainParam.getShowUrl());
                    payResultVo.setReturnUrl(payAgainParam.getReturnUrl());
                }

                KylinOrderTickets orderTickets = new KylinOrderTickets();
                orderTickets.setOrderTicketsId(payAgainParam.getOrderId());
                orderTickets.setUpdatedAt(LocalDateTime.now());
                orderTickets.setPayCode(payResultVo.getCode());
                orderTickets.setPayType(payAgainParam.getPayType());

                //改vo
                HashMap<String, String> map = new HashMap<>();
                map.put("payType", payAgainParam.getPayType());
                map.put("payCode", payResultVo.getCode());
                map.put("updatedAt", orderTickets.getUpdatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                mongoTemplate.getCollection(KylinOrderTicketVo.class.getSimpleName()).updateOne(
                        Query.query(Criteria.where("orderTicketsId").is(payAgainParam.getOrderId())).getQueryObject(),
                        new BasicDBObject("$set", mongoConverter.convertToMongoType(map))
                );
                dataUtils.delOrderTicketRedis(orderTickets.getOrderTicketsId());

//            rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
//                    SqlMapping.get("kylin_order_ticket.payAgain", orderTickets.getPayAgainObject()));
                return ResponseDto.success(payResultVo);
            }
            return ResponseDto.failure("未选择支付方式");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseDto.failure("订单已失效");
        }

    }

    @Override
    public String syncOrder(SyncOrderParam syncOrderParam) {
        //支付时间
        LocalDateTime now = LocalDateTime.now();

        if(!RedisLockUtil.tryLock("order_lock:" + syncOrderParam.getOrder_code(),240,240)){
            return "fail";//参数错误
        }
        RLock lock = RedisLockUtil.lock("order_lock:" + syncOrderParam.getOrder_code(), 240);


        String timePay = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        KylinOrderTicketVo orderTicketData = mongoTemplate.findOne(Query.query(Criteria.where("orderCode").is(syncOrderParam.getOrder_code())), KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());
        if (orderTicketData == null) {
            lock.unlock();
            return "fail";//订单不存在
        }

        if (orderTicketData.getStatus() != 0) {
            if (orderTicketData.getPayCode().equals(syncOrderParam.getCode()) && orderTicketData.getStatus() == 1) {
                lock.unlock();
                return "success";//已经支付
            }

            if (!orderTicketData.getPayCode().equals(syncOrderParam.getCode())) {
                lock.unlock();
                return "fail";//重复支付
            }
        }

        if (orderTicketData.getPriceActual().compareTo(syncOrderParam.getPrice()) != 0) {
            lock.unlock();
            return "fail";//价格不符
        }

        LinkedList<String> sqls = new LinkedList<>();
        LinkedList<Object[]> paramsList = new LinkedList<>();

        //sql
        KylinOrderTickets orderTickets = new KylinOrderTickets();
        orderTickets.setOrderTicketsId(orderTicketData.getOrderTicketsId());
        orderTickets.setPaymentType(syncOrderParam.getPayment_type());
        orderTickets.setPayCode(syncOrderParam.getCode());
        orderTickets.setTimePay(timePay);
        orderTickets.setQrCode(IDGenerator.ticketQrCode(orderTicketData.getOrderTicketsId()));
        orderTickets.setUpdatedAt(now);
        sqls.add("kylin_order_ticket.synPay");
        paramsList.add(orderTickets.getSynOrderObject());

        KylinOrderTicketStatus orderTicketStatus = new KylinOrderTicketStatus();
        orderTicketStatus.setOrderId(orderTicketData.getOrderTicketsId());
        orderTicketStatus.setStatus(1);
        orderTicketStatus.setPayStatus(syncOrderParam.getStatus());
        orderTicketStatus.setUpdatedAt(orderTickets.getUpdatedAt());
        sqls.add("kylin_order_ticket_status.synPay");
        paramsList.add(orderTicketStatus.getSynOrderObject());

        KylinOrderTicketEntities orderTicketEntities = new KylinOrderTicketEntities();
        orderTicketEntities.setOrderId(orderTicketData.getOrderTicketsId());
        orderTicketEntities.setIsPayment(1);
        orderTicketEntities.setUpdatedAt(orderTickets.getUpdatedAt());
        sqls.add("kylin_order_ticket_entities.synPay");
        paramsList.add(orderTicketEntities.getSynOrderObject());


        //vo
        HashMap<String, Object> orderTicketVo = new HashMap<>();
        orderTicketVo.put("paymentType", orderTickets.getPaymentType());
        orderTicketVo.put("payCode", orderTickets.getPayCode());
        orderTicketVo.put("timePay", orderTickets.getTimePay());
        orderTicketVo.put("qrCode", orderTickets.getQrCode());
        orderTicketVo.put("status", 1);
        orderTicketVo.put("payStatus", orderTicketStatus.getStatus());
        orderTicketVo.put("updatedAt", timePay);
        mongoTemplate.getCollection(KylinOrderTicketVo.class.getSimpleName()).updateOne(
                Query.query(Criteria.where("orderTicketsId").is(orderTickets.getOrderTicketsId())).getQueryObject(),
                new BasicDBObject("$set", mongoConverter.convertToMongoType(orderTicketVo))
        );
        dataUtils.delOrderTicketRedis(orderTickets.getOrderTicketsId());

        HashMap<String, Object> orderTicketEntitiesVo = new HashMap<>();
        orderTicketEntitiesVo.put("isPayment", 1);
        orderTicketEntitiesVo.put("updatedAt", timePay);
        mongoTemplate.getCollection(KylinOrderTicketEntitiesVo.class.getSimpleName()).updateMany(
                Query.query(Criteria.where("orderId").is(orderTickets.getOrderTicketsId())).getQueryObject(),
                new BasicDBObject("$set", mongoConverter.convertToMongoType(orderTicketEntitiesVo))
        );
        List<KylinOrderTicketEntitiesVo> delList = mongoTemplate.find(Query.query(Criteria.where("orderId").is(orderTickets.getOrderTicketsId())), KylinOrderTicketEntitiesVo.class, KylinOrderTicketEntitiesVo.class.getSimpleName());
        for (KylinOrderTicketEntitiesVo item : delList) {
            dataUtils.delOrderTicketEntitiesRedis(item.getOrderTicketEntitiesId());
        }

//        rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
//                SqlMapping.get(sqls, paramsList));

        // 发短信
        return "success";
    }

    @Override
    public PageInfo<List<KylinOrderListVo>> orderList(int page, int size) {
        //TODO 缺快递
        PageInfo<List<KylinOrderListVo>> mPageInfo = null;
        String uid = CurrentUtil.getCurrentUid();
        checkOrderTime(uid);
        try {
            // 排序 分页
            Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "orderTicketsId"));

            //条件
            Query query = new Query();
            query.addCriteria(
                    Criteria.where("userId").is(uid).and("status").ne(-1)
            );

            // 查询总数
            long count = mongoTemplate.count(query, KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());
            query.with(pageable);

            List<KylinOrderListVo> voList = mongoTemplate.find(query, KylinOrderListVo.class, KylinOrderTicketVo.class.getSimpleName());
            for (KylinOrderListVo item : voList) {
                Expresses expresses = expressesMapper.selectOne(new UpdateWrapper<Expresses>().eq("order_code", item.getOrderCode()).eq("order_type", "order_ticket"));
                if (null != expresses) {
                    item.setExpress_number(expresses.getNumber());
                    item.setExpress_company(expresses.getName());
                    item.setKuaidi_status(expresses.getStatus());
                } else {
                    item.setExpress_number("");
                    item.setExpress_company("");
                    item.setKuaidi_status(-2);
                }

                if (item.getStatus() == 0) {
                    item.setRestTime(DateUtil.intervalSeconds(
                            DateUtil.parse(item.getOverdueAt(), "yyyy-MM-dd HH:mm:ss"),
                            DateUtil.parse(DateUtil.getNowTime(), "yyyy-MM-dd HH:mm:ss")
                    ));
                } else {
                    item.setRestTime(0L);
                }
            }

            mPageInfo = new PageInfo(voList);
            mPageInfo.setTotal(count);
            return mPageInfo;
        } catch (Exception e) {
            e.printStackTrace();
            return mPageInfo;
        }
    }

    @Override
    public OrderDetailsVo orderDetails(String orderId) {
        OrderDetailsVo vo = new OrderDetailsVo();
        try {
            String uid = CurrentUtil.getCurrentUid();
            checkOrderTime(uid);
            KylinOrderTicketVo orderTicketVo = dataUtils.getOrderTicketVo(orderId);
            if (null != orderTicketVo) {
                Expresses expresses = expressesMapper.selectOne(new UpdateWrapper<Expresses>().eq("order_id", orderId).eq("order_type", "order_ticket"));
                KylinPerformanceVo performanceVo = dataUtils.getPerformanceVo(orderTicketVo.getPerformanceId());
                KylinTicketVo ticketVo = dataUtils.getTicketVo(orderTicketVo.getTicketId());
                List<KylinOrderTicketEntitiesVo> kylinOrderTicketEntitiesVoList = mongoTemplate.find(Query.query(Criteria.where("orderId").is(orderTicketVo.getOrderTicketsId())), KylinOrderTicketEntitiesVo.class, KylinOrderTicketEntitiesVo.class.getSimpleName());
                // 数据脱敏
                for (KylinOrderTicketEntitiesVo item : kylinOrderTicketEntitiesVoList) {
                    if (item.getEnterIdCode().length() == 18) {
                        item.setEnterIdCode(item.getEnterIdCode().substring(0, 3) + "*************" + item.getEnterIdCode().substring(16));
                    }
                    if (item.getEnterMobile().length() == 11) {
                        item.setEnterMobile(item.getEnterMobile().substring(0, 3) + "****" + item.getEnterMobile().substring(7));
                    }
                }
                //计算 倒计时
                if (orderTicketVo.getStatus() == 0) {
                    vo.setRestTime(DateUtil.intervalSeconds(
                            DateUtil.parse(orderTicketVo.getOverdueAt(), "yyyy-MM-dd HH:mm:ss"),
                            DateUtil.parse(DateUtil.getNowTime(), "yyyy-MM-dd HH:mm:ss")
                    ));
                } else {
                    vo.setRestTime(0L);
                }
                //快递
                if (null != expresses) {
                    vo.setExpress_number(expresses.getNumber());
                    vo.setExpress_company(expresses.getName());
                    vo.setKuaidi_status(expresses.getStatus());
                } else {
                    vo.setExpress_number("");
                    vo.setExpress_company("");
                    vo.setKuaidi_status(-2);
                }
                vo.setOrderTicketEntitiesVo(kylinOrderTicketEntitiesVoList);
                vo.setPerformanceVo(performanceVo);
                vo.setTicketVo(ticketVo);
                vo.setOrderTicketVo(orderTicketVo);
            }
            return vo;
        } catch (Exception e) {
            return vo;
        }
    }

    @Override
    public boolean checkOrderTime(String userId) {
        Query query = new Query();
        query.addCriteria(Criteria.where("status").is(0).and("overdueAt").lte(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        if (null != userId) {
            query.addCriteria(Criteria.where("userId").is(userId));
        }
        List<KylinOrderTicketVo> orderTicketVo = mongoTemplate.find(query,
                KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());

        for (KylinOrderTicketVo item : orderTicketVo) {
            if(!RedisLockUtil.tryLock("order_lock:" + item.getOrderCode(),240,240)){
                continue;
            }
            RLock lock = redisLockUtil.lock("order_lock:" + item.getOrderCode(), 240);
            KylinOrderTicketVo itemData = dataUtils.getOrderTicketVo(item.getOrderTicketsId());
            if (itemData.getStatus() == 0) {
                LocalDateTime now = LocalDateTime.now();
                try {
                    //mysql
                    KylinOrderTickets orderTickets = new KylinOrderTickets();
                    orderTickets.setOrderTicketsId(item.getOrderTicketsId());
                    orderTickets.setUpdatedAt(now);

                    KylinOrderTicketStatus orderTicketStatus = new KylinOrderTicketStatus();
                    orderTicketStatus.setOrderId(item.getOrderTicketsId());
                    orderTicketStatus.setStatus(2);
                    orderTicketStatus.setUpdatedAt(now);

                    //vo
                    HashMap<String, Object> map = new HashMap<>();
                    map.put("status", 2);
                    map.put("updatedAt", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                    mongoTemplate.getCollection(KylinOrderTicketVo.class.getSimpleName()).updateOne(
                            Query.query(Criteria.where("orderTicketsId").is(orderTickets.getOrderTicketsId())).getQueryObject(),
                            new BasicDBObject("$set", mongoConverter.convertToMongoType(map))
                    );
                    dataUtils.delOrderTicketRedis(orderTickets.getOrderTicketsId());

                    dataUtils.changeSurplusGeneral(itemData.getTicketId(), itemData.getNumber());


//                    rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
//                            SqlMapping.get("kylin_order_ticket.close", orderTickets.getCloseOrderObject()));
//                    rabbitTemplate.convertAndSend(MQConst.EXCHANGES_LIQUIDNET_SQL, MQConst.ROUTING_KEY_SQL,
//                            SqlMapping.get("kylin_order_ticket_status.close", orderTicketStatus.getCloseOrderObject()));

                } catch (Exception e) {
                    lock.unlock();
                    e.printStackTrace();
                    return false;
                }
            } else {
                lock.unlock();
            }
        }
        return true;
    }

    @Override
    public ResponseDto<Integer> checkOrderResult(String orderId) {
        KylinOrderTicketVo orderTicketData = dataUtils.getOrderTicketVo(orderId);
        if (orderTicketData == null) {
            return ResponseDto.failure("订单不存在");
        }else{
            String returnCheckData = HttpUtil.get(checkUrl + "?code=" + orderTicketData.getPayCode(), null);
            PayResultVo checkVo = JsonUtils.fromJson(returnCheckData, PayResultVo.class);
            if (checkVo.getStatus() == 1) {
                return ResponseDto.success(1);
            }else{
                return ResponseDto.success(0);
            }
        }
    }

    public boolean checkAgent(String agentId, KylinTicketVo ticketData) {
        boolean isAgent = ticketData.getIsAgent() == 1;
        if (isAgent) {
            return redisUtil.sHasKey(KylinRedisConst.AGENT, agentId);
        } else {
            return true;
        }
    }
}
