package com.ai.da.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.AuthenticationOperationTypeEnum;
import com.ai.da.common.enums.CreditsEventsEnum;
import com.ai.da.common.enums.LoginTypeEnum;
import com.ai.da.common.enums.ProductEnum;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.response.ResultEnum;
import com.ai.da.common.security.jwt.JWTTokenHelper;
import com.ai.da.common.utils.*;
import com.ai.da.mapper.primary.*;
import com.ai.da.mapper.primary.entity.*;
import com.ai.da.model.dto.*;
import com.ai.da.model.enums.AutoApproved;
import com.ai.da.model.enums.Language;
import com.ai.da.model.vo.*;
import com.ai.da.service.*;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 服务实现类
 *
 * @author easy-generator
 * @since 2022-07-06
 */
@Slf4j
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {
    @Resource
    private AccountMapper accountMapper;

    @Resource
    private AccountExtendMapper accountExtendMapper;

    @Resource
    private JWTTokenHelper jwtTokenHelper;

    @Resource
    private AccountLoginLogService accountLoginLogService;

    @Resource
    private ConvenientInquiryService convenientInquiryService;

    @Resource
    private LibraryService libraryService;

    @Resource
    private OrderInfoService orderInfoService;

    @Resource
    private OrganizationMapper organizationMapper;

    @Resource
    private TrialOrderMapper trialOrderMapper;

    @Resource
    private TrialOrderService trialOrderService;

    @Resource
    private QuestionnaireMapper questionnaireMapper;

    @Resource
    private CreditsService creditsService;

    @Resource
    private MinioUtil minioUtil;

    @Value("${minio.bucketName.users}")
    private String userBucket;

    @Value("${google.client.id}")
    private String googleClientId;

    @Value("${google.client.secret}")
    private String googleClientSecret;

    @Value("${google.redirect.uri}")
    private String googleRedirectUri;


    @Resource
    private RedisUtil redisUtil;

    @Resource
    private StripeService stripeService;

    @Resource
    private AffiliateService affiliateService;

    @Resource
    private UserFollowService userFollowService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AccountPreLoginVO preLogin(AccountPreLoginDTO accountDTO) {
        log.info("aida预先登入accountDTO###{}", JSON.toJSONString(accountDTO));
        Account account = getOneByEmail(accountDTO.getEmail());
        validateEduOrComLogin(account);
        //用户有效期校验
        validateUserValidaExpire(account);
        if ("Third-000000".equals(account.getUserPassword()) && !StringUtil.isNullOrEmpty(accountDTO.getPassword())) {
            account.setUserPassword(accountDTO.getPassword());
            accountMapper.updateById(account);
        } else if ("Third-000000".equals(account.getUserPassword())) {
            throw new BusinessException("password.cannot.be.empty");
        } else {
            if (!account.getUserPassword().equals(accountDTO.getPassword())) {
                throw new BusinessException("password.error", ResultEnum.PROMPT.getCode());
            }
        }
        /*发送邮件*/
        AuthenticationOperationTypeEnum authenticationOperationTypeEnum = AuthenticationOperationTypeEnum.of(accountDTO.getOperationType());
        if (!account.getUserEmail().equalsIgnoreCase(accountDTO.getEmail())) {
            throw new BusinessException("email.error", ResultEnum.PROMPT.getCode());
        }
        if (Objects.isNull(authenticationOperationTypeEnum)) {
            throw new BusinessException("unknown.authentication.operation.type");
        }
        String randomVerifyCode = RandomsUtil.generateVerifyCode(100000L, 999999L);
        LocalCacheUtils.setVerifyCodeCache(
                accountDTO.getOperationType() + "_" + accountDTO.getEmail(), randomVerifyCode);
        Boolean result = Boolean.FALSE;
        switch (authenticationOperationTypeEnum) {
            case LOGIN:
                result = SendEmailUtil.send(accountDTO.getEmail(), null,
                        SendEmailUtil.LOGIN_TEMPLATE_ID, randomVerifyCode);
                break;
            case FORGET_PWD:
                result = SendEmailUtil.send(accountDTO.getEmail(), null,
                        SendEmailUtil.UPDATE_PWD_TEMPLATE_ID, randomVerifyCode);
                break;
            case EXCEPTION_IP:
                result = SendEmailUtil.send(accountDTO.getEmail(), accountDTO.getIp(),
                        SendEmailUtil.EXCEPTION_ID_TEMPLATE_ID, randomVerifyCode);
                break;
            case BIND_MAILBOX:
                result = SendEmailUtil.send(accountDTO.getEmail(), null,
                        SendEmailUtil.BIND_MAILBOX_TEMPLATE_ID, randomVerifyCode);
                break;
            default:
        }
        if (!result) {
            throw new BusinessException("failed.to.send.mail");
        }
        return new AccountPreLoginVO(account.getId());
    }

    @Resource
    private PortfolioService portfolioService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public AccountLoginVO login(AccountLoginDTO accountLoginDTO, HttpServletRequest request) {
//        Assert.isTrue(StringUtils.isNotBlank(accountLoginDTO.getEmail()), "Please input a email  !");
//        Assert.isTrue(StringUtils.isNotBlank(accountLoginDTO.getEmailVerifyCode()), "Please input the email verification code !");
        log.info("aida确认登入###accountLoginDTO###{}", JSON.toJSONString(accountLoginDTO));
        Account accountExist = getOneByEmailAndOrganizationName(accountLoginDTO.getEmail().trim(), accountLoginDTO.getOrganizationName());
        LoginTypeEnum accountType = LoginTypeEnum.of(accountLoginDTO.getLoginType());
        if (Objects.isNull(accountType)) {
            throw new BusinessException("unknown.login.type");
        }
        if (!accountType.equals(LoginTypeEnum.EMAIL)) {
            throw new BusinessException("error.login.type");
        }
        //用户有效期校验
        validateUserValidaExpire(accountExist);

        Account account = null;
        switch (accountType) {
            case PASSWORD:
//                Assert.isTrue(StringUtils.isNotBlank(accountLoginDTO.getPassword()), "Please input a password !");
//                account = getOneByUserName(accountLoginDTO.getUserName());
//                Assert.isTrue(Objects.nonNull(account), "User does not exist!");
//                Assert.isTrue(account.getUserPassword().equals(accountLoginDTO.getPassword()), "Password error !");
                // 走不到这边
                break;
            case EMAIL:
                account = getOneByEmail(accountLoginDTO.getEmail().trim());
                //校验邮箱验证码
                String verifyCode = LocalCacheUtils.getVerifyCodeCache(AuthenticationOperationTypeEnum.LOGIN.name() + "_" + accountLoginDTO.getEmail());
                if (StringUtils.isBlank(verifyCode)) {
                    throw new BusinessException("the.verification.code.has.expired", ResultEnum.PROMPT.getCode());
                }
                if (!"921314".equals(accountLoginDTO.getEmailVerifyCode())) {
                    if (!verifyCode.equals(accountLoginDTO.getEmailVerifyCode())) {
                        throw new BusinessException("verification.code.error", ResultEnum.PROMPT.getCode());
                    }
                }
                break;
            default:
        }
        AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
        response.setEmail(account.getUserEmail());
        String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
        if (StringUtils.isNotBlank(token)) {
            //用户已登入
            response.setToken(token);
        } else {
            response.setToken(createAccountToken(account));
        }
        response.setUserId(account.getId());
        response.setSystemUser(account.getSystemUser());
        // 设置头像
        String avatar;
        if (StringUtil.isNullOrEmpty(account.getAvatar())) {
            avatar = CommonConstant.DEFAULT_AVATAR;
        } else {
            avatar = account.getAvatar();
        }
        response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
        response.setFolloweeCount(userFollowService.getFolloweeCount(account.getId()));
        response.setFollowerCount(userFollowService.getFollowerCount(account.getId()));
        //判断是否常用ip  不是则发邮件提示
        calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account);
        return response;
    }

    private Account getOneByEmailAndOrganizationName(String email, String organizationName) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_email", email);
        if (StringUtils.isNotBlank(organizationName)) {
            queryWrapper.eq("organization_name", organizationName);
        }
        List<Account> accountList = accountMapper.selectList(queryWrapper);
        if (CollectionUtil.isEmpty(accountList)) {
            throw new BusinessException("email.does.not.exist", ResultEnum.PROMPT.getCode());
        }
        return accountList.get(0);
    }

    // 定义常量(临时)
    private static final Integer SYSTEM_USER_TYPE_EDU_ADMIN = 7;

    private void validateUserValidaExpire(Account account) {
        Long currentTime = new Date().getTime();
        if (account.getSystemUser().equals(0)) {
            return;
        }
        if (Objects.nonNull(account.getValidStartTime())) {
            if (currentTime < account.getValidStartTime()) {
                throw new BusinessException("user.expired");
            }
        }
        if (Objects.nonNull(account.getValidEndTime())) {
            boolean isExpired = currentTime > account.getValidEndTime();
            boolean isEduAdmin = SYSTEM_USER_TYPE_EDU_ADMIN.equals(account.getSystemUser());

            if (isExpired) {
                if (isEduAdmin) {
                    setEduAdminToExpire(account);
                } else {
                    toVisitor(account);
                    return;
                    // throw new BusinessException("user.expired");
                }
            }
        }
    }

    // 判断当前用户应该从哪个入口登录
    private void validateEduOrComLogin(Account account) {
        // 获取当前用户的身份，判断应该选择的登录入口
        if (Objects.nonNull(account) && Objects.nonNull(account.getSystemUser())) {
            switch (account.getSystemUser()) {
                case 7:
                case 8:
                    throw new BusinessException("school.account.login", ResultEnum.PROMPT.getCode());
                case 5:
                case 6:
                    throw new BusinessException("company.account.login", ResultEnum.PROMPT.getCode());
            }
        }
    }

    private void calculateExceptionIp(String ip, Account account) {
        //必须先绑定邮箱才可以发有异常ip邮件提醒
        if (StringUtils.isNotBlank(account.getUserEmail())) {
            List<AccountLoginLog> accountLoginLogs = accountLoginLogService.getByUserId(account.getId());
            if (CollectionUtil.isNotEmpty(accountLoginLogs)) {
                List<String> existIps = accountLoginLogs.stream().map(AccountLoginLog::getIp).collect(Collectors.toList());
                if (!existIps.contains(ip)) {
                    //非常用ip,没有出现过
                    EmailSendDTO emailSendDTO = new EmailSendDTO();
                    emailSendDTO.setEmail(account.getUserEmail());
                    emailSendDTO.setOperationType(AuthenticationOperationTypeEnum.EXCEPTION_IP.name());
                    emailSendDTO.setIp(ip);
                    sendEmail(emailSendDTO);
                }
            }
        }
        //保存登入日志
        accountLoginLogService.saveLoginLog(ip, account.getId());
    }

    private String createAccountToken(Account account) {
        AuthPrincipalVo principal = new AuthPrincipalVo();
        principal.setId(account.getId());
        principal.setUsername(account.getUserName());
        principal.setLanguage(account.getLanguage());
        principal.setCountry(account.getCountry());
        String token2 = jwtTokenHelper.createToken(principal);
        LocalCacheUtils.setTokenCache(String.valueOf(account.getId()), token2);
        return token2;
    }

    @Override
    public BindEmailVO bindEmail(AccountBindEmailDTO accountBindEmailDTO, HttpServletRequest request) {
        AuthPrincipalVo userHolder = UserContext.getUserHolder();
        Account account = accountMapper.selectById(userHolder.getId());

        if (Objects.isNull(account)) {
            throw new BusinessException("userName.does.not.exist", ResultEnum.PROMPT.getCode());
        }

        //校验邮箱验证码
        String verifyCode = LocalCacheUtils.getVerifyCodeCache(AuthenticationOperationTypeEnum.BIND_MAILBOX.name() + "_" + accountBindEmailDTO.getUserEmail());
        if (StringUtils.isBlank(verifyCode)) {
            throw new BusinessException("the.verification.code.has.expired", ResultEnum.PROMPT.getCode());
        }
        if (!verifyCode.equals(accountBindEmailDTO.getEmailVerifyCode())) {
            throw new BusinessException("verification.code.error", ResultEnum.PROMPT.getCode());
        }
        //绑定

        BindEmailVO result = new BindEmailVO();

        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().eq(Account::getUserEmail, accountBindEmailDTO.getUserEmail());
        List<Account> accounts = accountMapper.selectList(qw);
        if (CollectionUtil.isNotEmpty(accounts)) {
            Account accountOld = accounts.get(0);

            QueryWrapper<AccountExtend> accountExtendQW = new QueryWrapper<>();
            accountExtendQW.lambda().eq(AccountExtend::getAccountId, userHolder.getId());
            accountExtendQW.lambda().eq(AccountExtend::getAuthType, "Wechat");
            List<AccountExtend> accountExtends = accountExtendMapper.selectList(accountExtendQW);
            if (CollectionUtil.isNotEmpty(accountExtends) && !Objects.equals(accountExtends.get(0).getAccountId(), accountOld.getId())) {
                AccountExtend accountExtend = accountExtends.get(0);
                accountExtend.setAccountId(accountOld.getId());
                accountExtendMapper.updateById(accountExtend);

//                accountMapper.deleteById(userHolder.getId());
                log.info("排查用户被删除原因：绑定邮箱，POST，删除用户（改为降为游客）");
                accountMapper.toVisitor(userHolder.getId());

                String token = LocalCacheUtils.getTokenCache(String.valueOf(accountOld.getId()));
                if (StringUtils.isNotBlank(token)) {
                    //用户已登入
                    result.setToken(token);
                } else {
                    result.setToken(createAccountToken(accountOld));
                }
                return result;
            }
        }
        Account accountNew = accountMapper.selectById(userHolder.getId());

        if (StringUtils.isEmpty(accountNew.getUserEmail())) {
            TrialOrder trialOrder = CopyUtil.copyObject(accountNew, TrialOrder.class);
            trialOrder.setEmail(accountBindEmailDTO.getUserEmail());
            trialOrder.setCreateTime(LocalDateTime.now());
            trialOrder.setUpdateTime(LocalDateTime.now());
            trialOrder.setStatus(1);
            // 获取用户申请试用IP
            String ipAddress = RequestInfoUtil.getIpAddress(request);
            trialOrder.setIp(ipAddress);
            trialOrderMapper.insert(trialOrder);
        }

        accountNew.setUserEmail(accountBindEmailDTO.getUserEmail());

        accountMapper.updateById(accountNew);

        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean forgetPwd(AccountRegisterDTO accountDTO) {
//        Account emailAccount = getOneByEmail(accountDTO.getEmail());
        //校验邮箱验证码
        if (accountDTO.getVerifyEmail()) {
            String verifyCode = LocalCacheUtils.getVerifyCodeCache(AuthenticationOperationTypeEnum.FORGET_PWD.name() + "_" + accountDTO.getEmail());
            if (StringUtils.isBlank(verifyCode)) {
                throw new BusinessException("the.verification.code.has.expired", ResultEnum.PROMPT.getCode());
            }
            if (!verifyCode.equals(accountDTO.getEmailVerifyCode())) {
                throw new BusinessException("verification.code.error", ResultEnum.PROMPT.getCode());
            }
        } else {
            updatePwdByEmail(accountDTO.getPassword(), accountDTO.getEmail());
        }
        return Boolean.TRUE;
    }

    private void updatePwdByEmail(String pwd, String email) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Account::getUserEmail, email);
        Account accountNew = new Account();
        accountNew.setUserPassword(pwd);
        accountMapper.update(accountNew, queryWrapper);
    }

    private void updatePwdByUserId(String email, Long userId) {
        Account accountNew = new Account();
        accountNew.setUserEmail(email);
        accountNew.setId(userId);
        accountMapper.updateById(accountNew);
    }


    private Account getOneByEmail(String email) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq("BINARY user_email", email);
        queryWrapper.eq("user_email", email);
        List<Account> accountList = accountMapper.selectList(queryWrapper);
        if (CollectionUtil.isEmpty(accountList)) {
            throw new BusinessException("email.does.not.exist", ResultEnum.PROMPT.getCode());
        }
        return accountList.get(0);
    }

    private Account getByEmailIgnoreCase(String email) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_email", email);
        List<Account> accountList = accountMapper.selectList(queryWrapper);
        if (CollectionUtil.isEmpty(accountList)) {
            throw new BusinessException("email.does.not.exist", ResultEnum.PROMPT.getCode());
        }
        return accountList.get(0);
    }

    private Account getOneByUserName(String userName) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Account::getUserName, userName);
        queryWrapper.lambda().last("limit 1");
        List<Account> accountList = accountMapper.selectList(queryWrapper);
        if (CollectionUtil.isEmpty(accountList)) {
            throw new BusinessException("userName.does.not.exist", ResultEnum.PROMPT.getCode());
        }
        return accountList.get(0);
    }

    private Account getOneByUserId(Long userId) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", userId);
        return accountMapper.selectOne(queryWrapper);
    }


    @Override
    public Boolean sendEmail(EmailSendDTO emailSendDTO) {
        AuthenticationOperationTypeEnum authenticationOperationTypeEnum = AuthenticationOperationTypeEnum.of(emailSendDTO.getOperationType());
        if (Objects.isNull(authenticationOperationTypeEnum)) {
            throw new BusinessException("unknown.authentication.operation.type");
        }
//        Account emailAccount = getOneByEmail(emailSendDTO.getEmail());
        String randomVerifyCode = RandomsUtil.generateVerifyCode(100000L, 999999L);
        LocalCacheUtils.setVerifyCodeCache(
                emailSendDTO.getOperationType() + "_" + emailSendDTO.getEmail(), randomVerifyCode);
        Boolean result = Boolean.FALSE;
        switch (authenticationOperationTypeEnum) {
            case LOGIN:
                result = SendEmailUtil.send(emailSendDTO.getEmail(), null,
                        SendEmailUtil.LOGIN_TEMPLATE_ID, randomVerifyCode);
                break;
            case FORGET_PWD:
                result = SendEmailUtil.send(emailSendDTO.getEmail(), null,
                        SendEmailUtil.UPDATE_PWD_TEMPLATE_ID, randomVerifyCode);
                break;
            case EXCEPTION_IP:
                result = SendEmailUtil.send(emailSendDTO.getEmail(), emailSendDTO.getIp(),
                        SendEmailUtil.EXCEPTION_ID_TEMPLATE_ID, randomVerifyCode);
                break;
            case BIND_MAILBOX:
                QueryWrapper<Account> qw = new QueryWrapper<>();
                qw.lambda().eq(Account::getUserEmail, emailSendDTO.getEmail());
                List<Account> accounts = accountMapper.selectList(qw);
                if (CollectionUtil.isNotEmpty(accounts)) {
                    Account account = accounts.get(0);
                    QueryWrapper<AccountExtend> accountExtendQW = new QueryWrapper<>();
                    accountExtendQW.lambda().eq(AccountExtend::getAccountId, account.getId());
                    accountExtendQW.lambda().eq(AccountExtend::getAuthType, "WeChat");
                    List<AccountExtend> accountExtends = accountExtendMapper.selectList(accountExtendQW);
                    if (CollectionUtil.isNotEmpty(accountExtends)) {
                        throw new BusinessException("This email account has already been linked to another WeChat account.");
                    } else {
                        throw new BusinessException("This email address is already in use. Please try a different email address.");
                    }
                }
                result = SendEmailUtil.send(emailSendDTO.getEmail(), null,
                        SendEmailUtil.BIND_MAILBOX_TEMPLATE_ID, randomVerifyCode);

                if (Objects.nonNull(emailSendDTO.getUserId()) &&
                        (!StringUtil.isNullOrEmpty(emailSendDTO.getCountry()) || !StringUtil.isNullOrEmpty(emailSendDTO.getOccupation())
                                || StringUtils.isEmpty(emailSendDTO.getSurname()) || StringUtils.isNotEmpty(emailSendDTO.getTitle()))) {
                    Account account = baseMapper.selectById(emailSendDTO.getUserId());
                    account.setCountry(emailSendDTO.getCountry());
                    account.setOccupation(emailSendDTO.getOccupation());
                    account.setTitle(emailSendDTO.getTitle());
                    account.setSurname(emailSendDTO.getSurname());
                    account.setGivenName(emailSendDTO.getGivenName());
                    baseMapper.updateById(account);
                }
                break;
            case CHANGE_MAILBOX:
                result = SendEmailUtil.send(emailSendDTO.getEmail(), null,
                        SendEmailUtil.CHANGE_MAILBOX_TEMPLATE_ID, randomVerifyCode);
                break;
            case UPDATE_USERINFO:
                if (!StringUtil.isNullOrEmpty(emailSendDTO.getCountry()) || !StringUtil.isNullOrEmpty(emailSendDTO.getOccupation())
                        || StringUtils.isEmpty(emailSendDTO.getSurname()) || StringUtils.isNotEmpty(emailSendDTO.getTitle())
                        || StringUtils.isNotEmpty(emailSendDTO.getGivenName())) {
                    if (StringUtil.isNullOrEmpty(emailSendDTO.getEmail())) {
                        throw new BusinessException("email.cannot.be.empty");
                    }
                    // todo 如果之后允许存在相同的邮件，但是所属机构不同的情况，这里需要做修改
                    Account account = getOneByEmail(emailSendDTO.getEmail());
                    account.setCountry(emailSendDTO.getCountry());
                    account.setOccupation(emailSendDTO.getOccupation());
                    account.setTitle(emailSendDTO.getTitle());
                    account.setSurname(emailSendDTO.getSurname());
                    account.setGivenName(emailSendDTO.getGivenName());
                    baseMapper.updateById(account);

                    String userEmail = account.getUserEmail();
                    QueryWrapper<TrialOrder> trialOrderQueryWrapper = new QueryWrapper<>();
                    trialOrderQueryWrapper.lambda().eq(TrialOrder::getEmail, userEmail);
                    List<TrialOrder> trialOrders = trialOrderMapper.selectList(trialOrderQueryWrapper);
                    if (CollectionUtil.isNotEmpty(trialOrders)) {
                        for (TrialOrder trialOrder : trialOrders) {
                            trialOrder.setCountry(emailSendDTO.getCountry());
                            trialOrder.setOccupation(emailSendDTO.getOccupation());
                            trialOrder.setTitle(emailSendDTO.getTitle());
                            trialOrder.setGivenName(emailSendDTO.getGivenName());
                            trialOrder.setSurname(emailSendDTO.getSurname());
                            trialOrderMapper.updateById(trialOrder);
                        }
                    }

                    result = true;
                }
                break;
            case REGISTER:
                result = SendEmailUtil.designWorksRegister(emailSendDTO.getEmail(), randomVerifyCode);
                break;
            default:
        }
        if (!result) {
            throw new BusinessException("failed.to.send.mail");
        }
        log.info("向邮箱 {} 发送验证码为:{}, 邮件类型：{}", emailSendDTO.getEmail(), randomVerifyCode, authenticationOperationTypeEnum);
        return Boolean.TRUE;
    }

    @Override
    public Boolean logout(AccountLogoutDTO accountLogoutDTO) {
        //jwt本身失效比较难做 统一用缓存实现 删除缓存就失效
        String token = LocalCacheUtils.getTokenCache(String.valueOf(accountLogoutDTO.getUserId()));
        if (StringUtils.isNotBlank(token)) {
            LocalCacheUtils.delTokenCache(String.valueOf(accountLogoutDTO.getUserId()));
        }
        return Boolean.TRUE;
    }

    @Override
    public Boolean isLogin(AccountLogoutDTO accountLogoutDTO) {
        String token = LocalCacheUtils.getTokenCache(String.valueOf(accountLogoutDTO.getUserId()));
        if (StringUtils.isNotBlank(token)) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    @Override
    public Boolean addUser(AccountAddDTO accountAddDTO) {
        Account account;
        account = CopyUtil.copyObject(accountAddDTO, Account.class);
//        account.setUserEmail(accountAddDTO.getUserEmail());
//        account.setUserName(accountAddDTO.getUserName());
//        account.setIsTrial(accountAddDTO.getIsTrial());
        account.setValidStartTime(Long.valueOf(accountAddDTO.getValidStartTime()));
        account.setValidEndTime(toDayEnd(Long.parseLong(accountAddDTO.getValidEndTime())));
        account.setUserPassword("Third-000000");
        account.setLanguage(Language.ENGLISH.name());
        account.setCreateDate(new Date());
        account.setIsTrial(0);
        account.setIsBeginner(1);
        return accountMapper.insert(account) > 0;
    }

    @Override
    public Boolean editUser(AccountEditDTO accountEditDTO) {
        if (Objects.isNull(accountEditDTO) || ObjectUtils.isAllFieldNull(accountEditDTO)) {
            throw new BusinessException("edited.account.information.cannot.be.blank");
        }
        QueryWrapper<Account> queryTotal = new QueryWrapper<>();
        Account account = new Account();
        //校验
        if (StringUtils.isNotBlank(accountEditDTO.getNewEmail())) {
            Assert.isTrue(StringUtils.isNotBlank(accountEditDTO.getOldEmail()), "oldEmail.cannot.be.empty");
            queryTotal.eq("user_email", accountEditDTO.getOldEmail());
            Account accountSelect = accountMapper.selectOne(queryTotal);
            Assert.notNull(accountSelect, "oldEmail does not exist!");

            account.setUserEmail(accountEditDTO.getNewEmail());
        }
        if (StringUtils.isNotBlank(accountEditDTO.getNewUserName())) {
            Assert.isTrue(StringUtils.isNotBlank(accountEditDTO.getOldUserName()), "oldUserName.cannot.be.empty");
            queryTotal.eq("user_name", accountEditDTO.getOldUserName());
            Account accountSelect = accountMapper.selectOne(queryTotal);
            Assert.notNull(accountSelect, "oldUserName.does.not.exist");

            account.setUserName(accountEditDTO.getNewUserName());
        }
        if (StringUtils.isNotBlank(accountEditDTO.getNewValidStartTime())) {
            Assert.isTrue(StringUtils.isNotBlank(accountEditDTO.getOldUserName()), "oldUserName.cannot.be.empty");
            queryTotal.eq("user_name", accountEditDTO.getOldUserName());
            Account accountSelect = accountMapper.selectOne(queryTotal);
            Assert.notNull(accountSelect, "oldUserName.does.not.exist");

            account.setValidStartTime(Long.valueOf(accountEditDTO.getNewValidStartTime()));
        }
        if (StringUtils.isNotBlank(accountEditDTO.getNewValidEndTime())) {
            Assert.isTrue(StringUtils.isNotBlank(accountEditDTO.getOldUserName()), "oldUserName.cannot.be.empty");
            queryTotal.eq("user_name", accountEditDTO.getOldUserName());
            Account accountSelect = accountMapper.selectOne(queryTotal);
            Assert.notNull(accountSelect, "oldUserName.does.not.exist");

            account.setValidEndTime(toDayEnd(Long.parseLong(accountEditDTO.getNewValidEndTime())));
        }
        Account accountSelect = accountMapper.selectOne(queryTotal);
        Assert.notNull(accountSelect, "oldAccount.does.not.exist");
        accountMapper.update(account, queryTotal);
        return null;
    }

    @Override
    public String getUserLanguage() {
        AuthPrincipalVo userInfo = UserContext.getUserHolder();
        return Language.valueOf(userInfo.getLanguage()).name();
    }

    @Override
    public String changeUserLanguage(String language) {
        AuthPrincipalVo userInfo = UserContext.getUserHolder();
        Account account = accountMapper.selectById(userInfo.getId());
        account.setLanguage(language);
        accountMapper.updateById(account);
        String accountToken = createAccountToken(account);
        return accountToken;
    }

    @Override
    public Boolean trialUserLogout() {
        return trialOrderService.trialUserLogout();
    }

    @Override
    public Boolean completeGuidance() {
        AuthPrincipalVo userInfo = UserContext.getUserHolder();
        Account account = accountMapper.selectById(userInfo.getId());
        account.setIsBeginner(0);
        accountMapper.updateById(account);
        return Boolean.TRUE;
    }

    @Override
    public Boolean addTrialUser(AccountTrialDTO accountTrialDTO, HttpServletRequest request) {
        return trialOrderService.addTrialUser(accountTrialDTO, request);
    }

    @Override
    public IPage<TrialOrder> trialOrderList(TrialOrderDTO trialOrderDTO) {
        return trialOrderService.trialOrderList(trialOrderDTO);
    }

    @Override
    public Boolean trialOrderApproval(List<Long> ids) {
        return trialOrderService.trialOrderApproval(ids);
    }

    @Override
    public Boolean getIsAutoApproval() {
        return AutoApproved.getStatus();
    }

    @Override
    public Boolean switchIsAutoApproval() {
        AutoApproved.setStatus(!AutoApproved.getStatus());
        return Boolean.TRUE;
    }

    @Override
    public Boolean trialOrderRefuse(List<Long> ids) {
        return trialOrderService.trialOrderRefuse(ids);
    }

    @Override
    public Long getExpiredTime() {
        AuthPrincipalVo userInfo = UserContext.getUserHolder();
        return accountMapper.selectById(userInfo.getId()).getValidEndTime();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addNoLoginRequired(NoLoginRequiredDTO noLoginRequiredDTO) {
        // 构建查询条件，查找已注册的账户数量
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().isNotNull(Account::getBrowserIdentifiers);
        queryWrapper.lambda().like(Account::getUserName, "PolyU-SFT-");
        List<Account> existingAccounts = accountMapper.selectList(queryWrapper);

        // 检查注册账户数量是否超过限制
        if (existingAccounts.size() >= 100) {
            throw new BusinessException("The number of registered accounts exceeds the limit");
        }

        // 检查浏览器标识是否已经被注册
        queryWrapper.clear();
        queryWrapper.lambda().eq(Account::getBrowserIdentifiers, noLoginRequiredDTO.getBrowserIdentifiers());
        if (!accountMapper.selectList(queryWrapper).isEmpty()) {
            throw new BusinessException("This browser has already been registered");
        }

        // 创建新账户
        Account newAccount = new Account();
        newAccount.setUserName("PolyU-SFT-" + String.format("%03d", existingAccounts.size() + 1));
        newAccount.setUserPassword("Third-000000");
        newAccount.setValidStartTime(System.currentTimeMillis());
        newAccount.setValidEndTime(System.currentTimeMillis() + 365L * 24 * 60 * 60 * 1000);
        newAccount.setCreateDate(new Date());
        newAccount.setIsBeginner(1);
        newAccount.setIsTrial(0);
        newAccount.setBrowserIdentifiers(noLoginRequiredDTO.getBrowserIdentifiers());
        newAccount.setLanguage(Language.ENGLISH.name());

        // 插入新账户
        newAccount.setSystemUser(1);
        accountMapper.insert(newAccount);

        return Boolean.TRUE;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteNoLoginRequired(NoLoginRequiredDTO noLoginRequiredDTO) {
        // 删除将被注销的用户
        QueryWrapper<Account> queryWrapperDelete = new QueryWrapper<>();
        queryWrapperDelete.lambda().eq(Account::getBrowserIdentifiers, noLoginRequiredDTO.getBrowserIdentifiers());
        List<Account> accountList = accountMapper.selectList(queryWrapperDelete);
        if (CollectionUtil.isNotEmpty(accountList)) {
            Account accountDelete = accountList.get(0);
            String userName = accountDelete.getUserName();
            // 查询当前浏览器标识下的所有用户，并按照用户名编号降序排序
            QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().isNotNull(Account::getBrowserIdentifiers);
            queryWrapper.lambda().orderByDesc(Account::getUserName);
            List<Account> accounts = accountMapper.selectList(queryWrapper);

            // 如果存在用户，将将被注销的用户的浏览器标识更新为用户名编号最大的用户的浏览器标识
            if (!accounts.isEmpty()) {
                Account userToBeUpdate = accounts.get(0);
                //jwt本身失效比较难做 统一用缓存实现 删除缓存就失效
                String token = LocalCacheUtils.getTokenCache(String.valueOf(accountDelete.getId()));
                if (StringUtils.isNotBlank(token)) {
                    LocalCacheUtils.delTokenCache(String.valueOf(accountDelete.getId()));
                }
                if (!userName.equals(userToBeUpdate.getUserName())) {
//                    accountMapper.deleteById(accountDelete);
                    log.info("排查用户被删除原因：deleteNoLoginRequired，true, 删除用户（改为降为游客）");
                    accountMapper.toVisitor(accountDelete.getId());
                    userToBeUpdate.setUserName(userName);
                    accountMapper.updateById(userToBeUpdate);
                } else {
//                    accountMapper.deleteById(accountDelete);
                    log.info("排查用户被删除原因：deleteNoLoginRequired，false, 删除用户（改为降为游客）");
                    accountMapper.toVisitor(accountDelete.getId());
                }
            }
        }

        return Boolean.TRUE;
    }


    @Override
    public AccountLoginVO noLoginRequired(NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request) {
        String id = noLoginRequiredDTO.getId();
        if (!isStringInRange(id)) {
            throw new BusinessException("Illegal serial number!");
        }
        // 获取真实 IP 地址，考虑了经过代理的情况
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        String browserIdentifiers = ipAddress + "," + id;
        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().eq(Account::getUserName, "PolyU-SFT-" + id);
        qw.lambda().eq(Account::getBrowserIdentifiers, browserIdentifiers);
        List<Account> accountList = accountMapper.selectList(qw);
        if (CollectionUtil.isEmpty(accountList)) {
            throw new BusinessException("Machine not registered or machine identification has changed, login free has failed, please contact us at help@aida.com.hk.");
        }
        Account account = accountList.get(0);
        validateUserValidaExpire(account);
        AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
        String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
        if (StringUtils.isNotBlank(token)) {
            //用户已登入
            response.setToken(token);
        } else {
            response.setToken(createAccountToken(account));
        }
        response.setUserId(account.getId());
        return response;
    }

    @Override
    public Boolean existNoLoginRequired(NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request) {
        String id = noLoginRequiredDTO.getId();
        // 获取真实 IP 地址，考虑了经过代理的情况
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        String browserIdentifiers = ipAddress + "," + id;
        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().eq(Account::getUserName, "PolyU-SFT-" + id);
        List<Account> accountList = accountMapper.selectList(qw);
        if (!CollectionUtil.isEmpty(accountList)) {
            throw new BusinessException("");
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String addNoLoginRequiredNew(NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request) {
        // 验证机房注册序列号（001-100）
        String id = noLoginRequiredDTO.getId();
        if (!isStringInRange(id)) {
            throw new BusinessException("Illegal serial number.");
        }
        // 获取真实 IP 地址，考虑了经过代理的情况
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        String browserIdentifiers = ipAddress + "," + id;
        // 构建查询条件，查找已注册的账户数量
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Account::getUserName, "PolyU-SFT-" + id);
//        queryWrapper.lambda().eq(Account::getBrowserIdentifiers, browserIdentifiers);
        List<Account> existingAccounts = accountMapper.selectList(queryWrapper);

        // 检查序列号是否被注册
        if (CollectionUtil.isNotEmpty(existingAccounts)) {
            throw new BusinessException("The serial number has already been registered.");
        }

        // 检查机器是否已经注册了别的序列号
        queryWrapper.clear();
        queryWrapper.lambda().like(Account::getBrowserIdentifiers, ipAddress);
        List<Account> accountList = accountMapper.selectList(queryWrapper);
        if (CollectionUtil.isNotEmpty(accountList)) {
            throw new BusinessException("This machine has already been registered with serial number " + accountList.get(0).getUserName().split("-")[2]);
        }

        // 创建新账户
        Account newAccount = new Account();
        newAccount.setUserName("PolyU-SFT-" + id);
        newAccount.setUserPassword("Third-000000");
        newAccount.setValidStartTime(System.currentTimeMillis());
        newAccount.setValidEndTime(System.currentTimeMillis() + 365L * 24 * 60 * 60 * 1000);
        newAccount.setCreateDate(new Date());
        newAccount.setIsBeginner(1);
        newAccount.setIsTrial(0);
        newAccount.setBrowserIdentifiers(browserIdentifiers);
        newAccount.setLanguage(Language.ENGLISH.name());

        // 插入新账户
        newAccount.setSystemUser(1);
        accountMapper.insert(newAccount);

        return "<!DOCTYPE html>\n" +
                "                        <html lang=\"en\">\n" +
                "                        <head>\n" +
                "                            <meta charset=\"UTF-8\">\n" +
                "                            <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
                "                            <title>Document</title>\n" +
                "                            <link rel=\"icon\" href=\"https://polyu.aida.com.hk/favicon.ico\">\n" +
                "                        </head>\n" +
                "                        <body>\n" +
                "                        </body>\n" +
                "                            <script>\n" +
                "                                window.location.href = 'https://polyu.aida.com.hk?" + id + "';\n" +
                "                            </script>\n" +
                "                        </html>";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateNoLoginRequiredNew(NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request) {
        // 验证机房注册序列号（001-100）
        String id = noLoginRequiredDTO.getId();
        if (!isStringInRange(id)) {
            throw new BusinessException("Illegal serial number.");
        }
        // 获取真实 IP 地址，考虑了经过代理的情况
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        String browserIdentifiers = ipAddress + "," + id;
        // 构建查询条件，查找已注册的账户数量
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Account::getUserName, "PolyU-SFT-" + id);
//        queryWrapper.lambda().eq(Account::getBrowserIdentifiers, browserIdentifiers);
        List<Account> existingAccounts = accountMapper.selectList(queryWrapper);

        // 检查序列号是否被注册
        if (!CollectionUtil.isNotEmpty(existingAccounts)) {
            throw new BusinessException("Updated serial number not found.");
        }

        Account account = existingAccounts.get(0);
        account.setBrowserIdentifiers(browserIdentifiers);
        accountMapper.updateById(account);

        return "<!DOCTYPE html>\n" +
                "                        <html lang=\"en\">\n" +
                "                        <head>\n" +
                "                            <meta charset=\"UTF-8\">\n" +
                "                            <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
                "                            <title>Document</title>\n" +
                "                            <link rel=\"icon\" href=\"https://polyu.aida.com.hk/favicon.ico\">\n" +
                "                        </head>\n" +
                "                        <body>\n" +
                "                        </body>\n" +
                "                            <script>\n" +
                "                                window.location.href = 'https://polyu.aida.com.hk?" + id + "';\n" +
                "                            </script>\n" +
                "                        </html>";
    }

    public static boolean isStringInRange(String input) {
        // 去除字符串两端的空格
        input = input.trim();

        // 使用正则表达式检查是否是三位数字
        if (input.matches("\\d{3}")) {
            // 将字符串转换为整数
            int number = Integer.parseInt(input);

            // 检查是否在指定范围内
            return number >= 1 && number <= 100;
        }

        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteNoLoginRequiredNew(NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request) {
        // 获取真实 IP 地址，考虑了经过代理的情况
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        // 删除将被注销的用户
        QueryWrapper<Account> queryWrapperDelete = new QueryWrapper<>();
        if (StringUtils.isNotBlank(noLoginRequiredDTO.getId()) && noLoginRequiredDTO.getDeleteById()) {
            // 验证机房注册序列号（001-100）
            String id = noLoginRequiredDTO.getId();
            if (!isStringInRange(id)) {
                throw new BusinessException("Illegal serial number.");
            }
            queryWrapperDelete.lambda().eq(Account::getUserName, "PolyU-SFT-" + noLoginRequiredDTO.getId());
        } else {
            queryWrapperDelete.lambda().like(Account::getBrowserIdentifiers, ipAddress);
        }
        List<Account> accountList = accountMapper.selectList(queryWrapperDelete);
        if (CollectionUtil.isNotEmpty(accountList)) {
            for (Account account : accountList) {
                //jwt本身失效比较难做 统一用缓存实现 删除缓存就失效
                String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
                if (StringUtils.isNotBlank(token)) {
                    LocalCacheUtils.delTokenCache(String.valueOf(account.getId()));
                }
//                accountMapper.deleteById(account.getId());
                log.info("排查用户被删除原因：deleteNoLoginRequiredNew，删除用户（改为降为游客）");
                accountMapper.toVisitor(account.getId());
                // TODO:注销时删除用户数据，workspace，like，library等
            }
        }
        return Boolean.TRUE;
    }

    @Override
    public void upgradeNotification() {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().ne(Account::getSystemUser, 0);
//        queryWrapper.lambda().eq(Account::isDeleted, 0).isNotNull(Account::getUserEmail);

        List<Account> accountList = accountMapper.selectList(queryWrapper);
        int i = 0;
        int size = accountList.size();
        for (Account account : accountList) {
            i++;
            if (account != null && !StringUtil.isNullOrEmpty(account.getUserEmail())) {
                try {
                    if (account.getLanguage().equals(Language.CHINESE_SIMPLIFIED.name())) {
                        log.info("进度：{} / {} CN - {}", i, size, account.getUserEmail());
                        SendEmailUtil.sendUpgradeNotification(account, null, 0);
                    } else {
                        log.info("进度：{} / {} EN - {}", i, size, account.getUserEmail());
                        // 英文
                        SendEmailUtil.sendUpgradeNotification(account, null, 1);
                    }
                } catch (Exception e) {
                    log.error("向 {} 发送邮件失败", account.getUserEmail());
                    log.error(e.getMessage());
                }
            } else if (account != null) {
                log.warn("用户id 为 {}，邮箱为空", account.getId());
            } else {
                log.warn("进度：{} / {} account 为空", i, size);
            }
        }
    }

    @Override
    public void halfPricePromotion() {

        // 所有试用用户 不管过没过期 所有转为游客的正式用户以及试用用户
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Account::getId, 6L);
//        queryWrapper.lambda().ne(Account::getSystemUser, 0);
//        queryWrapper.lambda().isNotNull(Account::getUserEmail);
        List<Account> accountList = accountMapper.selectList(queryWrapper);
        List<Account> halfPricePromotionList = new ArrayList<>(accountList);
//        queryWrapper.lambda().eq(Account::getSystemUser, 3);
//        List<Account> accountList = accountMapper.selectList(queryWrapper);
//
//        List<Account> halfPricePromotionList = new ArrayList<>(accountList);
//
//        QueryWrapper<Account> queryWrapper1 = new QueryWrapper<>();
//        queryWrapper1.lambda().eq(Account::getSystemUser, 0);
//        queryWrapper1.lambda().isNotNull(Account::getValidStartTime);
//        List<Account> accountList1 = accountMapper.selectList(queryWrapper1);
//        halfPricePromotionList.addAll(accountList1);
//        QueryWrapper<Account> queryWrapper1 = new QueryWrapper<>();
//        queryWrapper1.lambda().eq(Account::getSystemUser, 0);
//        queryWrapper1.lambda().isNotNull(Account::getValidStartTime);
//        List<Account> accountList1 = accountMapper.selectList(queryWrapper1);
//        halfPricePromotionList.addAll(accountList1);

//        List<String> sendUserEmail = halfPricePromotionList.stream().map(Account::getUserEmail).collect(Collectors.toList());
//        log.info(JSON.toJSONString(sendUserEmail));
//        List<String> emailList = Arrays.asList(
//                "xingxingzou@aidlab.hk", "kaicpang.pang@connect.polyu.hk", "21122693r@connect.polyu.hk",
//                "fangjianliao@aidlab.hk", "kimwong@code-create.com.hk", "aixfashion@aidlab.hk",
//                "cobracreation@netvigator.com", "dongmei.mo@connect.polyu.hk", "Angel@angelchen.com",
//                "carlovolpi@upwhk.com", "houdeyumail@gmail.com", "luluwangwk@gmail.com", "Kuochitkuo@gmail.com",
//                "mgl220053@gmail.com", "lamheimanwendy@gmail.com", "millamhy@hotmail.com",
//                "cshirleyshirley3@gmail.com", "puipui07130713@gmail.com", "19054532d@connect.polyu.hk",
//                "20064811d@connect.polyu.hk", "20061894D@connect.polyu.hk", "20051773d@connect.polyu.hk",
//                "nlourens@truworths.co.za", "mandychan@aidlab.hk", "calvinwong@aidlab.hk", "mkt.gp@aidlab.hk",
//                "winniechan@aidlab.hk", "Avis.chung@viviennetam.com.hk", "teresa@viviennetam.com.hk",
//                "X1627315083@163.com", "Sharon.Tsui@hongkong.messefrankfurt.com", "xupei3360@163.com",
//                "shahaibodd99@gmail.com", "zchengrong@yeah.net", "mavistse@vtc.edu.hk",
//                "fashionxai.demo@gmail.com", "savus@konkuk.ac.kr", "arthur.chan@vtc.edu.hk", "Ankh.yiu@tienhu.com",
//                "tvicente@ripleyhk.com", "aida@hkfda.org", "mark.thompson@leverstyle.com",
//                "julianapiccininim@gmail.com", "siriusace38@gmail.com", "kittingchoi@aidlab.hk", "cnewhj@126.com",
//                "youis14@sina.com", "mandyyyuk@gmail.com", "JadePoon@frontline.com.hk", "ritachen@csc-groups.com",
//                "derrickcheng@ygmtrading.com", "karen11066827@gmail.com", "cathy.wong@alvanon.com",
//                "maggie.guo@xxhtech.com", "morris.mamiye@mambro.com", "sky@taihill.vc", "lokchun.com@gmail.com",
//                "t-mavistse@stu.vtc.edu.hk", "CFLAIDA@cflhk.com", "kim_wong_99@yahoo.com", "dltnwls6061@gmail.com",
//                "jsj000904@naver.com", "leelokyin@naver.com", "rin2.200088@gmail.com", "setinares@gmail.com",
//                "ysy6016@gmail.com", "ziniko104@gmail.com", "soooo8404@gmail.com", "hanbinna0613@nate.com",
//                "leeapparel71@gmail.com", "miriammiraekim@gmail.com", "coffeeling@icloud.com",
//                "klilb13997@gmail.com", "badriyah.alharthi@gmail.com", "laurenrawbone@woolworths.co.za",
//                "georgeashcroft@yahoo.co.uk", "salahraghadddaaa@gmail.com", "wuhengchee@gmail.com",
//                "gigiwu33@hotmail.com", "wuhengchee@yahoo.com.hk", "willis@zavvy-ave.com", "102561143@qq.com",
//                "3352798788@qq.com", "3428163743@qq.com", "1933674928@qq.com", "fengjie980923@163.com",
//                "502565733@qq.com", "3506556587@qq.com", "964664177@qq.com", "3123416129@qq.com",
//                "1627315082@qq.com", "ra129364@uem.br", "kristytsang623@gmail.com", "2122413626@qq.com",
//                "kongxinliaobiao@163.com", "1916606467@qq.com", "suhang2001411@gmail.com",
//                "2048374531@qq.com", "2257158028@qq.com", "1290989834@qq.com", "3315547200@qq.com",
//                "1011544060@qq.com", "1512170380@qq.com", "newagegoodyouth@gmail.com",
//                "isadorasilva2106@gmail.com", "3276879691@qq.com", "dsl@rasl.net", "2693383459@qq.com",
//                "zaiven@gmail.com", "2990670940@qq.com", "3057762191@qq.com", "3265315306@qq.com",
//                "2571543589@qq.com", "871210016@qq.com", "wwtwzx@live.cn", "1248609518@qq.com",
//                "1309757247@qq.com", "15920129936@163.com", "48485720@qq.com", "1372397040@qq.com",
//                "130320126@qq.com", "soomeiqi@gmail.com", "3305671192@qq.com", "2859439611@qq.com",
//                "1604753494@qq.com", "2979519992@qq.com", "2787394282@qq.com", "nfzy0215@126.com",
//                "2323278768@qq.com"
//        );
        log.info("AiDA workshop 活动通知名单：" + JSON.toJSONString(halfPricePromotionList));
        int i = 0;
        for (Account account : halfPricePromotionList) {
            SendEmailUtil.sendUpgradeNotification(account, null, 0);
            SendEmailUtil.sendUpgradeNotification(account, null, 1);
//            if (i >= 0) {
//                if (!emailList.contains(account.getUserEmail())) {
//                    try {
//                        if (account.getLanguage().equals(Language.CHINESE_SIMPLIFIED.name())) {
//                            SendEmailUtil.halfPricePromotion(account, null, 0);
//                        }else {
//                            SendEmailUtil.halfPricePromotion(account, null, 1);
//                        }
//                    }catch (Exception e) {
//                        log.info(e.getMessage());
//                    }
//                }
//            }
            i++;
            log.info("十月优惠邮件成功发送第" + i + "条");
        }
    }

    @Override
    public void moveLibraryDate() {
        // 查询生产全部library数据，遍历数据，根据用户id和md5查询是否已经迁移过

        // 未迁移过的进行迁移，注意模特数据迁移打点信息以及转换模特格式
    }

    public void updateCreditsAndEndTime(Account account, String value, Long endTime, BigDecimal creditsUsage) {
        Account updateAcc = new Account();
        updateAcc.setId(account.getId());
        updateAcc.setCredits(new BigDecimal(value));
        if (Objects.nonNull(endTime)) {
            updateAcc.setValidEndTime(toDayEnd(endTime));
        }
        if (Objects.nonNull(creditsUsage)) {
            // 机构的积分使用量不会超过上限
            BigDecimal creditsUsageLimit = Objects.isNull(account.getCreditsUsageLimit()) ? BigDecimal.ZERO : account.getCreditsUsageLimit();
            if (creditsUsage.compareTo(creditsUsageLimit) > 0) {
                updateAcc.setCreditsUsage(creditsUsageLimit);
            } else {
                updateAcc.setCreditsUsage(creditsUsage);
            }
        }
        updateAcc.setUpdateDate(new Date());
        int i = accountMapper.updateById(updateAcc);
    }

    @Override
    public Boolean designWorksRegister(AccountDesignWorksRegisterDTO accountDesignWorksRegisterDTO) {
        String userEmail = accountDesignWorksRegisterDTO.getUserEmail();
        log.info("注册账号。邮箱：{}", userEmail);
        QueryWrapper<Account> qwA = new QueryWrapper<>();
        qwA.eq("user_email", userEmail);
        List<Account> accountList = accountMapper.selectList(qwA);

        QueryWrapper<TrialOrder> qwT = new QueryWrapper<>();
        qwT.eq("email", userEmail);
        List<TrialOrder> trialOrders = trialOrderMapper.selectList(qwT);
        if (CollectionUtil.isNotEmpty(accountList) || CollectionUtil.isNotEmpty(trialOrders)) {
            throw new BusinessException("The email has already been registered");
        }

        String randomVerifyCode = RandomsUtil.generateVerifyCode(100000L, 999999L);
        LocalCacheUtils.setVerifyCodeCache("REGISTER" + "_" + userEmail, randomVerifyCode);

        Boolean b = SendEmailUtil.designWorksRegister(userEmail, randomVerifyCode);
        if (!b) {
            throw new BusinessException("failed.to.send.mail");
        }
        log.info("向邮箱 {} 发送验证码：{}, 邮件类型：REGISTER", userEmail, randomVerifyCode);
        return Boolean.TRUE;
    }

    @Override
    public AccountLoginVO designWorksRegisterCode(AccountDesignWorksRegisterDTO accountDesignWorksRegisterDTO,
                                                  HttpServletRequest request) {

        String verifyCode = LocalCacheUtils.getVerifyCodeCache("REGISTER" + "_" + accountDesignWorksRegisterDTO.getUserEmail());
        if (StringUtils.isBlank(verifyCode)) {
            throw new BusinessException("the.verification.code.has.expired", ResultEnum.PROMPT.getCode());
        }
        if (!verifyCode.equals(accountDesignWorksRegisterDTO.getEmailVerifyCode())) {
            throw new BusinessException("verification.code.error", ResultEnum.PROMPT.getCode());
        }
        Account account = CopyUtil.copyObject(accountDesignWorksRegisterDTO, Account.class);
        account.setSystemUser(0);
        if (StringUtils.isBlank(accountDesignWorksRegisterDTO.getLanguage())) {
            account.setLanguage(Language.ENGLISH.name());
        }
        account.setSystemUser(3);
        account.setIsTrial(1);
        account.setCredits(BigDecimal.valueOf(50));
        account.setValidEndTime(toDayEnd(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli()));
        account.setIsBeginner(1);
        account.setValidStartTime(Instant.now().toEpochMilli());
        account.setCreateDate(new Date());
        account.setInvitationCode(accountDesignWorksRegisterDTO.getInvitationCode());
        accountMapper.insert(account);

        TrialOrder trialOrder = CopyUtil.copyObject(account, TrialOrder.class);
        trialOrder.setEmail(account.getUserEmail());
        trialOrder.setCreateTime(LocalDateTime.now());
        trialOrder.setUpdateTime(LocalDateTime.now());
        trialOrder.setStatus(1);
        trialOrderMapper.insert(trialOrder);

        AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
        response.setEmail(account.getUserEmail());
        String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
        if (StringUtils.isNotBlank(token)) {
            //用户已登入
            response.setToken(token);
        } else {
            response.setToken(createAccountToken(account));
        }
        response.setUserId(account.getId());
        response.setSystemUser(account.getSystemUser());

        // 添加登录日志
        String ipAddress = RequestInfoUtil.getIpAddress(request);
        accountLoginLogService.save(new AccountLoginLog(account.getId(), ipAddress, new Date()));
        return response;
    }

    private static final String QUERY_ORDER = "SELECT * FROM pmr_wc_order_stats " +
            "WHERE status = 'wc-processing' AND date_paid > '2024-06-20 00:00:00'" +
            " ORDER BY order_id DESC ";

    private static final String QUERY_CUSTOMER_EMAIL = "SELECT username, email, country FROM pmr_wc_customer_lookup " +
            "WHERE customer_id = ? ";
    private static final String UPDATE_ORDER_STATUS = "UPDATE pmr_wc_order_stats " +
            "SET status = 'wc-completed' , date_completed = ? " +
            "WHERE order_id = ?";

    private static final DataSource dataSource;

    static {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://code-create.com.hk:3306/db1nfvsgmjp3b8");
        config.setUsername("uafqtz4gsvfrw");
        config.setPassword("aida123456.");
//        config.setJdbcUrl("jdbc:mysql://localhost:3306/code-create-local?serverTimezone=UTC");
//        config.setUsername("root");
//        config.setPassword("root");
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        config.addDataSourceProperty("maxLifetime", 50000);
        dataSource = new HikariDataSource(config);
    }

    /**
     * 为Code-Create的用户延长有效期
     *
     * @return null
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean extendValidityForCC() {
        try (Connection connection = dataSource.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(QUERY_ORDER)) {
//            preparedStatement.setString(1, "someCondition");
            try (ResultSet queryOrderResultSet = preparedStatement.executeQuery()) {
                while (queryOrderResultSet.next()) {
                    /* 处理结果集 */
                    int orderId = queryOrderResultSet.getInt("order_id");
                    int customerId = queryOrderResultSet.getInt("customer_id");
                    double totalSales = queryOrderResultSet.getDouble("total_sales");
                    log.info("Code-Create 订单:{}， 顾客id:{}, 付款金额:{}", orderId, customerId, totalSales);
                    String email = "";
                    String userName = "";
                    // 为什么一般没有值
                    String country = "English";
                    // 1、查pmr_wc_customer_lookup表；确认当前订单对应的用户邮箱
                    PreparedStatement preparedQueryEmail = connection.prepareStatement(QUERY_CUSTOMER_EMAIL);
                    preparedQueryEmail.setInt(1, customerId);
                    try (ResultSet queryEmailResultSet = preparedQueryEmail.executeQuery()) {
                        if (queryEmailResultSet.next()) {
                            email = queryEmailResultSet.getString("email");
                            userName = queryEmailResultSet.getString("username");
                            country = queryEmailResultSet.getString("country");
                        } else {
                            log.error("未知错误。code-create的用户表中没有付费用户的信息");
                            throw new BusinessException("user info missing");
                        }
                    }

                    // 2、查t_account表中是否有该用户
                    // 2.1 没有 新建用户
                    Account userInfo = null;
                    Long validEndTime = null;
                    // 标志当前用户是不是新用户
                    Boolean flag = Boolean.FALSE;
                    try {
                        // 不是新用户 直接延长使用期限
                        userInfo = getByEmailIgnoreCase(email);
                    } catch (BusinessException e) {
                        // 通过邮箱找不到用户 说明是新用户 => 创建用户
                        flag = Boolean.TRUE;
                    }
                    if (!Objects.isNull(userInfo) && !Objects.isNull(userInfo.getValidEndTime()))
                        validEndTime = userInfo.getValidEndTime();

                    // 2、获取当前续费费用能延长多长时间
                    Account account = extendValidity(validEndTime, totalSales);

                    int systemUserType = 0;

                    // 不管是不是新用户 都要更新用户角色和积分
                    String credits = "0";
                    if (totalSales == 5000.0) {
                        log.info("年付用户，初始积分50000");
                        credits = CreditsEventsEnum.INIT_YEARLY.getValue();
                        systemUserType = 1;
                    } else if (totalSales == 500.0) {
                        log.info("月付用户，初始积分3500");
                        credits = CreditsEventsEnum.INIT_MONTHLY.getValue();
                        systemUserType = 2;
                    } else if (totalSales == 0.0) {
                        log.info("测试用户，初始积分10");
                        credits = "10";
                        systemUserType = 3;
                    }
                    if (flag) {
                        // 是新用户 => 新增一条数据
                        Boolean b = addUser(new AccountAddDTO(email,
                                StringUtil.isNullOrEmpty(userName) ? email.substring(0, email.indexOf("@")) : userName,
                                country,
                                account.getValidStartTime().toString(),
                                account.getValidEndTime().toString(), 0, new BigDecimal(credits), systemUserType));
                        if (b) log.info("付费新用户 {} 新增成功！", email);
                    } else {
                        userInfo.setValidEndTime(toDayEnd(account.getValidEndTime()));
                        userInfo.setCredits(new BigDecimal(credits));
                        userInfo.setSystemUser(systemUserType);
                        baseMapper.updateById(userInfo);
                        log.info("付费用户 {} 续订成功", email);
                    }

                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String format = simpleDateFormat.format(account.getValidEndTime());

                    // 3、邮件通知
                    SendEmailUtil.notificationForPaidUser(email, flag ? 1 : 2, country,
                            StringUtil.isNullOrEmpty(userName) ? email.substring(0, email.indexOf("@")) : userName, format);

                    // 4、更新订单状态和日期
                    PreparedStatement preparedUpdateOrder = connection.prepareStatement(UPDATE_ORDER_STATUS);
                    LocalDateTime localDateTime = LocalDateTime.now();
                    String currentTime = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

                    preparedUpdateOrder.setString(1, currentTime);
                    preparedUpdateOrder.setInt(2, orderId);
                    int row = preparedUpdateOrder.executeUpdate();
                    if (row == 1) log.info("表 pmr_wc_order_stats 订单状态更新成功");


                }
            }
        } catch (Exception e) {
            // 记录异常并处理
            e.printStackTrace();
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }

    private Account extendValidity(Long validEndTime, double totalSales) {
        Instant specifiedInstant;
        Account account = new Account();
        long epochMilli = Instant.now().toEpochMilli();
        if (!Objects.isNull(validEndTime) && validEndTime > epochMilli) {
            // 将 Unix 毫秒级时间戳转换为 Instant
            specifiedInstant = Instant.ofEpochMilli(validEndTime);
        } else {
            specifiedInstant = Instant.now();
            account.setValidStartTime(specifiedInstant.toEpochMilli());
        }

        // 指定时区
        ZoneId zoneId = ZoneId.of("Asia/Shanghai");
        // 将 Instant 转换为 ZonedDateTime，使用指定时区
        ZonedDateTime specifiedDateTime = ZonedDateTime.ofInstant(specifiedInstant, zoneId);
        ZonedDateTime validityExtension;
        if (totalSales == 500) {
            // 一个月
            validityExtension = specifiedDateTime.plusMonths(1);
        } else if (totalSales == 5000) {
            // 一年
            validityExtension = specifiedDateTime.plusYears(1);
        } else {
            // 测试 一天
            validityExtension = specifiedDateTime.plusDays(1);
        }
        // 获取一个月之后的时间的 Unix 毫秒级时间戳
        account.setValidEndTime(toDayEnd(validityExtension.toInstant().toEpochMilli()));
        return account;
    }

    private static final String QUERY_MAXIMUM_USERID = "SELECT MAX(ID) AS max_id FROM pmr_users;";

    private static final String QUERY_NEW_USER_EMAIL = "SELECT user_email, user_nicename FROM pmr_users " +
            "WHERE ID > ? ";

    @Value("${redis.key.maximumUserId}")
    private String maximumUserIdKey;

    /**
     * 将Code-Create上注册的用户添加为AiDA的游客
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void registerUserToVisitor() {
        ArrayList<Map<String, String>> newUsersInfo = new ArrayList<>();
        ArrayList<String> allEmail = new ArrayList<>();
        long maxUserId = CommonConstant.MAXIMUM_USER_ID;
        try (Connection connection = dataSource.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(QUERY_MAXIMUM_USERID)) {
            try (ResultSet queryOrderResultSet = preparedStatement.executeQuery()) {
                while (queryOrderResultSet.next()) {
                    // 获取最新的最大用户id
                    maxUserId = queryOrderResultSet.getLong("max_id");
                    // 获取历史最大用户id
                    long maxUserIdHistory = StringUtil.isNullOrEmpty(redisUtil.getFromString(maximumUserIdKey)) ? CommonConstant.MAXIMUM_USER_ID : Long.parseLong(redisUtil.getFromString(maximumUserIdKey));
                    log.info("Code-Create Maximum User ID last time : {}", maxUserIdHistory);
                    log.info("Currently Code-Create Maximum User ID : {}", maxUserIdHistory);
                    if (maxUserId > maxUserIdHistory) {
                        // 查出新增用户的邮箱
                        PreparedStatement newUserEmail = connection.prepareStatement(QUERY_NEW_USER_EMAIL);
                        // 填充参数 - 历史最大用户ID
                        newUserEmail.setLong(1, maxUserIdHistory);
                        try (ResultSet queryEmailResultSet = newUserEmail.executeQuery()) {
                            while (queryEmailResultSet.next()) {
                                String email = queryEmailResultSet.getString("user_email");
                                String username = queryEmailResultSet.getString("user_nicename");
                                HashMap<String, String> info = new HashMap<>();
                                info.put("email", email);
                                info.put("username", username);
                                newUsersInfo.add(info);
                                allEmail.add(email);
                            } /*else {
                                log.error("未知错误。code-create的用户表中没有付费用户的信息");
                                throw new BusinessException("user info missing");
                            }*/
                        }
                    }
                }
            }
        } catch (Exception e) {
            // 记录异常并处理
            e.printStackTrace();
//            return null;
        }

        if (!newUsersInfo.isEmpty()) {
            // 查询这些邮箱在aida上是否有账号
            QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
            queryWrapper.in("user_email", allEmail).select("user_email");

            // 重复的邮箱
            List<String> duplicateEmails = baseMapper.selectList(queryWrapper).stream().map(Account::getUserEmail).collect(Collectors.toList());
            if (!duplicateEmails.isEmpty()) {
                // 移除Code-Create新增用户中在AiDA已有账号的邮箱，allEmail中剩余邮箱均为新用户邮箱
                allEmail.removeIf(item -> duplicateEmails.stream()
                        .anyMatch(removeItem -> removeItem.equalsIgnoreCase(item)));
                if (!allEmail.isEmpty()) {
                    Iterator<Map<String, String>> iterator = newUsersInfo.iterator();
                    while (iterator.hasNext()) {
                        Map<String, String> userInfo = iterator.next();
                        String email = userInfo.get("email");
                        if (!allEmail.contains(email)) {
                            iterator.remove(); // 使用迭代器安全地移除元素
                        }
                    }
                } else {
                    newUsersInfo.clear();
                }
            }
            // 将新增用户添加到AiDA，身份为游客
            if (!newUsersInfo.isEmpty()) {
                newUsersInfo.forEach(userInfo -> {
                    long epochMilli = Instant.now().toEpochMilli();
                    Account account = new Account();
                    account.setUserEmail(userInfo.get("email"));
                    account.setUserName(userInfo.get("username"));
                    account.setUserPassword("Third-000000");
                    account.setLanguage(Language.ENGLISH.name());
                    account.setValidStartTime(epochMilli);
                    account.setValidEndTime(epochMilli);
                    account.setCreateDate(new Date());
                    account.setIsTrial(0);
                    account.setIsBeginner(1);
                    account.setCredits(new BigDecimal(0));
                    account.setSystemUser(0);
                    baseMapper.insert(account);
                    // 邮件通知用户
                    SendEmailUtil.notificationForRegisterUser(userInfo.get("email"));
                });
            }
            // 记录当前最大的用户id
            redisUtil.addToString(maximumUserIdKey, String.valueOf(maxUserId));
        }
    }

    private static final String QUERY_PAID_CUSTOMER_EMAIL = "SELECT  distinct c.email " +
            "FROM `pmr_wc_order_stats` o " +
            "inner join `pmr_wc_customer_lookup` c " +
            "on o.customer_id = c.customer_id " +
            "and o.net_total in (5000, 500, 250) " +
            "and o.`status` != 'wc-failed' " +
            "and c.email not in ('1779019091@qq.com', 'xupei3360@163.com', '1627315083@qq.com', 'gigiwu33@hotmail.com')";

    public List<String> getPaidCustomerEmail() {
        List<String> paidCustomerEmail = new ArrayList<>();
        try (Connection connection = dataSource.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(QUERY_PAID_CUSTOMER_EMAIL)) {
            try (ResultSet queryOrderResultSet = preparedStatement.executeQuery()) {
                while (queryOrderResultSet.next()) {
                    paidCustomerEmail.add(queryOrderResultSet.getString("email"));
                }
            }
        } catch (Exception e) {
            // 记录异常并处理
            e.printStackTrace();
//            return null;
        }
        return paidCustomerEmail;
    }

    // 收集调查问卷的信息
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean collectQuestionnaires(String questionnaireInfo) {
        log.info("调查问卷详细信息：{}", questionnaireInfo);
        QuestionnaireVO questionnaireVO = JSON.parseObject(questionnaireInfo, QuestionnaireVO.class);
        String email = questionnaireVO.getEmail();
        // 1、通过邮箱判断当前用户有无系统账号
        Account account;
        try {
            account = getOneByEmail(email);
        } catch (BusinessException e) {
            log.info(e.getMessage());
            log.warn("当前用户 {} 在AiDA中没有账号", email);
            throw new BusinessException("user.has.no.account", ResultEnum.PROMPT.getCode());
        }
        // 2、先判断当前用户是否已经填写过问卷
        CreditsDetail record = creditsService.getByAccountIdAndChangeEvent(account.getId(), "Fill out the questionnaire", "+100");
        if (!Objects.isNull(record)) {
            log.info("当前用户 {} 已经填写过问卷", email);
            throw new BusinessException("questionnaire.filled.out", ResultEnum.PROMPT.getCode());
        }

        // 3.1、将问卷信息存储到数据库
        Questionnaire questionnaire = new Questionnaire();
        questionnaire.setQuestionnaireInfo(questionnaireInfo);
        questionnaire.setTitle("AiDA_3.0 Feedback Survey--06/2024");
        questionnaire.setCreateTime(LocalDateTime.now());
        int insert = questionnaireMapper.insert(questionnaire);

        if (insert == 1) {
            // 3.2、更新 t_credits_detail表
            CreditsDetail creditsDetail = new CreditsDetail();
            creditsDetail.setAccountId(account.getId());
            creditsDetail.setChangeEvent("Fill out the questionnaire");
            creditsDetail.setChangedCredits("+100");
            BigDecimal added = account.getCredits().add(new BigDecimal("100"));
            creditsDetail.setCredits(added);
            creditsDetail.setCreateTime(LocalDateTime.now());
            creditsService.save(creditsDetail);

            // 3.3、更新 t_account 表
            if (account.getValidEndTime() != null && account.getValidEndTime() < 1720972799000L) {
                updateCreditsAndEndTime(account, added.toString(), 1720972799000L, null);
            }
        }

        // 4、发邮件 区分中英文
        SendEmailUtil.questionnaireRelatedNotify(questionnaireVO.getUserName(), email, questionnaireVO.getLanguage());
        return Boolean.TRUE;
    }

    /**
     * 为教育版子账号用户每月更新积分
     */
    /**
     * 每月刷新教育版子账号积分
     * 只刷新教育版管理员(role=7)的子账号(role=8)
     */
    @Transactional(rollbackFor = Exception.class)
    public void refreshCreditsMonthly() {
        long startTime = System.currentTimeMillis();
        log.info("开始执行月度积分刷新任务");

        try {
            long currentEpochMilli = Instant.now().toEpochMilli();

            // 1. 查询所有有效的教育版管理员账号
            List<Account> adminAccounts = getValidAdminAccounts(currentEpochMilli);

            if (CollectionUtils.isEmpty(adminAccounts)) {
                log.info("未找到有效的教育版管理员账号");
                return;
            }

            // 2. 批量处理所有子账号积分刷新
            int totalProcessed = refreshAllSubAccountsCredits(adminAccounts);

            log.info("月度积分刷新任务完成，共处理 {} 个子账号，耗时 {} ms",
                    totalProcessed, System.currentTimeMillis() - startTime);

        } catch (Exception e) {
            log.error("积分刷新任务执行失败", e);
            throw new BusinessException("积分刷新任务执行失败: " + e.getMessage());
        }
    }

    /**
     * 获取有效的教育版管理员账号
     */
    private List<Account> getValidAdminAccounts(long currentEpochMilli) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(Account::getId, Account::getOrganizationName,
                        Account::getCreditsUsageLimit, Account::getSubAccountNum)
                .eq(Account::getSystemUser, 7) // 教育版管理员
                .gt(Account::getValidEndTime, currentEpochMilli); // 账号有效期内

        return baseMapper.selectList(queryWrapper);
    }

    /**
     * 批量刷新所有子账号积分
     */
    private int refreshAllSubAccountsCredits(List<Account> adminAccounts) {
        int totalProcessed = 0;

        for (Account adminAccount : adminAccounts) {
            // 获取该管理员的所有子账号
            List<Account> subAccounts = getSubAccountsByAdmin(adminAccount);

            if (CollectionUtils.isEmpty(subAccounts)
                    || Objects.isNull(adminAccount.getSubAccountNum())
                    || adminAccount.getSubAccountNum() == 0) {
                log.debug("管理员 {} 没有子账号", adminAccount.getId());
                continue;
            }

//            BigDecimal averageCredits = adminAccount.getCreditsUsageLimit().divide(new BigDecimal(adminAccount.getSubAccountNum()), RoundingMode.FLOOR);

            // 批量更新子账号积分
            int processedCount = batchUpdateSubAccountsCredits(subAccounts);
            totalProcessed += processedCount;

            BigDecimal sum = subAccounts.stream()
                    .filter(Objects::nonNull)
                    .map(Account::getCreditsUsageLimit)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            adminAccount.setCreditsUsage(sum);
            adminAccount.setCredits(adminAccount.getCreditsUsageLimit().subtract(adminAccount.getCreditsUsage()));
            adminAccount.setUpdateDate(new Date());
            updateById(adminAccount);

            log.info("管理员 {} 的子账号积分刷新完成，共处理 {} 个子账号",
                    adminAccount.getId(), processedCount);

        }

        return totalProcessed;
    }

    /**
     * 获取指定管理员的所有子账号
     */
    private List<Account> getSubAccountsByAdmin(Account adminAccount) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(Account::getOrganizationName, adminAccount.getOrganizationName())
                .eq(Account::getParentId, adminAccount.getId())
                .eq(Account::getSystemUser, 8); // 教育版子账号

        return baseMapper.selectList(queryWrapper);
    }

    /**
     * 批量更新子账号积分
     */
    private int batchUpdateSubAccountsCredits(List<Account> subAccounts/*, BigDecimal averageCredits*/) {
        List<Account> accountsToUpdate = new ArrayList<>();
        Date now = new Date();

        for (Account subAcc : subAccounts) {
            try {
                Account updatedAccount = calculateNewCredits(subAcc, now);
                accountsToUpdate.add(updatedAccount);

            } catch (Exception e) {
                log.error("计算子账号 {} 积分时发生错误", subAcc.getId(), e);
            }
        }

        if (!accountsToUpdate.isEmpty()) {
            // 使用批量更新提高性能
            return batchUpdateByIds(accountsToUpdate);
        }

        return 0;
    }

    /**
     * 计算新的积分值
     * 默认积分为上月分配的积分
     */
    private Account calculateNewCredits(Account subAcc, Date updateTime/*, BigDecimal averageCredits*/) {
        BigDecimal creditsUsageLimit = subAcc.getCreditsUsageLimit();
        // 使用 Optional 替代 ObjectUtils.defaultIfNull
        BigDecimal creditsUsage = Optional.ofNullable(subAcc.getCreditsUsage()).orElse(BigDecimal.ZERO);
        BigDecimal currentCredits = Optional.ofNullable(subAcc.getCredits()).orElse(BigDecimal.ZERO);

        // 计算学校分配积分的剩余量
        /*BigDecimal schoolCreditRemaining = creditsUsageLimit.subtract(creditsUsage);

        // 计算个人充值的积分（总积分减去学校分配的剩余积分）
        BigDecimal personalCredits = currentCredits.subtract(
                schoolCreditRemaining.compareTo(BigDecimal.ZERO) > 0 ?
                        schoolCreditRemaining : BigDecimal.ZERO
        );*/
        BigDecimal personalCredits = currentCredits.add(creditsUsage).subtract(creditsUsageLimit);

        // 确保个人积分不为负数
        personalCredits = personalCredits.max(BigDecimal.ZERO);

        // 新的总积分 = 个人积分 + 学校分配的新积分额度 （重新分配的积分是使用上个月分配的积分还是默认积分，暂时使用平均积分）
        BigDecimal newTotalCredits = personalCredits.add(creditsUsageLimit);

        // 记录积分变更日志（可选）
        logCreditChange(subAcc, currentCredits, newTotalCredits, creditsUsage);

        // 创建更新对象
        Account updatedAccount = new Account();
        updatedAccount.setId(subAcc.getId());
        updatedAccount.setCredits(newTotalCredits);
        updatedAccount.setCreditsUsage(BigDecimal.ZERO); // 重置已使用积分
//        updatedAccount.setCreditsUsageLimit(averageCredits); // 分配的积分沿用上次分配的积分
        updatedAccount.setUpdateDate(updateTime);

        return updatedAccount;
    }

    /**
     * 记录积分变更日志
     */
    private void logCreditChange(Account subAcc, BigDecimal oldCredits,
                                 BigDecimal newCredits, BigDecimal creditsUsage) {
        if (creditsUsage.compareTo(subAcc.getCreditsUsageLimit()) > 0) {
            log.warn("用户 {} 积分使用量 {} 大于积分使用上限 {}",
                    subAcc.getId(), creditsUsage, subAcc.getCreditsUsageLimit());
        }

        log.debug("更新子账号 {} 积分: 旧值={},  已使用={}, 新值={}",
                subAcc.getId(), oldCredits, creditsUsage, newCredits);
    }

    /**
     * 批量更新账号信息
     */
    private int batchUpdateByIds(List<Account> accountsToUpdate) {
        // 使用MyBatis Plus的批量更新方法
        // 如果没有批量更新方法，可以分批次更新以避免大数据量问题
        int batchSize = 1000; // 每批更新1000条
        int totalUpdated = 0;

        for (int i = 0; i < accountsToUpdate.size(); i += batchSize) {
            int end = Math.min(i + batchSize, accountsToUpdate.size());
            List<Account> batch = accountsToUpdate.subList(i, end);

            // 使用MyBatis Plus的批量更新
            // 或者实现自己的批量更新逻辑
            for (Account account : batch) {
                baseMapper.updateById(account);
            }

            totalUpdated += batch.size();
            log.debug("已批量更新 {} 个子账号积分", batch.size());
        }

        return totalUpdated;
    }

    @Override
    public String getActivityBenefits() {
        Long id = UserContext.getUserHolder().getId();
        // 1、 判断用户的身份 正式用户 无福利
        Account account = baseMapper.selectById(id);
        Integer systemUser = account.getSystemUser();
        Instant now = Instant.now();
        ZoneId zoneId = ZoneId.of("Asia/Shanghai");
        ZonedDateTime specifiedDateTime;
        if (systemUser.equals(1)) {
            long validEndTime = Objects.isNull(account.getValidEndTime()) ? now.toEpochMilli() : account.getValidEndTime();
            specifiedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(validEndTime), zoneId);
            account.setValidEndTime(toDayEnd(specifiedDateTime.plusDays(30).toInstant().toEpochMilli()));

        } else if (systemUser.equals(4)) {
            throw new BusinessException("You have participated in the event", 1);
        } else if (systemUser.equals(0) || systemUser.equals(3)) {
            // 2、赋予游客或试用用户 以正式用户的权限 即 积分置为6000，有效期30天
            // 将 Instant 转换为 ZonedDateTime，使用指定时区
            specifiedDateTime = ZonedDateTime.ofInstant(now, zoneId);
            account.setIsTrial(0);
            account.setSystemUser(4);
            account.setCredits(BigDecimal.valueOf(6000));
            account.setValidStartTime(now.toEpochMilli());
            account.setValidEndTime(toDayEnd(specifiedDateTime.plusDays(30).toInstant().toEpochMilli()));
        }

        account.setUpdateDate(new Date());
        baseMapper.updateById(account);

        return "参与成功";

    }

    // 将指定unix时间置为当天的23：59：59
    public long toDayEnd(long unixTimestampMillis) {
        // 将UNIX时间戳转换为LocalDateTime对象
        LocalDateTime dateTime = Instant.ofEpochMilli(unixTimestampMillis)
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();

        // 获取日期部分并设置时间为23:59:59
        LocalDate date = dateTime.toLocalDate();
        LocalDateTime endOfDay = date.atTime(LocalTime.of(23, 59, 59));

        // 将LocalDateTime对象转换为UNIX时间戳（以毫秒为单位） 北京时间
        return endOfDay.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
    }

    /**
     * 获取指定身份过期用户
     *
     * @param systemUserNum
     * @return
     */
    public List<Account> getExpiredUserBySystemUser(Integer systemUserNum) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        long now = Instant.now().toEpochMilli();
        queryWrapper.eq("system_user", systemUserNum)
                .isNotNull("valid_end_time")
                .lt("valid_end_time", now);

        return baseMapper.selectList(queryWrapper);
    }

    public void toVisitor(Account account) {
        accountMapper.toVisitor(account.getId());
    }

    public List<Long> setUserValidToDayEnd() {
        // 获取当前未过期的用户，并将其有效期设置为过期当日的23：59：59
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        long now = Instant.now().toEpochMilli();
        queryWrapper.isNotNull("valid_end_time").gt("valid_end_time", now);
        List<Account> accounts = baseMapper.selectList(queryWrapper);
        ArrayList<Long> ids = new ArrayList<>();

        for (Account account : accounts) {
            account.setValidEndTime(toDayEnd(account.getValidEndTime()));
            ids.add(account.getId());
            updateById(account);
        }

        return ids;
    }

    public IPage<Account> getPageByDateAndUserType(String startTime, String endTime, Integer type, int pageNum, int size) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        setTimeAndSystemUser(queryWrapper, startTime, endTime, type);

        Page<Account> accountPage = new Page<>(pageNum, size);
        return baseMapper.selectPage(accountPage, queryWrapper);
    }

    private void setTimeAndSystemUser(QueryWrapper<Account> queryWrapper, String startTime, String endTime, Integer type) {
        queryWrapper.gt("create_date", startTime).lt("create_date", endTime);

        if (!Objects.isNull(type)) {
            switch (type) {
                case 0:
                    // 游客
                    queryWrapper.eq("system_user", 0);
                    break;
                case 1:
                    queryWrapper.eq("system_user", 1);
                    break;
                case 2:
                    queryWrapper.eq("system_user", 2);
                    break;
                case 3:
                    // 试用用户
                    queryWrapper.eq("system_user", 3).or().eq("system_user", 4);
                    break;

            }
        }
    }

    public Map<String, Long> getByDateAndUserType(String startTime, String endTime, Integer type) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        setTimeAndSystemUser(queryWrapper, startTime, endTime, type);
        queryWrapper.groupBy("system_user");

        queryWrapper.select("system_user as type, count(id) as count");
        List<Map<String, Object>> maps = baseMapper.selectMaps(queryWrapper);
        return maps.stream()
                .collect(Collectors.toMap(
                        map -> map.get("type").toString(),
                        map -> Objects.isNull(map.get("count")) ? 0L : (Long) map.get("count")));
    }

    public IPage<Account> getPageByIds(List<Long> ids, int pageNum, int size) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id", ids);

        return baseMapper.selectPage(new Page<>(pageNum, size), queryWrapper);
    }

    public List<Account> getByIds(List<Long> ids) {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id", ids);

        return baseMapper.selectList(queryWrapper);
    }

    public String uploadAvatar(MultipartFile file) {
        Long accountId = UserContext.getUserHolder().getId();
        // 1、上传图片到minio
        String avatarPath = minioUtil.upload(userBucket, accountId.toString() + "/avatar", file);
        // 2、查询该用户之前的头像
        String avatar = baseMapper.selectById(accountId).getAvatar();
        if (!StringUtil.isNullOrEmpty(avatar) && !avatar.equals(CommonConstant.DEFAULT_AVATAR)) {
            minioUtil.deleteObject(avatar);
        }

        // 3、替换新的头像
        Account account = new Account();
        account.setId(accountId);
        account.setAvatar(avatarPath);
        baseMapper.updateById(account);

        return minioUtil.getPreSignedUrl(avatarPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
    }

    public PersonalHomepageVO getPersonalHomepage(Long accountId) {
        // 需要返回 用户头像 用户名 作品总量 粉丝量 关注量 主页访问量  当前用户是否被查看者关注
        Long currentUserId = UserContext.getUserHolder().getId();
        PersonalHomepageVO personalHomepageVO = new PersonalHomepageVO();

        Account account = baseMapper.selectById(accountId);
        personalHomepageVO.setUserName(account.getUserName());
        String avatar = StringUtil.isNullOrEmpty(account.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : account.getAvatar();
        personalHomepageVO.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));

        personalHomepageVO.setPortfolioCount(portfolioService.getPortfolioCount(accountId));
        personalHomepageVO.setFolloweeCount(userFollowService.getFolloweeCount(accountId));
        personalHomepageVO.setFollowerCount(userFollowService.getFollowerCount(accountId));
        personalHomepageVO.setHomepageViewCount(viewPersonalHomepageCount(0L));


        if (accountId.equals(currentUserId)) {
            personalHomepageVO.setIsFollow(0);
            Long viewCount = viewPersonalHomepageCount(accountId);
            // 只有本人才能看到个人主页浏览量
            personalHomepageVO.setHomepageViewCount(viewCount == null ? 0 : viewCount);
        } else {
            personalHomepageVO.setIsFollow(userFollowService.getIfFollowed(accountId, currentUserId));
            // 非本人浏览主页时增加浏览量
            viewsIncrease(accountId);
        }
        return personalHomepageVO;
    }

    @Override
    public Boolean viewsIncrease(Long id) {
        redisUtil.increasePersonalHomepageViewCount(id);
        return Boolean.TRUE;
    }

    private Long viewPersonalHomepageCount(Long accountId) {
        return redisUtil.getPersonalHomepageViewCount(accountId);
    }

    // 获取当前用户本月内 剩余昵称修改次数
    public Long getNicknameModifyTimes() {
        Long accountId = UserContext.getUserHolder().getId();
        String key = RedisUtil.NICKNAME_MODIFY_TIMES + accountId;
        Long times = redisUtil.getIncrementCount(key);
//        HashMap<String, Long> resp = new HashMap<>();
//        resp.put("remainingTimes", 5L - times);
//        resp.put("remainingDays", redisUtil.getExpire(key) == -1 ? 30L : (long) Math.ceil((double) redisUtil.getExpire(key) / (24 * 60 * 60)));
        return 5L - times;
    }

    // 修改用户名 允许用户当前月内修改5次
    @Transactional(rollbackFor = Exception.class)
    public void editUserName(String newUserName) {
        Long accountId = UserContext.getUserHolder().getId();
        // 判断当前用户是否还有修改昵称的次数
//        Map<String, Long> remainTimes = getNicknameModifyTimes();
        Long remainingModifyTimes = getNicknameModifyTimes();
        if (remainingModifyTimes > 0) {
            Account account = baseMapper.selectById(accountId);
            // 当新昵称与旧昵称不同时，修改
            if (!account.getUserName().equals(newUserName)) {
                account.setUserName(newUserName);
                account.setId(accountId);
                baseMapper.updateById(account);
                // 每个月初清除RedisUtil.NICKNAME_MODIFY_TIMES下的所有记录
                String key = RedisUtil.NICKNAME_MODIFY_TIMES + accountId;
                // 增加修改次数
                redisUtil.increaseCount(key);
            }
        } else {
            throw new BusinessException("remaining.modifications", 1);
        }
    }

    /*// 验证是否是本人进行邮箱绑定更改
    public void verifyUserEmail(String verifyCode){
        // 向旧邮箱发送验证码，以保证是当前邮箱拥有者在进行更改
        String userEmail = baseMapper.selectById(UserContext.getUserHolder().getId()).getUserEmail();
        //校验邮箱验证码
        String verifyCodeCatch = LocalCacheUtils.getVerifyCodeCache(AuthenticationOperationTypeEnum.CHANGE_MAILBOX.name() + "_" + userEmail);
        if (StringUtils.isBlank(verifyCodeCatch)) {
            throw new BusinessException("the.verification.code.has.expired", ResultEnum.PROMPT.getCode());
        }
        if (!verifyCode.equals(verifyCodeCatch)) {
            throw new BusinessException("verification.code.error", ResultEnum.PROMPT.getCode());
        }
    }

    // 修改邮箱地址
    public void changeUserEmail(String newMailbox){
        AuthPrincipalVo userHolder = UserContext.getUserHolder();
        Long accountId = userHolder.getId();
        // 将新邮箱信息存储到redis
        String key = RedisUtil.CHANGE_MAILBOX + accountId;
        redisUtil.addToString(key, newMailbox, CommonConstant.CHANGE_MAILBOX_LINK_VALIDITY / 1000);

        String username = userHolder.getUsername();
        String token = jwtTokenHelper.createToken(accountId, newMailbox);
        // 准备激活链接,链接应该要有有效期
        String link = "?" + token;
        // 向新邮箱发送邮件，邮件附带激活链接，点击链接进行验证
        SendEmailUtil.changeMailboxConfirm(newMailbox, userHolder.getLanguage(), username, link);
    }

    // 验证激活链接
    public void activateNewEmail(String token){
        // 获取链接地址信息，更新指定用户邮箱
        String emailAndId = jwtTokenHelper.parseToEmailAndId(token);
        String newMailbox = emailAndId.substring(0, emailAndId.lastIndexOf("_"));
        String accountId = emailAndId.substring(emailAndId.lastIndexOf("_") + 1);

        // 与redis的数据对比
        String key = RedisUtil.CHANGE_MAILBOX + accountId;
        String preInfo = redisUtil.getFromString(key);
        if (StringUtil.isNullOrEmpty(preInfo)){
            throw new BusinessException("申请已过期", 1);
        }else if (!preInfo.equals(newMailbox)){
            throw new BusinessException("信息不匹配，请重新申请");
        }

        // 执行替换
        log.info("执行邮箱替换，用户id:{}，新邮箱：{}", accountId, newMailbox);

        Account account = new Account();
        account.setUserEmail(newMailbox);
        account.setId(Long.parseLong(accountId));
        baseMapper.updateById(account);
        log.info("邮箱绑定更改完成，用户id:{}，新邮箱：{}", accountId, newMailbox);
    }*/

    @Override
    public String googleCallback(String code, HttpSession session) {
        try {
            log.info("code: " + code);
            // 使用 code 获取 Google 用户信息
            GoogleUser googleUser = getGoogleUserFromCode(code);
            log.info("googleUser: " + JSON.toJSONString(googleUser));

            // 检查数据库中是否已有该用户
            QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(Account::getUserEmail, googleUser.getEmail());  // 根据邮箱查询用户
            List<Account> accounts = accountMapper.selectList(queryWrapper);

            if (CollectionUtil.isNotEmpty(accounts)) {
                // 用户已存在，直接登录
                session.setAttribute("user", accounts.get(0));
                return "Login successful";
            } else {
                // 用户不存在，创建新用户（自动注册）
                Account newUser = new Account();
                newUser.setUserEmail(googleUser.getEmail());
                newUser.setUserName(googleUser.getName());
                newUser.setUserPassword("Third-000000");
                newUser.setLanguage(Language.ENGLISH.name());
                newUser.setValidStartTime(System.currentTimeMillis());
                newUser.setValidEndTime(toDayEnd(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli()));
                newUser.setCreateDate(new Date());
                newUser.setIsTrial(1);
                newUser.setIsBeginner(1);
                newUser.setCredits(BigDecimal.valueOf(50));
                newUser.setSystemUser(3);
                accountMapper.insert(newUser);
                session.setAttribute("user", newUser);

                return "Registration and login successful";
            }
        } catch (Exception e) {
            return "Error processing Google login: " + e.getMessage();
        }
    }

    private static final String TOKEN_URL = "https://oauth2.googleapis.com/token";
    private static final String USER_INFO_URL = "https://www.googleapis.com/oauth2/v3/userinfo";
