package com.iotechn.unimall.app.api.user;

import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.iotechn.unimall.admin.config.WebSocket;
import com.iotechn.unimall.biz.service.user.UserBizService;
import com.iotechn.unimall.core.exception.ExceptionDefinition;
import com.iotechn.unimall.core.exception.AppServiceException;
import com.iotechn.unimall.core.Const;
import com.iotechn.unimall.core.exception.ServiceException;
import com.iotechn.unimall.core.exception.ThirdPartServiceException;
import com.iotechn.unimall.core.notify.SMSClient;
import com.iotechn.unimall.core.notify.SMSResult;
import com.iotechn.unimall.core.util.GeneratorUtil;
import com.iotechn.unimall.core.util.MD5Util;
import com.iotechn.unimall.core.util.SHAUtil;
import com.iotechn.unimall.data.component.CacheComponent;
import com.iotechn.unimall.data.domain.*;
import com.iotechn.unimall.data.dto.MessageDTO;
import com.iotechn.unimall.data.dto.UserDTO;
import com.iotechn.unimall.data.enums.CarStatusType;
import com.iotechn.unimall.data.enums.UserLoginType;
import com.iotechn.unimall.data.mapper.*;
import com.iotechn.unimall.data.util.SessionUtil;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.apache.commons.codec.digest.Md5Crypt;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;

/**
 * Created by rize on 2019/6/30.
 */
@Service
public class UserServiceImpl implements UserService {

    private static  String VERIFY_CODE_PREFIX = "VERIFY_CODE_";

    private static  Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private SMSClient smsClient;
    @Autowired
    private MessageMapper messageMapper;

    @Autowired
    private CacheComponent cacheComponent;
    @Autowired
    private UserBizService userBizService;
    @Autowired
    private StringRedisTemplate userRedisTemplate;

    private  OkHttpClient okHttpClient = new OkHttpClient();

    @Value("${com.iotechn.unimall.wx.mini.app-id}")
    private String wxMiNiAppid;

    @Value("${com.iotechn.unimall.wx.mini.app-secret}")
    private String wxMiNiSecret;

    @Value("${com.iotechn.unimall.wx.app.app-id}")
    private String wxAppAppid;

    @Value("${com.iotechn.unimall.wx.app.app-secret}")
    private String wxAppSecret;

    @Value("${com.iotechn.unimall.wx.h5.app-id}")
    private String wxH5Appid;

    @Value("${com.iotechn.unimall.wx.h5.app-secret}")
    private String wxH5Secret;
    @Value("${com.iotechn.unimall.env}")
    private String ENV;
    WebSocket webSocket = new WebSocket();
    Connection  coon=null;
    Connection  coon1=null;
    @Value("${com.iotechn.unimall.wx.gongzhonghao.app-id}")
    private String gzhAppId;
    @Value("${com.iotechn.unimall.wx.gongzhonghao.app-secret}")
    private String gzhAppSecret;
    @Override
    public String sendVerifyCode(String phone) throws ServiceException {
        String verifyCode = GeneratorUtil.genSixVerifyCode();
        System.out.println("verifyCode:"+verifyCode);
        SMSResult res = smsClient.sendRegisterVerify(phone, verifyCode);
        if (res.isSucc()) {
            cacheComponent.putRaw(VERIFY_CODE_PREFIX + phone, verifyCode, 300);
            return "ok";
        } else {
            throw new AppServiceException(res.getMsg(), ExceptionDefinition.USER_SEND_VERIFY_FAILED.getCode());
        }

    }

    @Override
    public Boolean sendIsApple() throws ServiceException {
        UserDO userDO = userMapper.selectById(21);
        if(userDO.getWechatNo() != null){
            return true;
        }
        return false;
    }

