package com.m2pool.lease.service.impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.m2pool.common.core.Result.R;
import com.m2pool.common.core.text.Convert;
import com.m2pool.common.core.utils.CodeUtils;
import com.m2pool.common.core.utils.GoogleAuthenticator;
import com.m2pool.common.core.utils.StringUtils;
import com.m2pool.common.core.utils.uuid.IdUtils;
import com.m2pool.common.core.web.Result.AjaxResult;
import com.m2pool.common.redis.service.RedisService;
import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.lease.dto.*;
import com.m2pool.lease.dto.v2.GoogleInfoDto;
import com.m2pool.lease.entity.*;
import com.m2pool.lease.exception.AuthException;
import com.m2pool.lease.exception.PaymentException;
import com.m2pool.lease.mapper.*;
import com.m2pool.lease.mq.message.RabbitmqPayRechargeMessage;
import com.m2pool.lease.mq.message.RabbitmqPayWithdrawMessage;
import com.m2pool.lease.redis.EmailCodeValue;
import com.m2pool.lease.redis.RedisAuthKey;
import com.m2pool.lease.service.LeaseUserService;
import com.m2pool.lease.utils.*;
import com.m2pool.lease.vo.*;
import com.m2pool.lease.vo.v2.GoogleBindVo;
import com.m2pool.lease.vo.v2.GoogleCloseVo;
import com.m2pool.lease.vo.v2.UserGoogleAuthInfo;
import com.m2pool.system.api.entity.EmailCodeEntity;
import com.m2pool.system.api.entity.EmailEntity;
import com.m2pool.system.api.entity.EmailTemplateEntity;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.m2pool.lease.constant.CoinCharge.getAllChargesAsDtoList;
import static com.m2pool.lease.constant.CoinCharge.getChargeByChainAndCoin;
import static com.m2pool.lease.constant.RabbitmqConstant.PAY_RECHARGE_QUEUE;
import static com.m2pool.lease.constant.RabbitmqConstant.PAY_WITHDRAW_QUEUE;
import static com.m2pool.lease.utils.JwtUtils.EXPIRETIME;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author yyb
 * @since 2025-07-23
 */
@Service
public class LeaseUserServiceImpl extends ServiceImpl<LeaseUserMapper, LeaseUser> implements LeaseUserService {

    @Resource
    private LeaseUserMapper leaseUserMapper;

    @Resource
    private LeaseUserWalletDataMapper leaseUserWalletDataMapper;

    @Resource
    private LeaseAutoAddressMapper leaseAutoAddressMapper;

    @Resource
    private RabbitTemplate  rabbitTemplate;

    @Resource
    private LeasePayWithdrawMessageMapper leasePayWithdrawMessageMapper;

    @Resource
    private LeasePayRechargeMessageMapper leasePayRechargeMessageMapper;


    @Resource
    private LeasePayRecordMessageMapper leasePayRecordMessageMapper;

    @Resource
    private LeaseShopConfigMapper leaseShopConfigMapper;

    @Resource
    private LeaseShopMapper leaseShopMapper;

    @Resource
    private RedisService redisService;

    @Autowired
    private JavaMailSenderImpl javaMailSender;

    @Resource
    private TemplateEngine templateEngine;

    @Value("${client.download.client.linux}")
    private String downloadClientLinux;

    @Value("${client.download.client.windows}")
    private String downloadClientWindows;

    @Value("${client.download.version}")
    private String version;

    @Value("${spring.mail.username}")
    private String sendMailer;


    @Value("${image.prefix}")
    private String imagePrefix;


    public static String EMAIL_REGEX="^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";

    public static String PWD_REGEX="^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)(?=.*[!@#$%^&*()./_])[A-Za-z\\d!@#$%^&*()./_]{8,32}$";
    public static final int PASSWORD_MIN_LENGTH = 6;

    public static final int PASSWORD_MAX_LENGTH = 32;



    public void verifyParams(String email, String password){
        if (StringUtils.isAnyBlank(email,password)) {
            throw new AuthException("邮箱/密码必须填写");
        }
        if(!StringUtils.isBlank(email)){
            if(!email.matches(EMAIL_REGEX)){
                throw new AuthException("邮箱格式错误");
            }
        }else {
            throw new AuthException("邮箱为必填项");
        }
        if(!password.matches(PWD_REGEX)){
            throw new AuthException("密码格式错误,密码应包含大写字母、小写字母、数字以及特殊字符，8到32位");
        }
        if (password.length() < PASSWORD_MIN_LENGTH || password.length() > PASSWORD_MAX_LENGTH) {
            throw new AuthException("用户密码长度应该为:"+PASSWORD_MIN_LENGTH+"~"+PASSWORD_MAX_LENGTH);
        }
    }

