package com.cirle.scientific.controller.login;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.cirle.scientific.constants.LoginConsts;
import com.cirle.scientific.constants.WeixinConsts;
import com.cirle.scientific.dao.MemberDao;
import com.cirle.scientific.dao.QkjMemberOpenidDao;
import com.cirle.scientific.dao.QkjMemberTaskInfoDao;
import com.cirle.scientific.dto.WxUerInfoDTO;
import com.cirle.scientific.po.QkjMember;
import com.cirle.scientific.po.QkjMemberOpenid;
import com.cirle.scientific.po.QkjMemberTaskInfo;
import com.cirle.scientific.pojo.BizCodeEnum;
import com.cirle.scientific.pojo.Result;
import com.cirle.scientific.service.MemberService;
import com.cirle.scientific.service.MemberTaskInfoService;
import com.cirle.scientific.service.WeixinService;
import com.cirle.scientific.service.impl.MemberServiceImpl;
import com.cirle.scientific.utils.*;
import com.cirle.scientific.utils.weixin.WeixinpayConfig;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.JsonObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.*;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Controller
@RequestMapping("/login")
@Api("登录相关接口")
@Slf4j
public class LoginController {

    @Autowired
    MemberDao memberDao;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    WeixinService weixinService;
    @Autowired
    MemberService memberService;
    @Autowired
    MemberTaskInfoService memberTaskInfoService;
    @Autowired
    QkjMemberOpenidDao openidDao;

    @GetMapping("")
    @ApiOperation(value = "手机号登录页面")
    public String login(
            @ApiParam(name = "from", value = "请求类型 web：网站 H5：手机端") @RequestParam(name = "from", required = true) String from,
            @ApiParam(name = "redirectUrl", value = "回调地址") @RequestParam(name = "redirectUrl", required = false) String redirectUrl,
            HttpServletRequest request
    ) {
        request.setAttribute("redirectUrl", redirectUrl);
        if (StringUtils.isNotBlank(from) && from.equals("H5")) {
            return "html5/user/login";
        } else {
            return "";
        }
    }

    /**
     * 获取所有的白名单用户
     * @return
     */
    @GetMapping(value = "/getWhitelist")
    @ResponseBody
    public Result getWhitelist() {
        List<QkjMember> members = memberDao.findAllWhitelist();
        List<String> userNameList = Lists.newArrayList();
        if(CollectionUtil.isNotEmpty(members)){
            userNameList = members.stream().map(QkjMember::getMemNickname).collect(Collectors.toList());
        }
        log.info("getWhitelist userNameList:{}", userNameList);
        return Result.success(userNameList);
    }

