package com.liquidnet.service.adam.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.JsonNode;
import com.liquidnet.common.cache.redis.util.RedisDataSourceUtil;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.IdentityUtils;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.SensitizeUtil;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto;
import com.liquidnet.service.adam.dto.AdamCaomeiPerformanceIdTitleDto;
import com.liquidnet.service.adam.dto.AdamUserInfoDto;
import com.liquidnet.service.adam.dto.vo.*;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
import com.liquidnet.service.adam.entity.AdamEnters;
import com.liquidnet.service.adam.entity.AdamRealName;
import com.liquidnet.service.adam.entity.AdamUserMember;
import com.liquidnet.service.kylin.constant.KylinRedisConst;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinPerformanceVo;
import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeMapper;
import com.liquidnet.service.adam.mapper.AdamCaomeiPassportMapper;
import com.liquidnet.service.adam.mapper.AdamEntersMapper;
import com.liquidnet.service.adam.mapper.AdamRealNameMapper;
import com.liquidnet.service.adam.mapper.AdamUserMapper;
import com.liquidnet.service.adam.mapper.AdamUserMemberMapper;
import com.liquidnet.service.adam.util.ObjectUtil;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.constant.RedisKeyExpireConst;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AdamRdmService {
//    @Autowired
//    MongoTemplate mongoTemplate;
    @Autowired
    RedisUtil redisUtil;

    @Autowired
    RedisDataSourceUtil redisDataSourceUtil;

    @Autowired
    AdamUserMapper adamUserMapper;
    @Autowired
    AdamUserMemberMapper adamUserMemberMapper;
    @Autowired
    AdamRealNameMapper adamRealNameMapper;
    @Autowired
    AdamEntersMapper adamEntersMapper;
    @Autowired
    AdamCaomeiBadgeMapper adamCaomeiBadgeMapper;
    @Autowired
    AdamCaomeiPassportMapper adamCaomeiPassportMapper;

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Switch config */

    public Integer getSwitch(String rk) {
        Integer i = (Integer) redisUtil.get(rk);
        return null == i ? 1 : i;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Loader meta-fil */



    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | library of dict for locate by mobile */

    /**
     * 根据手机前缀（前7位）设置地理区域信息
     *
     * @param mobilePrefix 手机号（11位）前7位
     * @param locateInfo   地理区域信息.eg:"{province}|{city}|{corp}|{areaCode}|{postCode}"
     */
    public void setMobileLocateStr(String mobilePrefix, String locateInfo) {
        redisUtil.set(AdamRedisConst.LIB_DICT_LOCATE_MOBILE.concat(mobilePrefix), locateInfo);
    }

    /**
     * 根据手机号获取地理区域信息
     *
     * @param mobile 手机号（11位）
     * @return String[]{ province,city,corp,areaCode,postCode }
     */
    public String[] getMobileLocateArr(String mobile) {
        String locateInfo = (String) redisUtil.get(AdamRedisConst.LIB_DICT_LOCATE_MOBILE.concat(mobile.substring(0, 7)));
        return StringUtils.isEmpty(locateInfo) ? null : locateInfo.split("\\|");
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <Mobile, SMS CODE> */

    public boolean setSmsCodeByMobile(String mobile, String smsCode) {
        // TODO: 2021/7/29 改为1分钟
        return redisUtil.set(AdamRedisConst.VALID_SMS_CODE_MOBILE + mobile, smsCode, 60);
    }

    public String getSmsCodeByMobile(String mobile) {
        return (String) redisUtil.get(AdamRedisConst.VALID_SMS_CODE_MOBILE + mobile);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <Mobile｜OPENID, UID> */

    public boolean setUidByMobile(String mobile, String uid) {
        return redisUtil.set(AdamRedisConst.IDENTITY_MOBILE.concat(mobile), uid, RedisKeyExpireConst.ADAM_IDENTITY_MOBILE_EXPIRE);
    }

    public String getUidByMobile(String mobile) {
        String key = AdamRedisConst.IDENTITY_MOBILE.concat(mobile);
        long s = System.currentTimeMillis();
        String val = (String) redisUtil.get(key);
        if (StringUtils.isEmpty(val)) {
            // 从MySQL查询
            log.info("redis cache value is empty, key: {}", key);
            List<AdamUserInfoDto> adamUserInfoDtos = adamUserMapper.selectByPhones(mobile);
            if (!CollectionUtils.isEmpty(adamUserInfoDtos)) {
                log.info("query MySQL success");
                val = adamUserInfoDtos.get(0).getUid();
                boolean setResult = setUidByMobile(mobile, val);
                log.info("save value to redis cache result: {}", setResult);
            }
        }
//        if (StringUtils.isEmpty(val)) {
//            Query query = Query.query(Criteria.where("mobile").is(mobile).and("state").is(1));
//            query.fields().include("uid");
//            s = System.currentTimeMillis();
//            AdamUserInfoVo vo = mongoTemplate.findOne(query, AdamUserInfoVo.class, AdamUserInfoVo.class.getSimpleName());
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//            if (null != vo) {
//                val = vo.getUid();
//                redisUtil.set(key, vo.getUid());
//            }
//        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return val;
    }

    public void delUidByMobile(String mobile) {
        redisUtil.del(AdamRedisConst.IDENTITY_MOBILE.concat(mobile));
    }

    public boolean setUidByPlatformOpenId(String platform, String openId, String uid) {
        return redisUtil.set(AdamRedisConst.IDENTITY_THIRD_PARTY + platform + ":" + openId, uid);
    }

    public String getUidByPlatformOpenId(String platform, String openId) {
        String key = AdamRedisConst.IDENTITY_THIRD_PARTY + platform + ":" + openId;
        long s = System.currentTimeMillis();
        String val = (String) redisUtil.get(key);
//        if (StringUtils.isEmpty(val)) {
//            Query query = Query.query(Criteria.where("openId").is(openId).and("platform").is(platform).and("state").is(1));
//            query.fields().include("uid");
//            s = System.currentTimeMillis();
//            AdamThirdPartInfoVo vo = mongoTemplate.findOne(query, AdamThirdPartInfoVo.class, AdamThirdPartInfoVo.class.getSimpleName());
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//            if (null != vo) {
//                val = vo.getUid();
//                redisUtil.set(key, vo.getUid());
//            }
//        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return val;
    }

    public void delUidByPlatformOpenId(String platform, String openId) {
        redisUtil.del(AdamRedisConst.IDENTITY_THIRD_PARTY + platform + ":" + openId);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <UID, USER_INFO> */

    public boolean setUserInfoVoByUid(String uid, AdamUserInfoVo vo) {
        // 缓存30天
        return redisUtil.set(AdamRedisConst.INFO_USER.concat(uid), vo, RedisKeyExpireConst.ADAM_INFO_USER_EXPIRE);
    }

    public AdamUserInfoVo getUserInfoVoByUid(String uid) {
        String rk = AdamRedisConst.INFO_USER.concat(uid);
        long s = System.currentTimeMillis();
        AdamUserInfoVo vo = (AdamUserInfoVo) redisUtil.get(rk);
        // 如果不存在则从数据库查询
        if (null == vo) {
            log.info("not find value in redis cache, key: {}", rk);
            AdamUserInfoDto adamUserInfoDto = adamUserMapper.selectByUid(uid);
            if (null != adamUserInfoDto) {
                vo = AdamUserInfoVo.getNew();
                vo.copy(adamUserInfoDto);

                AdamUserMember adamUserMember = adamUserMemberMapper.selectByUid(uid);
                if (null == adamUserMember) {
                    vo.rating(null);
                } else {
                    AdamUserMemberVo adamUserMemberVo = new AdamUserMemberVo();
                    adamUserMemberVo.copy(adamUserMember);
                    vo.rating(adamUserMemberVo);
                }
            }

            if (null != vo) {
                boolean setResult = setUserInfoVoByUid(uid, vo);
                log.info("save value to redis cache, key: {}, result: {}", rk, setResult);
            }
        }
//        if (null == vo) {
//            s = System.currentTimeMillis();
//            vo = mongoTemplate.findOne(Query.query(Criteria.where("uid").is(uid).and("state").is(1)),
//                    AdamUserInfoVo.class, AdamUserInfoVo.class.getSimpleName());
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//
//            if (null != vo) redisUtil.set(rk, vo);
//        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vo;
    }

    public void delUserInfoVoByUid(String uid) {
        redisUtil.del(AdamRedisConst.INFO_USER.concat(uid));
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <UID, BIZ_ACCT_INFO> */

//    public List<AdamUserBizAcctVo> getBizAcctVosByUid(String uid) {
//        return (List<AdamUserBizAcctVo>) redisUtil.get(AdamRedisConst.INFO_BIZ.concat(uid));
//    }
//
//    public boolean addBizAcctVoByUid(String uid, List<AdamUserBizAcctVo> befVos, AdamUserBizAcctVo vo) {
//        if (null == befVos) {
//            befVos = ObjectUtil.getAdamUserBizAcctVoArrayList();
//        }
//        befVos.add(vo);
//        return redisUtil.set(AdamRedisConst.INFO_BIZ.concat(uid), befVos);
//    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <UID, REAL_INFO> */

    public boolean setRealInfoVoByUid(String uid, AdamRealInfoVo vo) {
        return redisUtil.set(AdamRedisConst.INFO_REAL_NAME.concat(uid), vo);
    }

    public AdamRealInfoVo getRealInfoVoByUid(String uid) {
        String rk = AdamRedisConst.INFO_REAL_NAME.concat(uid);
        long s = System.currentTimeMillis();
        AdamRealInfoVo vo = (AdamRealInfoVo) redisUtil.get(rk);
//        if (null == vo) {
//            s = System.currentTimeMillis();
//            vo = mongoTemplate.findOne(Query.query(Criteria.where("uid").is(uid).and("state").is(1)),
//                    AdamRealInfoVo.class, AdamRealInfoVo.class.getSimpleName());
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//
//            if (null != vo) redisUtil.set(rk, vo);
//        }
        if (null != vo) {
            vo.setName(SensitizeUtil.chineseName(vo.getName()));
            vo.setIdCard(SensitizeUtil.custom(vo.getIdCard(), 3, 2));
        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vo;
    }

    public AdamRealInfoVo getRealInfoVoByUidPlain(String uid) {
        return (AdamRealInfoVo) redisUtil.get(AdamRedisConst.INFO_REAL_NAME.concat(uid));
    }

    public void delRealInfoVoByUid(String uid) {
        redisUtil.del(AdamRedisConst.INFO_REAL_NAME.concat(uid));
    }

    /**
     * 根据身份证号查询已实名绑定的 uid（优先 Redis，未命中回源 MySQL 并回写 Redis）
     * 返回空表示未绑定或不可用。
     */
    public String getRealNameBoundUidByIdCard(String idCard) {
        if (StringUtils.isEmpty(idCard)) {
            return "";
        }
        String key = AdamRedisConst.INFO_REAL_NAME_UID_BY_IDCARD.concat(idCard);
        try {
            String uid = (String) redisUtil.get(key);
            if (!StringUtils.isEmpty(uid)) {
                return uid;
            }
        } catch (Exception e) {
            log.warn("[getRealNameBoundUidByIdCard] redis get failed, key: {}", key, e);
        }
        // 回源 MySQL：取任意一个 state=1 的 uid（同证件号按规则应唯一）
        try {
            AdamRealName r = adamRealNameMapper.selectOne(
                    Wrappers.lambdaQuery(AdamRealName.class)
                            .eq(AdamRealName::getState, 1)
                            .eq(AdamRealName::getType, 1)
                            .eq(AdamRealName::getIdCard, idCard)
                            .orderByDesc(AdamRealName::getMid)
                            .last("limit 1")
            );
            String uid = r != null ? org.springframework.util.StringUtils.trimWhitespace(r.getUid()) : "";
            if (!StringUtils.isEmpty(uid)) {
                try {
                    // 30天过期：防止长期脏数据无法自愈；真实解绑/注销可再补 delete
                    redisUtil.set(key, uid, 60L * 60 * 24 * 30);
                } catch (Exception e) {
                    log.warn("[getRealNameBoundUidByIdCard] redis set failed, key: {}", key, e);
                }
            }
            return uid;
        } catch (Exception e) {
            log.error("[getRealNameBoundUidByIdCard] mysql fallback failed, idCard: {}", idCard, e);
            // 降级：查询失败直接放行（由上层决定是否继续阻断）
            return "";
        }
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 认证失败的<ID_TYPE+ID_NO, ID_NAME> */

    public boolean setCertificationJunk(int idType, String idNo, String idName) {
        return redisUtil.set(AdamRedisConst.INFO_CERTIFICATION_JUNK + idType + idNo, idName, 604800);
    }

    public boolean isCertificationJunk(int idType, String idNo, String idName) {
        String o = (String) redisUtil.get(AdamRedisConst.INFO_CERTIFICATION_JUNK + idType + idNo);
        return !StringUtils.isEmpty(o) && o.equals(idName);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 认证成功的<ID_TYPE+ID_NO, ID_NAME> */

    /**
     * 目前只针对身份证类型三方服务认证成功的标记
     *
     * @param idType
     * @param idNo
     * @param idName
     * @return
     */
    public boolean setCertification(int idType, String idNo, String idName) {
        return redisUtil.set(AdamRedisConst.INFO_CERTIFICATION + idType + idNo, idName);
    }

    /**
     * 目前只针对身份证类型三方服务认证成功的标记
     *
     * @param idType
     * @param idNo
     * @param idName
     * @return
     */
    public int isCertification(int idType, String idNo, String idName) {
        String o = (String) redisUtil.get(AdamRedisConst.INFO_CERTIFICATION + idType + idNo);
        if (StringUtils.isEmpty(o)) {
            return -1;
        }
        return o.equals(idName) ? 1 : 0;
//        return !StringUtils.isEmpty(o) && o.equals(idName);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 认证处理 */

    /**
     * 身份证实名处理
     *
     * @param uid
     * @param name
     * @param idCard
     */
    public void identityHandler1(String uid, String name, String idCard) {
        int rst = this.isCertification(1, idCard, name);
        //增加判断逻辑 用户添加的时候先去调自己的库,如果有身份证数据但和名字不匹配的时候,再请求第三方接口去验
        if (rst == 0) {
            //本地存在，验证不通过 删除标记
            redisUtil.del(AdamRedisConst.INFO_CERTIFICATION + 1 + idCard);
            rst = -1;
        }
        switch (rst) {
            case -1:// 本地不存在
                if (this.isCertificationJunk(1, idCard, name)) {
                    ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10102");
                    throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
                }

                String respStr = IdentityUtils.aliThird(name, idCard), respErrorCode = null;
                JsonNode respJNode = JsonUtils.fromJson(respStr, JsonNode.class);
                if (null == respJNode || !"0".equals(respErrorCode = String.valueOf(respJNode.get("error_code")))) {
                    log.info("###实名认证失败[{}]", respStr);

//                    this.setCertificationJunk(1, idCard, name);
                    if (!StringUtils.isEmpty(respErrorCode) && org.apache.commons.lang3.StringUtils.indexOf("3000290033", respErrorCode) < 0) {
                        // 认证服务商'30002'、'90033'为运营商导致的失败，这里不做缓存标记
                        this.setCertificationJunk(1, idCard, name);
                    }

                    ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10102");
                    throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
                }

                this.setCertification(1, idCard, name);
                break;
            case 0:// 本地存在，验证不通过
                ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10102");
                throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
            case 1:// 本地存在，验证通过
                break;
        }
    }

    /**
     * 身份证实名处理：三要素
     *
     * @param name
     * @param idCard
     * @param mobile
     */
    public void identityHandler3(String name, String idCard, String mobile) {
        switch (this.isCertmeta(1, idCard, name, mobile)) {
            case -1:// 本地不存在
                if (this.isCertmetaJunk(1, idCard, name, mobile)) {
                    ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10112");
                    throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
                }
                String respStr = IdentityUtils.ali3Meta(name, idCard, mobile);
                JsonNode respJNode = JsonUtils.fromJson(respStr, JsonNode.class);
                if (null == respJNode || !"200".equals(respJNode.get("code").asText())
                        || !respJNode.get("data").get("bizCode").asText().equals("1")) {
                    log.warn("###实名认证失败[{}]", respStr);
                    this.setCertmetaJunk(1, idCard, name, mobile);
                    ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10112");
                    throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
                }
                this.setCertmeta(1, idCard, name, mobile);
                break;
            case 0:// 本地存在，验证不通过
                ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10112");
                throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
            case 1:// 本地存在，验证通过
                break;
        }
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 三要素认证缓存的<ID_TYPE+ID_NO, "ID_NAME,MOBILE"> */

    public boolean setCertmetaJunk(int idType, String idNo, String idName, String mobile) {
        return redisUtil.set(AdamRedisConst.INFO_CERTMETA_JUNK + idType + idNo, idName.concat(",").concat(mobile), 604800);
    }

    public boolean isCertmetaJunk(int idType, String idNo, String idName, String mobile) {
        String val = (String) redisUtil.get(AdamRedisConst.INFO_CERTMETA_JUNK + idType + idNo);
        return !StringUtils.isEmpty(val) && val.equals(idName.concat(",").concat(mobile));
    }

    public boolean setCertmeta(int idType, String idNo, String idName, String mobile) {
        return redisUtil.set(AdamRedisConst.INFO_CERTMETA + idType + idNo, idName.concat(",").concat(mobile));
    }

    public int isCertmeta(int idType, String idNo, String idName, String mobile) {
        String o = (String) redisUtil.get(AdamRedisConst.INFO_CERTMETA + idType + idNo);
        if (StringUtils.isEmpty(o)) {
            return -1;
        }
        return o.equals(idName.concat(",").concat(mobile)) ? 1 : 0;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <UID, List<AdamThirdPartInfoVo>> */

    public boolean setThirdPartVoListByUid(String uid, List<AdamThirdPartInfoVo> voList) {
        return redisUtil.set(AdamRedisConst.INFO_THIRD_PARTY.concat(uid), voList);
    }

    public List<AdamThirdPartInfoVo> getThirdPartVoListByUid(String uid) {
        String rk = AdamRedisConst.INFO_THIRD_PARTY.concat(uid);
        long s = System.currentTimeMillis();
        List<AdamThirdPartInfoVo> thirdPartInfoVos = (List<AdamThirdPartInfoVo>) redisUtil.get(rk);
//        if (CollectionUtils.isEmpty(thirdPartInfoVos)) {
//            s = System.currentTimeMillis();
//            thirdPartInfoVos = mongoTemplate.find(Query.query(Criteria.where("uid").is(uid).and("state").is(1)),
//                    AdamThirdPartInfoVo.class, AdamThirdPartInfoVo.class.getSimpleName());
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//
//            if (!CollectionUtils.isEmpty(thirdPartInfoVos)) redisUtil.set(rk, thirdPartInfoVos);
//        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return thirdPartInfoVos;
    }

    public boolean addThirdPartVoListByUid(String uid, List<AdamThirdPartInfoVo> vos, AdamThirdPartInfoVo vo) {
        if (null == vos) {
            vos = ObjectUtil.getAdamThirdPartInfoVoArrayList();
        }
        vos.add(vo);
        return redisUtil.set(AdamRedisConst.INFO_THIRD_PARTY + uid, vos);
    }

    public boolean rmvThirdPartVoListByUid(String uid, List<AdamThirdPartInfoVo> vos, String platform) {
        if (CollectionUtils.isEmpty(vos)) {
            return true;
        }
        vos.removeIf(r -> r.getPlatform().equals(platform));
        return redisUtil.set(AdamRedisConst.INFO_THIRD_PARTY + uid, vos);
    }

    public AdamThirdPartInfoVo getThirdPartVoByUidPlatform(List<AdamThirdPartInfoVo> vos, String platform) {
        if (!CollectionUtils.isEmpty(vos)) {
            return vos.stream().filter(r -> r.getPlatform().equals(platform)).findAny().orElse(null);
        }
        return null;
    }

    public AdamThirdPartInfoVo getThirdPartVoByUidPlatform(String uid, String platform) {
        List<AdamThirdPartInfoVo> vos = this.getThirdPartVoListByUid(uid);
        if (!CollectionUtils.isEmpty(vos)) {
//            for (AdamThirdPartInfoVo r : vos)
//                if (platform.equals(r.getPlatform())) return r;
            return vos.stream().filter(r -> r.getPlatform().equals(platform)).findAny().orElse(null);
        }
        return null;
    }

    public void delThirdPartVoListByUid(String uid) {
        redisUtil.del(AdamRedisConst.INFO_THIRD_PARTY.concat(uid));
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <UID, List<AdamEntersVo>> */

    public boolean setEntersVoByUid(String uid, List<AdamEntersVo> list) {
        return redisUtil.set(AdamRedisConst.INFO_ENTERS.concat(uid), list, RedisKeyExpireConst.ADAM_INFO_ENTERS_EXPIRE);
    }

    public List<AdamEntersVo> getEntersVoByUid(String uid) {
        String rk = AdamRedisConst.INFO_ENTERS.concat(uid);
        long s = System.currentTimeMillis();
        ArrayList<AdamEntersVo> vos = (ArrayList<AdamEntersVo>) redisUtil.get(rk);
        if (CollectionUtils.isEmpty(vos)) {
            log.info("not find value in redis cache, key: {}", rk);
            // 从数据库查询入场人信息VO
            LambdaQueryWrapper<AdamEnters> lambda = new QueryWrapper<AdamEnters>().lambda();
            lambda.eq(AdamEnters::getUid, uid);
            lambda.eq(AdamEnters::getState, 1);
            lambda.orderByDesc(AdamEnters::getCreatedAt);
            List<AdamEnters> adamEnters = adamEntersMapper.selectList(lambda);
            if (!CollectionUtils.isEmpty(adamEnters)) {
                log.info("query value in MySQL, size: {}", adamEnters.size());
                ArrayList<AdamEntersVo> voList = new ArrayList<>(adamEnters.size());
                adamEnters.forEach(e -> {
                    voList.add(AdamEntersVo.getNew().copy(e));
                });
                vos = voList;
                boolean setResult = setEntersVoByUid(uid, vos);
                log.info("save value to redis cache, key: {}, result: {}", rk, setResult);
            }
        }
//        if (CollectionUtils.isEmpty(vos)) {
//            s = System.currentTimeMillis();
//            vos = mongoTemplate.find(
//                    Query.query(Criteria.where("uid").is(uid).and("state").is(1)).with(Sort.by(Sort.Direction.DESC, "createdAt")),
//                    AdamEntersVo.class, AdamEntersVo.class.getSimpleName()
//            );
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//
//            if (!CollectionUtils.isEmpty(vos)) redisUtil.set(rk, vos);
//        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vos;
    }

    public boolean addEntersVoByUid(String uid, List<AdamEntersVo> vos, AdamEntersVo vo) {
        if (null == vos) {
            vos = ObjectUtil.getAdamEntersVoArrayList();
        }
        vos.add(0, vo);
        return redisUtil.set(AdamRedisConst.INFO_ENTERS + uid, vos, RedisKeyExpireConst.ADAM_INFO_ENTERS_EXPIRE);
    }

    public boolean rmvEntersVoByUid(String uid, List<AdamEntersVo> vos, String entersId) {
        if (CollectionUtils.isEmpty(vos)) {
            return true;
        }
        vos.removeIf(r -> r.getEntersId().equals(entersId));
        return redisUtil.set(AdamRedisConst.INFO_ENTERS + uid, vos, RedisKeyExpireConst.ADAM_INFO_ENTERS_EXPIRE);
    }

    public AdamEntersVo getEntersVoByUidEntersId(List<AdamEntersVo> vos, String entersId) {
        return CollectionUtils.isEmpty(vos) ? null : vos.stream().filter(r -> r.getEntersId().equals(entersId)).findAny().orElse(null);
    }

    public AdamEntersVo getEntersVoByUidEntersId(String uid, String entersId) {
        List<AdamEntersVo> vos = this.getEntersVoByUid(uid);
        return CollectionUtils.isEmpty(vos) ? null : vos.stream().filter(r -> r.getEntersId().equals(entersId)).findAny().orElse(null);
    }

    public List<AdamEntersVo> getEntersVoByUidEntersIds(String uid, List<String> entersIds) {
//        List<String> entersIdList = Arrays.asList(entersIds);
        List<AdamEntersVo> vos = this.getEntersVoByUid(uid);
        return CollectionUtils.isEmpty(vos) ? null : vos.stream().filter(r -> entersIds.contains(r.getEntersId())).collect(Collectors.toList());
    }

    public void delEntersVoByUid(String uid) {
        redisUtil.del(AdamRedisConst.INFO_ENTERS.concat(uid));
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <UID, List<AdamAddressesVo>> */

    public boolean setAddressesVoByUid(String uid, List<AdamAddressesVo> list) {
        return redisUtil.set(AdamRedisConst.INFO_ADDRESSES.concat(uid), list);
    }

    public List<AdamAddressesVo> getAddressesVoByUid(String uid) {
        String rk = AdamRedisConst.INFO_ADDRESSES.concat(uid);
        long s = System.currentTimeMillis();
        List<AdamAddressesVo> vos = (List<AdamAddressesVo>) redisUtil.get(rk);
//        if (CollectionUtils.isEmpty(vos)) {
//            s = System.currentTimeMillis();
//            vos = mongoTemplate.find(
//                    Query.query(Criteria.where("uid").is(uid).and("state").is(1)).with(Sort.by(Sort.Direction.DESC, "createdAt")),
//                    AdamAddressesVo.class, AdamAddressesVo.class.getSimpleName()
//            );
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//
//            if (!CollectionUtils.isEmpty(vos)) redisUtil.set(rk, vos);
//        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);

        if (!CollectionUtils.isEmpty(vos)) {// TODO: 2021/12/1 兼容处理老版本，识别行政码并记录（历史数据处理完，删除该逻辑）
            for (int i = 0; i < vos.size(); i++) {
                AdamAddressesVo r = vos.get(i);
                if (StringUtils.isEmpty(r.getCountyId())) {
                    String adcodeStr = (String) redisUtil.get(AdamRedisConst.LIB_DICT_ADCODE_REGION.concat(r.getProvince()).concat(r.getCity()).concat(r.getCounty()));
                    if (!StringUtils.isEmpty(adcodeStr)) {
                        String[] adcodeArr = adcodeStr.split("-");
                        switch (adcodeArr.length) {
                            case 3:
                                r.setProvinceId(adcodeArr[2]);
                            case 2:
                                r.setCityId(adcodeArr[1]);
                            case 1:
                                r.setCountyId(adcodeArr[0]);
                        }
                    } else {
                        vos.removeIf(rm -> rm.getAddressesId().equals(r.getAddressesId()));
                    }
                }
            }
        }
        return vos;
    }

    public boolean addAddressesVoByUid(String uid, List<AdamAddressesVo> vos, AdamAddressesVo vo) {
        if (null == vos) {
            vos = ObjectUtil.getAdamAddressesVoArrayList();
        }
        if (org.apache.commons.lang3.StringUtils.isBlank(vo.getCountyId())) {// 兼容处理老版本，识别行政码并记录
            String adcodeStr = (String) redisUtil.get(AdamRedisConst.LIB_DICT_ADCODE_REGION.concat(vo.getProvince()).concat(vo.getCity()).concat(vo.getCounty()));
            if (!StringUtils.isEmpty(adcodeStr)) {
                String[] adcodeArr = adcodeStr.split("-");
                switch (adcodeArr.length) {
                    case 3:
                        vo.setProvinceId(adcodeArr[2]);
                    case 2:
                        vo.setCityId(adcodeArr[1]);
                    case 1:
                        vo.setCountyId(adcodeArr[0]);
                }
            }
        }
        vos.add(vo);
        return redisUtil.set(AdamRedisConst.INFO_ADDRESSES + uid, vos);
    }

    public boolean rmvAddressesVoByUid(String uid, List<AdamAddressesVo> vos, String addressesId) {
        if (CollectionUtils.isEmpty(vos)) {
            return true;
        }
        vos.removeIf(r -> r.getAddressesId().equals(addressesId));
        return redisUtil.set(AdamRedisConst.INFO_ADDRESSES + uid, vos);
    }

    public AdamAddressesVo getAddressesVoByUidAddressesId(List<AdamAddressesVo> vos, String addressesId) {
        return CollectionUtils.isEmpty(vos) ? null : vos.stream().filter(r -> r.getAddressesId().equals(addressesId)).findAny().orElse(null);
    }

    public AdamAddressesVo getAddressesVoByUidAddressesId(String uid, String addressesId) {
        List<AdamAddressesVo> vos = this.getAddressesVoByUid(uid);
        return CollectionUtils.isEmpty(vos) ? null : vos.stream().filter(r -> r.getAddressesId().equals(addressesId)).findAny().orElse(null);
    }

    public void delAddressesVoByUid(String uid) {
        redisUtil.del(AdamRedisConst.INFO_ADDRESSES.concat(uid));
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 草莓徽章缓存 */

    public boolean setPublishedCaomeiBadges(List<AdamCaomeiBadge> badges) {
        return redisUtil.set(AdamRedisConst.INFO_CAOMEI_BADGE_PUBLISHED, badges, RedisKeyExpireConst.CAOMEI_BADGE_PUBLISHED_EXPIRE);
    }

    /**
     * 获取所有已上架的徽章列表
     * @return
     */
    public List<AdamCaomeiBadge> getPublishedCaomeiBadges() {
        String rk = AdamRedisConst.INFO_CAOMEI_BADGE_PUBLISHED;
        List<AdamCaomeiBadge> badges = (List<AdamCaomeiBadge>) redisUtil.get(rk);
        if (CollectionUtils.isEmpty(badges)) {
            badges = adamCaomeiBadgeMapper.selectList(
                    new QueryWrapper<AdamCaomeiBadge>().lambda()
                            .eq(AdamCaomeiBadge::getDisplayStatus, 1)
                            .orderByAsc(AdamCaomeiBadge::getType)
                            .orderByDesc(AdamCaomeiBadge::getSort)
                            .orderByDesc(AdamCaomeiBadge::getMid)
            );
            if (!CollectionUtils.isEmpty(badges)) {
                setPublishedCaomeiBadges(badges);
            }
        }
        return badges;
    }

    public void delPublishedCaomeiBadges() {
        redisUtil.del(AdamRedisConst.INFO_CAOMEI_BADGE_PUBLISHED);
    }

    /**
     * 设置用户领取徽章redis
     * @param uid
     * @param badges
     * @return
     */
    public boolean setUserCaomeiBadgesByUid(String uid, List<AdamCaomeiPassportUserBadgeDto> badges) {
        return redisUtil.set(AdamRedisConst.INFO_CAOMEI_BADGE_USER.concat(uid), badges, 300);
    }

    /**
     * 获取用户徽章
     * @param uid
     * @return
     */
    public List<AdamCaomeiPassportUserBadgeDto> getUserCaomeiBadgesByUid(String uid) {
        String rk = AdamRedisConst.INFO_CAOMEI_BADGE_USER.concat(uid);
        List<AdamCaomeiPassportUserBadgeDto> badges = (List<AdamCaomeiPassportUserBadgeDto>) redisUtil.get(rk);
        if (CollectionUtils.isEmpty(badges)) {
            badges = adamCaomeiPassportMapper.selectUserBadgesByUid(uid);
            if (!CollectionUtils.isEmpty(badges)) {
                setUserCaomeiBadgesByUid(uid, badges);
            }
        }
        return badges;
    }

    /**
     * 用户认领徽章时追加一条（与收货地址 addAddressesVoByUid 相同：写回整列表到 Redis）
     */
    public boolean addUserCaomeiBadgeDtoByUid(String uid,
                                              List<AdamCaomeiPassportUserBadgeDto> vos,
                                              AdamCaomeiPassportUserBadgeDto dto) {
        if (vos == null) {
            vos = new ArrayList<>();
        }
        vos.add(dto);
        return setUserCaomeiBadgesByUid(uid, vos);
    }

    /**
     * 用户认领徽章时批量追加（一次 set 回 Redis，避免循环多次写 Redis）
     */
    public boolean addUserCaomeiBadgeDtosByUid(String uid,
                                               List<AdamCaomeiPassportUserBadgeDto> vos,
                                               List<AdamCaomeiPassportUserBadgeDto> appendVos) {
        if (vos == null) {
            vos = new ArrayList<>();
        }
        if (CollectionUtils.isEmpty(appendVos)) {
            return setUserCaomeiBadgesByUid(uid, vos);
        }
        vos.addAll(appendVos);
        return setUserCaomeiBadgesByUid(uid, vos);
    }

    /**
     * 根据身份证获取已支付演出ID列表（60秒短缓存，缓解无索引热点查询）
     */
    public List<String> getPaidPerformanceIdsByIdCard(String idCard) {
        if (StringUtils.isEmpty(idCard)) {
            return Collections.emptyList();
        }
        String rk = AdamRedisConst.INFO_CAOMEI_PAID_PERFORMANCE_IDS_BY_IDCARD.concat(idCard);
        List<String> performanceIds = (List<String>) redisUtil.get(rk);
        if (CollectionUtils.isEmpty(performanceIds)) {
            performanceIds = adamCaomeiBadgeMapper.selectPaidPerformanceIdsByIdCard(idCard);
            if (performanceIds == null) {
                performanceIds = Collections.emptyList();
            }
            redisUtil.set(rk, performanceIds, 60);
        }
        return performanceIds;
    }

    /**
     * 从演出缓存（kylin redis）读取演出标题，用于草莓护照演出徽章分组展示。
     * 读取不到时返回 null。
     */
    public String getPerformanceTitleById(String performanceId) {
        if (StringUtils.isEmpty(performanceId)) {
            return null;
        }
        String id = performanceId.trim();
        Map<String, String> map = getPerformanceTitleMapByIds(Collections.singletonList(id));
        return map.get(id);
    }

    /**
     * 批量解析演出 ID → 标题：先走 Kylin Redis，缓存未命中的 ID 合并为一次 DB IN 查询。
     */
    public Map<String, String> getPerformanceTitleMapByIds(Collection<String> performanceIds) {
        if (CollectionUtils.isEmpty(performanceIds)) {
            return Collections.emptyMap();
        }
        List<String> ids = performanceIds.stream()
                .filter(id -> !StringUtils.isEmpty(id))
                .map(String::trim)
                .distinct()
                .collect(Collectors.toList());
        if (ids.isEmpty()) {
            return Collections.emptyMap();
        }

        Map<String, String> result = new HashMap<>(ids.size() * 2);
        List<String> missingFromCache = new ArrayList<>();
        for (String id : ids) {
            try {
                KylinPerformanceVo vo = getKylinPerformanceVoById(id);
                if (vo != null) {
                    String title = vo.getTitle();
                    if (!StringUtils.isEmpty(title)) {
                        result.put(id, title);
                    }
                } else {
                    missingFromCache.add(id);
                }
            } catch (Exception e) {
                log.warn("[getPerformanceTitleMapByIds] 读取演出缓存失败, performanceId: {}", id, e);
                missingFromCache.add(id);
            }
        }

        if (!missingFromCache.isEmpty()) {
            List<AdamCaomeiPerformanceIdTitleDto> rows =
                    adamCaomeiBadgeMapper.selectKylinPerformanceTitlesByIds(missingFromCache);
            if (!CollectionUtils.isEmpty(rows)) {
                for (AdamCaomeiPerformanceIdTitleDto row : rows) {
                    if (row == null || StringUtils.isEmpty(row.getPerformanceId()) || StringUtils.isEmpty(row.getTitle())) {
                        continue;
                    }
                    result.put(row.getPerformanceId().trim(), row.getTitle());
                }
            }
        }
        return result;
    }

    /**
     * 从演出缓存读取开演/结束时间（yyyyMMddHHmmss），用于草莓徽章补签自动审核。
     * 读取不到时返回 null。
     */
    public PerformanceTimeRange getPerformanceTimeRangeById(String performanceId) {
        try {
            KylinPerformanceVo vo = getKylinPerformanceVoById(performanceId);
            if (vo == null) {
                return null;
            }
            String start = vo.getTimeStart();
            String end = vo.getTimeEnd();
            LocalDateTime st = parseKylinTime(start);
            LocalDateTime et = parseKylinTime(end);
            if (st == null || et == null) {
                return null;
            }
            return new PerformanceTimeRange(st, et);
        } catch (Exception e) {
            log.warn("[getPerformanceTimeRangeById] 读取演出时间失败, performanceId: {}", performanceId, e);
            return null;
        }
    }

    /**
     * 统一从 kylin redis 缓存读取演出 VO。
     * 缓存若因序列化差异读成 Map，则尽量转换成 {@link KylinPerformanceVo} 后返回。
     */
    public KylinPerformanceVo getKylinPerformanceVoById(String performanceId) {
        return (KylinPerformanceVo) redisDataSourceUtil.getRedisKylinUtil().get(KylinRedisConst.PERFORMANCES + performanceId);
    }

    private static final DateTimeFormatter KYLIN_TIME_COMPACT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
    private static final DateTimeFormatter KYLIN_TIME_DASH = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private static LocalDateTime parseKylinTime(String v) {
        if (StringUtils.isEmpty(v)) {
            return null;
        }
        String t = v.trim();
        // 优先 kylin 常见格式：yyyyMMddHHmmss
        try {
            if (t.length() >= 14) {
                return LocalDateTime.parse(t.substring(0, 14), KYLIN_TIME_COMPACT);
            }
        } catch (Exception ignored) { }
        // 兼容：yyyy-MM-dd HH:mm:ss
        try {
            if (t.length() >= 19) {
                return LocalDateTime.parse(t.substring(0, 19), KYLIN_TIME_DASH);
            }
        } catch (Exception ignored) { }
        return null;
    }

    @Getter
    public static final class PerformanceTimeRange {
        private final LocalDateTime start;
        private final LocalDateTime end;

        public PerformanceTimeRange(LocalDateTime start, LocalDateTime end) {
            this.start = start;
            this.end = end;
        }

    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | List<购买会员黑名单UID> */

    public boolean setBlacklistForMember(List<String> uids) {
        return redisUtil.set(AdamRedisConst.BLK_LIST_MEMBER_UID, uids);
    }

    public List<String> getBlacklistForMember() {
        return (List<String>) redisUtil.get(AdamRedisConst.BLK_LIST_MEMBER_UID);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <UID, USER_MEMBER> */

    public boolean setUserMemberVoByUid(String uid, AdamUserMemberVo vo) {
        return redisUtil.set(AdamRedisConst.INFO_USER_MEMBER.concat(uid), vo);
    }

    public AdamUserMemberVo getUserMemberVoByUid(String uid) {
        String rk = AdamRedisConst.INFO_USER_MEMBER.concat(uid);
        long s = System.currentTimeMillis();
        AdamUserMemberVo vo = (AdamUserMemberVo) redisUtil.get(rk);
//        if (null == vo) {
//            s = System.currentTimeMillis();
//            vo = mongoTemplate.findOne(Query.query(Criteria.where("uid").is(uid)), AdamUserMemberVo.class, AdamUserMemberVo.class.getSimpleName());
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//
//            if (null != vo) redisUtil.set(rk, vo);
//        }
        if (null != vo) {
            vo.setState(vo.getState() == 1 ? (vo.getExpiryAt().isAfter(LocalDateTime.now()) ? 1 : 2) : vo.getState());
        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vo;
    }

    public void delUserMemberVoByUid(String uid) {
        redisUtil.del(AdamRedisConst.INFO_USER_MEMBER.concat(uid));
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <MEMBER_SIMPLE_INFO> */

    public AdamMemberSimpleVo getMemberSimpleVo() {
        long s = System.currentTimeMillis();
        AdamMemberSimpleVo vo = (AdamMemberSimpleVo) redisUtil.get(AdamRedisConst.INFO_MEMBER_SIMPLE);
        if (null == vo) {
            AdamMemberVo memberVo = this.getMemberVoByMemberId("1");
            if (null != memberVo) {
                vo = AdamMemberSimpleVo.getNew().copy(memberVo);

                if (null != vo) redisUtil.set(AdamRedisConst.INFO_MEMBER_SIMPLE, vo);
            }
        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vo;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <MEMBER_ID, MEMBER_INFO> */

    public boolean setMemberVoByMemberId(String memberId, AdamMemberVo vo) {
        return redisUtil.set(AdamRedisConst.INFO_MEMBER_CATEGORY.concat(memberId), vo);
    }

    public AdamMemberVo getMemberVoByMemberId(String memberId) {
        String key = AdamRedisConst.INFO_MEMBER_CATEGORY.concat(memberId);
        long s = System.currentTimeMillis();
        AdamMemberVo vo = (AdamMemberVo) redisUtil.get(key);
//        if (null == vo) {
//            s = System.currentTimeMillis();
//            vo = mongoTemplate.findOne(
//                    Query.query(Criteria.where("memberId").is(memberId).and("state").is(1)),
//                    AdamMemberVo.class, AdamMemberVo.class.getSimpleName()
//            );
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//            if (null != vo) redisUtil.set(key, vo);
//        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vo;
    }

    public void delMemberVoByMemberId(String memberId) {
        redisUtil.del(AdamRedisConst.INFO_MEMBER_CATEGORY.concat(memberId));
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <MEMBER_ID, List<AdamMemberPriceVo>> */

    public boolean setMemberPriceVoByMemberId(String memberId, List<AdamMemberPriceVo> vos) {
        return redisUtil.set(AdamRedisConst.INFO_MEMBER_PRICE.concat(memberId), vos);
    }

    public List<AdamMemberPriceVo> getMemberPriceVoByMemberId(String memberId) {
        String key = AdamRedisConst.INFO_MEMBER_PRICE.concat(memberId);
        long s = System.currentTimeMillis();
        List<AdamMemberPriceVo> vos = (List<AdamMemberPriceVo>) redisUtil.get(key);
//        if (CollectionUtils.isEmpty(vos)) {
//            s = System.currentTimeMillis();
//            vos = mongoTemplate.find(
//                    Query.query(Criteria.where("memberId").is(memberId).and("state").is(1)),
//                    AdamMemberPriceVo.class, AdamMemberPriceVo.class.getSimpleName()
//            );
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//
//            if (!CollectionUtils.isEmpty(vos)) redisUtil.set(key, vos);
//        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vos;
    }

    public AdamMemberPriceVo getMemberPriceVoByPriceId(String memberId, String memberPriceId) {
        List<AdamMemberPriceVo> vos = this.getMemberPriceVoByMemberId(memberId);
        return CollectionUtils.isEmpty(vos) ? null : vos.stream().filter(r -> r.getMemberPriceId().equals(memberPriceId)).findAny().orElse(null);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <MEMBER_ID, List<AdamMemberRightsVo>> */

    public boolean setMemberRightsVoByMemberId(String memberId, List<AdamMemberRightsVo> vos) {
        return redisUtil.set(AdamRedisConst.INFO_MEMBER_RIGHTS.concat(memberId), vos);
    }

    /**
     * 会员权益列表
     *
     * @param memberId 会员Id
     * @param vFlag    新老会员标识[0-默认新会员,1-老会员]
     * @return
     */
    public List<AdamMemberRightsVo> getMemberRightsVoByMemberId(String memberId, int vFlag) {
        List<AdamMemberRightsVo> vos = null;
        switch (vFlag) {
            case 0:
                vos = (List<AdamMemberRightsVo>) redisUtil.get(AdamRedisConst.INFO_MEMBER_RIGHTS.concat(memberId));
                break;
            case 1:
                vos = (List<AdamMemberRightsVo>) redisUtil.get(AdamRedisConst.INFO_MEMBER_RIGHTS_V.concat(memberId));
                if (CollectionUtils.isEmpty(vos)) {
                    vos = Arrays.asList(AdamMemberRightsVo.getNew().setTitle("专属").setSeqNo(1)
                                    .setCover("https://img.zhengzai.tv/files/2020/08/28/5f490c4477d72.png"),
                            AdamMemberRightsVo.getNew().setTitle("优惠").setSeqNo(2)
                                    .setCover("https://img.zhengzai.tv/files/2020/08/28/5f490c685e7a8.png"),
                            AdamMemberRightsVo.getNew().setTitle("优先").setSeqNo(3)
                                    .setCover("https://img.zhengzai.tv/files/2020/08/28/5f490c726a750.png"),
                            AdamMemberRightsVo.getNew().setTitle("专属").setSeqNo(4)
                                    .setCover("https://img.zhengzai.tv/files/2020/08/28/5f490c5de2870.png")
                    );

                    redisUtil.set(AdamRedisConst.INFO_MEMBER_RIGHTS_V.concat(memberId), vos);
                }
                break;
        }
        return vos;
    }

    public AdamMemberRightsDetailVo getMemberRightsDetailVoByRightsId(String memberId, String mrightsId) {
        List<AdamMemberRightsVo> vos = this.getMemberRightsVoByMemberId(memberId, 0);

        AdamMemberRightsDetailVo detailVo = AdamMemberRightsDetailVo.getNew();
        if (!CollectionUtils.isEmpty(vos)) {
            AdamMemberRightsVo memberRightsVo = vos.stream().filter(r -> r.getMrightsId().equals(mrightsId)).findAny().orElse(null);
            detailVo.setRightsVo(memberRightsVo);

            if (null != memberRightsVo) {
                List<AdamMemberRightsCouponVo> memberRightsCouponVoList = this.getMemberRightsCouponVoByRightsId(mrightsId);

                detailVo.setCouponVoList(memberRightsCouponVoList);
            }
        }

        return detailVo;
    }

    public List<AdamMemberRightsCouponVo> getMemberRightsCouponVoByRightsId(String mrightsId) {
        return (List<AdamMemberRightsCouponVo>) redisUtil.get(AdamRedisConst.INFO_MEMBER_RIGHTS_COUPON.concat(mrightsId));
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | MEMBER_AGREEMENT */

    public String getMemberAgreement() {
        String val = (String) redisUtil.get(AdamRedisConst.INFO_MEMBER_AGREEMENT);
        if (StringUtils.isEmpty(val)) {
            val = "<p style=\"text-align: center;\"><span class=\"ql-size-large\">摩登天空登登登会员服务协议</span></p><p style=\"text-align: center;\"><br></p><p style=\"text-align: center;\">本版本更新时间：2021年1月26日&nbsp;</p><p style=\"text-align: right;\">&nbsp;</p><p style=\"text-align: justify;\">一 、接受协议</p><p style=\"text-align: justify;\">1.1一旦您注册成为摩登天空登登登会员用户，便表示您（以下简称“用户”）已经完全理解并同意接受本协议（以下简称“协议”）。</p><p style=\"text-align: justify;\">1.2在您接受本协议之后，本协议可能因国家政策、产品与/或服务以及履行本协议的环境发生变化而进行修改，一旦本协议内容发生变更的，本网站将在相应页面、站内信或以其他合理方式进行通知，请您仔细阅读。如您在本协议更新后继续使用摩登天空登登登会员服务，即表示您已充分阅读、理解并接受更新后的本协议并愿意接受更新后的协议约束。如果您不同意变更的内容，您可以选择停止使用摩登天空登登登会员服务。更新后的协议自发布之日起生效。</p><p style=\"text-align: justify;\">1.3若您是未成年人，请您在监护人监护、指导下阅读并决定是否同意本协议。未成年人行使和履行本协议项下的权利和义务视为已获得了监护人的认可。</p><p style=\"text-align: justify;\">&nbsp;</p><p style=\"text-align: justify;\">二、服务内容</p><p style=\"text-align: justify;\">2.1摩登天空登登登会员是指完成了成为会员的所有程序，且在遵守本协议的前提下，在会员时长有效期内享受摩登天空提供的登登登会员服务的自然人。</p><p style=\"text-align: justify;\"><strong>2.2 摩登天空有权根据法律法规及政策变更，合作方要求以及自身运营策略变更对登登登会员服务内容（包括但不限于登登登会员权益细则，收费标准和收费方式）进行部分或全部变更。就前述变更，本网站将在相应页面、站内信或以其他合理方式进行发布，发布之后立即生效。如果您不同意变更的内容，您可以选择停止使用摩登天空登登登会员服务。</strong></p><p style=\"text-align: justify;\">2.3摩登天空登登登会员服务协议是《正在现场注册协议》以及《正在现场隐私权保护协议》的补充协议。</p><p style=\"text-align: justify;\">&nbsp;</p><p style=\"text-align: justify;\">三、会员账号及权益</p><p style=\"text-align: justify;\">3.1在用户的登登登会员服务有效期内，用户在本网站所使用的账号即为用户的登登登会员账号。用户有责任维持自己所注册的账号和密码的安全，用户应对所有使用本人的密码及账号的活动负完全责任。用户同意：(a) 本人的密码或账号遭到未获授权的使用。或者发生其他任何安全问题时，请立即通知本网站，且（b）每次上网完毕都会把本人的账号关掉。<strong>如未能依前述规定行事，对于任何人利用该密码及账号所进行的任何行为，用户应负完全的责任。对于因此而产生的任何损失或损害，本网站无法也不承担任何责任。 根据有关法律法规，本网站在此郑重提请用户注意，任何经由我们的服务以上载、张贴、发送电子邮件或其他任何方式传送的资讯、资料、文字、软件、音乐、音讯、照片、图形、视讯、信息或其它资料（以下简称“内容”），无论系公开还是私下传送，均由内容提供者、上传者承担责任。本网站无法控制经由我们的服务传送之内容，因此不保证内容的正确性、完整性或品质。 如用户已预知使用我们的服务时，可能会接触到令人不适之内容，在任何情况下，本网站均不为此负责。包含但不限于任何内容之任何错误或遗漏，以及经由我们的服务及张贴、发送电子邮件或其它方式传送任何内容而衍生之任何损失或损害。本网站有权依法停止传输任何前述内容并采取相应行动，包含但不限于暂停用户使用我们的服务的全部或部分，保持有关记录，并向有关机关报备。</strong></p><p style=\"text-align: justify;\">3.2 登登登会员权益:</p><p>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;摩登天空登登登会员权益包括：</p><p>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.超过百场演出优先购票权、入场权及专属通道*</p><p>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.会员专属纪念品礼盒</p><p>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3.会员独享限定款商品及音像制品的购买权利（专属商品上线）</p><p>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4.超过百款商品的会员优惠价</p><p>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5.会员专属的演出以及线上线下活动</p><p>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6.会员专属的视频音频节目</p><p>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7.想不到的惊喜</p><p>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8.免费观演以及免费商品的抽奖权利。</p><p>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>（具体享有会员权益的演出场次和商品以本网站指定为准）</strong></p><p style=\"text-align: justify;\"><strong>本网站可能会根据法律法规及政策变更、版权状态变化、自身运营策略对前述会员权益作出部分修改。</strong></p><p style=\"text-align: justify;\">3.3 登登登会员服务期限及延期：</p><p style=\"text-align: justify;\"><strong>用户的登登登会员服务的服务期限自用户成为登登登会员之时起算，以用户自行选择并支付相应会员费用对应的服务期限为准，用户可以通过登录本网站会员中心免费查询。该期限不因您未使用等情况而延长。当会员服务期限到期后，本网站将停止继续向您提供登登登会员服务；但如您开通自动续费服务且您在服务期限到期前续费成功的，会员服务期限将在原服务期限的基础上顺延。</strong></p><p style=\"text-align: justify;\">&nbsp;</p><p style=\"text-align: justify;\">四、隐私保护</p><p style=\"text-align: justify;\">4.1 本网站保证不对外公开或向第三方提供单个用户的注册资料及用户在使用网络服务时存储在本网站的非公开内容，但下列情况除外：</p><p style=\"text-align: justify;\">4.1.1事先获得用户的明确授权；</p><p style=\"text-align: justify;\">4.1.2根据有关的法律法规要求；</p><p style=\"text-align: justify;\">4.1.3按照相关政府主管部门的要求；</p><p style=\"text-align: justify;\">4.1.4为维护社会公众的利益；</p><p style=\"text-align: justify;\">4.1.5为维护本网站的合法权益；</p><p style=\"text-align: justify;\">4.1.6其他需要公开、编辑或透露个人信息的情况。</p><p style=\"text-align: justify;\"><strong>4.2 本网站可能会与第三方合作向用户提供相关的网络服务，在此情况下，如第三方同意承担与本网站同等的保护用户隐私的责任，则本网站有权将用户的注册资料等提供给该第三方。</strong></p><p style=\"text-align: justify;\"><strong>4.3 在不透露单个用户隐私资料的前提下，本网站有权对整个用户数据库进行分析并对用户数据库进行商业上的利用。</strong></p><p style=\"text-align: justify;\">&nbsp;</p><p>五、服务终止</p><p>本网站终止摩登天空登登登会员服务包括但不限于如下情况：</p><p>1. 用户自主提出的</p><p>2. 用户利用会员身份获得/购得的音乐节以及场馆演出类门票或商城商品，不得转让、代拍。如果私自转让或代拍，一经本网站发现，本网站有权取消该用户的会员资格且该用户利用会员身份进行转票/代拍所获得/购得的音乐节以及场馆演出类门票视为无效（本网站有权视具体情况要求该用户寄回或直接作废），同时清算该用户的使用权益并退回差价。会员权益计价按如下标准执行：</p><p>&nbsp;</p><p>1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;优先购票权、入场权及专属通道按照50元/次计价</p><p>2)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;会员专属纪念品礼盒按照100元计价</p><p>3)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;会员专属优惠商品的会员优惠价按照实际优惠价格计算</p><p>4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;会员专属的演出以及线上线下活动，已经观看的按照50元/次计价</p><p>5)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;抽取到会员免费观演以及免费商品的按照实际演出和商品的价格计算</p><p style=\"text-align: justify;\">&nbsp;</p><p style=\"text-align: justify;\">l&nbsp;用户存在或本网站独立判断用户存在任何违反国家法律法规或监管政策的行为</p><p style=\"text-align: justify;\">l&nbsp;根据法律法规、政策规定、维护账号与系统安全等紧急情况以及不可抗力因素等其他本网站无法抗拒的情况</p><p style=\"text-align: justify;\">&nbsp;</p><p style=\"text-align: justify;\">六、通知送达</p><p style=\"text-align: justify;\">6.1本网站对于摩登天空登登登用户所有的通知均可通过网页公告、电子邮件、手机短信或其他常规的信件传送等方式进行；该等通知于发送之日视为已送达收件人。</p><p style=\"text-align: justify;\">6.2用户对于本网站的通知应当通过本网站对外正式公布的通信地址、传真号码、电子邮件等联系信息进行送达。</p><p style=\"text-align: justify;\">&nbsp;</p><p style=\"text-align: justify;\">七、责权限制</p><p style=\"text-align: justify;\">用户明确了解并同意，基于以下原因造成的包括但不限于利益、信誉、应用、数据损失或其它无形损失，本网站不承担任何直接、间接、附带、特别、衍生性或惩罚性赔偿责任（即使本网站事先已被告知发生此种赔偿之可能性亦然）： （i）我们的服务之使用或无法使用，（ii）为替换从或通过我们的服务购买或取得之任何商品、数据、信息、服务、收到的讯息，或缔结之交易而发生的成本，（iii）用户的传输数据遭到未获授权的存取或编造，（iv）任何第三方在我们的服务中所做之声明或行为；或（v）与我们的服务相关的其它事宜，但本协议有明确规定的除外。</p><p style=\"text-align: justify;\">&nbsp;</p><p style=\"text-align: justify;\">八、法律管辖与适用</p><p style=\"text-align: justify;\">8.1 本服务协议的生效、履行、解释及争议的解决均适用于中华人民共和国法律。</p><p style=\"text-align: justify;\">8.2 如本协议内容或其执行发生任何争议，应尽量友好协商解决；协商不成时，您同意将争议提交至北京市朝阳区人民法院诉讼解决。双方同意，解决争议时，应以您同意的最新《摩登天空登登登会员服务协议》为准。</p><p style=\"text-align: justify;\">&nbsp;</p><p style=\"text-align: justify;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;北京正在映画互联网科技有限公司</p><p style=\"text-align: justify;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2021年1月26日</p><p style=\"text-align: justify;\">&nbsp;</p>";

            redisUtil.set(AdamRedisConst.INFO_MEMBER_AGREEMENT, val);
        }
        return val;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <UID, MEMBER_ORDER_LIST(SUCC)> */

    public boolean setMemberOrderVosByUid(String uid, List<AdamMemberOrderVo> vos) {
        return redisUtil.set(AdamRedisConst.INFO_MEMBER_ORDER + uid, vos);
    }

    public ArrayList<AdamMemberOrderVo> getMemberOrderVosByUid(String uid) {
        long s = System.currentTimeMillis();
        ArrayList<AdamMemberOrderVo> vos = (ArrayList<AdamMemberOrderVo>) redisUtil.get(AdamRedisConst.INFO_MEMBER_ORDER + uid);
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vos;
    }

    public boolean addMemberOrderVoByUid(String uid, List<AdamMemberOrderVo> vos, AdamMemberOrderVo vo) {
        if (null == vos) {
            vos = ObjectUtil.getAdamMemberOrderVoArrayList();
        }
        vos.add(vo);
        return redisUtil.set(AdamRedisConst.INFO_MEMBER_ORDER + uid, vos);
    }

    public boolean rmvMemberOrderVoByUid(String uid, List<AdamMemberOrderVo> vos, String orderNo) {
        if (CollectionUtils.isEmpty(vos)) {
            return true;
        }
        vos.removeIf(r -> r.getOrderNo().equals(orderNo));
        return redisUtil.set(AdamRedisConst.INFO_MEMBER_ORDER + uid, vos);
    }

    public AdamMemberOrderVo getMemberOrderVoByOrderNo(List<AdamMemberOrderVo> vos, String orderNo) {
        return CollectionUtils.isEmpty(vos) ? null : vos.stream().filter(r -> r.getOrderNo().equals(orderNo)).findAny().orElse(null);
    }

    public void delMemberOrderVoByUid(String uid) {
        redisUtil.del(AdamRedisConst.INFO_MEMBER_ORDER + uid);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <ORDER_NO, MEMBER_ORDER_INFO> */

    public boolean setShotMemberOrderVoByOrderNo(String orderNo, AdamMemberOrderVo vo) {
        return redisUtil.set(AdamRedisConst.SHOT_MEMBER_ORDER + orderNo, vo, 604800);
    }

    public AdamMemberOrderVo getShotMemberOrderVoByOrderNo(String orderNo) {
        String key = AdamRedisConst.SHOT_MEMBER_ORDER + orderNo;
        long s = System.currentTimeMillis();
        AdamMemberOrderVo vo = (AdamMemberOrderVo) redisUtil.get(key);
//        if (null == vo) {
//            s = System.currentTimeMillis();
//            vo = mongoTemplate.findOne(
//                    Query.query(Criteria.where("orderNo").is(orderNo)),
//                    AdamMemberOrderVo.class, AdamMemberOrderVo.class.getSimpleName()
//            );
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//
//            if (null != vo) redisUtil.set(key, vo);
//        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vo;
    }

    public void delShotMemberOrderVoByOrderNo(String orderNo) {
        redisUtil.del(AdamRedisConst.SHOT_MEMBER_ORDER + orderNo);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | INCR_MEMBER_NO */

    public boolean setMaxMemberNo(int val) {
        return redisUtil.set(AdamRedisConst.INCR_MEMBER_NO, val);
    }

    public long incrMemberNo() {
        return redisUtil.incr(AdamRedisConst.INCR_MEMBER_NO, 1);
    }

    public int getMaxMemberNo() {
        Object o = redisUtil.get(AdamRedisConst.INCR_MEMBER_NO);
        return null == o ? -1 : (int) o;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | INCR_MEMBER_LIMITATION */

    public boolean setMaxMemberLimitation(int val) {
        return redisUtil.set(AdamRedisConst.INCR_MEMBER_LIMITATION, val);
    }

    public long incrMemberNoLimitation() {
        return redisUtil.incr(AdamRedisConst.INCR_MEMBER_LIMITATION, 1);
    }

    public int getMaxMemberLimitation() {
        Object o = redisUtil.get(AdamRedisConst.INCR_MEMBER_LIMITATION);
        return null == o ? -1 : (int) o;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <MEMBER_CODE, MEMBER_CODE_INFO> */

    public boolean setMemberCodeVoByCode(String memberCode, AdamMemberCodeVo vo) {
        return redisUtil.set(AdamRedisConst.INFO_MEMBER_CODE.concat(memberCode), vo);
    }

    public AdamMemberCodeVo getMemberCodeVoByCode(String memberCode) {
        String key = AdamRedisConst.INFO_MEMBER_CODE.concat(memberCode);
        long s = System.currentTimeMillis();
        AdamMemberCodeVo vo = (AdamMemberCodeVo) redisUtil.get(key);
//        if (null == vo) {
//            s = System.currentTimeMillis();
//            vo = mongoTemplate.findOne(
//                    Query.query(Criteria.where("code").is(memberCode)),
//                    AdamMemberCodeVo.class, AdamMemberCodeVo.class.getSimpleName()
//            );
//            log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//            if (null != vo && vo.getState() == 0) redisUtil.set(AdamRedisConst.INFO_MEMBER_CODE.concat(memberCode), vo);
//        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vo;
    }

    public void delMemberCodeVoByCode(String memberCode) {
        redisUtil.del(AdamRedisConst.INFO_MEMBER_CODE.concat(memberCode));
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | <AdamMemberCodeVo::buyOrderNo, MEMBER_CODE> */

    public boolean setMemberCodeByBuyOrderNo(String buyOrderNo, String memberCode) {
        return redisUtil.set(AdamRedisConst.INFO_MEMBER_ORDER_BUY_CODE + buyOrderNo, memberCode);
    }

    public String getMemberCodeByBuyOrderNo(String buyOrderNo) {
        return (String) redisUtil.get(AdamRedisConst.INFO_MEMBER_ORDER_BUY_CODE + buyOrderNo);
    }

    public void delMemberCodeByBuyOrderNo(String buyOrderNo) {
        redisUtil.del(AdamRedisConst.INFO_MEMBER_ORDER_BUY_CODE + buyOrderNo);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |  */

    /**
     * Temporarily connect Mongo
     */
    public AdamMemberCodeVo getMemberCodeVoByBuyOrderNo(String buyOrderNo) {
//        Query query = Query.query(Criteria.where("buyOrderNo").is(buyOrderNo));
//        query.fields().include("code").include("memberNo").include("state").include("type");
//        long s = System.currentTimeMillis();
//        AdamMemberCodeVo vo = mongoTemplate.findOne(
//                query, AdamMemberCodeVo.class, AdamMemberCodeVo.class.getSimpleName()
//        );
//        log.debug("#MDB耗时:{}ms", System.currentTimeMillis() - s);
//        return vo;
        // TODO: 2021/7/30 存在购买会员兑换码功能时，按需调整
        AdamMemberCodeVo vo = AdamMemberCodeVo.getNew();
        vo.setCode(this.getMemberCodeByBuyOrderNo(buyOrderNo));
        return vo;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | TAGS:List<AdamTagVo> */

    public boolean setTagsForSex(List<AdamTagVo> voList) {
        return redisUtil.set(AdamRedisConst.INFO_TAGS_SEX, voList);
    }

    public List<AdamTagVo> getTagsForSex() {
        return (List<AdamTagVo>) redisUtil.get(AdamRedisConst.INFO_TAGS_SEX);
    }

    public boolean setTagsForMusic(List<AdamTagParentVo> voList) {
        return redisUtil.set(AdamRedisConst.INFO_TAGS_MUSIC, voList);
    }

    public List<AdamTagParentVo> getTagsForMusic() {
        return (List<AdamTagParentVo>) redisUtil.get(AdamRedisConst.INFO_TAGS_MUSIC);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |  */

    /**
     * 赋值新加属性[省、市、区县]
     *
     * @param vo AdamUserInfoVo
     * @return AdamUserInfoVo
     */
    public AdamUserInfoVo ratingProvince(AdamUserInfoVo vo) {
        Optional.ofNullable(vo).filter(r -> StringUtils.isEmpty(r.getProvince())).ifPresent(r -> {
            String[] mobileLocateArr = this.getMobileLocateArr(r.getMobile());
            if (null != mobileLocateArr && mobileLocateArr.length > 0) {
                r.setProvince(mobileLocateArr[0]);
                r.setCity(mobileLocateArr[1]);
            }
        });
        return vo;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |  */

    public String generateMemberUniqueSerialNumber(String mobile) {
        LocalDateTime now = LocalDateTime.now();

//        String snT = String.format("%s%sMS%s", now.getYear(), now.getMonthValue(), mobile.substring(7)).substring(2);
//        String sn = snT.replace("MS", String.valueOf(now.getLong(ChronoField.MILLI_OF_SECOND)));
//        String snT = String.format("%s%sMS%s", now.getYear(), now.getMonthValue(), org.apache.commons.lang3.StringUtils.right(mobile, 4)).substring(2);
        String snT = String.format("%s%sMS%s", now.getYear(), org.apache.commons.lang3.StringUtils.leftPad(String.valueOf(now.getMonthValue()), 2, "0"), org.apache.commons.lang3.StringUtils.right(mobile, 4)).substring(2);
        String sn = snT.replace("MS", org.apache.commons.lang3.StringUtils.leftPad(String.valueOf(LocalDateTime.now().getLong(ChronoField.MILLI_OF_SECOND)), 3, "0"));
        Object o = redisUtil.get(AdamRedisConst.SERIAL_NUMBER_MEMBER.concat(sn));
        while (null != o) {
            String nano = String.valueOf(System.nanoTime());

            sn = snT.replace("MS", nano.substring(nano.length() - 3));
            o = redisUtil.get(AdamRedisConst.SERIAL_NUMBER_MEMBER.concat(sn));
        }
        LocalDateTime lastDayTime = now.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59);
        redisUtil.set(sn, 1, ChronoUnit.SECONDS.between(now, lastDayTime));

        return sn;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |  */

    public AdamMemberJoinusVo getMemberJoinusVo(int type) {
        String key = AdamRedisConst.INFO_MEMBER_JOINUS.concat(String.valueOf(type));
        long s = System.currentTimeMillis();
        AdamMemberJoinusVo vo = (AdamMemberJoinusVo) redisUtil.get(key);
        if (null == vo) {
            vo = AdamMemberJoinusVo.getNew();
            vo.setTitle("摩登天空会员卡");
            vo.setSubTitle("摩登天空专属会员卡");
            vo.setType(type);
            vo.setState(0);
            switch (type) {
                case 0:
                    vo.setCardface("https://img.zhengzai.tv/member_card/member1.png");
                    break;
                case 10:
                    vo.setCardface("https://img.zhengzai.tv/member_card/member1.png");
                    break;
                case 50:
                    vo.setCardface("https://img.zhengzai.tv/member_card/member2.png");
                    break;
            }
            String interestsDetail = type == 50 ? "优先购票&入场：<br />会员享有超过百场演出会员享有优先购票权<br />会员享有超过百场演出会员享有优先入场权<br /><br />商品优享会员价：<br />会员享有本站商城内超过百款周边商品和音像制品的会员优享价<br /><br />专属活动&商品&内容：<br />超过百场演出会员专属通道<br />会员专属的演出以及线上线下活动<br />会员专属的视频音频节目<br />会员享有限定款商品及音像制品的专属购买权利<br /><br />不定期惊喜：<br />免费观演以及免费商品的抽奖权利<br /><br />意想不到的惊喜：<br />我们会时不时制造一些意外的惊喜"
                    : "摩登天空会员卡权益描述：<br />每人限领1张<br /><br />会员权益：<br />关注「摩登天空服务号」注册即可成为摩登天空会员，享受购票&购物积分<br />积分有效期：历史上购买过商品演出的积分将于2022年1月15日过期；之后过期时间为每年1月1日，保留次年12月份积分<br /><br />使用须知：<br />1.每人限领1张<br />2.积分不能兑换，不可转让<br />3.积分及优惠券，一经使用，概不退换";
            vo.setInterestsDetail(interestsDetail);

            redisUtil.set(key, vo);
        }
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vo;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |  */

    public boolean setUserEntersOprNo(String uid) {
        LocalDateTime now = LocalDateTime.now();
        return redisUtil.set(AdamRedisConst.INCR_USER_ENTERS.concat(uid), 1,
                ChronoUnit.SECONDS.between(now, LocalDateTime.of(now.getYear(), now.getMonth(), now.getDayOfMonth(), 23, 59, 59)));
    }

    public long incrUserEntersOprNo(String uid) {
        return redisUtil.incr(AdamRedisConst.INCR_USER_ENTERS.concat(uid), 1);
    }

    public int getUserEntersOprNo(String uid) {
        Object o = redisUtil.get(AdamRedisConst.INCR_USER_ENTERS.concat(uid));
        return null == o ? -1 : (int) o;
    }

    public boolean hasNft(String currentUid) {
        final String rdsKey = "goblin:u_d_art_ct:" + currentUid;
        Object o = redisUtil.get(rdsKey);

        if (Objects.isNull(o)) {
            return false;
        }
        int num = (int) o;
        return num > 0;
    }

    public boolean hasNftOrder(String currentUid) {
        final String rdsKey = "goblin:nft:order:idList:" + currentUid;
        Object obj = redisUtil.get(rdsKey);
        if (obj != null) {
            return true;
        }
        return false;
    }
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |  */
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |  */



    /* ========================================================== | Other micro-service data inquiry */

//    public KylinPerformanceVo kylinPerformanceVo(String performancesId) {
//        return (KylinPerformanceVo) redisUtil.get(KylinRedisConst.PERFORMANCES + performancesId);
//    }
}
