package com.liquidnet.service.adam.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.liquidnet.common.sms.constant.SmsEnum;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.adam.dto.AdamMemberOrderCallbackParam;
import com.liquidnet.service.adam.dto.AdamMemberOrderCodeParam;
import com.liquidnet.service.adam.dto.AdamMemberOrderParam;
import com.liquidnet.service.adam.dto.AdamMemberOrderResult;
import com.liquidnet.service.adam.dto.vo.*;
import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.service.IAdamMemberOrderService;
import com.liquidnet.service.adam.service.IAdamUserMemberService;
import com.liquidnet.service.adam.util.MemberUtil;
import com.liquidnet.service.adam.util.ObjectUtil;
import com.liquidnet.service.adam.util.QueueUtils;
import com.liquidnet.service.base.*;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.feign.stone.api.FeignStoneIntegralClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AdamMemberOrderServiceImpl implements IAdamMemberOrderService {
    @Autowired
    Environment env;
    @Autowired
    QueueUtils queueUtils;
    @Autowired
    AdamRdmService adamRdmService;
    @Autowired
    IAdamUserMemberService adamUserMemberService;
    @Autowired
    @Lazy
    FeignStoneIntegralClient feignStoneIntegralClient;


    @Value("${liquidnet.service.kylin.url}")
    private String serviceKylinUrl;

    @Override
//    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ResponseDto<AdamMemberOrderResult> buyMemberOrMemberCode(String currentUid, AdamMemberOrderParam param) {
        AdamAddressesVo addressesVo = adamRdmService.getAddressesVoByUidAddressesId(currentUid, param.getAddressId());
        if (null == addressesVo) {
            return ResponseDto.failure(ErrorMapping.get("10106"));
        }
        AdamMemberVo memberVo = adamRdmService.getMemberVoByMemberId(param.getMemberId());
        if (null == memberVo) {
            return ResponseDto.failure(ErrorMapping.get("10201"));
        }
        AdamMemberPriceVo memberPriceVo = adamRdmService.getMemberPriceVoByPriceId(param.getMemberId(), param.getMemberPriceId());
        if (null == memberPriceVo) {
            return ResponseDto.failure(ErrorMapping.get("10202"));
        }

        LocalDateTime now = LocalDateTime.now();
        String nowStr = DateUtil.Formatter.yyyyMMddHHmmss.format(now);
        String orderNo = IDGenerator.nextSnowId().concat("V");
        String clientIp = CurrentUtil.getCliIpAddr();
        BigDecimal paymentPrice = memberPriceVo.getPriceFixed();

        // 价格处理，普通用户享受首次购买享受特价，非首次购买享受折扣价
        AdamUserMemberVo userMemberVo = adamRdmService.getUserMemberVoByUid(currentUid);
        if (null == userMemberVo) {// 标记[0-普通用户｜2-过期会员｜10-老会员｜11-新会员]
            paymentPrice = memberPriceVo.getPriceSpecial();
        }

        // 调取对应支付接口
        LinkedMultiValueMap<String, String> payParam = CollectionUtil.linkedMultiValueMapStringString();
        payParam.add("type", "VIP");// TICKET,PRODUCT,COST,MBEANS,LIVE,VIDEO,VIP,CLUB
        payParam.add("price", String.valueOf(paymentPrice));
        payParam.add("name", memberVo.getName());
        payParam.add("detail", "正在现场:购买会员".concat(param.getMode() == 0 ? "" : "码"));
        payParam.add("orderCode", orderNo);
        payParam.add("clientIp", clientIp);
        payParam.add("notifyUrl", env.getProperty("liquidnet.url-pay.callback"));
        payParam.add("deviceFrom", param.getDeviceFrom());
        payParam.add("payType", param.getPayType());

        String payUri = "/" + param.getDeviceFrom() + "/" + param.getPayType();
        switch (payUri) {// applet/wepay | js/wepay | wap/alipay | wap/wepay | app/iappay | app/wepay
            /**
             * auth_code open_id为空时必传 微信网页静默获取，可用于后台请求获取openid
             * open_id auth_code为空时必传 微信网页静默获取auth_code，然后请求后台获取openid
             * 建议传open_id
             */
            case "/applet/wepay":
            case "/js/wepay":
                if (StringUtils.isEmpty(param.getOpenId())) {
                    return ResponseDto.failure(ErrorMapping.get("10204"));
                }
                payParam.add("openId", param.getOpenId());
                break;
            case "/wap/alipay":
                if (StringUtils.isEmpty(param.getShowUrl()) || StringUtils.isEmpty(param.getReturnUrl())) {
                    return ResponseDto.failure(ErrorMapping.get("10205"));
                }
                // 取消支付，点击取消支付宝回调地址
                payParam.add("showUrl", param.getShowUrl());
                // 支付成功，点击完成支付宝回调地址
                payParam.add("returnUrl", param.getReturnUrl() + orderNo);
                break;
            case "/app/iappay":
                if (StringUtils.isEmpty(param.getOpenId())) {
                    return ResponseDto.failure(ErrorMapping.get("10206"));
                }
                // iOS App唯一标识，目前为：com.zhengzai.zhengzai-tv
                payParam.add("bundleId", "com.zhengzai.zhengzai-tv");
                // iTunes上架的商品唯一标识
                payParam.add("productId", param.getProductId());
                break;
        }
        if (param.getPayType().equals("douyinpay")) {
            payParam.add("showUrl", param.getShowUrl());
            payParam.add("returnUrl", param.getReturnUrl() + orderNo);
        }
        if (param.getPayType().equals("unionpay")) {
            payParam.add("returnUrl", param.getReturnUrl() + orderNo);
        }

        payParam.add("createDate", nowStr);
        payParam.add("expireTime", "5");// 过期时间，单位分钟，默认5

        // 生成订单信息
        AdamMemberOrderVo memberOrderVo = AdamMemberOrderVo.getNew();
        memberOrderVo.setOrderNo(orderNo);
        memberOrderVo.setMemberId(param.getMemberId());
        memberOrderVo.setDays(memberPriceVo.getDays());
        memberOrderVo.setMemberPriceId(param.getMemberPriceId());
        memberOrderVo.setCreatedAt(now);
        memberOrderVo.setMemberName(memberVo.getName());
        memberOrderVo.setMode(param.getMode());
        memberOrderVo.setBirthday(param.getBirthday());
        memberOrderVo.setArea(param.getProvince().concat("-").concat(param.getCity()).concat("-").concat(param.getCounty()));
        memberOrderVo.setDeviceFrom(param.getDeviceFrom());
        memberOrderVo.setPayType(param.getPayType());
        memberOrderVo.setState(0);// 0-待支付
        memberOrderVo.setUid(currentUid);
        memberOrderVo.setPrice(param.getPrice());
        memberOrderVo.setPricePaid(paymentPrice);
        memberOrderVo.setClientIp(clientIp);
        String headerCliVersion = CurrentUtil.getHeaderCliVersion(), headerCliSource = CurrentUtil.getHeaderCliSource();
        memberOrderVo.setSource(null == headerCliSource ? "" : headerCliSource);
        memberOrderVo.setVersion(null == headerCliVersion ? "" : headerCliVersion);


        AdamMemberOrderExtVo memberOrderExtVo = AdamMemberOrderExtVo.getNew();
        memberOrderExtVo.setOrderNo(orderNo);
        memberOrderExtVo.setExpressStatus(-1);
        memberOrderExtVo.setExpressReceiver(addressesVo.getName());
        memberOrderExtVo.setExpressPhone(addressesVo.getPhone());
        memberOrderExtVo.setExpressAddress(addressesVo.getProvince() + "｜" + addressesVo.getCity() + "｜" + addressesVo.getCounty() + "｜" + addressesVo.getAddress());

        memberOrderVo.setExtendVo(memberOrderExtVo);


        if (!adamRdmService.setShotMemberOrderVoByOrderNo(orderNo, memberOrderVo)) {
            log.warn("###购买会员创建订单失败[memberOrderVo:{}]", JsonUtils.toJson(memberOrderVo));
            return ResponseDto.failure(ErrorMapping.get("10210"));
        }
        AdamMemberOrderResult result = AdamMemberOrderResult.getNew();
        try {
//            String url = env.getProperty("liquidnet.url-pay.pay") + payUri;
            String url = env.getProperty("liquidnet.url-pay.pay");
            log.debug("HttpUtil.resquest.url:{},param:{}", url, JsonUtils.toJson(payParam));
            long s = System.currentTimeMillis();
            String respStr = HttpUtil.post(url, payParam);
            log.debug("#DRAGON.API耗时:{}ms", System.currentTimeMillis() - s);
            log.debug("HttpUtil.response.body:{}", respStr);
            JsonNode respJNode = JsonUtils.fromJson(respStr, JsonNode.class);
            if (null == respJNode || !"0".equals(respJNode.get("code").asText())) {
                log.warn("###购买会员调用支付失败[respStr:{}]", respStr);
                return ResponseDto.failure(ErrorMapping.get("10210"));
            }
            JsonNode respDataJNode = respJNode.get("data");
            result.setCode(respDataJNode.get("code").asText());
            result.setPayData(respDataJNode.get("payData"));

            memberOrderVo.setPayNo(result.getCode());
        } catch (Exception e) {
            log.warn("###购买会员下单调用支付中心异常[payParam:{}]", JsonUtils.toJson(payParam), e);

            return ResponseDto.failure(ErrorMapping.get("10203"));
        }
        adamRdmService.setShotMemberOrderVoByOrderNo(orderNo, memberOrderVo);

        result.setOrderNo(memberOrderVo.getOrderNo());
        result.setShowUrl(param.getShowUrl());
        result.setReturnUrl(param.getReturnUrl());
        result.setPayType(param.getPayType());
        return ResponseDto.success(result);
    }

    @Override
    public ResponseDto<Object> paymentNotifyCallBack(AdamMemberOrderCallbackParam parameter) {
        final AdamMemberOrderVo shotMemberOrderVo = adamRdmService.getShotMemberOrderVoByOrderNo(parameter.getOrderCode());
        if (null == shotMemberOrderVo) {
            return ResponseDto.failure(ErrorMapping.get("10501"), parameter);
        }
        // AdamMemberConst.STATUS_*:0-待支付,1-已支付,2-已过期,3-超时付,4-退款中,5-退款完成
        if (1 == shotMemberOrderVo.getState() || 3 == shotMemberOrderVo.getState()) {
            return ResponseDto.failure(ErrorMapping.get("10502"), parameter);
        }
        if (parameter.getPrice().compareTo(shotMemberOrderVo.getPricePaid()) != 0) {
            // 订单金额比对
            return ResponseDto.failure(ErrorMapping.get("10503"), parameter);
        }
        AdamMemberPriceVo memberPriceVo = adamRdmService.getMemberPriceVoByPriceId(
                shotMemberOrderVo.getMemberId(), shotMemberOrderVo.getMemberPriceId());
        if (null == memberPriceVo) {
            return ResponseDto.failure(ErrorMapping.get("10504"));
        }
        /**
         * 购买方式[order.mode]:
         * 0-购买会员
         * 1-购买会员码(不需要填写地址)
         * 2-使用会员码(购买的) 仅限从未购买过会员的用户使用，需要填写地址
         * 3-使用礼包码(赠送的) 仅限从未购买过会员的用户使用，不需要填写地址
         */
        boolean handlerResultFailed = false;
        AdamMemberCodeVo initMemberCodeVo = null;
        AdamUserMemberVo existUserMemberVo = null;
        try {
            AdamMemberOrderVo handleMemberOrderVo = AdamMemberOrderVo.getNew();
            BeanUtils.copyProperties(shotMemberOrderVo, handleMemberOrderVo);
            LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
            LinkedList<Object[]> operationObjs = CollectionUtil.linkedListObjectArr(),
                    initMemberOrderObjs = CollectionUtil.linkedListObjectArr(),
                    initMemberOrderExtObjs = CollectionUtil.linkedListObjectArr(),
                    updateUserMobileLocateObjs = CollectionUtil.linkedListObjectArr();
            LocalDateTime now = LocalDateTime.now();
            if (0 == handleMemberOrderVo.getMode()) {// 购买会员回调
                existUserMemberVo = adamRdmService.getUserMemberVoByUid(handleMemberOrderVo.getUid());
                if (null == existUserMemberVo) {// 创建会员
                    String memberNo = adamUserMemberService.getNextMemberNo(handleMemberOrderVo.getMemberId());

                    AdamUserMemberVo initUserMemberVo = AdamUserMemberVo.getNew();
                    initUserMemberVo.setUid(handleMemberOrderVo.getUid());
                    initUserMemberVo.setMemberId(handleMemberOrderVo.getMemberId());
                    initUserMemberVo.setMemberNo(memberNo);
                    initUserMemberVo.setState(1);// 1-正常,2-失效
                    initUserMemberVo.setExpiryAt(now.plusDays(memberPriceVo.getDays()).withHour(23).withMinute(59).withSecond(59));
                    initUserMemberVo.setCreatedAt(now);

                    long s = System.currentTimeMillis();
                    adamRdmService.setUserMemberVoByUid(handleMemberOrderVo.getUid(), initUserMemberVo);
                    log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

                    handleMemberOrderVo.setMemberNo(memberNo);

                    toMqSqls.add(SqlMapping.get("adam_user_member.add"));
                    operationObjs.add(new Object[]{
                            handleMemberOrderVo.getUid(), initUserMemberVo.getMemberId(), initUserMemberVo.getMemberNo(),
                            initUserMemberVo.getState(), initUserMemberVo.getExpiryAt(), initUserMemberVo.getCreatedAt()
                    });
                } else {// 续费
                    AdamUserMemberVo updateExistUserMemberVo = AdamUserMemberVo.getNew();
                    BeanUtils.copyProperties(existUserMemberVo, updateExistUserMemberVo);
                    LocalDateTime currentExpiryAt = existUserMemberVo.getExpiryAt();
                    LocalDateTime expiryAt = (currentExpiryAt.isBefore(now) ? now : currentExpiryAt)
                            .plusDays(memberPriceVo.getDays()).withHour(23).withMinute(59).withSecond(59);

                    updateExistUserMemberVo.setState(1);
                    updateExistUserMemberVo.setExpiryAt(expiryAt);
                    updateExistUserMemberVo.setUpdatedAt(now);
                    long s = System.currentTimeMillis();
                    adamRdmService.setUserMemberVoByUid(handleMemberOrderVo.getUid(), updateExistUserMemberVo);
                    log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

                    handleMemberOrderVo.setMemberNo(updateExistUserMemberVo.getMemberNo());

                    toMqSqls.add(SqlMapping.get("adam_user_member.update"));
                    operationObjs.add(new Object[]{
                            updateExistUserMemberVo.getState(), updateExistUserMemberVo.getExpiryAt(), now, handleMemberOrderVo.getUid()
                    });
                }
            }
            if (1 == handleMemberOrderVo.getMode()) {// 购买会员码回调
                String memberNo = adamUserMemberService.getNextMemberNo(handleMemberOrderVo.getMemberId());

                initMemberCodeVo = AdamMemberCodeVo.getNew();
                initMemberCodeVo.setCode(MemberUtil.buyCode());
                initMemberCodeVo.setMemberId(handleMemberOrderVo.getMemberId());
                initMemberCodeVo.setMemberPriceId(handleMemberOrderVo.getMemberPriceId());
                initMemberCodeVo.setType(1);// 1-购买,2-赠送
                initMemberCodeVo.setMemberNo(memberNo);
                initMemberCodeVo.setState(0);
                initMemberCodeVo.setBuyOrderNo(handleMemberOrderVo.getOrderNo());
                initMemberCodeVo.setBuyUid(handleMemberOrderVo.getUid());
                initMemberCodeVo.setBuyAt(handleMemberOrderVo.getCreatedAt());
                initMemberCodeVo.setCreatedAt(now);

                long s = System.currentTimeMillis();
                adamRdmService.setMemberCodeByBuyOrderNo(initMemberCodeVo.getBuyOrderNo(), initMemberCodeVo.getCode());
                adamRdmService.setMemberCodeVoByCode(initMemberCodeVo.getCode(), initMemberCodeVo);
                log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

                toMqSqls.add(SqlMapping.get("adam_member_code.add"));
                operationObjs.add(new Object[]{
                        initMemberCodeVo.getCode(), initMemberCodeVo.getType(), initMemberCodeVo.getMemberId(), initMemberCodeVo.getMemberPriceId(),
                        initMemberCodeVo.getMemberNo(), initMemberCodeVo.getState(), initMemberCodeVo.getCreatedAt(),
                        initMemberCodeVo.getBuyOrderNo(), initMemberCodeVo.getBuyUid(), initMemberCodeVo.getBuyAt()
                });
            }
            handleMemberOrderVo.setPricePaid(parameter.getPrice());
            handleMemberOrderVo.setPaymentId(parameter.getPaymentId());
            handleMemberOrderVo.setPaymentAt(DateUtil.Formatter.yyyyMMddHHmmss.parse(parameter.getPaymentAt()));
            handleMemberOrderVo.setUpdatedAt(now);
            handleMemberOrderVo.setState(1);// 1-已支付

            long s = System.currentTimeMillis();
//            adamRdmService.setShotMemberOrderVoByOrderNo(handleMemberOrderVo.getOrderNo(), handleMemberOrderVo);
            adamRdmService.addMemberOrderVoByUid(
                    handleMemberOrderVo.getUid(), adamRdmService.getMemberOrderVosByUid(handleMemberOrderVo.getUid()), handleMemberOrderVo);
            adamRdmService.delShotMemberOrderVoByOrderNo(handleMemberOrderVo.getOrderNo());
            log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

            toMqSqls.add(SqlMapping.get("adam_member_order.add"));
            initMemberOrderObjs.add(new Object[]{
                    handleMemberOrderVo.getOrderNo(), handleMemberOrderVo.getUid(), handleMemberOrderVo.getMode(), handleMemberOrderVo.getPrice(),
                    handleMemberOrderVo.getPricePaid(), handleMemberOrderVo.getMemberName(), handleMemberOrderVo.getMemberId(), handleMemberOrderVo.getMemberPriceId(),
                    handleMemberOrderVo.getDays(), handleMemberOrderVo.getState(), handleMemberOrderVo.getMemberNo(),
                    handleMemberOrderVo.getBirthday(),
                    handleMemberOrderVo.getArea(),
                    handleMemberOrderVo.getDeviceFrom(), handleMemberOrderVo.getPayType(),
                    handleMemberOrderVo.getPayNo(), handleMemberOrderVo.getPaymentId(), handleMemberOrderVo.getPaymentAt(),
                    handleMemberOrderVo.getCreatedAt(),
                    handleMemberOrderVo.getUpdatedAt(),
                    handleMemberOrderVo.getClientIp(), handleMemberOrderVo.getSource(), handleMemberOrderVo.getVersion()
            });

            AdamMemberOrderExtVo memberOrderExtVo = handleMemberOrderVo.getExtendVo();
//            if (null != memberOrderExtVo) {
                toMqSqls.add(SqlMapping.get("adam_member_order_ext.add"));
                initMemberOrderExtObjs.add(new Object[]{
                        memberOrderExtVo.getOrderNo(), memberOrderExtVo.getExpressStatus(), memberOrderExtVo.getExpressReceiver(),
                        memberOrderExtVo.getExpressPhone(), memberOrderExtVo.getExpressAddress()
                });

            AdamUserInfoVo userInfoVo = adamRdmService.getUserInfoVoByUid(handleMemberOrderVo.getUid());
            userInfoVo.setUpdatedAt(now);
            String[] areaArr = handleMemberOrderVo.getArea().split("-");
            if (!org.apache.commons.lang3.StringUtils.equals(userInfoVo.getProvince(), areaArr[0])
                    || !org.apache.commons.lang3.StringUtils.equals(userInfoVo.getCity(), areaArr[1])
                    || !org.apache.commons.lang3.StringUtils.equals(userInfoVo.getCounty(), areaArr[2])
            ) {
                userInfoVo.setProvince(areaArr[0]);
                userInfoVo.setCity(areaArr[1]);
                userInfoVo.setProvince(areaArr[2]);

                updateUserMobileLocateObjs.add(new Object[]{
                        userInfoVo.getProvince(), userInfoVo.getCity(), userInfoVo.getCounty(), userInfoVo.getUpdatedAt(), userInfoVo.getUid()
                });
            }
            // 生日、地区同步到用户资料
            toMqSqls.add(SqlMapping.get("adam_user_info.update_by_member"));
            LinkedList<Object[]> updateUserInfoObjs = this.syncUserInfoProcessing(now, userInfoVo, handleMemberOrderVo);
            toMqSqls.add(SqlMapping.get("adam_user_mobile_locate.update_province"));

            // 根据新会员开售时间发放新权益
            if (now.isAfter(AdamUserMemberVo.spotTime)) {
                // 权益券发放
                toMqSqls.add(SqlMapping.get("candy_mgt_coupon.add_for_member"));
                LinkedList<Object[]> initMemberRightsCouponObjs = this.issueMemberRightsCouponProcessing(
                        now, userInfoVo.getMobile(), handleMemberOrderVo.getMemberId(), handleMemberOrderVo.getMemberPriceId());

                queueUtils.sendMsgByRedis(MQConst.AdamQueue.SQL_UMEMBER.getKey(),
                        SqlMapping.gets(toMqSqls, operationObjs, initMemberOrderObjs, initMemberOrderExtObjs, updateUserInfoObjs, updateUserMobileLocateObjs, initMemberRightsCouponObjs)
                );
            } else {
                queueUtils.sendMsgByRedis(MQConst.AdamQueue.SQL_UMEMBER.getKey(),
                        SqlMapping.gets(toMqSqls, operationObjs, initMemberOrderObjs, initMemberOrderExtObjs, updateUserInfoObjs, updateUserMobileLocateObjs)
                );
            }
            queueUtils.sendMsgByRedis(MQConst.AdamQueue.SMS_NOTICE.getKey(), SmsMessage.builder().setPhone(userInfoVo.getMobile())
                    .setSignName(SmsEnum.ADSignName.M01.getVal()).setTemplateCode(SmsEnum.ADTemplate.SMS_232908714.name()).toJson()
            );

            try {
                HttpUtil.get(serviceKylinUrl + "/inner/cache/member/".concat(handleMemberOrderVo.getUid()), CollectionUtil.linkedMultiValueMapStringString());
            } catch (Exception e) {
                log.error("购买会员支付回调处理成功:同步Kylin会员缓存异常[UID={},URL={}]", handleMemberOrderVo.getUid(), serviceKylinUrl, e);
            }
            return ResponseDto.success();
        } catch (Exception e) {
            log.error("购买会员支付回调处理异常[CallbackParam={}]", JsonUtils.toJson(parameter), e);

            handlerResultFailed = true;

            return ResponseDto.failure(ErrorMapping.get("10505"));
        } finally {
            if (handlerResultFailed) {
                adamRdmService.rmvMemberOrderVoByUid(
                        shotMemberOrderVo.getUid(), adamRdmService.getMemberOrderVosByUid(shotMemberOrderVo.getUid()), shotMemberOrderVo.getOrderNo());
                adamRdmService.setShotMemberOrderVoByOrderNo(shotMemberOrderVo.getOrderNo(), shotMemberOrderVo);
                if (0 == shotMemberOrderVo.getMode()) {// 购买会员回调
                    if (null == existUserMemberVo) {// 新会员
                        adamRdmService.delUserMemberVoByUid(shotMemberOrderVo.getUid());
                    } else {// 已是会员
                        adamRdmService.setUserMemberVoByUid(shotMemberOrderVo.getUid(), existUserMemberVo);
                    }
                }
                if (1 == shotMemberOrderVo.getMode() && null != initMemberCodeVo) {// 购买会员码回调
                    adamRdmService.delMemberCodeByBuyOrderNo(initMemberCodeVo.getBuyOrderNo());
                    adamRdmService.delMemberCodeVoByCode(initMemberCodeVo.getCode());
                }
            } else {
                feignStoneIntegralClient.in2111(shotMemberOrderVo.getUid(), shotMemberOrderVo.getPricePaid().intValue(), "购买会员");
            }
        }
    }

    @Override
    public ResponseDto<AdamMemberOrderResult> exchangeMemberCode(String currentUid, AdamMemberOrderCodeParam param) {
        AdamAddressesVo addressesVo = adamRdmService.getAddressesVoByUidAddressesId(currentUid, param.getAddressId());
        if (null == addressesVo) {
            return ResponseDto.failure(ErrorMapping.get("10106"));
        }
        AdamMemberCodeVo memberCodeVo = adamRdmService.getMemberCodeVoByCode(param.getMemberCode());
        if (null == memberCodeVo) {
            return ResponseDto.failure(ErrorMapping.get("10207"));
        }

        List<AdamMemberOrderVo> memberOrderVos = null;
        AdamMemberOrderVo initMemberOrderVo = null;
        AdamUserMemberVo existUserMemberVo = null;
        AdamUserMemberVo initUserMemberVo = null;
        try {
            adamRdmService.delMemberCodeVoByCode(param.getMemberCode());
            if (memberCodeVo.getState() != 0) {
                return ResponseDto.failure(ErrorMapping.get(memberCodeVo.getState() == 1 ? "10208" : "10209"));
            }
            AdamMemberPriceVo memberPriceVo = adamRdmService.getMemberPriceVoByPriceId(memberCodeVo.getMemberId(), memberCodeVo.getMemberPriceId());
            if (null == memberPriceVo) {
                return ResponseDto.failure(ErrorMapping.get("10202"));
            }
            AdamMemberVo memberVo = adamRdmService.getMemberVoByMemberId(memberCodeVo.getMemberId());

            LocalDateTime now = LocalDateTime.now();
            // 创建会员订单
            initMemberOrderVo = AdamMemberOrderVo.getNew();
            initMemberOrderVo.setOrderNo(IDGenerator.nextSnowId().concat("V"));
            initMemberOrderVo.setUid(currentUid);
            initMemberOrderVo.setMode(param.getMode());
            initMemberOrderVo.setPrice(memberPriceVo.getPrice());
            initMemberOrderVo.setPricePaid(BigDecimal.ZERO);
            initMemberOrderVo.setMemberName(memberVo.getName());
            initMemberOrderVo.setMemberId(memberCodeVo.getMemberId());
            initMemberOrderVo.setMemberPriceId(memberCodeVo.getMemberPriceId());
            initMemberOrderVo.setDays(memberPriceVo.getDays());
            initMemberOrderVo.setState(1);// 0-待支付,1-已支付
            initMemberOrderVo.setMemberNo(memberCodeVo.getMemberNo());
            initMemberOrderVo.setBirthday(param.getBirthday());
            initMemberOrderVo.setArea(param.getProvince().concat("-").concat(param.getCity()).concat("-").concat(param.getCounty()));
            initMemberOrderVo.setDeviceFrom(param.getDeviceFrom());
            initMemberOrderVo.setPayType(param.getMode() == 2 ? "vipcode" : "giftcode");
            initMemberOrderVo.setPayNo(memberCodeVo.getCode());
            initMemberOrderVo.setPaymentAt(now);
            initMemberOrderVo.setCreatedAt(now);
            initMemberOrderVo.setClientIp(CurrentUtil.getCliIpAddr());
            String headerCliVersion = CurrentUtil.getHeaderCliVersion(), headerCliSource = CurrentUtil.getHeaderCliSource();
            initMemberOrderVo.setSource(null == headerCliSource ? "" : headerCliSource);
            initMemberOrderVo.setVersion(null == headerCliVersion ? "" : headerCliVersion);

            AdamMemberOrderExtVo memberOrderExtVo = AdamMemberOrderExtVo.getNew();
            memberOrderExtVo.setOrderNo(initMemberOrderVo.getOrderNo());
            memberOrderExtVo.setExpressStatus(-1);
            memberOrderExtVo.setExpressReceiver(addressesVo.getName());
            memberOrderExtVo.setExpressPhone(addressesVo.getPhone());
            memberOrderExtVo.setExpressAddress(addressesVo.getProvince() + "｜" + addressesVo.getCity() + "｜" + addressesVo.getCounty() + "｜" + addressesVo.getAddress());

            initMemberOrderVo.setExtendVo(memberOrderExtVo);

            LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
            LinkedList<Object[]> updateMemberCodeObjs = CollectionUtil.linkedListObjectArr(),
                    initMemberOrderObjs = CollectionUtil.linkedListObjectArr(),
                    upsertUserMemberObjs = CollectionUtil.linkedListObjectArr(),
                    initMemberOrderExtObjs = CollectionUtil.linkedListObjectArr(),
                    updateUserMobileLocateObjs = CollectionUtil.linkedListObjectArr();
            existUserMemberVo = adamRdmService.getUserMemberVoByUid(currentUid);
            if (null != existUserMemberVo) {// 已是会员
                AdamUserMemberVo updateExistUserMemberVo = AdamUserMemberVo.getNew();
                BeanUtils.copyProperties(existUserMemberVo, updateExistUserMemberVo);

                updateExistUserMemberVo.setState(1);
                LocalDateTime expiryAt = existUserMemberVo.getExpiryAt();
                updateExistUserMemberVo.setExpiryAt(
                        (expiryAt.isBefore(now) ? now : expiryAt).plusDays(initMemberOrderVo.getDays())
                                .withHour(23).withMinute(59).withSecond(59)
                );
                updateExistUserMemberVo.setUpdatedAt(now);
                toMqSqls.add(SqlMapping.get("adam_user_member.update"));
                upsertUserMemberObjs.add(new Object[]{
                        updateExistUserMemberVo.getState(), updateExistUserMemberVo.getExpiryAt(), now, currentUid
                });
                adamRdmService.setUserMemberVoByUid(currentUid, updateExistUserMemberVo);
            } else {// 新会员
                initUserMemberVo = AdamUserMemberVo.getNew();
                initUserMemberVo.setUid(currentUid);
                initUserMemberVo.setMemberId(memberCodeVo.getMemberId());
                initUserMemberVo.setMemberNo(memberCodeVo.getMemberNo());
                initUserMemberVo.setState(1);
                initUserMemberVo.setExpiryAt(now.plusDays(initMemberOrderVo.getDays()).withHour(23).withMinute(59).withSecond(59));
                initUserMemberVo.setCreatedAt(now);

                toMqSqls.add(SqlMapping.get("adam_user_member.add"));
                upsertUserMemberObjs.add(new Object[]{
                        currentUid, initUserMemberVo.getMemberId(), initUserMemberVo.getMemberNo(),
                        initUserMemberVo.getState(), initUserMemberVo.getExpiryAt(), initUserMemberVo.getCreatedAt()
                });
                adamRdmService.setUserMemberVoByUid(currentUid, initUserMemberVo);
            }
            long s = System.currentTimeMillis();
            memberOrderVos = adamRdmService.getMemberOrderVosByUid(currentUid);
            adamRdmService.addMemberOrderVoByUid(currentUid, memberOrderVos, initMemberOrderVo);
//            adamRdmService.delMemberCodeVoByCode(memberCodeVo.getCode());
            log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

            toMqSqls.add(SqlMapping.get("adam_member_code.exchange"));
            updateMemberCodeObjs.add(new Object[]{
                    1, now, initMemberOrderVo.getOrderNo(), currentUid, now, memberCodeVo.getCode()
            });

            toMqSqls.add(SqlMapping.get("adam_member_order.add"));
            initMemberOrderObjs.add(new Object[]{
                    initMemberOrderVo.getOrderNo(), currentUid, initMemberOrderVo.getMode(), initMemberOrderVo.getPrice(),
                    initMemberOrderVo.getPricePaid(), initMemberOrderVo.getMemberName(), initMemberOrderVo.getMemberId(), initMemberOrderVo.getMemberPriceId(),
                    initMemberOrderVo.getDays(), initMemberOrderVo.getState(), initMemberOrderVo.getMemberNo(),
                    initMemberOrderVo.getBirthday(), initMemberOrderVo.getArea(),
                    initMemberOrderVo.getDeviceFrom(), initMemberOrderVo.getPayType(),
                    initMemberOrderVo.getPayNo(), initMemberOrderVo.getPaymentId(), initMemberOrderVo.getPaymentAt(),
                    initMemberOrderVo.getCreatedAt(),
                    initMemberOrderVo.getUpdatedAt(),
                    initMemberOrderVo.getClientIp(), initMemberOrderVo.getSource(), initMemberOrderVo.getVersion()
            });

            toMqSqls.add(SqlMapping.get("adam_member_order_ext.add"));
            initMemberOrderExtObjs.add(new Object[]{
                    memberOrderExtVo.getOrderNo(), memberOrderExtVo.getExpressStatus(), memberOrderExtVo.getExpressReceiver(),
                    memberOrderExtVo.getExpressPhone(), memberOrderExtVo.getExpressAddress()
            });

            AdamUserInfoVo userInfoVo = adamRdmService.getUserInfoVoByUid(currentUid);
            userInfoVo.setUpdatedAt(now);
            String[] areaArr = initMemberOrderVo.getArea().split("-");
            if (!org.apache.commons.lang3.StringUtils.equals(userInfoVo.getProvince(), areaArr[0])
                    || !org.apache.commons.lang3.StringUtils.equals(userInfoVo.getCity(), areaArr[1])
                    || !org.apache.commons.lang3.StringUtils.equals(userInfoVo.getCounty(), areaArr[2])
            ) {
                userInfoVo.setProvince(areaArr[0]);
                userInfoVo.setCity(areaArr[1]);
                userInfoVo.setProvince(areaArr[2]);

                updateUserMobileLocateObjs.add(new Object[]{
                        userInfoVo.getProvince(), userInfoVo.getCity(), userInfoVo.getCounty(), userInfoVo.getUpdatedAt(), userInfoVo.getUid()
                });
            }
            // 生日、地区同步到用户资料
            toMqSqls.add(SqlMapping.get("adam_user_info.update_by_member"));
            LinkedList<Object[]> updateUserInfoObjs = this.syncUserInfoProcessing(now, userInfoVo, initMemberOrderVo);
            toMqSqls.add(SqlMapping.get("adam_user_mobile_locate.update_province"));

            // 权益券发放
            toMqSqls.add(SqlMapping.get("candy_mgt_coupon.add_for_member"));
            LinkedList<Object[]> initMemberRightsCouponObjs = this.issueMemberRightsCouponProcessing(
                    now, userInfoVo.getMobile(), initMemberOrderVo.getMemberId(), initMemberOrderVo.getMemberPriceId());

            s = System.currentTimeMillis();
            queueUtils.sendMsgByRedis(MQConst.AdamQueue.SQL_UMEMBER.getKey(),
                    SqlMapping.gets(toMqSqls, upsertUserMemberObjs, updateMemberCodeObjs, initMemberOrderObjs, initMemberOrderExtObjs, updateUserInfoObjs, updateUserMobileLocateObjs, initMemberRightsCouponObjs)
            );
            queueUtils.sendMsgByRedis(MQConst.AdamQueue.SMS_NOTICE.getKey(), SmsMessage.builder().setPhone(userInfoVo.getMobile())
                    .setSignName(SmsEnum.ADSignName.M01.getVal()).setTemplateCode(SmsEnum.ADTemplate.SMS_232908714.name()).toJson()
            );
            log.debug("#MQ耗时:{}ms", System.currentTimeMillis() - s);

            try {
                HttpUtil.get(serviceKylinUrl + "/inner/cache/member/".concat(currentUid), CollectionUtil.linkedMultiValueMapStringString());
            } catch (Exception e) {
                log.error("兑换会员成功:同步Kylin会员缓存异常[UID={},URL={}]", currentUid, serviceKylinUrl, e);
            }
        } catch (Exception e) {
            log.error("会员兑换发生异常[memberCodeVo={}]", JsonUtils.toJson(memberCodeVo), e);

            adamRdmService.setMemberCodeVoByCode(memberCodeVo.getCode(), memberCodeVo);
            if (null != initMemberOrderVo) {
                if (null != existUserMemberVo) {// 已是会员
                    adamRdmService.setUserMemberVoByUid(currentUid, existUserMemberVo);
                } else {// 新会员
                    adamRdmService.delUserMemberVoByUid(currentUid);
                }
                adamRdmService.rmvMemberOrderVoByUid(currentUid, memberOrderVos, initMemberOrderVo.getOrderNo());
            }
            return ResponseDto.failure(ErrorMapping.get("10214"));
        }

        AdamMemberOrderResult result = AdamMemberOrderResult.getNew();
        result.setOrderNo(initMemberOrderVo.getOrderNo());
        return ResponseDto.success(result);
    }

    @Override
    public PagedResult<AdamMemberOrderSimpleVo> queryPage(String uid, int pageNo, int pageSize) {
        // TODO: 2021/7/29 会员订单列表查取
        PagedResult<AdamMemberOrderSimpleVo> pagedResult = ObjectUtil.getAdamMemberOrderSimpleVoPagedResult();
        ArrayList<AdamMemberOrderVo> orderVos = adamRdmService.getMemberOrderVosByUid(uid);
        if (!CollectionUtils.isEmpty(orderVos)) {
            ArrayList<AdamMemberOrderSimpleVo> vos = ObjectUtil.getAdamMemberOrderSimpleVoArrayList();

            orderVos.forEach(r -> vos.add(AdamMemberOrderSimpleVo.getNew().copy(r)));

            vos.sort(Comparator.comparing(AdamMemberOrderSimpleVo::getCreatedAt).reversed());

            pagedResult.setTotal(orderVos.size(), 40);
            pagedResult.setList(vos);
        }

        return pagedResult;
    }

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

    /**
     * 同步用户资料（生日、地区-常驻地）
     *
     * @param now           时间
     * @param userInfoVo    用户信息
     * @param memberOrderVo 订单信息
     * @return LinkedList<Object[]>
     */
    private LinkedList<Object[]> syncUserInfoProcessing(LocalDateTime now, AdamUserInfoVo userInfoVo, AdamMemberOrderVo memberOrderVo) {
        userInfoVo.setBirthday(memberOrderVo.getBirthday());
        userInfoVo.setArea(memberOrderVo.getArea());
        adamRdmService.setUserInfoVoByUid(userInfoVo.getUid(), userInfoVo);

        LinkedList<Object[]> updateUserInfoObjs = CollectionUtil.linkedListObjectArr();
        updateUserInfoObjs.add(new Object[]{userInfoVo.getBirthday(), userInfoVo.getArea(), userInfoVo.getUid()});
        return updateUserInfoObjs;
    }

    /**
     * 发放会员专属特权券（仅发放绑定当前价格包的权益）
     */
    private LinkedList<Object[]> issueMemberRightsCouponProcessing(LocalDateTime now, String mobile, String memberId, String memberPriceId) {
        LinkedList<Object[]> initMemberRightsCouponObjs = CollectionUtil.linkedListObjectArr();

        List<AdamMemberRightsVo> memberRightsVoList = adamRdmService.getMemberRightsVoByMemberId(memberId, 0);
        memberRightsVoList = filterRightsByMemberPriceId(memberRightsVoList, memberPriceId);
        if (!CollectionUtils.isEmpty(memberRightsVoList)) {
            memberRightsVoList.forEach(r -> {
                List<AdamMemberRightsCouponVo> memberRightsCouponVoList = adamRdmService.getMemberRightsCouponVoByRightsId(r.getMrightsId());

                if (!CollectionUtils.isEmpty(memberRightsCouponVoList)) {
                    Map<String, Long> rightsCouponCtMap = memberRightsCouponVoList.stream()
                            .collect(Collectors.groupingBy(AdamMemberRightsCouponVo::getCouponId, Collectors.counting()));

                    rightsCouponCtMap.forEach((couponId, num) -> {
                        if (num <= 0) {
                            return;
                        }
                        initMemberRightsCouponObjs.add(
                                new Object[]{r.getMrightsId(), couponId, num, mobile, now, mobile, now}
                        );
                    });
                }
            });
        }

        return initMemberRightsCouponObjs;
    }

    /**
     * 只使用绑定当前价格包的权益（需先执行洗数把历史 NULL 绑到年档）
     */
    private List<AdamMemberRightsVo> filterRightsByMemberPriceId(List<AdamMemberRightsVo> rightsVoList, String memberPriceId) {
        if (CollectionUtils.isEmpty(rightsVoList) || StringUtils.isEmpty(memberPriceId)) {
            return Collections.emptyList();
        }
        return rightsVoList.stream()
                .filter(r -> memberPriceId.equals(r.getMemberPriceId()))
                .collect(Collectors.toList());
    }
}