    /**
     * 验证码校验
     * @param key
     * @param code
     */
    public void verifyCode(String key, String code){
        if(redisService.hasKey(key)){
            Object o = redisService.getCacheObject(key);//user:emailCode:email
            EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class);
            //验证验证码
            if(!code.equals(emailCodeValue.getEmailCode())){
                throw new AuthException("验证码错误");
            }
        }else{
            throw new AuthException("验证码未获取或已过期，请重新获取验证码");
        }
    }

    /**
     * jwt 令牌创建
     * @param loginUser
     * @return
     */
    public Map<String, Object> createToken(LeaseUser loginUser)
    {
        String token = IdUtils.fastUUID();
        loginUser.setToken(token);
        //刷新token
        String userKey = RedisAuthKey.getUserLoginKey(loginUser.getUserId());
        redisService.setCacheObject(userKey, loginUser, EXPIRETIME, TimeUnit.MINUTES);

        // Jwt存储信息
        Map<String, Object> claimsMap = new HashMap<String, Object>();
        claimsMap.put(JwtUtils.USER_KEY, token);
        claimsMap.put(JwtUtils.DETAILS_USER_ID, loginUser.getId());
        claimsMap.put(JwtUtils.DETAILS_USERNAME, loginUser.getUserId());

        // 接口返回信息
        Map<String, Object> rspMap = new HashMap<String, Object>();
        rspMap.put("access_token", JwtUtils.createToken(claimsMap));
        rspMap.put("expires_in", EXPIRETIME);
        rspMap.put("userName", loginUser.getUserId());
        return rspMap;
    }


    @Override
    @Transactional
    public Result<Map<String, Object>> login(UserLoginVo userLoginVo) {
        String email = userLoginVo.getEmail();
        String password = userLoginVo.getPassword();
        String code = userLoginVo.getCode();
        //1.校验密码，邮箱等参数格式
        verifyParams(email,password);
        //2.验证码校验
        verifyCode(RedisAuthKey.getLoginCodeKey(email),code);
        //3.校验用户信息
        LeaseUser user = getUser(email);
        if (user == null){
            throw new AuthException("用户不存在或已注销");
        }
        //4.密码输入错误次数过多，禁止登录
        String userLockKey = RedisAuthKey.getUserLockKey(email);
        if(redisService.hasKey(userLockKey)){
            throw new AuthException("该账户因密码错误多次被暂时限制登陆24h！");
        }
        //校验登录密码是否是注册密码
        String passwordErrorCountKey =  RedisAuthKey.getPasswordErrorCountKey( email);
        if (!CryptoUtils.matchesPassword(password, user.getPassword())){
            int count = 0;
            if(redisService.hasKey(passwordErrorCountKey)){
                //判断key是否过期
                long expire = redisService.getExpire(passwordErrorCountKey, TimeUnit.SECONDS);
                if(expire > 0){
                    count = Convert.toInt(redisService.getCacheObject(passwordErrorCountKey));
                }
            }
            count++;
            if(count == 1){
                redisService.setCacheObject(passwordErrorCountKey,count,1L,TimeUnit.HOURS);
            }else if(count < 6){
                long expire = redisService.getExpire(passwordErrorCountKey, TimeUnit.SECONDS);
                System.out.println(expire);
                redisService.setCacheObject(passwordErrorCountKey,count,redisService.getExpire(passwordErrorCountKey,TimeUnit.SECONDS),TimeUnit.SECONDS);
            }
            if(count >= 5){
                //锁定用户账号 并清除输入错误次数的记录
                redisService.setCacheObject(userLockKey, email, 24L, TimeUnit.HOURS);
                //清除输入错误次数的记录
                redisService.deleteObject(passwordErrorCountKey);
            }
            throw new AuthException("密码错误,一小时内错误五次之后锁定账户24h,当前已失败"+count+"次");
        }
        user.setLastLoginTime(LocalDateTime.now());
        leaseUserMapper.updateById(user);
        return Result.success(createToken(user));
    }


    @Override
    @Transactional
    public Result<String> register(UserRegisterVo userRegisterVo) {
        String email = userRegisterVo.getUserEmail();
        String password = userRegisterVo.getPassword();
        //1.校验密码，邮箱等参数格式
        verifyParams(email,password);
        //2.验证码校验
        verifyCode(RedisAuthKey.getRegisterCodeKey(email),userRegisterVo.getCode());
       //3.注册用户信息
        int insert = leaseUserMapper.insert(LeaseUser.builder()
                .userId(email)
                .password(CryptoUtils.encryptPassword(password))
                .del(false)
                .build());
        if (insert > 0){
            return Result.success("注册成功");
        }
        return Result.fail("注册失败");
    }

    @Override
    @Transactional
    public Result<String> updatePassword(UserLoginVo userLoginVo) {
        String email = userLoginVo.getEmail();
        String password = userLoginVo.getPassword();
        String updatePwdCode = userLoginVo.getCode();
        //1.校验密码，邮箱等参数格式
        verifyParams(email,password);
        //2.验证码校验
        verifyCode(RedisAuthKey.getUpdatePasswordCodeKey(email),userLoginVo.getCode());
        //3.修改密码
        LeaseUser user = getUser(email);
        if (CryptoUtils.matchesPassword(password, user.getPassword())){
            throw new AuthException("新密码不能与原密码一致");
        }
        String updatePasswordCodeKey = RedisAuthKey.getUpdatePasswordCodeKey(email);
        if (redisService.hasKey(updatePasswordCodeKey)){
            Object o = redisService.getCacheObject(updatePasswordCodeKey);//user:emailCode:username
            EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class);
            if(updatePwdCode.equals(emailCodeValue.getEmailCode())){
                user.setPassword(CryptoUtils.encryptPassword(password));
                leaseUserMapper.updateById(user);
            }else {
                return Result.fail("邮箱验证码错误");
            }
        }

        return Result.success("修改密码成功");
    }

    @Override
    public Result<String> logout() {
        String userEmail = UserThreadLocal.getUserEmail();
        boolean b = redisService.deleteObject(RedisAuthKey.getUserLoginKey(userEmail));
        if (b){
            return Result.success("登出成功");
        }
        return Result.fail("登出失败");
    }


    @Override
    @Transactional
    public Result<String> sendLoginCode(EmailCodeVo emailCodeVo) {
        //1.邮箱格式校验(注解校验)
        //String email = emailCodeVo.getEmail();
        //if(!StringUtils.isBlank(email)){
        //    if(!email.matches(EMAIL_REGEX)){
        //        return  Result.fail("邮箱格式错误");
        //    }
        //}else {
        //    return  Result.fail("601,\"邮箱不能为空\"");
        //}
        String email = emailCodeVo.getEmail();
        //2.请求次数限制
        String loginCodeKey = RedisAuthKey.getLoginCodeKey(email);
        if (redisService.hasKey(loginCodeKey)){
            if (checkSendTimes(email, loginCodeKey,0)){
                return Result.fail("请求次数过多，请10分钟后再试,(同一用户10分钟内只能请求5次)");
            }
        }else{
            LeaseUser user = getUser(email);
            if (user == null){
                return Result.fail(606,"邮箱"+email+"未注册");
            }else{
                String emailCode = CodeUtils.creatCode(5);
                // 最多允许用户在10分钟内发送2次的邮箱验证
                // 0s倒计时后用户可以再发送验证码，但是间隔在10分钟内只能再发送1次
                EmailCodeValue emailCodeValue = new EmailCodeValue(
                        email, emailCode,1
                );

                sendLoginCodeMailMessage(email, emailCodeValue.getEmailCode());
                //设置失效时间10分钟
                redisService.setCacheObject(loginCodeKey, emailCodeValue,
                        10L, TimeUnit.MINUTES
                );
            }
        }
        return Result.success("发送验证码成功，验证码已经发送至用户邮箱");
    }

    @Override
    @Transactional
    public Result<String> sendRegisterCode(EmailCodeVo emailCodeVo) {
        String email = emailCodeVo.getEmail();
        //2.请求次数限制
        String loginCodeKey = RedisAuthKey.getRegisterCodeKey(email);
        if (redisService.hasKey(loginCodeKey)){
            if (checkSendTimes(email, loginCodeKey,1)){
                return Result.fail("请求次数过多，请10分钟后再试,(同一用户10分钟内只能请求5次)");
            }
        }else{
            LeaseUser user = getUser(email);
            if (user != null){
                return Result.fail(606,"邮箱"+email+"已被注册");
            }else{
                String emailCode = CodeUtils.creatCode(5);
                // 最多允许用户在10分钟内发送2次的邮箱验证
                // 0s倒计时后用户可以再发送验证码，但是间隔在10分钟内只能再发送1次
                EmailCodeValue emailCodeValue = new EmailCodeValue(
                        email, emailCode,1
                );

                sendRegisterMessage(email, emailCodeValue.getEmailCode());
                //设置失效时间10分钟
                redisService.setCacheObject(loginCodeKey, emailCodeValue,
                        10L, TimeUnit.MINUTES
                );
            }
        }
        return Result.success("发送验证码成功，验证码已经发送至用户邮箱");
    }



    @Override
    @Transactional
    public Result<String> sendUpdatePwdCode(EmailCodeVo emailCodeVo) {
        String email = emailCodeVo.getEmail();
        //2.请求次数限制
        String loginCodeKey = RedisAuthKey.getUpdatePasswordCodeKey(email);
        if (redisService.hasKey(loginCodeKey)){
            if (checkSendTimes(email, loginCodeKey,2)){
                return Result.fail("请求次数过多，请10分钟后再试,(同一用户10分钟内只能请求5次)");
            }
        }else{
            String emailCode = CodeUtils.creatCode(5);
            // 最多允许用户在10分钟内发送2次的邮箱验证
            // 0s倒计时后用户可以再发送验证码，但是间隔在10分钟内只能再发送1次
            EmailCodeValue emailCodeValue = new EmailCodeValue(
                    email, emailCode,1
            );

            sendLoginCodeMailMessage(email, emailCodeValue.getEmailCode());
            //设置失效时间10分钟
            redisService.setCacheObject(loginCodeKey, emailCodeValue,
                    10L, TimeUnit.MINUTES
            );
        }
        return Result.success("发送验证码成功，验证码已经发送至用户邮箱");
    }





    /**
     * 检查发送次数是否大于5次
     * @param email
     * @param key
     * @param type 0 登录验证码 1 注册验证码 2 修改密码验证码
     * @return
     */
    private boolean checkSendTimes(String email, String key,Integer type) {
        Object o = redisService.getCacheObject(key);
        EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class);
        if (emailCodeValue.getTimes() >= 5){
            return true;
        }else{
            String emailCode = CodeUtils.creatCode(6);
            emailCodeValue.setEmailCode(emailCode);
            emailCodeValue.setTimes(emailCodeValue.getTimes() + 1);
            long overTime = redisService.getExpire(key);
            redisService.setCacheObject(key, emailCodeValue, overTime, TimeUnit.SECONDS);
            if (type == 0){
                sendLoginCodeMailMessage(email, emailCodeValue.getEmailCode());
            }else if (type == 1){
                sendRegisterMessage(email, emailCodeValue.getEmailCode());
            }else if (type == 2){
                sendUpdatePwdMailMessage(email, emailCodeValue.getEmailCode());
            }

        }
        return false;
    }

    /**
     * 根据邮箱获取用户
     * @param userEmail
     * @return
     */
    public LeaseUser getUser(String userEmail){
        return leaseUserMapper.selectOne(new LambdaQueryWrapper<LeaseUser>()
                .eq(LeaseUser::getUserId,userEmail ).eq(LeaseUser::getDel,false));
    }

    /**
     * 发送登录验证码
     * @param to
     * @param code
     */
    public void sendLoginCodeMailMessage(String to, String code) {
        //String subject = "用户登录，邮箱验证码";
        //String text = "登录验证码10分钟内有效:\n\t"+code;
        Map<String, Object> content = new HashMap<>();
        content.put("code",code);
        content.put("text","Login verification code is valid within 10 minutes");
        EmailTemplateEntity entity = new EmailTemplateEntity(to,"User login, email verification code","emailCode-en",content);
        sendHtmlEmail(entity.getEmail(),entity.getSubject(),entity.getTemplateName(),entity.getData());
    }

    /**
     * 发送注册验证码
     * @param to
     * @param code
     */
    public void sendRegisterMessage(String to, String code) {
        //String subject = "账号注册，邮箱验证码";
        //String text = "注册验证码10分钟内有效:\n\t"+code;
        Map<String, Object> content = new HashMap<>();
        content.put("code",code);
        content.put("text","The verification code for registration is valid for 10 minutes");
        EmailTemplateEntity message = new EmailTemplateEntity(to,"Account registration, email verification code","emailCode-en",content);
        sendHtmlEmail(message.getEmail(),message.getSubject(),message.getTemplateName(),message.getData());
    }

    public void sendUpdatePwdMailMessage(String to, String code) {
        //String subject = "修改密码，邮箱验证码";
        //String text = "您正在修改密码，如果不是您本人操作，请忽略。验证码10分钟内有效:\n\t"+code;

        Map<String, Object> content = new HashMap<>();
        content.put("code",code);
        content.put("text","You are currently modifying your password. If this is not done by you, please ignore it. The verification code is valid for 10 minutes.");
        EmailTemplateEntity entity = new EmailTemplateEntity(to,"Change password, email verification code","emailCode-en",content);
        sendHtmlEmail(entity.getEmail(),entity.getSubject(),entity.getTemplateName(),entity.getData());
    }

    /**
     * 通过thymeleaf发送html邮件
     * @param to
     * @param subject
     * @param templateName
     * @param variables
     * @throws MessagingException
     */
    public void sendHtmlEmail(String to, String subject, String templateName, Map<String, Object> variables)  {

        // 创建 MimeMessage 对象
        MimeMessage message = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = null;
        try {
            helper = new MimeMessageHelper(message, true);
            //邮件发信人
            helper.setFrom(sendMailer);

            // 收件人一个活多个
            helper.setTo(to.split(","));
            helper.setSubject(subject);

            // 创建 Thymeleaf 上下文并添加变量
            Context context = new Context();
            //设置图片访问前缀
            variables.put("imagePrefix", imagePrefix);
            context.setVariables(variables);

            // 处理 HTML 模板
            String htmlContent = templateEngine.process(templateName, context);

            // 设置邮件内容为 HTML
            helper.setText(htmlContent, true);

            //邮件发送时间
            helper.setSentDate(new Date());

            // 发送邮件
            javaMailSender.send(message);
        } catch (Exception e) {
            System.out.println("发送失败原因"+e.getMessage());
            throw new AuthException("发送邮箱验证码失败,请稍后重试");
        }
    }

    @Value("${myenv.domain}")
    private String domain;

    @Override
    public Result<String> bindGoogle(GoogleBindVo googleBindVo) {
        // 参数校验
        if (StringUtils.isNull(googleBindVo)){
            return Result.fail("未获取到请求参数");
        }
        String email = UserThreadLocal.getUserEmail();
        if(StringUtils.isNull(email)){
            return Result.fail("token解析异常");
        }
        // 根据邮箱查询是否已绑定
        UserGoogleAuthInfo info = leaseUserMapper.getUserInfoByEmail(email);

        if(StringUtils.isBlank(info.getSecret())){
            //未绑定 正常走绑定流程
            //校验gcode
            boolean gResult = GoogleAuthenticator.checkCode(googleBindVo.getSecret(),
                    googleBindVo.getGCode(), System.currentTimeMillis());
            if(!gResult){
                return Result.fail("谷歌验证码错误");
            }
            //校验邮箱验证码
            String redisKey = RedisAuthKey.getGoogleOpenCodeKey(email);
            if(redisService.hasKey(redisKey)){

                Object o = redisService.getCacheObject(redisKey);//google:email:code

                EmailCodeEntity emailCodeEntity = JSON.parseObject(JSON.toJSONString(o), EmailCodeEntity.class);

                //验证验证码
                if(!googleBindVo.getECode().equals(emailCodeEntity.getEmailCode())){
                    return Result.fail("邮箱验证码错误");
                }
            }else {
                return Result.fail("邮箱验证码 未获取或已过期(有效期10分钟)，请重新获取验证码");
            }

            //校验密码
            if (!SecurityUtils.matchesPassword(googleBindVo.getPwd(), info.getPwd())){
                return Result.fail("密码错误");
            }
            //入库
            UserGoogleAuthInfo bingInfo = new UserGoogleAuthInfo();
            bingInfo.setSecret(googleBindVo.getSecret());
            bingInfo.setStatus(1);
            bingInfo.setEmail(info.getEmail());
            boolean bingResult = leaseUserMapper.bingGoogleAuthenticator(bingInfo);
            if(bingResult){
                return Result.success("绑定成功");
            }else {
                return Result.fail("请求服务器失败,请重试");
            }

        }else {
            if(info.getStatus()  == 2){
                return Result.fail("您绑定但是关闭了双重验证,请重新打开");
            }if(info.getStatus()  == 1){
                return Result.fail("当前用户已绑定过谷歌登录器");
            }else {
                return Result.fail("当前用户已绑定过谷歌登录器.");
            }
        }
    }

    @Override
    public Result<GoogleInfoDto> getBindInfo() {
        String email = UserThreadLocal.getUserEmail();
        if(StringUtils.isNull(email)){
            return Result.fail("token解析异常");
        }
        GoogleInfo info = leaseUserMapper.getGoogleInfoByEmail(email);

        GoogleInfoDto dto = new GoogleInfoDto();
        if(StringUtils.isBlank(info.getSecret())){
            //生成谷歌验证码
            String secret = GoogleAuthenticator.getSecretKey();
            String qrCodeText = GoogleAuthenticator.getQrCodeText(secret, email, domain);
            String img = QrCodeUtils.creatRrCode(qrCodeText, 200, 200);

            boolean ifExist = leaseUserMapper.checkSecretIfExist(secret);
            if(ifExist){
                secret = GoogleAuthenticator.getSecretKey();
                qrCodeText = GoogleAuthenticator.getQrCodeText(secret, email, domain);
                img = QrCodeUtils.creatRrCode(qrCodeText,200,200);

                ifExist = leaseUserMapper.checkSecretIfExist(secret);
                if(ifExist){
                    return Result.fail("网络异常,请稍后再试");
                }
            }
            dto.setSecret(secret);
            dto.setImg(img);

        }else {
            //数据库已有谷歌验证码 直接返回
            BeanUtils.copyProperties(info,dto);
            dto.setImg(QrCodeUtils.creatRrCode( GoogleAuthenticator.getQrCodeText(dto.getSecret(),email  , domain),200,200));
        }
        return Result.success(dto);
    }

    @Override
    @Transactional
    public Result<String> sendOpenGoogleCode() {
        String email = UserThreadLocal.getUserEmail();
        if(StringUtils.isNull(email)){
            return Result.fail("token解析异常");
        }
        boolean success = true;

        //判断用户是不是恶意刷邮箱，在规定时间内进行的
        String redisKey = RedisAuthKey.getGoogleOpenCodeKey( email);
        if (redisService.hasKey(redisKey)) {
            Object o = redisService.getCacheObject(redisKey);

            EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class);
            if (emailCodeValue.getTimes() > 4) {
                return Result.fail("请求次数过多，请10分钟后再试,(同一用户10分钟内只能请求4次)");
            } else {

                String emailCode = CodeUtils.creatCode(6);
                emailCodeValue.setEmailCode(emailCode);
                emailCodeValue.setTimes(emailCodeValue.getTimes() + 1);
                long overTime = redisService.getExpire(redisKey);
                redisService.setCacheObject(redisKey, emailCodeValue, overTime, TimeUnit.SECONDS
                );
                success = sendEmail(email,emailCode);
            }
        } else {
            String emailCode = CodeUtils.creatCode(6);
            // 最多允许用户在10分钟内发送2次的邮箱验证
            // 0s倒计时后用户可以再发送验证码，但是间隔在10分钟内只能再发送1次
            EmailCodeValue emailCodeValue = new EmailCodeValue();
            emailCodeValue.setEmail(email);
            emailCodeValue.setEmailCode(emailCode);
            emailCodeValue.setTimes(1);
            //设置失效时间10分钟
            redisService.setCacheObject(redisKey, emailCodeValue,
                    10L, TimeUnit.MINUTES
            );
            success =  sendEmail(email,emailCode);
        }

        return success  ? Result.success("发送邮箱验证码成功") : Result.fail("发送邮箱验证码失败,请稍后重试");
    }

    public boolean sendEmail(String email,String code){
        String text = "您正在绑定谷歌验证器！\n" +
                "您的邮箱验证验证码是:\n\t"+code +
                "\n此验证码10分钟有效。";
        try {
            //true 代表支持复杂的类型
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(),true);
            //邮件发信人
            mimeMessageHelper.setFrom(sendMailer);
            //邮件收信人  1或多个
            mimeMessageHelper.setTo(email);
            //邮件主题
            mimeMessageHelper.setSubject("[M2Pool] 邮箱验证码");
            //邮件内容
            mimeMessageHelper.setText(text);
            //邮件发送时间
            mimeMessageHelper.setSentDate(new Date());

            //发送邮件
            javaMailSender.send(mimeMessageHelper.getMimeMessage());
            //System.out.println("发送邮件成功："+sendMailer+"->"+to);
            return true;
        } catch (Exception e) {
            System.out.println("发送邮件失败："+e.getMessage());
            return  false;
        }
    }


    @Override
    public Result<String> sendCloseGoogleCode() {
        String email = SecurityUtils.getUsername();
        if(StringUtils.isNull(email)){
            return Result.fail("token解析异常");
        }

        boolean success;
        //判断用户是不是恶意刷邮箱，在规定时间内进行的
        String redisKey = RedisAuthKey.getGoogleCloseCodeKey( email);
        if (redisService.hasKey(redisKey)) {
            Object o = redisService.getCacheObject(redisKey);

            EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class);
            if (emailCodeValue.getTimes() > 4) {
                return Result.fail("请求次数过多，请10分钟后再试,(同一用户10分钟内只能请求4次)");
            } else {

                String emailCode = CodeUtils.creatCode(6);
                emailCodeValue.setEmailCode(emailCode);

                emailCodeValue.setTimes(emailCodeValue.getTimes() + 1);
                long overTime = redisService.getExpire(redisKey);
                redisService.setCacheObject(redisKey, emailCodeValue, overTime, TimeUnit.SECONDS
                );
                success = sendEmail(email,emailCode);
            }
        } else {
            String emailCode = CodeUtils.creatCode(6);
            // 最多允许用户在10分钟内发送2次的邮箱验证
            // 0s倒计时后用户可以再发送验证码，但是间隔在10分钟内只能再发送1次
            EmailCodeEntity emailCodeEntity = new EmailCodeEntity();
            emailCodeEntity.setEmail(email);
            emailCodeEntity.setEmailCode(emailCode);
            emailCodeEntity.setTimes(1);
            //设置失效时间10分钟
            redisService.setCacheObject(redisKey, emailCodeEntity,
                    10L, TimeUnit.MINUTES
            );
            success = sendCloseEmail(email,emailCode);
        }
        return success  ? Result.success("发送邮箱验证码成功") : Result.fail("发送邮箱验证码失败,请稍后重试");
    }
    public boolean sendCloseEmail(String email,String code){
        String text = "M2Pool:\n" +
                "您正在解绑谷歌验证器！\n" +
                "您的邮箱验证验证码是:\n\t"+code +
                "\n此验证码10分钟有效。";
        try {
            //true 代表支持复杂的类型
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(),true);
            //邮件发信人
            mimeMessageHelper.setFrom(sendMailer);
            //邮件收信人  1或多个
            mimeMessageHelper.setTo(email);
            //邮件主题
            mimeMessageHelper.setSubject("[M2Pool] 邮箱验证码");
            //邮件内容
            mimeMessageHelper.setText(text);
            //邮件发送时间
            mimeMessageHelper.setSentDate(new Date());

            //发送邮件
            javaMailSender.send(mimeMessageHelper.getMimeMessage());
            //System.out.println("发送邮件成功："+sendMailer+"->"+to);
            return true;
        } catch (Exception e) {
            System.out.println("发送邮件失败："+e.getMessage());
            return  false;
        }
    }

    @Override
    public Result<String> closeStepTwo(GoogleCloseVo vo) {
        // 参数校验
        if (StringUtils.isNull(vo)){
            return Result.fail("未获取到请求参数");
        }
        String email = SecurityUtils.getUsername();
        if(StringUtils.isNull(email)){
            return Result.fail("token解析异常");
        }

        // 根据邮箱查询是否已绑定
        UserGoogleAuthInfo info = leaseUserMapper.getUserInfoByEmail(email);

        if(StringUtils.isBlank(info.getSecret())){
            return Result.success();
        }else {

            //已绑定 正常走关闭谷歌验证流程
            //校验gcode
            boolean gResult = GoogleAuthenticator.checkCode(info.getSecret(), vo.getGCode(), System.currentTimeMillis());
            if(!gResult){
                return Result.fail("谷歌验证码错误");
            }
            //校验邮箱验证码
            String redisKey = RedisAuthKey.getGoogleCloseCodeKey( email);
            if(redisService.hasKey(redisKey)){

                Object o = redisService.getCacheObject(redisKey);//google:email:close

                EmailCodeValue emailCodeValue = JSON.parseObject(JSON.toJSONString(o), EmailCodeValue.class);

                //验证验证码
                if(!vo.getECode().equals(emailCodeValue.getEmailCode())){
                    return Result.fail("邮箱验证码错误");
                }
            }else {
                return Result.fail("邮箱验证码 未获取或已过期(有效期10分钟)，请重新获取验证码");
            }

            //入库
            boolean bingResult = leaseUserMapper.closeGoogleAuthenticator(info);
            if(bingResult){
                return Result.success();
            }else {
                return Result.fail("请求服务器失败,请重试");
            }
        }
    }

    @Override
    @Transactional
    public Result<UserWalletDataDto> bindWallet(ChainAndCoinVo chainAndCoinVo) {
        //同一用户同一个链只会存在一个钱包
        List<UserWalletDataDto> userWalletDataDtoList = leaseUserWalletDataMapper.getWalletInfoByChain(chainAndCoinVo.getChain(), SecurityUtils.getUsername());
        UserWalletDataDto result;
        //如果某个链 未查找到钱包地址 去新绑定一个
        if (userWalletDataDtoList.isEmpty()){
            LeaseAutoAddress bindAddress = getBindAddress(chainAndCoinVo.getChain(), chainAndCoinVo.getCoin());
            if (bindAddress != null){
                        String address = bindAddress.getAddress();
                        String qrcode = QrCodeUtils.creatRrCode(bindAddress.getAddress(), 200, 200);
                        String username = SecurityUtils.getUsername();
                        String queueId = UuidGeneratorUtil.generateUuidWithoutHyphen();
                result = UserWalletDataDto.builder()
                                .queueId(queueId)
                                .fromAddress(address)
                                .fromChain(chainAndCoinVo.getChain())
                                .fromSymbol(chainAndCoinVo.getCoin())
                                .balance(BigDecimal.ZERO)
                                .userId(username)
                                .qrcode(qrcode)
                                .id(bindAddress.getId())
                                .build();
                leaseUserWalletDataMapper.insert(LeaseUserWalletData.builder()
                        .queueId(queueId)
                        .fromChain(chainAndCoinVo.getChain())
                        .fromAddress(address)
                        .fromSymbol(chainAndCoinVo.getCoin())
                        .qrcode(qrcode)
                        .userId( username)
                        .balance(BigDecimal.ZERO)
                        .build());
                bindAddress.setStatus(1);
                leaseAutoAddressMapper.updateById(bindAddress);
                //监听钱包
                sendMessage(result);
            }else{
                return Result.fail("没有可用的地址,请联系管理员生成可用的钱包地址");
            }
        }else{
            for (UserWalletDataDto userWalletDataDto : userWalletDataDtoList) {
                if (userWalletDataDto.getFromSymbol().equals(chainAndCoinVo.getCoin())){
                    //开发环境
                    //sendMessage(UserWalletDataDto.builder()
                    //        .queueId(userWalletDataDto.getQueueId())
                    //        .fromAddress(userWalletDataDto.getFromAddress())
                    //        .fromChain(chainAndCoinVo.getChain())
                    //        .fromSymbol(chainAndCoinVo.getCoin())
                    //        .balance(BigDecimal.ZERO)
                    //        .userId(userWalletDataDto.getUserId())
                    //        .qrcode(userWalletDataDto.getQrcode())
                    //        .build());
                  return  Result.success(userWalletDataDto);
                }
            }
            UserWalletDataDto userWalletDataDto = userWalletDataDtoList.get(0);
            result = UserWalletDataDto.builder()
                    .queueId(userWalletDataDto.getQueueId())
                    .fromAddress(userWalletDataDto.getFromAddress())
                    .fromChain(chainAndCoinVo.getChain())
                    .fromSymbol(chainAndCoinVo.getCoin())
                    .balance(BigDecimal.ZERO)
                    .userId(userWalletDataDto.getUserId())
                    .qrcode(userWalletDataDto.getQrcode())
                    .build();

            LeaseUserWalletData build = LeaseUserWalletData.builder()
                    .queueId(userWalletDataDto.getQueueId())
                    .fromChain(userWalletDataDto.getFromChain())
                    .fromAddress(userWalletDataDto.getFromAddress())
                    .fromSymbol(chainAndCoinVo.getCoin())
                    .qrcode(userWalletDataDto.getQrcode())
                    .userId(userWalletDataDto.getUserId())
                    .balance(BigDecimal.ZERO)
                    .build();
            //链钱包存在 但是新增币种
            leaseUserWalletDataMapper.insert(build);
        }

        return Result.success(result);
    }


    @Override
    @Transactional
    public Result<List<UserWalletDataDto>> getWalletInfo() {
        List<UserWalletDataDto> userWalletDataDto = leaseUserWalletDataMapper.selectByUserId(SecurityUtils.getUsername());
        //获取手续费
        for (UserWalletDataDto walletDataDto : userWalletDataDto) {
            BigDecimal chargeByChainAndCoin = getChargeByChainAndCoin(walletDataDto.getFromChain(), walletDataDto.getFromSymbol());
            walletDataDto.setCharge(chargeByChainAndCoin);
        }

        if (!userWalletDataDto.isEmpty()){
            List<UserWalletDataDto> collect = userWalletDataDto.stream().peek(item -> item.setBalance(item.getBalance().subtract(item.getBlockedBalance()))).collect(Collectors.toList());
            return Result.success(collect);
        }
        return Result.success(null);
    }

    /**
     * 检查地址是否半年没有操作（支付，充值，提现）  这个方法可以改成监测 钱包（提现和充值都会修改钱包） 的最近修改时间 + 支付操作
     * @param fromAddress
     * @return
     */
    public boolean checkAddress(String fromAddress){
        //支付操作
        boolean b = leasePayRecordMessageMapper.checkPayOperator(fromAddress);
        //提现操作
        boolean b1 = leasePayWithdrawMessageMapper.checkWithdrawOperator(fromAddress);
        //充值操作
        boolean b2 = leasePayRechargeMessageMapper.checkRechargeOperator(fromAddress);
        if (b || b1 || b2){
            return true;
        }
        return false;
    }

    /**
     * 获取一个未使用的地址
     * @return
     */
    public LeaseAutoAddress getBindAddress(String chain, String coin){
        LeaseAutoAddress leaseAutoAddress = leaseAutoAddressMapper.selectOne(new LambdaQueryWrapper<LeaseAutoAddress>()
                .eq(LeaseAutoAddress::getStatus, 0)
                .eq(LeaseAutoAddress::getFromChain, chain)
                .last("limit 1"));
        return leaseAutoAddress;
    }

    /**
     * 发送mq 消息，从而启动钱包监听
     */
   public void sendMessage(UserWalletDataDto userWalletDataDto){
       try {
           System.out.println("发送mq消息");
           long l = System.currentTimeMillis()/1000;
           RabbitmqPayRechargeMessage build = RabbitmqPayRechargeMessage.builder()
                   .queue_id(userWalletDataDto.getQueueId())
                   .address(userWalletDataDto.getFromAddress())
                   .chain(userWalletDataDto.getFromChain())
                   .symbol(userWalletDataDto.getFromSymbol())
                   .timestamp(l)
                   .sign(HashUtils.sha256(l))
                   .build();
           rabbitTemplate.convertAndSend(PAY_RECHARGE_QUEUE,build);
       }catch (Exception e){
           throw new PaymentException("获取钱包信息失败,请刷新重试");
       }
   }


    @Override
    @DSTransactional
    public Result<String> withdrawBalance(BalanceVo balanceVo) {
        String username = SecurityUtils.getUsername();
        UserWalletDataDto userWalletDataDto = leaseUserWalletDataMapper.
                getWalletInfoByAddress(balanceVo.getFromAddress(),balanceVo.getToSymbol(),username);
        if (balanceVo.getAmount().compareTo(BigDecimal.valueOf(2)) < 0){
            return Result.fail("提现失败:提现金额不能小于 2 USDT");
        }

        if (StringUtils.isNull(userWalletDataDto) || StringUtils.isEmpty(userWalletDataDto.getFromAddress())){
            return Result.fail("未查询到需要提现的钱包地址");
        }
        //校验地址
        if (StringUtils.isEmpty(balanceVo.getToAddress()) || StringUtils.isEmpty(balanceVo.getToChain()) || StringUtils.isEmpty(balanceVo.getToSymbol()) ){
            return Result.fail("提现地址,链,币种 不能为空");
        }

        if (!WalletRuleCheckUtils.checkAddress(balanceVo.getToChain(),balanceVo.getToAddress())){
            return Result.fail("提现收款地址格式不正确");
        }

        //获取未冻结余额
        BigDecimal noBlockedBalance = userWalletDataDto.getBalance().subtract(userWalletDataDto.getBlockedBalance());

        //校验余额是否足够
        if (noBlockedBalance.compareTo(balanceVo.getAmount()) < 0){
            return Result.fail("提现失败:当前未冻结余额:"+noBlockedBalance+"USDT,需提现金额："+balanceVo.getAmount()+"USDT");
        }

        GoogleInfo googleInfo = leaseUserMapper.getGoogleInfoByEmail(SecurityUtils.getUsername());
        //开发环境
        //if(googleInfo == null || StringUtils.isBlank(googleInfo.getSecret())){
        //    //未绑定定谷歌验证器
        //    return Result.fail("您的账号未开启双重验证,请先开启验证!");
        //}
        //if(!GoogleAuthenticator.checkCode(googleInfo.getSecret(), balanceVo.getCode(), System.currentTimeMillis())){
        //    return Result.fail("谷歌验证码错误");
        //}

        //同一个from只能存在一笔提现中的订单
        Integer recentlyOneData = leasePayWithdrawMessageMapper.getRecentlyOneData(balanceVo.getFromAddress(), balanceVo.getToChain());
        if (recentlyOneData != null && recentlyOneData == 2){
            return Result.fail("提现申请失败,已存在一笔金额正在提现中...");
        }

        BigDecimal serviceCharge = getChargeByChainAndCoin(balanceVo.getToChain(), balanceVo.getToSymbol());
        String queueId = UuidGeneratorUtil.generateUuidWithoutHyphen();
        Long timestamp = System.currentTimeMillis()/1000;
        //验证通过存储提现记录
        int insert = leasePayWithdrawMessageMapper.insert(LeasePayWithdrawMessage.builder()
                        .queueId(queueId)
                        .toChain(balanceVo.getToChain())
                        .toSymbol(balanceVo.getToSymbol())
                        .fromChain(userWalletDataDto.getFromChain())
                        .fromSymbol(userWalletDataDto.getFromSymbol())
                        .fromAddress(userWalletDataDto.getFromAddress())
                        .serviceCharge(serviceCharge)
                        .toAddress(balanceVo.getToAddress())
                        .amount(balanceVo.getAmount())
                .build());

        LeaseUserWalletData build = LeaseUserWalletData.builder()
                .id(userWalletDataDto.getId())
                .blockedBalance(userWalletDataDto.getBlockedBalance().add(balanceVo.getAmount()))
                .build();
        //toAddress 为空，或则与这次请求的钱包地址不同，修改表中toAddress 的值为本次请求参数的钱包地址
        if (userWalletDataDto.getToAddress().isEmpty() || !userWalletDataDto.getToAddress().equals(balanceVo.getToAddress())){
            build.setToAddress(balanceVo.getToAddress());
            build.setToChain(balanceVo.getToChain());
            build.setToSymbol(balanceVo.getToSymbol());
        }
        //修改钱包冻结余额 乐观锁
        int i = leaseUserWalletDataMapper.update(build, new LambdaUpdateWrapper<LeaseUserWalletData>()
                .eq(LeaseUserWalletData::getBlockedBalance, userWalletDataDto.getBlockedBalance()));
        if (insert > 0 && i > 0){
            //发送mq消息到队列
            RabbitmqPayWithdrawMessage message = RabbitmqPayWithdrawMessage.builder()
                    .queue_id(queueId)
                    .from_address(userWalletDataDto.getFromAddress())
                    .to_address(balanceVo.getToAddress())
                    .amount(balanceVo.getAmount().subtract(serviceCharge))
                    .chain(balanceVo.getToChain())
                    .symbol(balanceVo.getToSymbol())
                    .fee(serviceCharge)
                    .timestamp(timestamp)
                    .sign(HashUtils.sha256(timestamp))
                    .build();
            try {
                //发送 提现 消息
                rabbitTemplate.convertAndSend(PAY_WITHDRAW_QUEUE,message);
                return Result.success("手续费=" + serviceCharge.toString() + ";消息ID=" + queueId,"提现申请成功");
            } catch (Exception e) {
                throw new PaymentException("提现失败,失败原因"+e.getMessage());
            }
        }
        throw new PaymentException("提现失败,冻结金额已变化,请重新提现！");
    }

    @Override
    public PageResult<PayWithdrawMessageDto> balanceWithdrawList(BalancePageVo balancePageVo) {

        String userEmail = SecurityUtils.getUsername();
        List<LeaseUserWalletData> walletList = leaseUserWalletDataMapper.selectList(new LambdaQueryWrapper<LeaseUserWalletData>()
                .eq(LeaseUserWalletData::getUserId,userEmail));
        PageHelper.startPage(balancePageVo.getPageNum(), balancePageVo.getPageSize());
        List<PayWithdrawMessageDto> payWithdrawMessageDtos = leasePayWithdrawMessageMapper
                .balanceWithdrawList(
                        walletList,
                        balancePageVo.getStatus());
        PageInfo<PayWithdrawMessageDto> pageInfo = new PageInfo<>(payWithdrawMessageDtos);
        PageResult<PayWithdrawMessageDto> success = PageResult.success(payWithdrawMessageDtos);
        success.setTotal(pageInfo.getTotal());
        success.setTotalPage(pageInfo.getPages());
        PageHelper.clearPage();
        return success;
    }

    @Override
    public PageResult<PayRechargeMessageDto> balanceRechargeList(BalancePageVo balancePageVo) {

        List<LeaseUserWalletData> walletList = leaseUserWalletDataMapper.selectList(new LambdaQueryWrapper<LeaseUserWalletData>()
                .eq(LeaseUserWalletData::getUserId, SecurityUtils.getUsername()));
        PageHelper.startPage(balancePageVo.getPageNum(), balancePageVo.getPageSize());
        List<PayRechargeMessageDto> payWithdrawMessageDtos = leasePayRechargeMessageMapper.balanceRechargeList(walletList,balancePageVo.getStatus());
        PageInfo<PayRechargeMessageDto> pageInfo = new PageInfo<>(payWithdrawMessageDtos);
        PageResult<PayRechargeMessageDto> success = PageResult.success(payWithdrawMessageDtos);
        success.setTotal(pageInfo.getTotal());
        success.setTotalPage(pageInfo.getPages());
        PageHelper.clearPage();
        return success;
    }

    @Override
    public PageResult<PayRecordMessageDto> balancePayList(BalancePageVo balancePageVo) {
       //获取店铺信息
        LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>().eq(LeaseShop::getUserEmail, SecurityUtils.getUsername()));
        if (leaseShop == null){
            //不存在店铺
            return PageResult.success(new ArrayList<>());
        }
        //获取卖家的自定义钱包地址
        List<LeaseShopConfig> leaseShopConfigs = leaseShopConfigMapper.selectList(new LambdaQueryWrapper<LeaseShopConfig>()
                .eq(LeaseShopConfig::getShopId, leaseShop.getId()));
        if(leaseShopConfigs.isEmpty()){
            //交易流水为空
            return PageResult.success(new ArrayList<>());
        }
        PageHelper.startPage(balancePageVo.getPageNum(), balancePageVo.getPageSize());
        List<PayRecordMessageDto> payRecordMessageDtos = leasePayRecordMessageMapper.balancePayList(leaseShopConfigs,balancePageVo.getStatus());
        PageInfo<PayRecordMessageDto> pageInfo = new PageInfo<>(payRecordMessageDtos);
        PageResult<PayRecordMessageDto> success = PageResult.success(payRecordMessageDtos);
        success.setTotal(pageInfo.getTotal());
        success.setTotalPage(pageInfo.getPages());
        PageHelper.clearPage();
        return success;
    }

    @Override
    public PageResult<TransactionRecordDto> transactionRecord(RecordTypePageVo recordTypePageVo) {
        Integer status = recordTypePageVo.getStatus();
        //先查到用户绑定的所有钱包
        List<LeaseUserWalletData> walletList = leaseUserWalletDataMapper.selectList(new LambdaQueryWrapper<LeaseUserWalletData>()
                .eq(LeaseUserWalletData::getUserId, SecurityUtils.getUsername()));
        if (walletList.isEmpty()){
            //不存在钱包
            return PageResult.success(new ArrayList<>());
        }
        PageHelper.startPage(recordTypePageVo.getPageNum(), recordTypePageVo.getPageSize());
        List<TransactionRecordDto> transactionRecordDtoList = new ArrayList<>();
        //查询支付记录
        if (status == 0){
            transactionRecordDtoList = leasePayRecordMessageMapper.transactionRecord(walletList);
        }
        //查询提现记录
        if (status == 1){
            transactionRecordDtoList = leasePayWithdrawMessageMapper.transactionRecord(walletList);
        }
        //查询充值记录
        if (status == 2){
            transactionRecordDtoList = leasePayRechargeMessageMapper.transactionRecord(walletList);
        }

        PageInfo<TransactionRecordDto> pageInfo = new PageInfo<>(transactionRecordDtoList);
        PageResult<TransactionRecordDto> success = PageResult.success(transactionRecordDtoList);
        success.setTotal(pageInfo.getTotal());
        success.setTotalPage(pageInfo.getPages());
        PageHelper.clearPage();
        return success;
    }

    @Override
    public Result<List<RecentlyTransactionDto>> getRecentlyTransaction() {
        List<LeaseUserWalletData> walletList = leaseUserWalletDataMapper.selectList(new LambdaQueryWrapper<LeaseUserWalletData>()
                .eq(LeaseUserWalletData::getUserId, SecurityUtils.getUsername()));
        List<RecentlyTransactionDto> recentlyTransaction = new ArrayList<>();
        if (!walletList.isEmpty()){
            recentlyTransaction = leasePayRecordMessageMapper.getRecentlyTransaction(walletList);
            recentlyTransaction.addAll(leasePayWithdrawMessageMapper.getRecentlyTransaction(walletList)) ;
            recentlyTransaction.addAll(leasePayRechargeMessageMapper.getRecentlyTransaction(walletList));
            // 按 updateTime 倒序排序，并取前五个元素
            recentlyTransaction = recentlyTransaction.stream()
                    .sorted((dto1, dto2) -> dto2.getUpdateTime().compareTo(dto1.getUpdateTime()))
                    .limit(5)
                    .collect(Collectors.toList());
        }
        return Result.success(recentlyTransaction);
    }


    @Override
    public Result<List<ChargeDto>> getCharge() {
        List<ChargeDto> allChargesAsDtoList = getAllChargesAsDtoList();
        return Result.success(allChargesAsDtoList);
    }

    public String getAuthCode(String userEmail){

        System.out.println("获取下载权限码"+userEmail);
        LeaseShop leaseShop = leaseShopMapper.selectOne(new LambdaQueryWrapper<LeaseShop>()
                .select(LeaseShop::getUserEmail)
                .eq(LeaseShop::getUserEmail, userEmail));

        if (leaseShop == null){
            throw new RuntimeException("下载失败,下载客户端权限,请重新登录并创建店铺");
        }
        // 直接把邮箱作为身份码
        //if (StringUtils.isEmpty(leaseShop.getIdentityCode())) {
        //    leaseShop.setIdentityCode(UuidGeneratorUtil.generateUuidWithoutHyphen());
        //    leaseShopMapper.updateById(leaseShop);
        //}
        return leaseShop.getUserEmail();
    }


    @Override
    public void downloadClient(String userEmail,String type,HttpServletRequest request, HttpServletResponse response) {
        String auth = getAuthCode(userEmail);
        //设置响应头信息
        response.reset();
        response.setCharacterEncoding("utf-8");
        response.setContentType("multipart/form-data");
        //设置压缩包的名字，date为时间戳
        String downloadName = "gpu.zip";
        String agent = request.getHeader("USER-AGENT");
        try {
            //针对IE或者以IE为内核的浏览器：
            if (agent.contains("MSIE") || agent.contains("Trident")) {
                downloadName = URLEncoder.encode(downloadName, "UTF-8");
            } else {
                //非IE浏览器的处理：
                downloadName = new String(downloadName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
            }

            // 创建临时目录
            Path tempDir = Files.createTempDirectory("client-download");


            // 创建auth.txt文件
            Path authFile = tempDir.resolve("auth");
            Files.write(authFile, auth.getBytes(StandardCharsets.UTF_8));

            //读取/home/ubuntu/web/client/linux 或 /home/ubuntu/web/client/windows 下所有文件和 auth文件一起压缩
            Path sourceDir = Paths.get(downloadClientLinux);
            try {
                Files.walk(sourceDir)
                        .filter(path -> !Files.isDirectory(path))
                        .forEach(path -> {
                            try {
                                Path dest = tempDir.resolve(sourceDir.relativize(path));
                                Files.createDirectories(dest.getParent());
                                Files.copy(path, dest);
                            } catch (IOException e) {
                                log.error("复制客户端文件失败", e);
                            }
                        });
            } catch (IOException e) {
                log.error("遍历客户端文件失败", e);
            }

            // 创建新的zip文件
            Path zipFile = Files.createTempFile("client", ".zip");
            try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipFile.toFile().toPath()))) {
                // 添加解压后的文件和auth.txt到新zip
                Files.walk(tempDir)
                        .filter(path -> !Files.isDirectory(path))
                        .forEach(path -> {
                            try {
                                // 修改这里，确保文件名编码正确
                                String entryName = tempDir.relativize(path).toString().replace('\\', '/');
                                ZipEntry zipEntry = new ZipEntry(entryName);
                                zos.putNextEntry(zipEntry);
                                Files.copy(path, zos);
                                zos.closeEntry();
                            } catch (IOException e) {
                                log.error("添加文件到zip失败", e);
                            }
                        });
            }

            // 设置响应头
            response.setHeader("Content-Disposition", "attachment;fileName=" + downloadName);
            response.setContentLength((int) Files.size(zipFile));

            // 发送文件
            Files.copy(zipFile, response.getOutputStream());
            response.flushBuffer();

            // 清理临时文件
            Files.walk(tempDir)
                    .sorted(Comparator.reverseOrder())
                    .forEach(path -> {
                        try {
                            Files.deleteIfExists(path);
                        } catch (IOException e) {
                            System.out.println("清理临时文件失败");
                        }
                    });
            Files.deleteIfExists(zipFile);

        } catch (Exception e) {
            log.error("系统异常", e);
            throw new RuntimeException("下载客户端失败", e);
        }

    }



    @Override
    public String getClientVersion() {
        try {
            return readTxtFromDir(downloadClientLinux, version);
        }catch (Exception e){
            return "获取配置信息失败"+e;
        }

    }


    /**
     * 读取ZIP压缩文件中指定txt文件的内容
     * @param filePath 文件目录
     * @param targetTxtName 目标txt文件名（含扩展名，如"config.txt"）
     * @return txt文件内容字符串
     * @throws IOException 文件操作异常
     */
    public String readTxtFromDir(String filePath, String targetTxtName) throws IOException {
       //遍历 zipFilePath 然后找到文件名未targetTxtName（带后缀） 的文件
        File dir = new File(filePath);
        if (!dir.isDirectory()) {
            throw new IllegalArgumentException("提供的路径不是一个目录");
        }

        File[] files = dir.listFiles((dir1, name) -> name.equals(targetTxtName));
        if (files == null || files.length == 0) {
            throw new FileNotFoundException("未找到目标文件: " + targetTxtName);
        }

        return new String(Files.readAllBytes(files[0].toPath()), StandardCharsets.UTF_8);
    }

}