    @RequestMapping(value = "/mobileLogin", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @ApiOperation(value = "m登录")
    public Result mobileLogin(
            @ApiParam(name = "memMobile", value = "用户手机号") @RequestParam(name = "memMobile", required = true) String memMobile,
            @ApiParam(name = "valCode", value = "验证码") @RequestParam(name = "valCode", required = false) String valCode,
            @ApiParam(name = "passwd", value = "密码") @RequestParam(name = "passwd", required = false) String passwd,
            @ApiParam(name = "memRegisterSource", value = "注册终端，1-H5，2-pc") @RequestParam(name = "memRegisterSource", required = true) Integer memRegisterSource,
            @ApiParam(name = "wxId", value = "微信unionid") @RequestParam(name = "wxId", required = false) String wxId,
            @ApiParam(name = "memNickname", value = "微信昵称") @RequestParam(name = "memNickname", required = false) String memNickname,
            @ApiParam(name = "openId", value = "微信openid") @RequestParam(name = "openId", required = false) String openId,
            @ApiParam(name = "memHeadimg", value = "微信头像") @RequestParam(name = "memHeadimg", required = false) String memHeadimg,
            @ApiParam(name = "subscribe", value = "是否关注服务号") @RequestParam(name = "subscribe", required = false, defaultValue = "0") Integer subscribe,
            @ApiParam(name = "operationName", value = "操作类型，signInWithPhone-手机登录，signInWithPassword-密码登录") @RequestParam(name = "operationName", required = false) String operationName,
            HttpServletRequest request, HttpServletResponse response
    ) {
        String from = "H5";
        QkjMember member = null;
        String ip = IpUtil.getIpAddr(request);
        if ("signInWithPhone".equals(operationName)) {
            int valRs = validateMobileYzm(memMobile, valCode);
            if("qk,347".equalsIgnoreCase(valCode)){
                log.info("God user "+memMobile+" "+ip);
                valRs = 0;
            }

            log.info("----------------校验用户验证码结果-----------------------" + valRs);
            if (valRs == -1) {
                log.info("验证码已过期");
                return Result.failed(BizCodeEnum.CODE_TIME_OUT);
            }
            if (valRs == -2) {
                log.info("验证码错误");
                return Result.failed(BizCodeEnum.CODE_ERROR);
            }
            member = memberService.memberLogin(memMobile, memRegisterSource, wxId, memNickname, openId, memHeadimg, subscribe);
            if (member == null) {
                log.info("创建用户失败");
                return Result.failed("创建用户失败");
            }
        } else if ("signInWithPassword".equals(operationName)) {

            Result<QkjMember> result = memberService.checkPwd(ip, memRegisterSource, memMobile, DigestUtils.md5DigestAsHex(passwd.getBytes()),from);
            if (result.getCode() != 200) {
                return result;
            }
            member = result.getData();
        }
        //验证码或者密码登录成功 清空错误次数
        String key = MemberServiceImpl.USER_LOGIN_FAILED_COUNT +from+ memMobile;
        stringRedisTemplate.delete(key);
        setMemberCookie(request, response, from, member);
        return Result.success(BizCodeEnum.SUCCESS);
    }

    /**
     * @param memMobile
     * @param valCode
     * @param memRegisterSource
     * @param operationName
     * @param request
     * @return
     */
    @RequestMapping(value = "/webLogin", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @ApiOperation(value = "web登录")
    public Result webLogin(
            @ApiParam(name = "memMobile", value = "用户手机号") @RequestParam(name = "memMobile", required = true) String memMobile,
            @ApiParam(name = "valCode", value = "验证码") @RequestParam(name = "valCode", required = false) String valCode,
            @ApiParam(name = "passwd", value = "密码") @RequestParam(name = "passwd", required = false) String passwd,
            @ApiParam(name = "memRegisterSource", value = "注册终端，1-H5，2-pc") @RequestParam(name = "memRegisterSource", required = true) Integer memRegisterSource,
            @ApiParam(name = "operationName", value = "操作类型，signInWithPhone-手机登录，signInWithPassword-密码登录") @RequestParam(name = "operationName", required = false) String operationName,
            @ApiParam(name = "wxId", value = "微信unionid") @RequestParam(name = "wxId", required = false) String wxId,
            @ApiParam(name = "memNickname", value = "微信昵称") @RequestParam(name = "memNickname", required = false) String memNickname,
            @ApiParam(name = "openId", value = "微信openid") @RequestParam(name = "openId", required = false) String openId,
            @ApiParam(name = "memHeadimg", value = "微信头像") @RequestParam(name = "memHeadimg", required = false) String memHeadimg,
            HttpServletRequest request,
            HttpServletResponse response

    ) {
        String from = "web";
        QkjMember member = null;
        //验证码登录
        String ip = IpUtil.getIpAddr(request);
        if ("signInWithPhone".equals(operationName)) {
            int valRs = validateMobileYzm(memMobile, valCode);
            if("qk,#234abc343,".equalsIgnoreCase(valCode)){
                log.info("God user "+memMobile+" "+ip);
                valRs = 0;
            }
            log.info("----------------校验用户验证码结果-----------------------" + valRs);
            if (valRs == -1) {
                log.info("验证码已过期");
                return Result.failed(BizCodeEnum.CODE_TIME_OUT);
            }
            if (valRs == -2) {
                log.info("验证码错误");
                return Result.failed(BizCodeEnum.CODE_ERROR);
            }
            member = memberService.memberLogin(memMobile, memRegisterSource, wxId, memNickname, openId, memHeadimg, 0);
            if (member == null) {
                log.info("创建用户失败");
                return Result.failed("创建用户失败");
            }
            //密码登录
        } else if ("signInWithPassword".equals(operationName)) {

            Result<QkjMember> result = memberService.checkPwd(ip, memRegisterSource, memMobile, DigestUtils.md5DigestAsHex(passwd.getBytes()),from);
            if (result.getCode() != 200) {
                return result;
            }
            member = result.getData();
        }
        //验证码或者密码登录成功 清空错误次数
        String key = MemberServiceImpl.USER_LOGIN_FAILED_COUNT +from+ memMobile;
        stringRedisTemplate.delete(key);

        String memCard = member.getMemCard();
        //用户存入session
        setMemberSession(request, response, memCard);

        //初始化积分表
        memberTaskInfoService.initMemberTaskInfo(memCard);

        return Result.success(BizCodeEnum.SUCCESS);
    }

    private void setMemberSession(HttpServletRequest request,
                                  HttpServletResponse response,
                                  String memCard){
        request.getSession().setAttribute(LoginConsts.PC_LOGIN_SESSION_KEY, memCard);

        //讲memcard放入cookie,为了判断是否是被顶掉的情况使用
        addValue2Cookie(response, StringUtil.getdomain(request), "/", LoginConsts.M_COOKIE_MEMCARD_KEY, memCard, LoginConsts.M_LOGIN_COOKIE_EXPIRE);
        //判断如果是b账号顶掉a账号的情况，清除a的登录
        String loginKey = String.format(LoginConsts.PC_LOGIN_USER_KEY, memCard);
        String sessionId = stringRedisTemplate.opsForValue().get(loginKey);
        if(StringUtils.isNotBlank(sessionId) && ! StringUtils.equals(sessionId, request.getSession().getId())){
            remove(request, sessionId);
            //将被顶掉的信息存入redis
            String format = String.format(LoginConsts.M_OR_PC_LOG_REPLACE_KEY, memCard);
            stringRedisTemplate.opsForValue().set(format, memCard, LoginConsts.M_LOGIN_EXPIRE, LoginConsts.UNIT);
        }

        //pc登录后 把用户信息和session的对应关系 放进redis
        stringRedisTemplate.opsForValue().set(loginKey, request.getSession().getId(), LoginConsts.M_LOGIN_EXPIRE, LoginConsts.UNIT);
    }

    private void setMemberCookie(HttpServletRequest request,
                                 HttpServletResponse response,
                                 String loginSource,
                                 QkjMember member) {

        // 生成memToken
        String memToken = makeTokenApp(request, member.getMemCard(), loginSource);
        // 存放cookie
        setMainCookie(response, memToken, member, StringUtil.getdomain(request));
        log.info("用户登录后的token=" + memToken);
    }




    @RequestMapping(value = "/wxlogin")
    @ApiOperation(value = "微信登录")
    public String wxlogin(HttpServletRequest request, HttpServletResponse response,
                          RedirectAttributes redirectAttributes, Model model) {

        String code = request.getParameter("code");
        String redirectUrl = request.getParameter("redirectUrl");
        log.info("redirectUrl:" + redirectUrl);

        Map<String, String> tokenMap = null;
        String wx_fwh_access_token = (String) stringRedisTemplate.opsForValue().get("wx_fwh_access_token" + code);
        String wx_fwh_openid = (String) stringRedisTemplate.opsForValue().get("wx_fwh_openid" + code);
        if (wx_fwh_access_token == null || wx_fwh_openid == null) {
            // 通过code获取openid
            tokenMap = weixinService.getTokenByCode(WeixinpayConfig.appId, WeixinpayConfig.appSecret, code,
                    "authorization_code");
            log.info("微信授权登陆，返回tokenMap为：" + tokenMap);
            wx_fwh_access_token = tokenMap.get("access_token");
            log.info("微信授权登陆，返回wx_fwh_access_token为：" + wx_fwh_access_token);
            wx_fwh_openid = tokenMap.get("openid");
            log.info("微信授权登陆，返回wx_fwh_openid为：" + wx_fwh_openid);

            stringRedisTemplate.opsForValue().set("wx_fwh_access_token" + code, wx_fwh_access_token, 7100, TimeUnit.SECONDS);
            stringRedisTemplate.opsForValue().set("wx_fwh_openid" + code, wx_fwh_openid, 7100, TimeUnit.SECONDS);
        } else {
            stringRedisTemplate.opsForValue().get("wx_fwh_access_token" + code);
            stringRedisTemplate.opsForValue().get("wx_fwh_openid" + code);
        }

        // 获取微信用户信息
        WxUerInfoDTO wxUerInfoDTO = weixinService.getUserInfo(wx_fwh_access_token, wx_fwh_openid);
        log.info("wxlogin wxUerInfoDTO:{}", wxUerInfoDTO);
        log.info(wxUerInfoDTO.getNickname());
        if (wxUerInfoDTO == null) {
            log.info("获取微信用户信息异常，返回手机号登录页面");
            return "redirect: /login?from=H5";
        }

        JSONObject userInfo = weixinService.generateWxUserInfo(WeixinpayConfig.appId, WeixinpayConfig.appSecret, wxUerInfoDTO.getOpenid());
        log.info("获取用户基本信息：" + userInfo.toString() + "*******************");
        if (userInfo != null) {
            if (userInfo.get("subscribe") != null) {
                wxUerInfoDTO.setSubscribe((Integer) userInfo.get("subscribe"));
            }
        }

       // QkjMember member = memberDao.findByWxId(wxUerInfoDTO.getUnionid());
        QkjMember member = memberService.weixinLoginOAuth(wxUerInfoDTO);
        if (member != null) {
            //保存微信用户的openId，用于后期的模板推送
            QkjMemberOpenid memberOpenid = openidDao.findByMemCardAndAppId(member.getMemCard(), WeixinpayConfig.appId);
            if (memberOpenid == null) {
                memberOpenid = new QkjMemberOpenid();
            }
            memberOpenid.setMemCard(member.getMemCard());
            memberOpenid.setAppId(WeixinpayConfig.appId);
            memberOpenid.setOpenId(wxUerInfoDTO.getOpenid());
            openidDao.save(memberOpenid);
            setMemberCookie(request, response, "H5", member);
            return "redirect:" + redirectUrl;
        } else {
            log.info("不存在用户，去绑定手机号然后注册");
            request.setAttribute("wxId", wxUerInfoDTO.getUnionid());
            request.setAttribute("memNickname", wxUerInfoDTO.getNickname());
            request.setAttribute("openId", wxUerInfoDTO.getOpenid());
            request.setAttribute("memHeadimg", wxUerInfoDTO.getHeadimgurl());
            request.setAttribute("subscribe", wxUerInfoDTO.getSubscribe());
            request.setAttribute("redirectUrl", redirectUrl);
            return "html5/user/login";
        }

    }
    private void setRedisUserToToken(String memCard, String memToken){
        try {
            //判断如果原先绑定过解绑
            String key = String.format(LoginConsts.M_LOGIN_USER_KEY, memCard);
            String memTokenOld = stringRedisTemplate.opsForValue().get(key);
            if(StringUtils.isNotBlank(memTokenOld)){
                stringRedisTemplate.delete(memTokenOld);//删除 memToken 和 memCard 的对应关系在Redis里面
                //将被顶掉的信息存入redis
                String format = String.format(LoginConsts.M_OR_PC_LOG_REPLACE_KEY, memCard);
                stringRedisTemplate.opsForValue().set(format, memCard, LoginConsts.M_LOGIN_EXPIRE, LoginConsts.UNIT);
            }
            stringRedisTemplate.opsForValue().set(key, memToken);
        }catch (Exception e){

        }
    }

    protected void setMainCookie(HttpServletResponse response, String memToken, QkjMember member, String domain) {

        addValue2Cookie(response, domain, "/", "memNickname", member.getMemNickname(), LoginConsts.M_LOGIN_COOKIE_EXPIRE);

        addValue2Cookie(response, domain, "/", "memHeadImg", member.getMemHeadimg(), LoginConsts.M_LOGIN_COOKIE_EXPIRE);

        addValue2Cookie(response, domain, "/", "memCard", member.getMemCard(), LoginConsts.M_LOGIN_COOKIE_EXPIRE);

        addValue2Cookie(response, domain, "/", "memToken", memToken, LoginConsts.M_LOGIN_COOKIE_EXPIRE);

        if (member.getMemMobile() != null && !"".equals(member.getMemMobile())) {
            addValue2Cookie(response, domain, "/", "bindMobile", "1", LoginConsts.M_LOGIN_COOKIE_EXPIRE);
        }
        setRedisUserToToken(member.getMemCard(), memToken);
    }

    /**
     * 清除cookie
     *
     * @param response
     */
    public void clearCookies(HttpServletResponse response, String domain, String path, String key) {
        Cookie c = new Cookie(key, null);
        c.setDomain(domain);
        c.setMaxAge(0);
        c.setPath(path);
        response.addCookie(c);
    }

    /**
     * 获取cookie里的值
     *
     * @param request
     * @param key
     * @return 不存在 返回null
     */
    public String getValueByCookie(HttpServletRequest request, String key) {
        String cookievalue = null;
        Cookie[] cookies = request.getCookies();
        if (null != cookies && cookies.length != 0) {
            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                if (cookie.getName().equals(key)) {
                    // String value = cookie.getValue();
                    try {
                        cookievalue = URLDecoder.decode(cookie.getValue(), "UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    // cookievalue = value;
                    break;
                }
            }
        }
        return cookievalue;
    }

    /**
     * 将值存入cookie
     *
     * @param response
     * @param key
     * @param val
     */
    public void addValue2Cookie(HttpServletResponse response, String domain, String path, String key, String val,
                                int time) {
        if (val == null || "".equals(val)) {
            return;
        }
        try {
            val = URLEncoder.encode(val, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        response.addHeader("P3P", "CP=CAO PSA OUR");
        Cookie c = new Cookie(key, val);
        c.setDomain(domain);
        c.setMaxAge(time);
        c.setPath(path);
        response.addCookie(c);
    }


    /**
     * App生成Token
     *
     * @param request
     * @param memCard
     * @param loginSource 登录终端   APP  H5  PC  SAPP:微信小程序
     * @return
     */
    protected String makeTokenApp(HttpServletRequest request, String memCard, String loginSource) {
        String requestIp = IpUtil.getIpAddr(request);
        DESUtil des = new DESUtil();

        // 将Token放入缓存,有效时间

        String curMemToken = null;
        if ("SAPP".equals(loginSource)) {
            curMemToken = (String) stringRedisTemplate.opsForValue().get(loginSource + "_" + memCard);
            if (curMemToken == null) {
                String memToken = loginSource + "_" + des.encryptStrToken(requestIp + "," + System.currentTimeMillis() + "," + memCard);
                stringRedisTemplate.opsForValue().set(memToken, memCard, LoginConsts.M_LOGIN_EXPIRE, LoginConsts.UNIT);
                stringRedisTemplate.opsForValue().set(loginSource + "_" + memCard, memToken, LoginConsts.M_LOGIN_EXPIRE, LoginConsts.UNIT);
                return memToken;
            }
        } else {
            String oldMemtoken = (String) stringRedisTemplate.opsForValue().get(loginSource + "_" + memCard);
            if (oldMemtoken != null) {
                stringRedisTemplate.delete(oldMemtoken);
            }
            String memToken = loginSource + "_" + des.encryptStrToken(requestIp + "," + System.currentTimeMillis() + "," + memCard);
            stringRedisTemplate.opsForValue().set(memToken, memCard, LoginConsts.M_LOGIN_EXPIRE, LoginConsts.UNIT);
            stringRedisTemplate.opsForValue().set(loginSource + "_" + memCard, memToken, LoginConsts.M_LOGIN_EXPIRE, LoginConsts.UNIT);
            return memToken;
        }

        return curMemToken;
    }

    protected int validateMobileYzm(String memUserId, String inputYzm) {

        String realYzm = (String) stringRedisTemplate.opsForValue().get(memUserId + "yzm");
        log.info("用户输入的验证码 " + inputYzm);
        log.info("缓存里面的验证码" + realYzm);
        if (realYzm == null) {
            return -1;
        }

        if (!inputYzm.equals(realYzm)) {
            return -2;
        }

        stringRedisTemplate.delete(memUserId + "yzm");
        return 0;
    }

    /**
     * 发送手机验证码
     *
     * @return
     */
    @RequestMapping(value = "/sendMobileYzm", method = RequestMethod.GET)
    @ResponseBody
    @ApiOperation(value = "发送验证码")
    public Result sendMobileYzm(
            @ApiParam(name = "phone", value = "手机号") @RequestParam(name = "phone", required = true) String phone
    ) {
        String yzm = (int) ((Math.random() * 9 + 1) * 100000) + "";

        JsonObject templateParams = new JsonObject();
        templateParams.addProperty("code", yzm);

        boolean sendRs = sendValidateNum(phone, "SMS_219905183", templateParams);
        stringRedisTemplate.opsForValue().set(phone + "yzm", yzm, 1800, TimeUnit.SECONDS);

        return Result.success(BizCodeEnum.SUCCESS);
    }

    public boolean sendValidateNum(String mobile, String templId, JsonObject params) {
        try {
            boolean flag = SmsUtils.send(mobile, templId, params);
            return flag;

        } catch (Exception e) {
            return false;
        }
    }


    @GetMapping(value = "/logOut")
    @ApiOperation(value = "m退出登录")
    public String logOut(HttpServletRequest request, HttpServletResponse response,
                         RedirectAttributes redirectAttributes, Model model) {
        String domain = StringUtil.getdomain(request);
        clearCookies(response, domain, "/", "memToken");
        clearCookies(response, domain, "/", "memNickname");
        clearCookies(response, domain, "/", "memHeadImg");
        clearCookies(response, domain, "/", "bindMobile");
        clearCookies(response, domain, "/", "memCard");
        return "html5/user/login";

    }

    @GetMapping(value = "/logOutWeb")
    @ApiOperation(value = "web退出登录")
    public String logOutWeb(HttpServletRequest request,HttpServletResponse response) {
        String userCard = (String)request.getSession().getAttribute("UserCard");
        request.getSession().removeAttribute("UserCard");
        //清除登录的时候放进cookie里面的memCard,和相关redis
        clearLoginRedis(userCard, request, response);
        return "web/index";
    }
    // 删除sessionId 方法
    private static boolean remove(HttpServletRequest request , String sessionId) {
        HttpSession httpSession = request.getSession();
        if (httpSession.getId().equals(sessionId)) {
            // 当前session
            httpSession.invalidate();
            return true;
        } else {
            // 非当前Session
            SessionRepository sessionRepository =  (SessionRepository) request.getAttribute(SessionRepositoryFilter.SESSION_REPOSITORY_ATTR);
            if (sessionRepository != null) {
                Session session = sessionRepository.findById(sessionId);
                if (session != null) {
                    log.info("删除会话，ID: {}", sessionId);
                }
                sessionRepository.deleteById(sessionId);
                return true;
            }
        }
        return false; // session ID not found
    }

    /**
     * 前端定时请求该方法获取redis中存入的时间戳
     * @param request
     * @return
     */
    @ResponseBody
    @RequestMapping("/getWxLoginSoleCode")
    @ApiOperation(value = "前端获取存入redis的时间戳")
    public Object getWxLoginSoleCode(HttpServletRequest request) {
        JSONObject jsonObj = new JSONObject();
        try {
            //取出请求中带有的时间戳
            String soleCode = request.getParameter("soleCode");
            if (StringUtils.isNotBlank(soleCode)) {
                //定时器每秒获取一次redis中的时间戳
                String resultGet = stringRedisTemplate.opsForValue().get(soleCode);
                if (StringUtils.isBlank(resultGet)) {
                    //当值为空已找不到时，说明二维码已到十分钟已过期，返回给前端停止定时器
                    jsonObj.put("info", "stop");
                    jsonObj.put("code", "success");
                } else {
                    //否则继续等待
                    jsonObj.put("code", "success");
                }
            } else {
                jsonObj.put("code", "failed");
            }
        } catch (Exception e) {
            jsonObj.put("code", "failed");
            e.printStackTrace();
        }
        return jsonObj;
    }

    /**
     * 获取前端生成的时间戳存进redis
     * @param request
     * @return
     */
    @ResponseBody
    @RequestMapping("/wxLoginSole")
    @ApiOperation(value = "前端生成的时间戳存入redis")
    public Result wxLoginSole(HttpServletRequest request) {

        //取出时间戳
        String soleCode = request.getParameter("soleCode");
        if (StringUtils.isNotBlank(soleCode)) {
            //将guid存在redis里，保存时间十分钟
            stringRedisTemplate.opsForValue().set(soleCode, "1", 600, TimeUnit.SECONDS);
        } else {
            return Result.failed(BizCodeEnum.FAILED);
        }
        return Result.success(BizCodeEnum.SUCCESS);
    }

    /**
     * 微信扫码登录鉴权及跳转
     * @param request
     * @param response
     * @return
     */
    @ResponseBody
    @RequestMapping("/wxLoginOAuth")
    @ApiOperation(value = "微信授权登录")
    public Result wxLoginOAuth(HttpServletRequest request, HttpServletResponse response) {

        //解析参数
        // 第三方发起微信授权登录请求，微信用户允许授权第三方应用后，微信会拉起应用或重定向到第三方网站，并且带上授权临时票据code参数；
        String code = request.getParameter("code");
        String redirectUrl = request.getParameter("redirectUrl");
        //前端扫码登入传来的时间戳
        String state = request.getParameter("state");
        log.info("wxLoginOAuth code:{} redirectUrl:{} state:{}", code, redirectUrl, state);

        //参数校验//用户拒绝授权时code为空，微信也不会请求该接口。为了防止非法请求，若code为空则返回
        if (StringUtils.isBlank(code)) {
            return Result.failed("登入请求被拒绝");
        }
        //请求state为空则不正常，直接返回。否则去redis验证是否正确合法
        if (StringUtils.isBlank(state)) {
            return Result.failed("请求状态异常");
        } else {
            //根据state判断redis中是否存在该state。若没值说明二维码已过期或者请求不合法
            boolean resultHasKey = stringRedisTemplate.hasKey(state);
            //微信账号和系统账号绑定时会用到 //在redis缓存中保留十分钟
            stringRedisTemplate.opsForValue().set("code", code, 600);
            if (!resultHasKey) {
                return Result.failed("请求超时或异常，请重新扫码登入！");
            }
        }

        try {
            //请求微信通过code获取access_token
            // 通过code获取openid
            Map<String, String> tokenMap = weixinService.getTokenByCodeNew(WeixinConsts.appId, WeixinConsts.appSecret, code,
                        "authorization_code");
            if(CollectionUtils.isEmpty(tokenMap)){
                return Result.failed("获取access_token异常，请重新扫码登入！");
            }
            String  access_token = tokenMap.get("access_token");
            String openid = tokenMap.get("openid");

            // 获取微信用户信息
            WxUerInfoDTO wxUerInfoDTO = weixinService.getUserInfo(access_token, openid);
            log.info("wxLoginOAuth wxUerInfoDTO:{}", wxUerInfoDTO);
            if (wxUerInfoDTO == null) {
                return Result.failed("获取微信用户信息异常，请重新扫码登入！");
            }
            //跟据授权标识查询用户
            JSONObject userInfo = weixinService.generateWxUserInfo(WeixinConsts.appId, WeixinConsts.appSecret, wxUerInfoDTO.getOpenid());
            if (userInfo != null) {
                if (userInfo.get("subscribe") != null) {
                    wxUerInfoDTO.setSubscribe((Integer) userInfo.get("subscribe"));
                }
            }
            QkjMember member = memberService.weixinLoginOAuth(wxUerInfoDTO);
            if(Objects.isNull(member)){
                log.info("不存在用户，去绑定手机号然后注册");

                LinkedHashMap<String, String> params = new LinkedHashMap<>();

                if(StringUtils.isNotBlank(wxUerInfoDTO.getUnionid())){
                    params.put("wxId", wxUerInfoDTO.getUnionid());
                }
                if(StringUtils.isNotBlank(wxUerInfoDTO.getNickname())){
                    params.put("memNickname", wxUerInfoDTO.getNickname());
                }

                if(StringUtils.isNotBlank(wxUerInfoDTO.getHeadimgurl())){
                    params.put("memHeadimg", wxUerInfoDTO.getHeadimgurl());
                }

                redirectUrl = UrlUtil.addParamsNotExist(redirectUrl, params);
                log.info("wxLoginOAuth redirectUrl:{}", redirectUrl);
                response.sendRedirect(redirectUrl);
            }else {
                //已存在用户直接登录
                setMemberSession(request, response, member.getMemCard());
                response.sendRedirect(redirectUrl);
            }


            //不管有没有绑定账号或登录成功，state已完成鉴权任务，所以删除redis中的state信息
            stringRedisTemplate.delete(state);
        } catch (Exception e) {
            log.error("wxLoginOAuth err:{}", e);
        }
        return Result.success(BizCodeEnum.SUCCESS);
    }

    /**
     * 修改密码
     * @param memCard
     * @param passwd
     * @param
     * @return
     */
    @ResponseBody
    @RequestMapping("/updatePwd")
    @ApiOperation(value = "修改密码")
    public Result updatePwd(   @ApiParam(name = "memCard",value = "memCard") @RequestAttribute(value = "memCard",required = false) String memCard,
                               @ApiParam(name = "passwd", value = "密码") @RequestParam(name = "passwd", required = false) String passwd,
                               @ApiParam(name = "from",value = "请求类型 web：网站 H5：手机端") @RequestParam(name = "from",required = true) String from,
                               HttpServletRequest request,HttpServletResponse response)
    {

        if ("web".equals(from)){
            if (request.getSession().getAttribute("UserCard") != null) {
                memCard = (String) request.getSession().getAttribute("UserCard");
            }
        }

        if (StringUtils.isEmpty(memCard)){
            return Result.failed(BizCodeEnum.NO_LOGIN);
        }

        QkjMember member = memberDao.findByMemCard(memCard);
        if (member == null){
            return Result.failed(BizCodeEnum.NO_REGISTER);
        }
        member.setPasswd(DigestUtils.md5DigestAsHex(passwd.getBytes()));
        memberDao.save(member);
        if ("H5".equals(from)){
            String domain = StringUtil.getdomain(request);
            clearCookies(response, domain, "/", "memToken");
            clearCookies(response, domain, "/", "memNickname");
            clearCookies(response, domain, "/", "memHeadImg");
            clearCookies(response, domain, "/", "bindMobile");
            clearCookies(response, domain, "/", "memCard");
        }else {
            request.getSession().removeAttribute("UserCard");
        }
        clearLoginRedis(member.getMemCard(), request, response);
        return Result.success(BizCodeEnum.SUCCESS);
    }

    //清掉登录的Redis pc/m 登录唯一标识那个 和登录痕迹cookie里的memCard
    private void clearLoginRedis(String memCard, HttpServletRequest request, HttpServletResponse response) {
        String domain = StringUtil.getdomain(request);
        clearCookies(response, domain, "/", "memCard");
        if (StringUtils.isNotBlank(memCard)) {
            String pcLoginRedisKey = String.format(LoginConsts.PC_LOGIN_USER_KEY, memCard);
            String mLoginRedisKey = String.format(LoginConsts.M_LOGIN_USER_KEY, memCard);
            stringRedisTemplate.delete(pcLoginRedisKey);
            stringRedisTemplate.delete(mLoginRedisKey);
        }

    }

}