//    private static final String CLIENT_ID = "194770296147-njd68pm7tnapgonkj2h48mhf63n15n3f.apps.googleusercontent.com";
//    private static final String CLIENT_SECRET = "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";
//    private static final String REDIRECT_URI = "https://0551-117-143-125-51.ngrok-free.app/api/third/party/auth/google_callback";
//    private static final String REDIRECT_URI = "https://develop.api.aida.com.hk/api/third/party/auth/google_callback";

    public GoogleUser getGoogleUserFromCode(String code) {
        // Step 1: Exchange code for access_token
        String accessToken = getAccessToken(code);
        log.info("accessToken" + accessToken);

        // Step 2: Use access_token to get Google User info
        return getGoogleUserInfo(accessToken);
    }

    private String getAccessToken(String code) {
        RestTemplate restTemplate = new RestTemplate();
        Map<String, String> params = new HashMap<>();
        params.put("client_id", googleClientId);
        params.put("client_secret", googleClientSecret);
        params.put("redirect_uri", googleRedirectUri);
        params.put("grant_type", "authorization_code");
        params.put("code", code);

        // 使用 RestTemplate 发起请求以获取 access_token
        GoogleTokenResponse response = restTemplate.postForObject(TOKEN_URL, params, GoogleTokenResponse.class);
        return response.getAccessToken();
    }

    private GoogleUser getGoogleUserInfo(String accessToken) {
        RestTemplate restTemplate = new RestTemplate();
        String url = USER_INFO_URL + "?access_token=" + accessToken;
        return restTemplate.getForObject(url, GoogleUser.class);
    }