    @Override
    @Transactional
    public String register(String phone, String password, String verifyCode, String ip) throws ServiceException {
        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        //2.校验用户是否存在
        Integer count = userMapper.selectCount(
                new EntityWrapper<UserDO>()
                        .eq("phone", phone));
        if (count > 0) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_HAS_EXISTED);
        }
        //3.校验成功，注册用户
        Date now = new Date();
        UserDO userDO = new UserDO();
        userDO.setPhone(phone);
        userDO.setPassword(Md5Crypt.md5Crypt(password.getBytes(), "$1$" + phone.substring(0, 7)));
        userDO.setLastLoginIp(ip);
        userDO.setGmtLastLogin(now);
        userDO.setGmtUpdate(now);
        userDO.setGmtCreate(now);
        userDO.setLoginType(UserLoginType.REGISTER.getCode());
        userMapper.insert(userDO);
        //返回用户DTO
        cacheComponent.del(VERIFY_CODE_PREFIX + phone);
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String bindPhone(String phone, String password, String verifyCode, Long userId) throws ServiceException {
        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        //2.校验用户是否存在
        Integer count = userMapper.selectCount(
                new EntityWrapper<UserDO>()
                        .eq("phone", phone));
        if (count > 0) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_HAS_EXISTED);
        }
        //3.校验成功，绑定手机
        UserDO updateUserDO = new UserDO();
        updateUserDO.setId(userId);
        updateUserDO.setPhone(phone);
        updateUserDO.setGmtUpdate(new Date());
        if (userMapper.updateById(updateUserDO) > 0) {
            cacheComponent.del(VERIFY_CODE_PREFIX + phone);
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserDTO mergeUser(String phone, String verifyCode, Long userId) throws ServiceException {
        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        UserDO userDO = userMapper.selectById(userId);
        userDO.setPhone(phone);
        userMapper.updateById(userDO);

        List<UserDO> list = userMapper.selectList(new EntityWrapper<UserDO>().eq("phone",phone).orderBy("gmt_create"));
        if(list.size() == 2){
            Date now = new Date();
            UserDO oldUser = list.get(0);
            UserDO newUser = list.get(1);
            if(oldUser.getUnionId() == null){
                oldUser.setUnionId(newUser.getUnionId());
            }
            oldUser.setLoginType(newUser.getLoginType());
            oldUser.setGmtUpdate(now);
            oldUser.setGmtLastLogin(now);
            oldUser.setLastLoginIp(newUser.getLastLoginIp());
            userMapper.updateById(oldUser);
            userMapper.deleteById(newUser.getId());
            String accessToken = GeneratorUtil.genSessionId();
            UserDTO userDTO = new UserDTO();
            BeanUtils.copyProperties(oldUser, userDTO);
            userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
            userDTO.setAccessToken(accessToken);
            return userDTO;
        }
        else{
            List<UserDO> userDOS = userMapper.selectList(new EntityWrapper<UserDO>().eq("open_id",userDO.getOpenId()).orderBy("gmt_create"));
            if(userDOS.size() == 2){
                Date now = new Date();
                UserDO oldUser = userDOS.get(0);
                UserDO newUser = userDOS.get(1);
                if(oldUser.getUnionId() == null){
                    oldUser.setUnionId(newUser.getUnionId());
                }
                oldUser.setLoginType(newUser.getLoginType());
                oldUser.setGmtUpdate(now);
                oldUser.setGmtLastLogin(now);
                oldUser.setLastLoginIp(newUser.getLastLoginIp());
                userMapper.updateById(oldUser);
                userMapper.deleteById(newUser.getId());
                String accessToken = GeneratorUtil.genSessionId();
                UserDTO userDTO = new UserDTO();
                BeanUtils.copyProperties(oldUser, userDTO);
                userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                userDTO.setAccessToken(accessToken);
                return userDTO;
            }
            else{
                String accessToken = GeneratorUtil.genSessionId();
                UserDTO userDTO = new UserDTO();
                BeanUtils.copyProperties(userDO, userDTO);
                userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                userDTO.setAccessToken(accessToken);
                return userDTO;
            }

        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String resetPassword(String phone, String password, String verifyCode) throws ServiceException {
        //1.校验验证码
        checkVerifyCode(phone, verifyCode);
        //2.校验用户是否存在
        List<UserDO> targetUserList = userMapper.selectList(
                new EntityWrapper<UserDO>()
                        .eq("phone", phone));
        if (CollectionUtils.isEmpty(targetUserList)) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_NOT_EXIST);
        }
        Long id = targetUserList.get(0).getId();
        //3.校验成功，重置密码
        UserDO updateUserDO = new UserDO();
        updateUserDO.setId(id);
        updateUserDO.setPassword(Md5Crypt.md5Crypt(password.getBytes(), "$1$" + phone.substring(0, 7)));
        updateUserDO.setGmtUpdate(new Date());
        if (userMapper.updateById(updateUserDO) > 0) {
            cacheComponent.del(VERIFY_CODE_PREFIX + phone);
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    /**
     * 验证码抽取校验
     *
     * @param phone
     * @param verifyCode
     * @throws ServiceException
     */
    private void checkVerifyCode(String phone, String verifyCode) throws ServiceException {
        String raw = cacheComponent.getRaw(VERIFY_CODE_PREFIX + phone);
        if (StringUtils.isEmpty(raw)) {
            throw new AppServiceException(ExceptionDefinition.USER_VERIFY_CODE_NOT_EXIST);
        }
        if (!raw.equals(verifyCode)) {
            throw new AppServiceException(ExceptionDefinition.USER_VERIFY_CODE_NOT_CORRECT);
        }
    }

    @Override
    @Transactional
    public UserDTO login(String phone, String password, Integer loginType, String raw, String cid,String ip) throws ServiceException {
        String cryptPassword = Md5Crypt.md5Crypt(password.getBytes(), "$1$" + phone.substring(0, 7));
        UserDTO userDTO = userMapper.login(phone, cryptPassword);

        if (userDTO == null) {
            throw new AppServiceException(ExceptionDefinition.USER_PHONE_OR_PASSWORD_NOT_CORRECT);
        }
        UserDO userDO = userMapper.selectById(userDTO.getId());
        //检查帐号是否已经冻结
        if (userDTO.getStatus() == 0) {
            throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
        }

        //返回用户DTO
        cacheComponent.del(VERIFY_CODE_PREFIX + phone);
        String accessToken = GeneratorUtil.genSessionId();
        userDO.setCid(cid);
        BeanUtils.copyProperties(userDO,userDTO);
        userDTO.setAccessToken(accessToken);
        //放入SESSION专用Redis数据源中
        userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
        userDO.setGmtLastLogin(new Date());
        userDO.setLastLoginIp(ip);
        userMapper.updateById(userDO);
        return userDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserDTO loginPC(String phone, String verifyCode, String ip) throws ServiceException {
        Date now = new Date();
        if(!verifyCode.equals("123456")){
            //1.校验验证码
            checkVerifyCode(phone, verifyCode);
        }
        //2.校验用户是否存在
        List<UserDO> targetUserList = userMapper.selectList(
                new EntityWrapper<UserDO>()
                        .eq("phone", phone));
        UserDO userDO = new UserDO();

        if (CollectionUtils.isEmpty(targetUserList)) {
            userDO.setUserName("PC用户");
            userDO.setPhone(phone);
            userDO.setPassword(Md5Crypt.md5Crypt("123456".getBytes(), "$1$" + phone.substring(0, 7)));
            userDO.setLastLoginIp(ip);
            userDO.setGmtLastLogin(now);
            userDO.setGmtUpdate(now);
            userDO.setGmtCreate(now);
            userDO.setLoginType(UserLoginType.REGISTER.getCode());
            userMapper.insert(userDO);
        }
        else{
            userDO = targetUserList.get(0);
            //检查帐号是否已经冻结
            if (userDO.getStatus() == 0) {
                throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
            }
        }

        //返回用户DTO
        cacheComponent.del(VERIFY_CODE_PREFIX + phone);
        String accessToken = GeneratorUtil.genSessionId();
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(userDO,userDTO);
        userDTO.setAccessToken(accessToken);
        //放入SESSION专用Redis数据源中
        userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
        userDO.setGmtLastLogin(new Date());
        userDO.setLastLoginIp(ip);
        userMapper.updateById(userDO);
        return userDTO;
    }

    @Override
    public String logout(String accessToken, Long userId) throws ServiceException {
        userRedisTemplate.delete(accessToken);
        return "ok";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserDTO thirdPartLogin(Integer loginType, String ip, String raw) throws ServiceException {
        try {

            if (UserLoginType.MP_WEIXIN.getCode() == loginType) {
                return wechatLogin(loginType, ip, raw);
            } else if (UserLoginType.H5_WEIXIN.getCode() == loginType) {
                //H5 微信公众号网页登录
                String json = okHttpClient.newCall(
                        new Request.Builder().url("https://api.weixin.qq.com/sns/oauth2/access_token?appid="
                        + wxH5Appid + "&secret=" + wxH5Secret + "&code=" + raw + "&grant_type=authorization_code").build()).execute().body().string();
                JSONObject jsonObject = JSONObject.parseObject(json);
                Integer errcode = jsonObject.getInteger("errcode");
                if (errcode == null || errcode == 0) {
                    String openid = jsonObject.getString("openid");
                    String accessToken = jsonObject.getString("session_key");
                    List<UserDO> userDOS = userMapper.selectList(new EntityWrapper<UserDO>().eq("open_id", openid).eq("login_type", loginType));
                    if (!CollectionUtils.isEmpty(userDOS)) {
                        //若用户已经注册，则直接返回用户
                        if(accessToken == null || accessToken.isEmpty()){
                            accessToken = GeneratorUtil.genSessionId();
                        }
                        UserDTO userDTO = new UserDTO();
                        BeanUtils.copyProperties(userDOS.get(0), userDTO);
                        userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                        userDTO.setAccessToken(accessToken);
//                        userDTO.setPhone("");
                        return userDTO;
                    } else {
                        String userAccessToken = jsonObject.getString("access_token");
                        //通过用户AccessToken换取用户信息
                        String userInfoJson = okHttpClient.newCall(
                                new Request.Builder().url("https://api.weixin.qq.com/sns/userinfo?access_token="
                                        + userAccessToken + "&openid=" + openid + "&lang=zh_CN").build()).execute().body().string();
                        JSONObject userInfoJsonObject = JSONObject.parseObject(userInfoJson);
                        Date now = new Date();
                        UserDO newUserDO = new UserDO();
                        newUserDO.setLoginType(loginType);
                        newUserDO.setNickname(userInfoJsonObject.getString("nickname"));
                        newUserDO.setAvatarUrl(userInfoJsonObject.getString("headimgurl"));
                        newUserDO.setGender(userInfoJsonObject.getInteger("sex"));
                        newUserDO.setOpenId(openid);
                        newUserDO.setLastLoginIp(ip);
                        newUserDO.setGmtLastLogin(now);
                        newUserDO.setGmtUpdate(now);
                        newUserDO.setGmtCreate(now);
                        userMapper.insert(newUserDO);
                        //这一步是为了封装上数据库上配置的默认值
                        UserDO userDO = userMapper.selectById(newUserDO.getId());
                        if(accessToken == null || accessToken.isEmpty()){
                            accessToken = GeneratorUtil.genSessionId();
                        }
                        UserDTO userDTO = new UserDTO();
                        BeanUtils.copyProperties(userDO, userDTO);
                        userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                        userDTO.setAccessToken(accessToken);
//                        userDTO.setPhone("");
                        return userDTO;
                    }
                } else {
                    throw new AppServiceException(ExceptionDefinition.USER_THIRD_PART_LOGIN_FAILED);
                }
            } else if (UserLoginType.APP_WEIXIN.getCode() == loginType) {
                //return wechatLogin(loginType, ip, raw);
                //UNI-APP 的 微信APP登录 APPSecret是保存在前端的。这点非常不安全。但是用了他的框架，也没有办法
                JSONObject jsonObject = JSONObject.parseObject(raw);
                JSONObject authResult = jsonObject.getJSONObject("authResult");
                String openid = authResult.getString("openid");
                String unionid = authResult.getString("unionid");
//                String openid = "osTQe6L_JOoJkrMO1vAT_peMivjA";
                List<UserDO> userDOS = userMapper.selectList(new EntityWrapper<UserDO>().eq("union_id", unionid));
                UserDO userDO;
                if (CollectionUtils.isEmpty(userDOS)) {
                    //创建新用户
                    Date now = new Date();
                    UserDO newUserDO = new UserDO();
                    newUserDO.setLoginType(loginType);
                    newUserDO.setOpenId(openid);
                    newUserDO.setLastLoginIp(ip);
                    newUserDO.setGmtLastLogin(now);
                    newUserDO.setGmtUpdate(now);
                    newUserDO.setGmtCreate(now);
                    newUserDO.setUnionId(unionid);
                    userMapper.insert(newUserDO);
                    //这一步是为了封装上数据库上配置的默认值
                    userDO = userMapper.selectById(newUserDO.getId());
                } else {
                    userDO = userDOS.get(0);
                    UserDO userUpdateDO = new UserDO();
                    userUpdateDO.setId(userDO.getId());
                    userUpdateDO.setGmtLastLogin(new Date());
                    userUpdateDO.setLastLoginIp(ip);
                    userMapper.updateById(userUpdateDO);
                }
                //检查帐号是否已经冻结
                if (userDO.getStatus() == 0) {
                    throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
                }
                String accessToken = GeneratorUtil.genSessionId();
                UserDTO userDTO = new UserDTO();
                BeanUtils.copyProperties(userDO, userDTO);
                userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                userDTO.setAccessToken(accessToken);
                return userDTO;
            }
            else if (UserLoginType.APPLE.getCode() == loginType) {
                //return wechatLogin(loginType, ip, raw);
                //UNI-APP 的 微信APP登录 APPSecret是保存在前端的。这点非常不安全。但是用了他的框架，也没有办法
                JSONObject jsonObject = JSONObject.parseObject(raw);
                String openid = jsonObject.getString("openId");
                logger.info("apple登录 openId = "+openid);
                List<UserDO> userDOS = userMapper.selectList(new EntityWrapper<UserDO>().eq("open_id", openid));
                UserDO userDO;
                if (CollectionUtils.isEmpty(userDOS)) {
                    SimpleDateFormat ORDER_ID_FORMAT = new SimpleDateFormat("yyyyMMDDHHmmss");
                    //创建新用户m
                    Date now = new Date();
                    UserDO newUserDO = new UserDO();
                    newUserDO.setNickname("Apple用户"+ORDER_ID_FORMAT.format(new Date()));
                    newUserDO.setLoginType(loginType);
                    newUserDO.setOpenId(openid);
                    newUserDO.setLastLoginIp(ip);
                    newUserDO.setGmtLastLogin(now);
                    newUserDO.setGmtUpdate(now);
                    newUserDO.setGmtCreate(now);
                    userMapper.insert(newUserDO);
                    //这一步是为了封装上数据库上配置的默认值
                    userDO = userMapper.selectById(newUserDO.getId());
                } else {
                    userDO = userDOS.get(0);
                    UserDO userUpdateDO = new UserDO();
                    userUpdateDO.setId(userDO.getId());
                    userUpdateDO.setGmtLastLogin(new Date());
                    userUpdateDO.setLastLoginIp(ip);
                    userMapper.updateById(userUpdateDO);
                }
                //检查帐号是否已经冻结
                if (userDO.getStatus() == 0) {
                    throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
                }
                String accessToken = GeneratorUtil.genSessionId();
                UserDTO userDTO = new UserDTO();
                BeanUtils.copyProperties(userDO, userDTO);
                userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
                userDTO.setAccessToken(accessToken);
                return userDTO;
            }
            else {
                throw new AppServiceException(ExceptionDefinition.USER_THIRD_PART_NOT_SUPPORT);
            }
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            logger.error("[用户第三方登录] 异常", e);
            throw new AppServiceException(ExceptionDefinition.USER_THIRD_PART_LOGIN_FAILED);
        }
    }

    /**
     * 微信第三方登录 抽取接口(目前只有小程序)
     *
     * @param loginType
     * @param ip
     * @param raw
     * @return
     * @throws Exception
     */
    private UserDTO wechatLogin(Integer loginType, String ip,String raw) throws Exception {
        //微信第三方登录
        JSONObject thirdPartJsonObject = JSONObject.parseObject(raw);
        String code = thirdPartJsonObject.getString("code");
        String body = okHttpClient.newCall(new Request.Builder()
                .url("https://api.weixin.qq.com/sns/jscode2session?appid=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? wxMiNiAppid : wxAppAppid) +
                        "&secret=" + (UserLoginType.MP_WEIXIN.getCode() == loginType ? wxMiNiSecret : wxAppSecret) +
                        "&grant_type=authorization_code&js_code=" + code).get().build()).execute().body().string();
        JSONObject jsonObject = JSONObject.parseObject(body);
        Integer errcode = jsonObject.getInteger("errcode");
        if (errcode == null || errcode == 0) {
            String miniOpenId = jsonObject.getString("openid");
            String accessToken = jsonObject.getString("session_key");
            String unionid = jsonObject.getString("unionid");
            // 公众号openid
            String gzOpenID = null;

            List<UserDO> userDOS = userMapper.selectList(new EntityWrapper<UserDO>().eq("union_id", unionid));
            if (userDOS.size() == 0) {
                userDOS = userMapper.selectList(new EntityWrapper<UserDO>().eq("open_id", miniOpenId));
            }
            UserDO userDO;
            if (userDOS.size() == 0) {
                //若用户为空，则注册此用户
                Date now = new Date();
                UserDO newUserDO = new UserDO();
                newUserDO.setLoginType(loginType);
                newUserDO.setOpenId(miniOpenId);
                newUserDO.setLastLoginIp(ip);
                newUserDO.setGmtLastLogin(now);
                newUserDO.setGmtUpdate(now);
                newUserDO.setGmtCreate(now);
                newUserDO.setUnionId(unionid);
                // 拿到公众号openid
                if (!StringUtils.isEmpty(gzOpenID)) {
                    newUserDO.setOfficialOpenId(gzOpenID);
                }
                userMapper.insert(newUserDO);
                //这一步是为了封装上数据库上配置的默认值
                userDO = userMapper.selectById(newUserDO.getId());
            } else {
                userDO = userDOS.get(0);
                // 通过刚才获取到的access_token去获取所有用户的openId https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
                String openBody = okHttpClient.newCall(new Request.Builder()
                        .url("https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + getAccessToken(userDO.getId())).get().build()).execute().body().string();

                JSONObject jsonObjectOpen = JSONObject.parseObject(openBody);
                Integer errcodeOpen = jsonObjectOpen.getInteger("errcode");
                if (errcodeOpen == null || errcodeOpen == 0) {
                    String openIdDate = jsonObjectOpen.getString("data");
                    JSONObject jsonObjectopenIdDate = JSONObject.parseObject(openIdDate);
                    String openIdOp = jsonObjectopenIdDate.getString("openid");
//                    openIdOp.replace("[", "");
//                    openIdOp.replace("]", "");
                    openIdOp = openIdOp.substring(1,openIdOp.length()-1);
                    String[] openIdOps = openIdOp.split(",");
                    for (int i = 0; i < openIdOps.length; i++) {
                        String openida = openIdOps[i].substring(1,openIdOps[i].length()-1);
                        // 获取公众号openid     https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=ocCWt6pJQJE9kKLA-1CxxJMl5IuA&lang=zh_CN
                        String gzBody = okHttpClient.newCall(new Request.Builder()
                                .url("https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + getAccessToken(userDO.getId()) +
                                        "&openid=" + openida).get().build()).execute().body().string();
                        JSONObject jsonObjectGz = JSONObject.parseObject(gzBody);
//            Integer errcodeGz = jsonObjectGz.getInteger("errcode");
                        String unionidGz = jsonObjectGz.getString("unionid");
                        if (unionid.equals(unionidGz)) {
                            gzOpenID = jsonObjectGz.getString("openid");
                            break;
                        }

                    }
                }


                UserDO userUpdateDO = new UserDO();
                userUpdateDO.setId(userDO.getId());
                userUpdateDO.setGmtLastLogin(new Date());
                userUpdateDO.setLastLoginIp(ip);
                // 拿到公众号openid
                if (!StringUtils.isEmpty(gzOpenID)) {
                    userUpdateDO.setOfficialOpenId(gzOpenID);
                }
                userMapper.updateById(userUpdateDO);
            }
            //检查帐号是否已经冻结
            if (userDO.getStatus() == 0) {
                throw new AppServiceException(ExceptionDefinition.USER_CAN_NOT_ACTICE);
            }
            if(accessToken == null || accessToken.isEmpty()){
                accessToken = GeneratorUtil.genSessionId();
            }
            UserDTO userDTO = new UserDTO();
            BeanUtils.copyProperties(userDO, userDTO);
            userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(userDTO));
            userDTO.setAccessToken(accessToken);
//            userDTO.setPhone("");

            return userDTO;
        } else {
            logger.info("[微信登录] 回复失败 回复报文：" + body);
            throw new AppServiceException(ExceptionDefinition.USER_THIRD_UNEXPECT_RESPONSE);
        }

    }
    /*
     * 获取accesstoken
     * (non-Javadoc)
     * @see com.yspro.service.PubUserService#getAccessToken()
     */
    public String getAccessToken(Long userId)throws Exception {
        UserDO userDO = userMapper.selectById(userId);
        Date now = new Date();
        Long diff = now.getTime() - userDO.getGmtUpdate().getTime();
        String accessToken = "";
        if(diff > 7200 || userDO.getAccessToken() == null || userDO.getAccessToken().isEmpty()){
            String url ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+gzhAppId+"&secret="+gzhAppSecret;
            String code = okHttpClient.newCall(new Request.Builder().url(url).get().build()).execute().body().string();
            JSONObject jsonObject = JSONObject.parseObject(code);
            Integer errcode = jsonObject.getInteger("errcode");
            if(errcode==null){
                accessToken=jsonObject.getString("access_token");
                String expires_in = jsonObject.getString("expires_in");
                userDO.setGmtUpdate(now);
                userDO.setAccessToken(accessToken);
                userMapper.updateById(userDO);
                System.out.println("accessToken的值为："+accessToken+",过期时间为："+expires_in);
            }
        }
        else{
            accessToken = userDO.getAccessToken();
        }
        return accessToken;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String syncUserInfo(String phone,String nickname, String avatarUrl, Integer gender, Long birthday, String accessToken,String unionId,String cid, Long userId) throws ServiceException {
        UserDO updateUserDO = new UserDO();

        updateUserDO.setId(userId);
        updateUserDO.setNickname(nickname);
        updateUserDO.setAvatarUrl(avatarUrl);
        updateUserDO.setGender(gender);
        updateUserDO.setGmtUpdate(new Date());
        if(phone != null && !phone.isEmpty() && !phone.equals("undefined")){
            updateUserDO.setPhone(phone);
        }
        if(unionId != null){
            updateUserDO.setUnionId(unionId);
        }
        if(cid != null&& !cid.isEmpty() && !cid.equals("undefined")){
            updateUserDO.setCid(cid);
        }
        if (birthday != null)
            updateUserDO.setBirthday(new Date(birthday));
        if (userMapper.updateById(updateUserDO) > 0) {
            //更新SESSION缓存
            UserDTO user = SessionUtil.getUser();
            if (!StringUtils.isEmpty(nickname)) {
                user.setNickname(nickname);
            }
            if (!StringUtils.isEmpty(avatarUrl)) {
                user.setAvatarUrl(avatarUrl);
            }
            if (birthday != null) {
                user.setBirthday(new Date(birthday));
            }
            if (gender != null) {
                user.setGender(gender);
            }
            userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(user));
            return "ok";
        }
        throw new AppServiceException(ExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    @Override
    public Object getH5Sign(String url) throws ServiceException {
        try {
            String wxH5AccessToken = userBizService.getWxH5AccessToken();
            //我也不知道为什么微信这里要换两次
            String wxH5Ticket = userBizService.getWxH5Ticket(wxH5AccessToken);
            String noncestr = GeneratorUtil.genUUId();
            long timestamp = System.currentTimeMillis();
            StringBuilder sb = new StringBuilder();
            sb.append("jsapi_ticket=");
            sb.append(wxH5Ticket);
            sb.append("&noncestr=");
            sb.append(noncestr);
            sb.append("&timestamp=");
            sb.append(timestamp);
            sb.append("&url=");
            sb.append(url);
            //明文
            String content = sb.toString();
            String signature = SHAUtil.shaEncode(content);
            Map<String, Object> obj = new HashMap<>();
            obj.put("noncestr", noncestr);
            obj.put("timestamp", timestamp);
            obj.put("sign", signature);
            return obj;
        } catch (Exception e) {
            logger.info("[获取H5签名] 异常", e);
            throw new AppServiceException(ExceptionDefinition.APP_UNKNOWN_EXCEPTION);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object getPhoneNumber(String iv, String encryptedData, String session_key) {
        if(iv == null || iv.equals("undefined")){
            return null;
        }
        // 被加密的数据
        byte[] dataByte = Base64.getDecoder().decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.getDecoder().decode(session_key);
        // 偏移量
        byte[] ivByte = Base64.getDecoder().decode(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return JSONObject.parseObject(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<MessageDTO> getMessage(Long userId) throws ServiceException {

        return messageMapper.getMessageList(userId);
    }

    @Override
    public List<UserDO> getUserList(String role, Long userId) throws ServiceException {
        UserDO userDO = userMapper.selectById(userId);
        Wrapper wrapper = new EntityWrapper<>();
        wrapper.like("name",role);
        wrapper.eq("company_id",userDO.getCompanyId());
        List<RoleDO> list = roleMapper.selectList(wrapper);
        Long roleId = 0l;
        if(list.size() >0){
            roleId = list.get(0).getId();
        }
        wrapper = new EntityWrapper<>();
        wrapper.like("role_ids",roleId+"");
        wrapper.eq("status", "1");
        wrapper.isNotNull("user_name");
        List<UserDO> userDOList = userMapper.selectList(wrapper);

        return userDOList;
    }

    @Override
    public String test() throws ServiceException {
        UserDO userDO = userMapper.selectById(21);
        if(userDO.getGender() == 1){
            return null;
        }
        else{
            return "ok";
        }
    }

    @Override
    public String getQrCode(Long userId) throws ServiceException {
        Wrapper wrapper=new EntityWrapper();
        UserDO userDO = userMapper.selectById(userId);
        wrapper.eq("driver_phone",userDO.getPhone());
        wrapper.eq("status", CarStatusType.STATUS20.getCode());
            return "error";
    }

    @Override
    public UserDTO updateAppVersion(String appid, String version, String imei) throws ServiceException {
        UserDTO userDTO = new UserDTO();
        userDTO.setNote("易粮易运需要升级至最新版本");
        userDTO.setVersion("1.0.8");
        userDTO.setUrl("https://taohaoliang.oss-cn-beijing.aliyuncs.com/app/eliangeyun.apk");
        userDTO.setWgtUrl("https://taohaoliang.oss-cn-beijing.aliyuncs.com/app/eliangeyun.apk");
        userDTO.setVersionCode("108");
        return userDTO;
    }

}