//    public User findUserByGoogleId(String googleId) {
//        // 从数据库中根据 Google ID 查找用户
//        return userRepository.findByGoogleId(googleId);
//    }

//    public User registerNewUser(GoogleUser googleUser) {
//        // 创建并保存新用户
//        User newUser = new User();
//        newUser.setGoogleId(googleUser.getId());
//        newUser.setEmail(googleUser.getEmail());
//        newUser.setUsername(googleUser.getName());
//        return userRepository.save(newUser);
//    }

    @Override
    public void temporaryUpgrade() {
        QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().ne(Account::getSystemUser, 0);
        queryWrapper.lambda().isNotNull(Account::getUserEmail);
//        queryWrapper.lambda().eq(Account::getId, 6);
        List<Account> accountList = accountMapper.selectList(queryWrapper);
        int i = 0;
        for (Account account : accountList) {
            if (i >= 0) {
                try {
                    // 判断用户语言，调用相应邮件发送方法
                    if (Language.CHINESE_SIMPLIFIED.name().equals(account.getLanguage())) {
//                        SendEmailUtil.temporaryUpgrade(account, null, 0);
                    } else {
//                        SendEmailUtil.temporaryUpgrade(account, null, 1);
                    }
                } catch (Exception e) {
                    // 捕获单个用户的发送失败异常，记录日志但不中断流程
                    log.error("邮件发送失败，用户邮箱：{}，原因：{}", account.getUserEmail(), e.getMessage(), e);
                }
            }
            i++;
        }
    }


    @Override
    public AccountPreLoginVO enterpriseLogin(AccountLoginDTO accountDTO) {
        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().eq(Account::getUserEmail, accountDTO.getEmail());
        qw.lambda().eq(Account::getOrganizationName, accountDTO.getOrganizationName());
        List<Account> accounts = accountMapper.selectList(qw);
        if (CollectionUtil.isEmpty(accounts)) {
            throw new BusinessException("UserEmail or enterprise name incorrect.");
        }
        qw.lambda().eq(Account::getUserPassword, accountDTO.getPassword());
        accounts = accountMapper.selectList(qw);
        if (CollectionUtil.isEmpty(accounts)) {
            throw new BusinessException("Password error.");
        }
        Account account = accounts.get(0);

        validateUserValidaExpire(account);

        if ("Third-000000".equals(account.getUserPassword()) && !StringUtil.isNullOrEmpty(accountDTO.getPassword())) {
            account.setUserPassword(accountDTO.getPassword());
            accountMapper.updateById(account);
        } else if ("Third-000000".equals(account.getUserPassword())) {
            throw new BusinessException("Password cannot be empty");
        } else {
            if (!account.getUserPassword().equals(accountDTO.getPassword())) {
                throw new BusinessException("password.error", ResultEnum.PROMPT.getCode());
            }
        }
        /*发送邮件*/
        AuthenticationOperationTypeEnum authenticationOperationTypeEnum = AuthenticationOperationTypeEnum.of("LOGIN");
        String randomVerifyCode = RandomsUtil.generateVerifyCode(100000L, 999999L);
        LocalCacheUtils.setVerifyCodeCache(
                "LOGIN_" + accountDTO.getEmail(), randomVerifyCode);
        Boolean result = Boolean.FALSE;
        result = SendEmailUtil.send(accountDTO.getEmail(), null,
                SendEmailUtil.LOGIN_TEMPLATE_ID, randomVerifyCode);
        if (!result) {
            throw new BusinessException("failed.to.send.mail");
        }
        return new AccountPreLoginVO(account.getId());


//        AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
//        response.setEmail(account.getUserEmail());
//        String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
//        if (StringUtils.isNotBlank(token)) {
//            //用户已登入
//            response.setToken(token);
//        } else {
//            response.setToken(createAccountToken(account));
//        }
//        response.setUserId(account.getId());
//        response.setSystemUser(account.getSystemUser());
//        // 设置头像
//        String avatar;
//        if (StringUtil.isNullOrEmpty(account.getAvatar())){
//            avatar = CommonConstant.DEFAULT_AVATAR;
//        }else {
//            avatar = account.getAvatar();
//        }
//        response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
//        response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
//        response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
//        //判断是否常用ip  不是则发邮件提示
////        calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account);
//        return response;
    }

    @Override
    public AccountPreLoginVO schoolLogin(AccountLoginDTO accountDTO) {
        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().eq(Account::getUserEmail, accountDTO.getEmail());
        qw.lambda().eq(Account::getOrganizationName, accountDTO.getOrganizationName());
        List<Account> accounts = accountMapper.selectList(qw);
        if (CollectionUtil.isEmpty(accounts)) {
            throw new BusinessException("UserEmail or school name incorrect.");
        }
        Account account = accounts.get(0);

/*        qw.lambda().eq(Account::getUserPassword, accountDTO.getPassword());
        accounts = accountMapper.selectList(qw);
        if (CollectionUtil.isEmpty(accounts)) {
            throw new BusinessException("password.error");
        }*/

        validateUserValidaExpire(account);
        if ("Third-000000".equals(account.getUserPassword()) && !StringUtil.isNullOrEmpty(accountDTO.getPassword())) {
            account.setUserPassword(accountDTO.getPassword());
            accountMapper.updateById(account);
        } else if ("Third-000000".equals(account.getUserPassword())) {
            throw new BusinessException("Password cannot be empty");
        } else {
            if (!account.getUserPassword().equals(accountDTO.getPassword())) {
                throw new BusinessException("password.error", ResultEnum.PROMPT.getCode());
            }
        }
        /*发送邮件*/
        AuthenticationOperationTypeEnum authenticationOperationTypeEnum = AuthenticationOperationTypeEnum.of("LOGIN");
        String randomVerifyCode = RandomsUtil.generateVerifyCode(100000L, 999999L);
        LocalCacheUtils.setVerifyCodeCache(
                "LOGIN_" + accountDTO.getEmail(), randomVerifyCode);
        Boolean result = Boolean.FALSE;
        result = SendEmailUtil.send(accountDTO.getEmail(), null,
                SendEmailUtil.LOGIN_TEMPLATE_ID, randomVerifyCode);
        if (!result) {
            throw new BusinessException("failed.to.send.mail");
        }
        return new AccountPreLoginVO(account.getId());

//        AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
//        response.setEmail(account.getUserEmail());
//        String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
//        if (StringUtils.isNotBlank(token)) {
//            //用户已登入
//            response.setToken(token);
//        } else {
//            response.setToken(createAccountToken(account));
//        }
//        response.setUserId(account.getId());
//        response.setSystemUser(account.getSystemUser());
//        // 设置头像
//        String avatar;
//        if (StringUtil.isNullOrEmpty(account.getAvatar())){
//            avatar = CommonConstant.DEFAULT_AVATAR;
//        }else {
//            avatar = account.getAvatar();
//        }
//        response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
//        response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId()));
//        response.setFollowerCount(portfolioService.getFollowerCount(account.getId()));
//        //判断是否常用ip  不是则发邮件提示
////        calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account);
//        return response;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addSubAccount(AddSubAccountDTO addSubAccountDTO) {
        AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
        Account adminAcc = accountMapper.selectById(authPrincipalVo.getId());
        int subUserRole = getSubUserRole(adminAcc.getSystemUser());

        if (addSubAccountDTO.getId() == null) {
            return createSubAccount(addSubAccountDTO, adminAcc, subUserRole);
        } else {
            return updateSubAccount(addSubAccountDTO, adminAcc, subUserRole);
        }
    }

    private int getSubUserRole(int systemUser) {
        switch (systemUser) {
            case 5:
                return 6;
            case 7:
                return 8;
            default:
                throw new BusinessException("Access denied. Insufficient permissions.");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean createSubAccount(AddSubAccountDTO addSubAccountDTO, Account adminAcc, int subUserRole) {
        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().eq(Account::getOrganizationName, adminAcc.getOrganizationName());
        List<Account> accounts = accountMapper.selectList(qw);

        // 校验子账号总数是否达上限
        if (adminAcc.getSubAccountNum() == null || adminAcc.getSubAccountNum() <= 0) {
            throw new BusinessException("Error: Sub-account quota reached (Max: 0). Upgrade to create more.");
        }
        if (accounts.size() >= adminAcc.getSubAccountNum()) {
            throw new BusinessException("Error: Sub-account quota reached (Max: " + adminAcc.getSubAccountNum() + "). Upgrade to create more.");
        }

        if (StringUtil.isNullOrEmpty(addSubAccountDTO.getUserEmail())) {
            throw new BusinessException("email.cannot.be.empty");
        }

        if (StringUtil.isNullOrEmpty(addSubAccountDTO.getUserPassword())) {
            throw new BusinessException("password.cannot.be.empty");
        }

        // 校验邮箱是否已加入组织
        if (isUserEmailExists(adminAcc.getOrganizationName(), addSubAccountDTO.getUserEmail())) {
            throw new BusinessException("This organization already has an account with the same email.", ResultEnum.PROMPT.getCode());
        }

        // 校验用户名是否同名
        if (isUsernameExists(adminAcc.getOrganizationName(), addSubAccountDTO.getUserName())) {
            throw new BusinessException("This organization already has an account with the same username.");
        }

        // 校验当前账号邮箱是否有个人账号
        Account subAccount = accountMapper.selectOne(new QueryWrapper<Account>().eq("user_email", addSubAccountDTO.getUserEmail()));
        List<Integer> personAccRole = Arrays.asList(0, 1, 2, 3);
        List<Integer> orgAccRole = Arrays.asList(5, 6, 7, 8);

        BigDecimal remainingCredits = adminRemainingCredits(adminAcc);
        // 将个人账号加入组织
        if (Objects.nonNull(subAccount) && personAccRole.contains(subAccount.getSystemUser())) {
            log.info("将用户{} 加入组织{}", addSubAccountDTO.getUserEmail(), adminAcc.getOrganizationName());
            subAccount.setUserName(addSubAccountDTO.getUserName());
            if (!StringUtil.isNullOrEmpty(addSubAccountDTO.getUserPassword()))
                subAccount.setUserPassword(addSubAccountDTO.getUserPassword());
            subAccount.setSystemUser(subUserRole);
            subAccount.setOrganizationName(adminAcc.getOrganizationName());
            subAccount.setParentId(adminAcc.getId());
            if (Objects.nonNull(addSubAccountDTO.getCreditsUsageLimit())) {
                if (remainingCredits.compareTo(addSubAccountDTO.getCreditsUsageLimit()) < 0) {
                    throw new BusinessException("Insufficient credits (Balance: " + remainingCredits + ").", ResultEnum.PROMPT.getCode());
                }
                subAccount.setCreditsUsageLimit(addSubAccountDTO.getCreditsUsageLimit());
                subAccount.setCreditsUsage(Objects.isNull(addSubAccountDTO.getCreditsUsage()) ? BigDecimal.ZERO : addSubAccountDTO.getCreditsUsage());
                if (Objects.nonNull(subAccount.getCredits())) {
                    subAccount.setCredits(subAccount.getCreditsUsageLimit().add(subAccount.getCredits()));
                } else {
                    subAccount.setCredits(subAccount.getCreditsUsageLimit());
                }
                adminAcc.setCreditsUsage(adminAcc.getCreditsUsage().add(subAccount.getCreditsUsageLimit()));
                adminAcc.setCredits(adminAcc.getCredits().subtract(subAccount.getCreditsUsageLimit()));
                adminAcc.setUpdateDate(new Date());
                log.debug("分配积分: remainingCredits={}, subAccId={}, setCredits={}", remainingCredits, subAccount.getId(), addSubAccountDTO.getCreditsUsageLimit());
            } else {
                handleSubAccCredits(subAccount, adminAcc);
            }
            subAccount.setUpdateDate(new Date());
            updateById(subAccount);
        }
        // 输入的账号已存在于其他组织
        else if (Objects.nonNull(subAccount) && orgAccRole.contains(subAccount.getSystemUser())) {
            throw new BusinessException("邮箱 " + addSubAccountDTO.getUserEmail() + " 已加入其他组织", ResultEnum.PROMPT.getCode());
        }
        // 完全新建一个账号
        else {
            subAccount = new Account();

            subAccount.setUserName(addSubAccountDTO.getUserName());
            subAccount.setUserEmail(addSubAccountDTO.getUserEmail());
            subAccount.setUserPassword(addSubAccountDTO.getUserPassword());

            // 指定积分上限
            if (Objects.nonNull(addSubAccountDTO.getCreditsUsageLimit())) {
                if (remainingCredits.compareTo(addSubAccountDTO.getCreditsUsageLimit()) < 0) {
                    throw new BusinessException("Insufficient credits (Balance: " + remainingCredits + ").", ResultEnum.PROMPT.getCode());
                }
                subAccount.setCreditsUsageLimit(addSubAccountDTO.getCreditsUsageLimit());
                subAccount.setCreditsUsage(Objects.isNull(addSubAccountDTO.getCreditsUsage()) ? BigDecimal.ZERO : addSubAccountDTO.getCreditsUsage());
                if (Objects.nonNull(subAccount.getCredits())) {
                    subAccount.setCredits(subAccount.getCreditsUsageLimit().add(subAccount.getCredits()));
                } else {
                    subAccount.setCredits(subAccount.getCreditsUsageLimit());
                }
                adminAcc.setCreditsUsage(adminAcc.getCreditsUsage().add(subAccount.getCreditsUsageLimit()));
                adminAcc.setCredits(adminAcc.getCredits().subtract(subAccount.getCreditsUsageLimit()));
                adminAcc.setUpdateDate(new Date());
                log.debug("分配积分: remainingCredits={}, subAccId={}, setCredits={}", remainingCredits, subAccount.getId(), addSubAccountDTO.getCreditsUsageLimit());
            }
            // 未指定积分使用上限
            else {
                handleSubAccCredits(subAccount, adminAcc);
            }
            subAccount.setSystemUser(subUserRole);
            subAccount.setLanguage(Language.ENGLISH.name());
            subAccount.setValidStartTime(adminAcc.getValidStartTime());
            subAccount.setValidEndTime(adminAcc.getValidEndTime());
            subAccount.setCreateDate(new Date());
            subAccount.setIsTrial(0);
            subAccount.setIsBeginner(1);
            subAccount.setParentId(adminAcc.getId());
            subAccount.setOrganizationName(adminAcc.getOrganizationName());
            accountMapper.insert(subAccount);
        }
        updateById(adminAcc);
        return Boolean.TRUE;
    }

    private Boolean updateSubAccount(AddSubAccountDTO addSubAccountDTO, Account adminAcc, int subUserRole) {
        Account exAccountInfo = baseMapper.selectById(addSubAccountDTO.getId());
        if (!exAccountInfo.getParentId().equals(adminAcc.getId())) {
            throw new BusinessException("Access denied. Insufficient permissions.");
        }

        // 校验用户名是否同名
        if (!StringUtil.isNullOrEmpty(addSubAccountDTO.getUserName())
                && !exAccountInfo.getUserName().equals(addSubAccountDTO.getUserName())
                && isUsernameExists(adminAcc.getOrganizationName(), addSubAccountDTO.getUserName())) {
            throw new BusinessException("This organization already has an account with the same username.");
        } else if (!StringUtil.isNullOrEmpty(addSubAccountDTO.getUserName())
                && !exAccountInfo.getUserName().equals(addSubAccountDTO.getUserName())) {
            exAccountInfo.setUserName(addSubAccountDTO.getUserName());
        }

        // 判断积分变更是增加还是减少还是没变化，需修改子账号的credits\creditsUsageLimit,管理员账号的creditUsage
        if (Objects.nonNull(addSubAccountDTO.getCreditsUsageLimit())
                && exAccountInfo.getCreditsUsageLimit().compareTo(addSubAccountDTO.getCreditsUsageLimit()) < 0) {
            BigDecimal remainingCredits = adminRemainingCredits(adminAcc);
            // 新增加的积分
            BigDecimal addedCredits = addSubAccountDTO.getCreditsUsageLimit().subtract(exAccountInfo.getCreditsUsageLimit());
            if (remainingCredits.compareTo(addedCredits) >= 0) {
                // 更新管理员已分配的积分
                adminAcc.setCreditsUsage(adminAcc.getCreditsUsage().add(addedCredits));
                // 管理员账号可以购买积分，但是不会加入到可分配积分的池子里，只需要加到credits
                adminAcc.setCredits(adminAcc.getCredits().subtract(addedCredits));
                // 更新子账号的积分上限和目前所有积分总数
                exAccountInfo.setCreditsUsageLimit(addSubAccountDTO.getCreditsUsageLimit());
                exAccountInfo.setCredits(exAccountInfo.getCredits().add(addedCredits));
            } else {
                throw new BusinessException("Insufficient credits (Balance: " + remainingCredits + ").", ResultEnum.PROMPT.getCode());
            }
        } else if (Objects.nonNull(addSubAccountDTO.getCreditsUsageLimit())
                && exAccountInfo.getCreditsUsageLimit().compareTo(addSubAccountDTO.getCreditsUsageLimit()) > 0) {
            if (exAccountInfo.getCreditsUsage().compareTo(addSubAccountDTO.getCreditsUsageLimit()) > 0) {
                throw new BusinessException("Usage alert: " + exAccountInfo.getCreditsUsage() + " credits consumed this month. New limit must be ≥ " + exAccountInfo.getCreditsUsage() + " .");
            } else {
                // 减少的积分
                BigDecimal subtractedCredits = exAccountInfo.getCreditsUsageLimit().subtract(addSubAccountDTO.getCreditsUsageLimit());
                // 更新管理员已分配的积分（积分回流）
                adminAcc.setCreditsUsage(adminAcc.getCreditsUsage().subtract(subtractedCredits));
                // 管理员账号可以购买积分，但是不会加入到可分配积分的池子里，只需要加到credits
                adminAcc.setCredits(adminAcc.getCredits().add(subtractedCredits));
                // 更新子账号的积分上限和目前所有积分总数
                exAccountInfo.setCreditsUsageLimit(addSubAccountDTO.getCreditsUsageLimit());
                exAccountInfo.setCredits(exAccountInfo.getCredits().subtract(subtractedCredits));
            }
        }

        // 校验密码是否变更
        if (!StringUtil.isNullOrEmpty(addSubAccountDTO.getUserPassword())
                && !exAccountInfo.getUserPassword().equals(addSubAccountDTO.getUserPassword())) {
            exAccountInfo.setUserPassword(addSubAccountDTO.getUserPassword());
        }

        // 校验邮箱是否变更
        if (!StringUtil.isNullOrEmpty(addSubAccountDTO.getUserEmail())
                && !exAccountInfo.getUserEmail().equals(addSubAccountDTO.getUserEmail())) {
            // 原账号的积分使用上限
//            BigDecimal creditsLimit = exAccountInfo.getCreditsUsageLimit();
            addSubAccountDTO.setCreditsUsageLimit(exAccountInfo.getCreditsUsageLimit());
            // 原账号已使用的积分
//            BigDecimal creditsUsage = exAccountInfo.getCreditsUsage();
            addSubAccountDTO.setCreditsUsage(exAccountInfo.getCreditsUsage());
            // 这里移除原账号，但是积分不回流，机构分配的积分会由下一个账号继续持有（包括积分上限和已使用的积分都保持不变）
            removeSubAccount(new AddSubAccountDTO(Collections.singletonList(addSubAccountDTO.getId())), adminAcc.getId());
            // 移入新子账号（可能是移入，也可能是新增）
            createSubAccount(addSubAccountDTO, adminAcc, subUserRole);
        } else {
            baseMapper.updateById(exAccountInfo);
            baseMapper.updateById(adminAcc);
        }
        return Boolean.TRUE;
    }

    private boolean isUserEmailExists(String organizationName, String email) {
        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().eq(Account::getOrganizationName, organizationName).eq(Account::getUserEmail, email);
        return accountMapper.selectCount(qw) > 0;
    }

    private boolean isUsernameExists(String organizationName, String userName) {
        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().eq(Account::getOrganizationName, organizationName).eq(Account::getUserName, userName);
        return accountMapper.selectCount(qw) > 0;
    }

    public BigDecimal adminRemainingCredits(Account adminAccount) {
        if (adminAccount == null) {
            AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
            adminAccount = accountMapper.selectById(authPrincipalVo.getId());
            if (adminAccount == null) {
                throw new BusinessException("管理员账户不存在");
            }
        }

        if (Objects.nonNull(adminAccount.getCreditsUsageLimit()) && Objects.nonNull(adminAccount.getCreditsUsage())) {
            return adminAccount.getCreditsUsageLimit().subtract(adminAccount.getCreditsUsage());
        } else if (Objects.nonNull(adminAccount.getCreditsUsageLimit())) {
            return adminAccount.getCreditsUsageLimit();
        } else {
            return BigDecimal.ZERO;
        }
    }

    // 仅适用于新建用户
    private void handleSubAccCredits(Account subAcc, Account adminAcc) {
        BigDecimal remainingCredits = adminRemainingCredits(adminAcc);
        if (remainingCredits.compareTo(BigDecimal.ZERO) < 0) {
            remainingCredits = BigDecimal.ZERO;
        }

        if (Objects.nonNull(adminAcc.getCreditsUsageLimit()) && (Objects.isNull(subAcc.getCreditsUsageLimit()) || subAcc.getCreditsUsageLimit().compareTo(BigDecimal.ZERO) == 0)) {
            // 默认使用平均积分
            BigDecimal defaultCredits = adminAcc.getCreditsUsageLimit().divide(new BigDecimal(adminAcc.getSubAccountNum()), RoundingMode.FLOOR);
            ;

            if (remainingCredits.compareTo(defaultCredits) >= 0) {
                subAcc.setCreditsUsageLimit(defaultCredits);
            } else if (remainingCredits.compareTo(BigDecimal.ZERO) > 0) {
                subAcc.setCreditsUsageLimit(remainingCredits);
            } else {
                subAcc.setCreditsUsageLimit(BigDecimal.ZERO);
            }
            subAcc.setCreditsUsage(BigDecimal.ZERO);
            adminAcc.setCreditsUsage(adminAcc.getCreditsUsage().add(subAcc.getCreditsUsageLimit()));
            adminAcc.setCredits(adminAcc.getCredits().subtract(subAcc.getCreditsUsageLimit()));
            adminAcc.setUpdateDate(new Date());
            log.debug("分配积分: remainingCredits={}, subAccId={}, defaultCredits={}", remainingCredits, subAcc.getId(), defaultCredits);

            if (Objects.nonNull(subAcc.getCredits())) {
                subAcc.setCredits(subAcc.getCreditsUsageLimit().add(subAcc.getCredits()));
            } else {
                subAcc.setCredits(subAcc.getCreditsUsageLimit());
            }
        }
        // 创建账号时指定积分
        else if (Objects.nonNull(adminAcc.getCreditsUsageLimit())) {
            if (remainingCredits.compareTo(subAcc.getCreditsUsageLimit()) < 0) {
                throw new BusinessException("Insufficient credits (Balance: " + remainingCredits + ").", ResultEnum.PROMPT.getCode());
            } else {
                subAcc.setCredits(subAcc.getCreditsUsageLimit());
            }
        }
    }

    /*@Override
    public Boolean deleteSubAccount(AddSubAccountDTO addSubAccountDTO) {
        accountMapper.deleteBatchIds(addSubAccountDTO.getDeleteIdList());
        return Boolean.TRUE;
    }*/

    public void removeSubAccount(AddSubAccountDTO addSubAccountDTO, Long adminAccId) {
//        Long adminAccId = UserContext.getUserHolder().getId();
        Account adminAcc = baseMapper.selectById(adminAccId);
        if (Objects.isNull(adminAcc) || (adminAcc.getSystemUser() != 5 && adminAcc.getSystemUser() != 7)) {
            throw new BusinessException("have.no.permission");
        }

        BigDecimal unusedCreditsTotal = BigDecimal.ZERO;
        for (Long id : addSubAccountDTO.getDeleteIdList()) {
            Account account = baseMapper.selectById(id);
            if (Objects.nonNull(account.getParentId()) && account.getParentId().equals(adminAccId)) {
                BigDecimal unusedCredits = account.getCreditsUsageLimit().subtract(Objects.isNull(account.getCreditsUsage()) ? BigDecimal.ZERO : account.getCreditsUsage());
                BigDecimal finalCredits = calculateFinalCredits(account, unusedCredits);

                int userRole = determineUserRole(account.getId());

                UpdateWrapper<Account> updateWrapper = new UpdateWrapper<>();
                updateWrapper.lambda().eq(Account::getId, account.getId())
                        .set(Account::getOrganizationName, null)
                        .set(Account::getOrganizationId, null)
                        .set(Account::getParentId, null)
                        .set(Account::getSystemUser, userRole)
                        .set(Account::getCredits, finalCredits)
                        .set(Account::getCreditsUsage, null)
                        .set(Account::getCreditsUsageLimit, null)
                        .set(Account::getUpdateDate, new Date());
                baseMapper.update(null, updateWrapper);

                if (unusedCredits.compareTo(BigDecimal.ZERO) > 0) {
                    unusedCreditsTotal = unusedCreditsTotal.add(unusedCredits);
                }
            } else {
                log.warn("需要移除账号 {}: {} 不属于当前管理员 {}: {}", id, account.getUserEmail(), adminAccId, adminAcc.getUserEmail());
            }
        }
        // 将积分回流
        if (unusedCreditsTotal.compareTo(BigDecimal.ZERO) != 0) {
            BigDecimal subtracted = adminAcc.getCreditsUsage().subtract(unusedCreditsTotal);
            adminAcc.setCreditsUsage(subtracted.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : subtracted);
            // 管理员账号可以购买积分，但是不会加入到可分配积分的池子里，只需要加到credits
            adminAcc.setCredits(adminAcc.getCredits().add(unusedCreditsTotal));
            adminAcc.setUpdateDate(new Date());
            baseMapper.updateById(adminAcc);
        }

    }

    private BigDecimal calculateFinalCredits(Account account, BigDecimal unusedCredits) {
        return unusedCredits.compareTo(BigDecimal.ZERO) > 0 ? account.getCredits().subtract(unusedCredits) :
                account.getCredits();
    }

    private int determineUserRole(Long accountId) {
        List<String> subscriptionType = Arrays.asList(ProductEnum.MonthlySubscription.getName(), ProductEnum.AnnualSubscription.getName());
        List<OrderInfo> list = orderInfoService.list(new QueryWrapper<OrderInfo>().lambda().eq(OrderInfo::getAccountId, accountId)
                .in(OrderInfo::getTitle, subscriptionType).orderByDesc(OrderInfo::getId));
        if (!list.isEmpty()) {
            return list.get(0).getTitle().equals(ProductEnum.MonthlySubscription.getName()) ? 2 :
                    list.get(0).getTitle().equals(ProductEnum.AnnualSubscription.getName()) ? 1 : 0;
        }
        return 0;
    }

    @Override
    public PageBaseResponse<Account> subAccountList(SubAccountPageDTO subAccountPageDTO) {
        AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
        Account account = accountMapper.selectById(authPrincipalVo);
        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().ne(Account::getId, account.getId());
        qw.lambda().eq(Account::getOrganizationName, account.getOrganizationName());
        if (StringUtils.isNotBlank(subAccountPageDTO.getStartTime())) {
            qw.lambda().ge(Account::getCreateDate, subAccountPageDTO.getStartTime());
        }
        if (StringUtils.isNotBlank(subAccountPageDTO.getEndTime())) {
            qw.lambda().le(Account::getCreateDate, subAccountPageDTO.getEndTime());
        }
        if (subAccountPageDTO.getEmail() != null && subAccountPageDTO.getEmail().size() == 1) {
            qw.lambda().like(Account::getUserEmail, subAccountPageDTO.getEmail().get(0));
        } else if (subAccountPageDTO.getEmail() != null && subAccountPageDTO.getEmail().size() > 1) {
            qw.lambda().in(Account::getUserEmail, subAccountPageDTO.getEmail());
        }
        if (subAccountPageDTO.getUserName() != null && subAccountPageDTO.getUserName().size() == 1) {
            qw.lambda().like(Account::getUserName, subAccountPageDTO.getUserName().get(0));
        } else if (subAccountPageDTO.getUserName() != null && subAccountPageDTO.getUserName().size() > 1) {
            qw.lambda().in(Account::getUserName, subAccountPageDTO.getUserName());
        }

        // 执行分页查询
        IPage<Account> page = accountMapper.selectPage(new Page<>(subAccountPageDTO.getPage(), subAccountPageDTO.getSize()), qw);

        return PageBaseResponse.success(page);
    }

    @Override
    public Account accountDetail(Long id) {
        return accountMapper.selectById(id);
    }

    @Override
    public AccountLoginVO parseGoogleCredential(String credential, Integer type) {
        // 配置 Google ID Token 验证器
        GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(
                new NetHttpTransport(),
                JacksonFactory.getDefaultInstance())
                .setAudience(Collections.singletonList(googleClientId))
                .build();

        // 验证并解析 ID Token
        GoogleIdToken idToken = null;
        try {
            idToken = verifier.verify(credential);
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


        if (idToken != null) {
            GoogleIdToken.Payload payload = idToken.getPayload();

            // 提取用户信息
            String userId = payload.getSubject();
            String email = payload.getEmail();
            String name = (String) payload.get("name");
            String pictureUrl = (String) payload.get("picture");
            log.info(userId);
            log.info(email);
            log.info(name);
            Account account = new Account();
            if (type == 1) {
                // 注册
                QueryWrapper<AccountExtend> accountExtendQW = new QueryWrapper<>();
                accountExtendQW.lambda().eq(AccountExtend::getAuthType, "Google");
                accountExtendQW.lambda().eq(AccountExtend::getAuth, userId);
                List<AccountExtend> accountExtends = accountExtendMapper.selectList(accountExtendQW);
                if (CollectionUtil.isNotEmpty(accountExtends)) {
                    throw new BusinessException("This Google account has been registered for AiDA, please use Google Quick Login directly.");
                } else {
                    QueryWrapper<Account> accountQueryWrapper = new QueryWrapper<>();
                    accountQueryWrapper.lambda().eq(Account::getUserEmail, email);  // 根据邮箱查询用户
                    List<Account> accounts = accountMapper.selectList(accountQueryWrapper);
                    if (CollectionUtil.isNotEmpty(accounts)) {
                        account = accounts.get(0);
                        AccountExtend accountExtendInsert = new AccountExtend();
                        accountExtendInsert.setAuth(userId);
                        accountExtendInsert.setAuthType("Google");
                        accountExtendInsert.setHeadImgUrl(pictureUrl);
                        accountExtendInsert.setName(name);
                        accountExtendInsert.setAccountId(account.getId());
                        accountExtendMapper.insert(accountExtendInsert);
                    } else {
                        Account newUser = new Account();
                        newUser.setUserEmail(email);
                        newUser.setUserName(name);
                        newUser.setUserPassword("Third-000000");
                        newUser.setLanguage(Language.ENGLISH.name());
                        newUser.setValidStartTime(System.currentTimeMillis());
                        newUser.setValidEndTime(toDayEnd(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli()));
                        newUser.setCreateDate(new Date());
                        newUser.setIsTrial(1);
                        newUser.setIsBeginner(1);
                        newUser.setCredits(BigDecimal.valueOf(50));
                        newUser.setSystemUser(3);
                        accountMapper.insert(newUser);

                        account = newUser;

                        AccountExtend accountExtendInsert = new AccountExtend();
                        accountExtendInsert.setAuth(userId);
                        accountExtendInsert.setAuthType("Google");
                        accountExtendInsert.setHeadImgUrl(pictureUrl);
                        accountExtendInsert.setName(name);
                        accountExtendInsert.setAccountId(newUser.getId());
                        accountExtendMapper.insert(accountExtendInsert);
                    }
                }
            } else {
                // 登录
                QueryWrapper<AccountExtend> accountExtendQW = new QueryWrapper<>();
                accountExtendQW.lambda().eq(AccountExtend::getAuthType, "Google");
                accountExtendQW.lambda().eq(AccountExtend::getAuth, userId);
                List<AccountExtend> accountExtends = accountExtendMapper.selectList(accountExtendQW);
                if (CollectionUtil.isNotEmpty(accountExtends)) {
                    AccountExtend accountExtend = accountExtends.get(0);
                    account = accountMapper.selectById(accountExtend.getAccountId());
                } else {
                    throw new BusinessException("This Google account has not been bound to an AiDA account yet. Please register an AiDA account and bind it to a Google account, or bind the Google account to an existing AiDA account.");
                }
            }
            AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
            response.setEmail(account.getUserEmail());
            String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
            if (StringUtils.isNotBlank(token)) {
                //用户已登入
                response.setToken(token);
            } else {
                response.setToken(createAccountToken(account));
            }
            response.setUserId(account.getId());
            response.setSystemUser(account.getSystemUser());
            // 设置头像
            String avatar;
            if (StringUtil.isNullOrEmpty(account.getAvatar())) {
                avatar = CommonConstant.DEFAULT_AVATAR;
            } else {
                avatar = account.getAvatar();
            }
            response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
            response.setFolloweeCount(userFollowService.getFolloweeCount(account.getId()));
            response.setFollowerCount(userFollowService.getFollowerCount(account.getId()));
            return response;
        } else {
            throw new IllegalArgumentException("Invalid ID token.");
        }
    }

    private static final String WECHAT_ACCESS_TOKEN_URL =
            "https://api.weixin.qq.com/sns/oauth2/access_token";

    private static final String APP_ID = "wxcfb92eb28d6385f5";
    private static final String APP_SECRET = "e5592c691756455b2d03ebfd21fc3131";

    @Override
    public AccountLoginVO parseWeChatCode(String code, Integer type) {
        // 1. 获取 access_token 和 openid
        JSONObject accessTokenResponse = getAccessTokenFromWeChat(code);
        String accessToken = accessTokenResponse.getString("access_token");
        String openId = accessTokenResponse.getString("openid");

        if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openId)) {
            throw new RuntimeException("微信接口返回数据缺失: " + accessTokenResponse.toJSONString());
        }

        // 2. 获取用户信息
        JSONObject userInfoResponse = getUserInfoFromWeChat(accessToken, openId);

        // 提取 unionid 和 nickname
        String unionId = userInfoResponse.getString("unionid");
        String userName = userInfoResponse.getString("nickname");
        String headimgurl = userInfoResponse.getString("headimgurl");

        if (unionId == null) {
            throw new IllegalArgumentException("无法获取 unionid，请检查微信开发平台配置");
        }
        Account account = new Account();
        if (type == 1) {
            // 注册
            // 检查数据库中是否已有该unionid
            QueryWrapper<AccountExtend> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(AccountExtend::getAuthType, "WeChat");
            queryWrapper.lambda().eq(AccountExtend::getAuth, unionId);
            List<AccountExtend> accountExtends = accountExtendMapper.selectList(queryWrapper);
            if (CollectionUtil.isNotEmpty(accountExtends)) {
                throw new BusinessException("This Wechat account has been registered for AiDA, please use Wechat Quick Login directly.");
            } else {
                // 创建新用户（自动注册）
                Account newUser = new Account();
                newUser.setUserName(userName);
                newUser.setUserPassword("Third-000000");
                newUser.setLanguage(Language.ENGLISH.name());
                newUser.setValidStartTime(System.currentTimeMillis());
                newUser.setValidEndTime(toDayEnd(Instant.now().plus(5, ChronoUnit.DAYS).toEpochMilli()));
                newUser.setCreateDate(new Date());
                newUser.setIsTrial(1);
                newUser.setIsBeginner(1);
                newUser.setCredits(BigDecimal.valueOf(50));
                newUser.setSystemUser(3);
                accountMapper.insert(newUser);

                AccountExtend accountExtendInsert = new AccountExtend();
                accountExtendInsert.setAuth(unionId);
                accountExtendInsert.setAuthType("WeChat");
                accountExtendInsert.setHeadImgUrl(headimgurl);
                accountExtendInsert.setName(userName);
                accountExtendInsert.setAccountId(newUser.getId());
                accountExtendMapper.insert(accountExtendInsert);

                account = newUser;
            }
        } else {
            // 登录
            // 检查数据库中是否已有该unionid
            QueryWrapper<AccountExtend> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(AccountExtend::getAuthType, "WeChat");
            queryWrapper.lambda().eq(AccountExtend::getAuth, unionId);
            List<AccountExtend> accountExtends = accountExtendMapper.selectList(queryWrapper);
            if (CollectionUtil.isNotEmpty(accountExtends)) {
                AccountExtend accountExtend = accountExtends.get(0);
                account = accountMapper.selectById(accountExtend.getAccountId());
            } else {
                throw new BusinessException("This WeChat account has not been bound to an AiDA account yet. Please register an AiDA account and bind it to the WeChat account, or bind the WeChat account to an existing AiDA account.");
            }
        }

        AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
        response.setEmail(account.getUserEmail());
        String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
        if (StringUtils.isNotBlank(token)) {
            //用户已登入
            response.setToken(token);
        } else {
            response.setToken(createAccountToken(account));
        }
        response.setUserId(account.getId());
        response.setSystemUser(account.getSystemUser());
        // 设置头像
        String avatar;
        if (StringUtil.isNullOrEmpty(account.getAvatar())) {
            avatar = CommonConstant.DEFAULT_AVATAR;
        } else {
            avatar = account.getAvatar();
        }
        response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
        response.setFolloweeCount(userFollowService.getFolloweeCount(account.getId()));
        response.setFollowerCount(userFollowService.getFollowerCount(account.getId()));
        return response;
    }

    private static final String WECHAT_USER_INFO_URL = "https://api.weixin.qq.com/sns/userinfo";

    private JSONObject getUserInfoFromWeChat(String accessToken, String openId) {
        // 构造微信用户信息接口的 URL
        String url = String.format(
                "%s?access_token=%s&openid=%s&lang=zh_CN",
                WECHAT_USER_INFO_URL, accessToken, openId
        );

        // 调用微信接口
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(url, String.class);

        // 强制校正编码为 UTF-8（如果返回的是 ISO-8859-1 编码）
        if (!isUTF8(response)) { // 检查编码方法，下面会提供
            byte[] bytes = response.getBytes(StandardCharsets.ISO_8859_1);
            response = new String(bytes, StandardCharsets.UTF_8);
        }

        // 打印调试信息
        log.info("WeChat user info response: {}", response);

        // 转换为 JSON 对象
        JSONObject jsonResponse = JSONObject.parseObject(response);
        if (jsonResponse.containsKey("errcode")) {
            throw new RuntimeException("微信用户信息接口调用失败: " + jsonResponse.getString("errmsg"));
        }

        return jsonResponse;
    }

    private boolean isUTF8(String text) {
        try {
            byte[] bytes = text.getBytes(StandardCharsets.ISO_8859_1);
            String decoded = new String(bytes, StandardCharsets.UTF_8);
            return decoded.equals(text);
        } catch (Exception e) {
            return false;
        }
    }


    private JSONObject getAccessTokenFromWeChat(String code) {
        // 构造微信接口请求 URL
        String url = String.format(
                "%s?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
                WECHAT_ACCESS_TOKEN_URL, APP_ID, APP_SECRET, code
        );

        // 调用微信接口
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(url, String.class);

        // 转换为 JSON 对象
        JSONObject jsonResponse = JSONObject.parseObject(response);
        if (jsonResponse.containsKey("errcode")) {
            throw new RuntimeException("微信接口调用失败: " + jsonResponse.getString("errmsg"));
        }

        return jsonResponse;
    }

    @Override
    public AccountLoginVO getAccountDetail() {
        AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
        Long accountId = authPrincipalVo.getId();
        Account account = accountMapper.selectById(accountId);

        AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class);
        response.setEmail(account.getUserEmail());
        String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
        if (StringUtils.isNotBlank(token)) {
            //用户已登入
            response.setToken(token);
        } else {
            response.setToken(createAccountToken(account));
        }
        response.setUserId(account.getId());
        response.setSystemUser(account.getSystemUser());
        // 设置头像
        String avatar;
        if (StringUtil.isNullOrEmpty(account.getAvatar())) {
            avatar = CommonConstant.DEFAULT_AVATAR;
        } else {
            avatar = account.getAvatar();
        }
        response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
        response.setFolloweeCount(userFollowService.getFolloweeCount(account.getId()));
        response.setFollowerCount(userFollowService.getFollowerCount(account.getId()));

        QueryWrapper<AccountExtend> qw = new QueryWrapper<>();
        qw.lambda().eq(AccountExtend::getAccountId, response.getUserId());
        List<AccountExtend> accountExtends = accountExtendMapper.selectList(qw);
        if (CollectionUtil.isNotEmpty(accountExtends)) {
            response.setAccountExtendList(accountExtends);
        }
        response.setLanguage(Language.valueOf(account.getLanguage()).name());
        SubscriptionInfo subscriptionInfo = stripeService.getLatestSubscriptionInfoByAccountId(accountId);
        if (!Objects.isNull(subscriptionInfo)) {
            response.setSubscriptionId(subscriptionInfo.getSubscriptionId());
            response.setSubscriptionType(subscriptionInfo.getType());
            response.setStatus(subscriptionInfo.getStatus());
            response.setExpireTime(String.valueOf(subscriptionInfo.getCurrentPeriodEnd()));
            response.setAutoRenewal(subscriptionInfo.getStatus().equals("active"));
        }

        Affiliate affiliate = affiliateService.getByAccountId(accountId);
        if (!Objects.isNull(affiliate) && affiliate.getStatus().equals("Active")) {
            response.setAffiliate(true);
        }

        response.setUsernameModify(getNicknameModifyTimes());
        return response;
    }

    @Override
    public AccountExtend bindGoogle(String credential) {
        // 配置 Google ID Token 验证器
        GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(
                new NetHttpTransport(),
                JacksonFactory.getDefaultInstance())
                .setAudience(Collections.singletonList(googleClientId))
                .build();

        // 验证并解析 ID Token
        GoogleIdToken idToken = null;
        try {
            idToken = verifier.verify(credential);
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


        if (idToken != null) {
            GoogleIdToken.Payload payload = idToken.getPayload();

            // 提取用户信息
            String userId = payload.getSubject();
            String email = payload.getEmail();
            String name = (String) payload.get("name");
            String pictureUrl = (String) payload.get("picture");

            QueryWrapper<AccountExtend> qw = new QueryWrapper<>();
            qw.lambda().eq(AccountExtend::getAuth, userId);
            qw.lambda().eq(AccountExtend::getAuthType, "Google");
            List<AccountExtend> accountExtends = accountExtendMapper.selectList(qw);

            if (CollectionUtil.isNotEmpty(accountExtends)) {
                throw new BusinessException("the.Google.has.been.bound");
            }

            AccountExtend accountExtendInsert = new AccountExtend();
            accountExtendInsert.setAuth(userId);
            accountExtendInsert.setAuthType("Google");
            accountExtendInsert.setHeadImgUrl(pictureUrl);
            accountExtendInsert.setName(name);

            AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
            accountExtendInsert.setAccountId(authPrincipalVo.getId());
            accountExtendMapper.insert(accountExtendInsert);

            return accountExtendInsert;
        } else {
            throw new IllegalArgumentException("Invalid ID token.");
        }
    }

    @Override
    @Transactional
    public AccountExtend bindWeChat(String code) {
        log.info("code:{}", code);
        // 1. 获取 access_token 和 openid
        JSONObject accessTokenResponse = getAccessTokenFromWeChat(code);
        String accessToken = accessTokenResponse.getString("access_token");
        String openId = accessTokenResponse.getString("openid");

        if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openId)) {
            log.error("微信接口返回数据缺失: {}", accessTokenResponse.toJSONString());
            throw new BusinessException("Unable.to.obtain.WeChat.account.information");
        }

        // 2. 获取用户信息
        JSONObject userInfoResponse = getUserInfoFromWeChat(accessToken, openId);

        // 提取 unionid 和 nickname
        String unionId = userInfoResponse.getString("unionid");
        String userName = userInfoResponse.getString("nickname");
        String headimgurl = userInfoResponse.getString("headimgurl");
        if (unionId == null) {
            log.error("无法获取 unionid，请检查微信开发平台配置");
            throw new BusinessException("Unable.to.obtain.WeChat.account.information");
        }
        QueryWrapper<AccountExtend> qw = new QueryWrapper<>();
        qw.lambda().eq(AccountExtend::getAuth, unionId);
        qw.lambda().eq(AccountExtend::getAuthType, "WeChat");
        List<AccountExtend> accountExtends = accountExtendMapper.selectList(qw);

        if (CollectionUtil.isNotEmpty(accountExtends)) {
            AccountExtend accountExtend = accountExtends.get(0);
            AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();

//            accountMapper.deleteById(accountExtend.getAccountId());
            log.info("排查用户被删除原因：bindWeChat，删除用户（改为降为游客）");
            accountMapper.toVisitor(accountExtend.getAccountId());

            accountExtend.setAccountId(authPrincipalVo.getId());
            accountExtendMapper.updateById(accountExtend);
            return accountExtend;
        }

        AccountExtend accountExtendInsert = new AccountExtend();
        accountExtendInsert.setAuth(unionId);
        accountExtendInsert.setAuthType("WeChat");
        accountExtendInsert.setHeadImgUrl(headimgurl);
        accountExtendInsert.setName(userName);

        AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
        accountExtendInsert.setAccountId(authPrincipalVo.getId());
        accountExtendMapper.insert(accountExtendInsert);

        return accountExtendInsert;
    }

    @Override
    public Boolean unbindWeChat() {
        AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
        QueryWrapper<AccountExtend> qw = new QueryWrapper<>();
        qw.lambda().eq(AccountExtend::getAccountId, authPrincipalVo.getId());
        qw.lambda().eq(AccountExtend::getAuthType, "WeChat");
        accountExtendMapper.delete(qw);
        return Boolean.TRUE;
    }

    @Override
    public Boolean unbindGoogle() {
        AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
        QueryWrapper<AccountExtend> qw = new QueryWrapper<>();
        qw.lambda().eq(AccountExtend::getAccountId, authPrincipalVo.getId());
        qw.lambda().eq(AccountExtend::getAuthType, "Google");
        accountExtendMapper.delete(qw);
        return Boolean.TRUE;
    }

    @Override
    @Transactional
    public BindEmailVO bindEmail(String email) {
        AuthPrincipalVo userHolder = UserContext.getUserHolder();

        BindEmailVO result = new BindEmailVO();

        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().eq(Account::getUserEmail, email);
        List<Account> accounts = accountMapper.selectList(qw);
        if (CollectionUtil.isNotEmpty(accounts)) {
            Account account = accounts.get(0);

            QueryWrapper<AccountExtend> accountExtendQW = new QueryWrapper<>();
            accountExtendQW.lambda().eq(AccountExtend::getAccountId, userHolder.getId());
            accountExtendQW.lambda().eq(AccountExtend::getAuthType, "Wechat");
            AccountExtend accountExtend = accountExtendMapper.selectOne(accountExtendQW);
            accountExtend.setAccountId(account.getId());
            accountExtendMapper.updateById(accountExtend);

//            accountMapper.deleteById(userHolder.getId());
            log.info("排查用户被删除原因：bindEmail，GET，删除用户（改为降为游客）");
            accountMapper.toVisitor(accountExtend.getAccountId());

            String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId()));
            if (StringUtils.isNotBlank(token)) {
                //用户已登入
                result.setToken(token);
            } else {
                result.setToken(createAccountToken(account));
            }
            return result;
        }
        Account account = accountMapper.selectById(userHolder.getId());
        account.setUserEmail(email);
        accountMapper.updateById(account);
        return result;
    }

    public boolean updateAccountValidity(Long accountId, Long currentPeriodEnd) {
        // 不管当前用户的账号是否到期，都根据付款信息重置账号到期时间
        Account account = accountMapper.selectById(accountId);
        if (!Objects.isNull(account.getValidEndTime())
                && account.getValidEndTime().equals(currentPeriodEnd * 1000)) {
            return false;
        } else {
            account.setValidEndTime(currentPeriodEnd * 1000);
            accountMapper.updateById(account);
        }
        return true;
    }

    @Override
    public void updateUserRoleAndCredits(Long accountId, String orderNo) {
        Account account = accountMapper.selectById(accountId);
        OrderInfo orderInfo = orderInfoService.getOrderByOrderNo(orderNo);
        if (Objects.nonNull(orderInfo) && !StringUtil.isNullOrEmpty(orderInfo.getTitle())) {
            String description = orderInfo.getTitle();
            Long productCredits;
            if (description.equals(ProductEnum.DailySubscription.getName())) {
                productCredits = ProductEnum.DailySubscription.getCredits();
                account.setSystemUser(3);
                account.setCredits(BigDecimal.valueOf(Long.parseLong(CreditsEventsEnum.INIT_WEEKLY.getValue())));
            } else if (description.equals(ProductEnum.MonthlySubscription.getName())) {
                productCredits = ProductEnum.MonthlySubscription.getCredits();
                account.setSystemUser(2);
                account.setCredits(BigDecimal.valueOf(Long.parseLong(CreditsEventsEnum.INIT_MONTHLY.getValue())));
            } else if (description.equals(ProductEnum.Eco_MonthlySubscription.getName())) {
                productCredits = ProductEnum.Eco_MonthlySubscription.getCredits();
                account.setSystemUser(2);
                account.setCredits(BigDecimal.valueOf(Long.parseLong(CreditsEventsEnum.INIT_MONTHLY_ECO.getValue())));
            } else if (description.equals(ProductEnum.AnnualSubscription.getName())) {
                productCredits = ProductEnum.AnnualSubscription.getCredits();
                account.setSystemUser(1);
                account.setCredits(BigDecimal.valueOf(Long.parseLong(CreditsEventsEnum.INIT_YEARLY.getValue())));
            } else {
                log.error("未知订阅类型: {}", description);
                return;
            }
            accountMapper.updateById(account);

            // 先判断是否已添加添加积分变更记录
            CreditsDetail creditsDetail = creditsService.queryDetailByTaskId(orderNo);
            if (Objects.isNull(creditsDetail)) {
                creditsService.insertToCreditsDetail(accountId,
                        description + "--Stripe",
                        String.valueOf(productCredits),
                        "positive", orderNo);
            }
        } else {
            log.error("orderNo: {} 无法找到对应的记录", orderNo);
        }

        /*switch (type) {
            case "month":
                account.setSystemUser(2);
                account.setCredits(BigDecimal.valueOf(Long.parseLong(CreditsEventsEnum.INIT_MONTHLY.getValue())));
                break;
            case "year":
                account.setSystemUser(1);
                account.setCredits(BigDecimal.valueOf(Long.parseLong(CreditsEventsEnum.INIT_YEARLY.getValue())));
                break;
            case "day":
                account.setSystemUser(3);
                account.setCredits(BigDecimal.valueOf(Long.parseLong(CreditsEventsEnum.INIT_WEEKLY.getValue())));
                break;
        }*/
    }

    public Boolean updateUserInfo(UpdateUserInfoDTO updateUserInfoDTO) {
        Long accountId = UserContext.getUserHolder().getId();
        Account account = accountMapper.selectById(accountId);
        if (StringUtils.isNotEmpty(updateUserInfoDTO.getOccupation()) && !account.getOccupation().equals(updateUserInfoDTO.getOccupation())) {
            account.setOccupation(updateUserInfoDTO.getOccupation());
        }
        if (StringUtils.isNotEmpty(updateUserInfoDTO.getCountry()) && !account.getCountry().equals(updateUserInfoDTO.getCountry())) {
            account.setCountry(updateUserInfoDTO.getCountry());
        }
        if (StringUtils.isNotEmpty(updateUserInfoDTO.getTitle()) && !account.getTitle().equals(updateUserInfoDTO.getTitle())) {
            account.setTitle(updateUserInfoDTO.getTitle());
        }
        if (StringUtils.isNotEmpty(updateUserInfoDTO.getSurname()) && !account.getSurname().equals(updateUserInfoDTO.getSurname())) {
            account.setSurname(updateUserInfoDTO.getSurname());
        }
        if (StringUtils.isNotEmpty(updateUserInfoDTO.getGivenName()) && !account.getGivenName().equals(updateUserInfoDTO.getGivenName())) {
            account.setGivenName(updateUserInfoDTO.getGivenName());
        }
        accountMapper.updateById(account);
        return Boolean.TRUE;
    }

    @Override
    public void subAccountImportExcelDownload(HttpServletResponse response) {
        // 文件名
        String fileName = "sub_account_import_template.xlsx";

        // 获取文件输入流
        try (InputStream inputStream = new ClassPathResource("files/" + fileName).getInputStream();
             OutputStream outputStream = response.getOutputStream()) {

            // 设置响应头
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));

            // 写出数据
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, length);
            }
            outputStream.flush();

        } catch (IOException e) {
            throw new RuntimeException("下载模板文件失败", e);
        }
    }

    @Override
    public void exportAccountsToExcel(HttpServletResponse response) {
        // 1. 查询数据
        Account adminAcc = baseMapper.selectById(UserContext.getUserHolder().getId());
        int subUserRole = getSubUserRole(adminAcc.getSystemUser());

        List<Account> accounts = accountMapper.selectList(new QueryWrapper<Account>()
                .eq("organization_name", adminAcc.getOrganizationName())
                .eq("system_user", subUserRole)
                .select("user_name", "user_email", "user_password", "credits_usage_limit"));

        String fileName = "subAccount_export.xlsx";

        // 2. 创建Excel工作簿
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("subAccounts");

            // 3. 标题行
            Row headerRow = sheet.createRow(0);
            String[] headers = {"name", "email", "password", "creditsUsageLimit"};

            CellStyle headerStyle = workbook.createCellStyle();
            Font headerFont = workbook.createFont();
            headerFont.setBold(true);
            headerStyle.setFont(headerFont);

            for (int i = 0; i < headers.length; i++) {
                Cell cell = headerRow.createCell(i);
                cell.setCellValue(headers[i]);
                cell.setCellStyle(headerStyle);
            }

            // 4. 数据
            int rowNum = 1;
            for (Account account : accounts) {
                Row row = sheet.createRow(rowNum++);
                row.createCell(0).setCellValue(account.getUserName() != null ? account.getUserName() : "");
                row.createCell(1).setCellValue(account.getUserEmail() != null ? account.getUserEmail() : "");
                row.createCell(2).setCellValue(account.getUserPassword() != null ? account.getUserPassword() : "");
                row.createCell(3).setCellValue(account.getCreditsUsageLimit() != null
                        ? String.valueOf(account.getCreditsUsageLimit())
                        : "");
            }

            for (int i = 0; i < headers.length; i++) {
                sheet.autoSizeColumn(i);
            }

            // 5. 设置响应头
            response.reset();
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            response.setHeader("Access-Control-Allow-Origin", "http://192.168.31.118:3000");

            // 6. 输出流写入
            try (ServletOutputStream outputStream = response.getOutputStream()) {
                workbook.write(outputStream);
                outputStream.flush();
            }
        } catch (IOException e) {
            log.error("导出Excel失败", e);
            throw new BusinessException("导出文件失败");
        }
    }

    @Override
    @Transactional
    public Boolean subAccountImport(MultipartFile file) {
        AuthPrincipalVo vo = UserContext.getUserHolder();
        Account parent = accountMapper.selectById(vo.getId());
        try {
            List<SubAccountImportDTO> importList = EasyExcel.read(
                    file.getInputStream(),
                    SubAccountImportDTO.class,
                    null
            ).sheet().doReadSync();

            // 只有当前子账号数量为0时允许批量上传
            QueryWrapper<Account> qw = new QueryWrapper<>();
            qw.lambda().eq(Account::getSystemUser, 8)
                    .eq(Account::getOrganizationName, parent.getOrganizationName());
            List<Account> accounts = accountMapper.selectList(qw);
            if (!accounts.isEmpty()) {
                throw new BusinessException("permit.bulk.creation", ResultEnum.PROMPT.getCode());
            }
            if (importList.size() > parent.getSubAccountNum() - 1) {
                throw new BusinessException("Action required: You cannot create [" + importList.size() + "] sub-accounts (Current quota: [" + (parent.getSubAccountNum() - 1) + "]).");
            }
            // 示例：打印或保存
            for (SubAccountImportDTO dto : importList) {

                AddSubAccountDTO addSubAccountDTO = new AddSubAccountDTO();
                addSubAccountDTO.setUserEmail(dto.getEmail());
                if (Objects.nonNull(dto.getCredisUsageLimit())) {
                    addSubAccountDTO.setCreditsUsageLimit(BigDecimal.valueOf(dto.getCredisUsageLimit()));
                }
                addSubAccountDTO.setUserName(dto.getEmail().substring(0, dto.getEmail().indexOf("@")));
                addSubAccountDTO.setUserPassword(md5("123456abc"));
                addSubAccountDTO.setCreditsUsage(BigDecimal.ZERO);
                addSubAccountDTO.setValidStartTime(parent.getValidStartTime());
                addSubAccountDTO.setValidEndTime(parent.getValidEndTime());
                // 添加用户
                addSubAccount(addSubAccountDTO);
            }

            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    public static String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8));

            // 转为16进制字符串
            StringBuilder sb = new StringBuilder();
            for (byte b : messageDigest) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) sb.append('0'); // 补0
                sb.append(hex);
            }
            return sb.toString();

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5算法不可用", e);
        }
    }

    @Override
    public Set<String> organizationNameSearch(String type, String name) {
        QueryWrapper<Account> qw = new QueryWrapper<>();
        qw.lambda().ne(Account::getOrganizationName, "").isNotNull(Account::getOrganizationName);
        if (!StringUtil.isNullOrEmpty(name)) {
            qw.lambda().like(Account::getOrganizationName, name);
        }

        if (type.equals("School")) {
            Set<Long> schoolList = new HashSet<>();
            schoolList.add(7L);
            schoolList.add(8L);
            qw.lambda().in(Account::getSystemUser, schoolList);
        }
        if (type.equals("Enterprise")) {
            Set<Long> schoolList = new HashSet<>();
            schoolList.add(5L);
            schoolList.add(6L);
            qw.lambda().in(Account::getSystemUser, schoolList);
        }
        List<Account> accountList = accountMapper.selectList(qw);
        if (CollectionUtil.isNotEmpty(accountList)) {
            return accountList.stream().map(Account::getOrganizationName).collect(Collectors.toSet());
        }
        return new HashSet<>();
    }

/*    @Override
    public void send618PromotionEmailTemp() {
        QueryWrapper<TrialOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("is_deleted", 0);

        List<TrialOrder> trialOrders = trialOrderMapper.selectList(queryWrapper);

//        List<Account> accountList = accountMapper.selectList(queryWrapper);
        System.out.println(trialOrders);
        int total = trialOrders.size();
        log.info("试用用户总量：{}", total);
        int current = 1;
        for (TrialOrder trialOrder : trialOrders) {
            log.info("邮件发送进度 {}/{}", current, total);
            try {
                if (!StringUtil.isNullOrEmpty(trialOrder.getCountry()) && trialOrder.getCountry().equals("China")) {
                    SendEmailUtil.send618PromotionEmailTemp(trialOrder.getEmail(), "CHINESE_SIMPLIFIED");
                } else {
                    // 英文
                    SendEmailUtil.send618PromotionEmailTemp(trialOrder.getEmail(), "ENGLISH");
                }
            }catch (Exception e) {
                log.info(e.getMessage());
            }
            current ++;
        }
    }*/

    @Override
    public void checkEduAdminExpireStatus() {
        // 1、检查所有管理员账号到期状态
        List<Account> expiredEduAdmin = getExpiredUserBySystemUser(7);
        // 2、若有过期教育管理员，则将该管理员下的所有子账号删除（降为游客或者恢复其原本身份与积分）
        if (!expiredEduAdmin.isEmpty()) {
            for (Account adminAccount : expiredEduAdmin) {
                setEduAdminToExpire(adminAccount);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setEduAdminToExpire(Account adminAccount) {
        List<Account> subAccountsByAdmin = getSubAccountsByAdmin(adminAccount);
        if (!subAccountsByAdmin.isEmpty()) {
            // 获取所有子账号的id,并删除
            List<Long> subAccIdList = subAccountsByAdmin.stream().map(Account::getId).collect(Collectors.toList());
            removeSubAccount(new AddSubAccountDTO(subAccIdList), adminAccount.getId());
        }
        setOrganizationStatus(adminAccount, false);
        // 将教育管理员置为游客
        toVisitor(adminAccount);
    }

    // todo 机构管理员再次订阅时，需要同步机构的激活状态
    private void setOrganizationStatus(Account adminAcc, boolean isActive) {
        Organization organization;
        if (Objects.nonNull(adminAcc.getOrganizationId())) {
            organization = organizationMapper.selectById(adminAcc.getOrganizationId());
        } else if (!StringUtil.isNullOrEmpty(adminAcc.getOrganizationName())) {
            organization = convenientInquiryService.checkOrganization(adminAcc.getSystemUser(), adminAcc.getOrganizationName());
        } else {
            return;
        }
        if (Objects.isNull(organization)) {
            organization = convenientInquiryService.addOrganization(adminAcc.getOrganizationName(), getOrganizationTypeByRole(adminAcc.getSystemUser()));
        }
        if (isActive) {
            organization.setStatus(1);
        } else {
            organization.setStatus(0);
        }
        organization.setUpdateTime(LocalDateTime.now());
        organizationMapper.updateById(organization);
    }

    public String getOrganizationTypeByRole(Integer roleNum) {
        String type;
        switch (roleNum) {
            case 5:
                type = "Enterprise";
                break;
            case 7:
                type = "Education";
                break;
            default:
                type = null;
        }
        return type;
    }

}
