package com.kexue.skills.service.impl;

import com.alicp.jetcache.anno.CacheInvalidate;
import com.alicp.jetcache.anno.CachePenetrationProtect;
import com.alicp.jetcache.anno.Cached;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.kexue.skills.common.Assert;
import com.kexue.skills.common.CacheManager;
import com.kexue.skills.common.Const;
import com.kexue.skills.common.LoginUserCacheUtil;
import com.kexue.skills.config.CaptchaConfig;
import com.kexue.skills.entity.Account;
import com.kexue.skills.entity.ContentPurchase;
import com.kexue.skills.entity.SysUser;
import com.kexue.skills.entity.SysUserRole;
import com.kexue.skills.entity.dto.ContentPurchaseDto;
import com.kexue.skills.entity.dto.SessionDto;
import com.kexue.skills.entity.dto.SysUserDto;
import com.kexue.skills.entity.request.*;
import com.kexue.skills.exception.BizException;
import com.kexue.skills.mapper.AccountMapper;
import com.kexue.skills.mapper.ContentPurchaseMapper;
import com.kexue.skills.mapper.CmsContentLikeMapper;
import com.kexue.skills.mapper.CmsContentViewMapper;
import com.kexue.skills.mapper.CmsContentMapper;
import com.kexue.skills.mapper.SysUserMapper;
import com.kexue.skills.mapper.SysUserRoleMapper;
import com.kexue.skills.service.SysUserService;
import com.kexue.skills.utils.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * (SysUser)表服务实现类
 *
 * @author 王志维
 * @since 2025-02-21 23:01:48
 */
@Service("sysUserService")
@Slf4j
public class SysUserServiceImpl implements SysUserService {

    @Resource
    private SysUserMapper sysUserMapper;
    
    @Resource
    private RedissonClient redissonClient;
    
    @Resource
    private CaptchaConfig captchaConfig;
    

    @Resource
    private AccountMapper accountMapper;
    
    @Resource
    private SysUserRoleMapper sysUserRoleMapper;
    
    @Resource
    private ContentPurchaseMapper contentPurchaseMapper;
    
    @Resource
    private CmsContentLikeMapper cmsContentLikeMapper;
    
    @Resource
    private CmsContentViewMapper cmsContentViewMapper;
    
    @Resource
    private CmsContentMapper cmsContentMapper;

    /**
     * 分页查询数据
     *
     * @param queryDto
     * @return 数据分页列表
     */
    @Override
    public PageInfo<SysUser> getPageList(SysUserDto queryDto) {
        int pageNum = queryDto.getPageNum()==null?PAGENUM:queryDto.getPageNum();
        int pageSize = queryDto.getPageSize()==null? PAGESIZE :queryDto.getPageSize();
        PageHelper.startPage(pageNum,pageSize);
        List<SysUser> dataList = sysUserMapper.getPageList(queryDto);
        // 将所有返回的用户密码设置为null
        dataList.forEach(user -> user.setPwd(null));
        return new PageInfo<>(dataList);
    }

    /**
     * 通过ID查询单条数据
     *
     * @param userId 主键
     * @return 实例对象
     */
    @Override
    @Cached(name = "sysUser:", key = "#userId", expire = 3600, cacheType = com.alicp.jetcache.anno.CacheType.BOTH)
    @CachePenetrationProtect
    public SysUser queryById(Long userId) {
        SysUser sysUser = sysUserMapper.queryById(userId);
        sysUser.setPwd(null);
        return sysUser;
    }

    /**
     * 查询多条数据
     *
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return 对象列表
     */
    @Override
    public List<SysUser> queryAllByLimit(int offset, int limit) {
        List<SysUser> sysUsers = sysUserMapper.queryAllByLimit(offset, limit);
        // 将所有返回的用户密码设置为null
        sysUsers.forEach(user -> user.setPwd(null));
        return sysUsers;
    }

    /**
     * 新增数据
     *
     * @param sysUser 实例对象
     * @return 实例对象
     */
    @Override
    public SysUser insert(SysUser sysUser) {
        SysUser byUsername = getByUsername(sysUser.getUserName());
        Assert.isNull(byUsername, "用户名已存在");
        sysUser.setEnable(Const.USER_STATUS_NORMAL);
        sysUser.setDeleteFlag(Const.DELETE_FLAG_NO);
        //写一个salt生成方法
        sysUser.setSalt(System.currentTimeMillis()+"");
        try {
            // 假设客户端已经对密码进行了一次MD5加密，服务端使用双重加密验证
            String encryptedPwd = MD5Util.doubleEncrypt(sysUser.getPwd(), sysUser.getSalt());
            sysUser.setPwd(encryptedPwd);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        // 插入用户信息
        sysUserMapper.insert(sysUser);
        
        // 初始化账户（余额账户）
        Account account = new Account();
        account.setUserId(sysUser.getUserId());
        account.setUserName(sysUser.getUserName());
        account.setBalance(BigDecimal.ZERO); // 初始余额为0
        account.setFrozenAmount(BigDecimal.ZERO); // 初始冻结金额为0
        account.setDeleteFlag(Const.DELETE_FLAG_NO); // 初始未删除
        accountMapper.insert(account);
        
        // 将返回的用户密码设置为null
        sysUser.setPwd(null);
        return sysUser;
    }

    /**
     * 修改数据
     *
     * @param sysUser 实例对象
     * @return 实例对象
     */
    @Override
    @CacheInvalidate(name = "sysUser:", key = "#sysUser.userId")
    @CacheInvalidate(name = "sysUser:username:", key = "#sysUser.userName")
    public SysUser update(SysUser sysUser) {
        if (Objects.nonNull(sysUser.getUserId())){
            // 校验用户名是否已经存在
            if (sysUser.getUserName() != null && !sysUser.getUserName().isEmpty()) {
                SysUser existingUser = sysUserMapper.getByUsername(sysUser.getUserName());
                Assert.isTrue(existingUser == null || existingUser.getUserId().equals(sysUser.getUserId()), "用户名已存在");
            }
            sysUserMapper.update(sysUser);
        }else {
            insert(sysUser);
        }
        return queryById(sysUser.getUserId());
    }

    /**
     * 修改用户数据
     *
     * @param sysUserUpdateDto 用户更新请求参数
     * @return 实例对象
     */
    @Override
    @CacheInvalidate(name = "sysUser:", key = "#sysUserUpdateDto.userId")
    @CacheInvalidate(name = "sysUser:username:", key = "#sysUserUpdateDto.userName")
    public SysUser update(SysUserUpdateDto sysUserUpdateDto) {
        if (Objects.nonNull(sysUserUpdateDto.getUserId())){
            // 查询用户信息
            SysUser sysUser = sysUserMapper.queryById(sysUserUpdateDto.getUserId());
            Assert.notNull(sysUser, "用户不存在");
            
            // 校验用户名是否已经存在
            if (sysUserUpdateDto.getUserName() != null && !sysUserUpdateDto.getUserName().isEmpty()) {
                SysUser existingUser = sysUserMapper.getByUsername(sysUserUpdateDto.getUserName());
                Assert.isTrue(existingUser == null || existingUser.getUserId().equals(sysUserUpdateDto.getUserId()), "用户名已存在");
                sysUser.setUserName(sysUserUpdateDto.getUserName());
            }
            
            // 更新邮箱
            if (sysUserUpdateDto.getEmail() != null) {
                sysUser.setEmail(sysUserUpdateDto.getEmail());
            }
            
            // 更新手机号
            if (sysUserUpdateDto.getTel() != null) {
                sysUser.setTel(sysUserUpdateDto.getTel());
            }
            
            // 更新状态
            if (sysUserUpdateDto.getEnable() != null) {
                sysUser.setEnable(sysUserUpdateDto.getEnable());
            }
            
            // 更新密码（如果有）
            if (sysUserUpdateDto.getPassword() != null && !sysUserUpdateDto.getPassword().isEmpty()) {
                try {
                    // 假设客户端已经对密码进行了一次MD5加密，服务端使用双重加密验证
                    String encryptedPwd = MD5Util.doubleEncrypt(sysUserUpdateDto.getPassword(), sysUser.getSalt());
                    sysUser.setPwd(encryptedPwd);
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException(e);
                }
            }
            
            // 执行更新
            sysUserMapper.update(sysUser);
        }else {
            // 如果没有用户ID，创建新用户
            SysUser sysUser = new SysUser();
            sysUser.setUserName(sysUserUpdateDto.getUserName());
            sysUser.setEmail(sysUserUpdateDto.getEmail());
            sysUser.setTel(sysUserUpdateDto.getTel());
            sysUser.setEnable(sysUserUpdateDto.getEnable());
            sysUser.setPwd(sysUserUpdateDto.getPassword());
            insert(sysUser);
        }
        return queryById(sysUserUpdateDto.getUserId());
    }

    /**
     * 通过主键删除数据
     *
     * @param userId 主键
     * @return 是否成功
     */
    @Override
    @CacheInvalidate(name = "sysUser:", key = "#userId")
    public boolean deleteById(Long userId) {

        SysUser sysUser = queryById(userId);
        //管理员不允许删除
        Assert.isFalse(Const.ADMIN_USER_LIST.contains(sysUser.getUserName().toLowerCase()), "该用户是管理员，不能删除");

        // 删除用户名缓存
        redissonClient.getBucket("sysUser:username:" + sysUser.getUserName()).delete();
        
        return sysUserMapper.deleteById(userId) > 0;
    }

    @Override
    public LoginUserDto login(LoginDto loginDto) {
        // 参数验证
        validateLoginParams(loginDto);
        
        // 验证码验证
        validateCaptcha(loginDto);
        
        // 查询用户（支持用户名或手机号）
        SysUser sysUser = getUserByUsernameOrPhone(loginDto.getUsername());
        
        // 密码验证
        validatePassword(loginDto.getPassword(), sysUser);
        
        // 检查用户是否已有token，如果有则使旧token失效
        String oldToken = CacheManager.getTokenFromCache(sysUser.getUserName());
        if (oldToken != null && !oldToken.isEmpty()) {
            // 从Redis中删除旧token对应的登录信息
            redissonClient.getBucket("loginUser:" + oldToken).delete();
            // 使旧token失效
            cn.dev33.satoken.stp.StpUtil.logoutByTokenValue(oldToken);
            // 从缓存中移除旧token
            CacheManager.removeTokenFromCache(sysUser.getUserName());
            log.info("用户：{}的旧token已失效", sysUser.getUserName());
        }
        
        // 查询用户角色列表
        List<String> roles = queryUserRoles(sysUser.getUserId());
        log.info("用户{}的角色列表：{}", sysUser.getUserName(), String.join(",", roles));
        
        // 生成token并设置角色
        String token = generateToken(sysUser.getUserId(), roles);
        log.info("设置后Sa-Token中的角色列表：{}", String.join(",", cn.dev33.satoken.stp.StpUtil.getRoleList()));
        
        // 构建登录用户信息
        LoginUser loginUser = buildLoginUser(sysUser, token);
        
        // 存储登录信息到Redis
        saveLoginUserToRedis(token, loginUser);
        
        // 存储token与用户名的映射关系到CacheManager
        CacheManager.putTokenToCache(sysUser.getUserName(), token);
        
        // 构建返回对象
        LoginUserDto sysUserDto = buildLoginUserDto(loginUser);
        
        log.info("用户：{}登录成功，token：{}", loginDto.getUsername(), token);
        
        return sysUserDto;
    }
    
    /**
     * 参数验证
     * 
     * @param loginDto 登录请求参数
     */
    private void validateLoginParams(LoginDto loginDto) {
        Assert.notNull(loginDto.getUsername(), "用户名或手机号不能位空");
        Assert.notNull(loginDto.getPassword(), "密码不能位空");
    }
    
    /**
     * 验证码验证
     * 
     * @param loginDto 登录请求参数
     */
    private void validateCaptcha(LoginDto loginDto) {
        if (captchaConfig.isEnabled()) {
            Assert.notNull(loginDto.getCaptchaId(), "验证码不能为空");
            Assert.notNull(loginDto.getCaptchaValue(), "验证码不能为空");
            
            String captchaKey = "captcha:" + loginDto.getCaptchaId();
            String captchaText = (String) redissonClient.getBucket(captchaKey).get();
            Assert.notNull(captchaText, "验证码已过期");
            Assert.equals(captchaText, loginDto.getCaptchaValue().toLowerCase(), "验证码错误");
            
            redissonClient.getBucket(captchaKey).delete();
        }
    }
    
    /**
     * 根据用户名查询用户
     * 
     * @param username 用户名
     * @return 用户对象
     */
    private SysUser getUserByUsername(String username) {
        SysUser sysUser = sysUserMapper.getByUsername(username);
        Assert.notNull(sysUser, "用户名不存在");
        return sysUser;
    }
    
    /**
     * 根据用户名或手机号查询用户
     * 
     * @param usernameOrPhone 用户名或手机号
     * @return 用户对象
     */
    public SysUser getUserByUsernameOrPhone(String usernameOrPhone) {
        // 先尝试通过用户名查询
        SysUser sysUser = sysUserMapper.getByUsername(usernameOrPhone);
        if (sysUser == null) {
            // 如果用户名查询不到，尝试通过手机号查询
            sysUser = sysUserMapper.getByTel(usernameOrPhone);
            Assert.notNull(sysUser, "用户不存在");
        }
        return sysUser;
    }
    
    /**
     * 密码验证
     * 
     * @param password 密码
     * @param sysUser 用户对象
     */
    private void validatePassword(String password, SysUser sysUser) {
        try {
            // 假设客户端已经对密码进行了一次MD5加密，服务端使用双重加密验证
            String encryptedPwd = MD5Util.doubleEncrypt(password, sysUser.getSalt());
            Assert.equals(encryptedPwd, sysUser.getPwd(), "密码不正确");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 生成token
     * 
     * @param userId 用户ID
     * @param roles 角色列表
     * @return token
     */
    private String generateToken(Long userId, List<String> roles) {
        // 登录用户
        cn.dev33.satoken.stp.StpUtil.login(userId);
        // 添加角色
        if (!roles.isEmpty()) {
            cn.dev33.satoken.stp.StpUtil.getRoleList().addAll(roles);
        }
        return cn.dev33.satoken.stp.StpUtil.getTokenValue();
    }
    
    /**
     * 构建登录用户信息
     * 
     * @param sysUser 用户对象
     * @param token token
     * @return 登录用户信息
     */
    private LoginUser buildLoginUser(SysUser sysUser, String token) {
        LoginUser loginUser = new LoginUser();
        
        // 设置用户基本信息
        sysUser.setPwd(null);
        loginUser.setUserInfo(sysUser);
        
        // 查询并设置用户角色列表
        loginUser.setRoles(queryUserRoles(sysUser.getUserId()));
        
        // 查询并设置用户已购买的内容ID列表
        loginUser.setPurchasedContentIds(queryPurchasedContentIds(sysUser.getUserId()));
        
        // 查询并设置用户账户信息
        loginUser.setAccount(queryUserAccount(sysUser.getUserId()));
        

        
        // 查询并设置用户最近点赞记录
        loginUser.setFavorites(queryRecentFavorites(sysUser.getUserId()));
        
        // 查询并设置用户最近查看记录
        loginUser.setHistory(queryRecentViews(sysUser.getUserId()));
        
        // 查询并设置用户最近创建记录
        loginUser.setCreate(queryRecentCreatedContent(sysUser.getUserId()));
        
        // 查询并设置用户最近购买记录
        loginUser.setHas(queryRecentPurchases(sysUser.getUserId()));
        
        // 设置token
        loginUser.setToken(token);
        
        return loginUser;
    }
    
    /**
     * 查询用户角色列表
     * 
     * @param userId 用户 ID
     * @return 角色编码列表
     */
    @Override
    public List<String> queryUserRoles(Long userId) {
        try {
            // 直接通过关联查询获取角色编码列表
            return sysUserRoleMapper.queryRoleCodesByUserId(userId);
        } catch (Exception e) {
            log.error("查询用户角色列表失败：{}", e.getMessage());
            return java.util.Collections.emptyList();
        }
    }
    
    /**
     * 查询用户已购买的内容ID列表
     * 
     * @param userId 用户ID
     * @return 内容ID列表
     */
    private List<Long> queryPurchasedContentIds(Long userId) {
        List<Long> purchasedContentIds = new java.util.ArrayList<>();
        try {
            ContentPurchaseDto purchaseDto = new ContentPurchaseDto();
            purchaseDto.setUserId(userId);
            List<ContentPurchase> purchases = contentPurchaseMapper.getList(purchaseDto);
            
            for (ContentPurchase purchase : purchases) {
                purchasedContentIds.add(purchase.getContentId());
            }
        } catch (Exception e) {
            log.error("查询用户已购买内容列表失败：{}", e.getMessage());
            purchasedContentIds = java.util.Collections.emptyList();
        }
        return purchasedContentIds;
    }
    
    /**
     * 查询用户账户信息
     * 
     * @param userId 用户ID
     * @return 账户信息
     */
    private Account queryUserAccount(Long userId) {
        return accountMapper.queryByUserId(userId);
    }
    


    /**
     * 查询用户最近点赞记录
     * 
     * @param userId 用户ID
     * @return 最近点赞记录
     */
    private List<Long> queryRecentFavorites(Long userId) {
        try {
            return cmsContentLikeMapper.queryRecentLikesByUserId(userId, 20);
        } catch (Exception e) {
            log.error("查询用户最近点赞记录失败：{}", e.getMessage());
            return java.util.Collections.emptyList();
        }
    }
    
    /**
     * 查询用户最近查看记录
     * 
     * @param userId 用户ID
     * @return 最近查看记录
     */
    private List<Long> queryRecentViews(Long userId) {
        try {
            return cmsContentViewMapper.queryRecentViewsByUserId(userId, 20);
        } catch (Exception e) {
            log.error("查询用户最近查看记录失败：{}", e.getMessage());
            return java.util.Collections.emptyList();
        }
    }
    
    /**
     * 查询用户最近创建记录
     * 
     * @param userId 用户ID
     * @return 最近创建记录
     */
    private List<Long> queryRecentCreatedContent(Long userId) {
        try {
            return cmsContentMapper.queryRecentCreatedByUserId(userId, 20);
        } catch (Exception e) {
            log.error("查询用户最近创建记录失败：{}", e.getMessage());
            return java.util.Collections.emptyList();
        }
    }
    
    /**
     * 查询用户最近购买记录
     * 
     * @param userId 用户ID
     * @return 最近购买记录
     */
    private List<Long> queryRecentPurchases(Long userId) {
        List<Long> has = new java.util.ArrayList<>();
        try {
            ContentPurchaseDto purchaseDto = new ContentPurchaseDto();
            purchaseDto.setUserId(userId);
            List<ContentPurchase> purchases = contentPurchaseMapper.getList(purchaseDto);
            if (purchases != null && !purchases.isEmpty()) {
                java.util.LinkedHashSet<Long> contentIdSet = new java.util.LinkedHashSet<>();
                for (ContentPurchase purchase : purchases) {
                    if (contentIdSet.size() < 20) {
                        contentIdSet.add(purchase.getContentId());
                    } else {
                        break;
                    }
                }
                has.addAll(contentIdSet);
            }
        } catch (Exception e) {
            log.error("查询用户最近购买记录失败：{}", e.getMessage());
            has = java.util.Collections.emptyList();
        }
        return has;
    }
    
    /**
     * 存储登录信息到Redis
     * 
     * @param token token
     * @param loginUser 登录用户信息
     */
    private void saveLoginUserToRedis(String token, LoginUser loginUser) {
        redissonClient.getBucket("loginUser:" + token)
            .set(cn.hutool.json.JSONUtil.toJsonStr(loginUser), 86400, java.util.concurrent.TimeUnit.SECONDS);
    }
    
    /**
     * 构建登录用户返回对象
     * 
     * @param loginUser 登录用户信息
     * @return 登录用户返回对象
     */
    private LoginUserDto buildLoginUserDto(LoginUser loginUser) {
        LoginUserDto sysUserDto = new LoginUserDto();
        sysUserDto.setUserInfo(loginUser.getUserInfo());
        sysUserDto.setToken(loginUser.getToken());
        sysUserDto.setFavorites(loginUser.getFavorites());
        sysUserDto.setHistory(loginUser.getHistory());
        sysUserDto.setCreate(loginUser.getCreate());
        sysUserDto.setHas(loginUser.getHas());
        return sysUserDto;
    }

    @Resource
    private LoginUserCacheUtil loginUserCacheUtil;

    @Override
    public boolean resetPassword(ResetPwdDto resetPasswordDto) {
        // 1. 检查登录状态
        Long currentUserId = loginUserCacheUtil.getCurrentUserId();
        Assert.notNull(currentUserId, "请先登录");

        // 2. 检查参数
        Assert.notNull(resetPasswordDto.getUserId(), "用户ID不能为空");
        Assert.notNull(resetPasswordDto.getNewPassword(), "新密码不能为空");

        // 3. 检查是否是管理员
        boolean isAdmin = isAdminUser(currentUserId);
        if (!isAdmin) {
            throw new BizException("只有管理员才能重置密码");
        }

        // 4. 查询用户（支持用户名或手机号）
        SysUser sysUser = sysUserMapper.queryById(resetPasswordDto.getUserId());

        try {
            // 5. 参照密码创建时候的逻辑，对新密码进行双重加密
            String newEncryptedPwd = MD5Util.doubleEncrypt(resetPasswordDto.getNewPassword(), sysUser.getSalt());
            sysUser.setPwd(newEncryptedPwd);
            sysUserMapper.update(sysUser);

            // 6. 清除旧的token
            CacheManager.removeTokenFromCache(sysUser.getUserName());

            return true;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 检查用户是否是管理员
     * 
     * @param userId 用户 ID
     * @return 是否是管理员
     */
    private boolean isAdminUser(Long userId) {
        // 先检查是否在管理员用户列表中
        SysUser currentUser = sysUserMapper.queryById(userId);
        if (currentUser != null && Const.ADMIN_USER_LIST.contains(currentUser.getUserName().toLowerCase())) {
            return true;
        }
            
        // 检查用户是否有管理员角色（通过角色编码判断）
        List<String> roles = queryUserRoles(userId);
        // 只要有一个角色编码是 "admin"，就是管理员
        for (String role : roles) {
            if ("admin".equalsIgnoreCase(role)) {
                return true;
            }
        }
            
        return false;
    }

    @Override
    @Cached(name = "sysUser:username:", key = "#username", expire = 3600, cacheType = com.alicp.jetcache.anno.CacheType.BOTH)
    @CachePenetrationProtect
    public SysUser getByUsername(String username) {
        if (Objects.nonNull(username)){
            SysUser sysUser = sysUserMapper.getByUsername(username);
            if (sysUser != null) {
                sysUser.setPwd(null);
            }
            return sysUser;
        }
        return null;
    }
    
    @Override
    public boolean resetPwd(Long userId, String newPassword, String operator) {
        // 获取当前登录用户ID
        Long currentUserId = loginUserCacheUtil.getCurrentUserId();
        Assert.notNull(currentUserId, "请先登录");
        
        // 查询用户是否存在
        SysUser sysUser = sysUserMapper.queryById(userId);
        Assert.notNull(sysUser, "用户不存在");

        // 检查是否是用户自己修改密码，或者是管理员
        boolean isAdmin = Const.ADMIN_USER_LIST.contains(operator.toLowerCase());
        boolean isSelf = currentUserId.equals(sysUser.getUserId());
        Assert.isTrue(isAdmin || isSelf, "只能修改自己的密码");
        
        try {
            // 假设客户端已经对密码进行了一次MD5加密，服务端使用双重加密验证
            String newEncryptedPwd = MD5Util.doubleEncrypt(newPassword, sysUser.getSalt());
            sysUser.setPwd(newEncryptedPwd);
            
            // 更新用户密码
            sysUserMapper.update(sysUser);
            
            // 清除旧的token
            CacheManager.removeTokenFromCache(sysUser.getUserName());
            
            return true;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
    
    @Override
    public boolean resetPasswordByUsernameOrPhone(String usernameOrPhone, String newPassword, String operator) {
        // 获取当前登录用户ID
        Long currentUserId = loginUserCacheUtil.getCurrentUserId();
        Assert.notNull(currentUserId, "请先登录");
        
        // 查询用户是否存在（先尝试通过用户名查询，再尝试通过手机号查询）
        SysUser sysUser = sysUserMapper.getByUsername(usernameOrPhone);
        if (sysUser == null) {
            sysUser = sysUserMapper.getByTel(usernameOrPhone);
        }
        Assert.notNull(sysUser, "用户不存在");

        // 检查是否是用户自己修改密码，或者是管理员
        boolean isAdmin = Const.ADMIN_USER_LIST.contains(operator.toLowerCase());
        boolean isSelf = currentUserId.equals(sysUser.getUserId());
        Assert.isTrue(isAdmin || isSelf, "只能修改自己的密码");
        
        try {
            // 假设客户端已经对密码进行了一次MD5加密，服务端使用双重加密验证
            String newEncryptedPwd = MD5Util.doubleEncrypt(newPassword, sysUser.getSalt());
            sysUser.setPwd(newEncryptedPwd);
            
            // 更新用户密码
            sysUserMapper.update(sysUser);
            
            // 清除旧的token
            CacheManager.removeTokenFromCache(sysUser.getUserName());
            
            return true;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 发送手机验证码
     * 
     * @param phone 手机号
     * @return 是否发送成功
     */
    @Override
    public boolean sendSmsCode(String phone) {
        try {
            // 参数校验
            Assert.notBlank(phone, "手机号不能为空");
            
            // 验证手机号格式
            String phoneRegex = "^1[3-9]\\d{9}$";
            if (!phone.matches(phoneRegex)) {
                Assert.isTrue(false, "手机号格式不正确");
            }
            
            // 生成6位随机验证码
            String code = generateSmsCode();
            
            // 存储验证码到Redis，有效期5分钟
            redissonClient.getBucket("sms_code:" + phone).set(code, 300, java.util.concurrent.TimeUnit.SECONDS);
            
            // 获取默认的短信发送器
            SmsBlend sms = SmsFactory.getSmsBlend();
            
            Assert.notNull(sms, "短信服务未正确配置");
            log.info("获取短信发送器成功");
            
            // 发送验证码 - 使用模板方式发送，避免直接文本发送可能导致的参数缺失
            LinkedHashMap<String, String> params = new LinkedHashMap<>();
            params.put("code", code);  // 参数名需要与阿里云模板中的变量名匹配
            
            // 发送短信，使用配置文件中定义的模板
            // 根据API文档和用户需求，使用单个手机号发送的方法：sendMessage(String phone, Map<String, String> params)
            // 系统会自动从配置中读取template-id
            sms.sendMessage(phone, params);
            
            log.info("向手机号 {} 发送验证码：{}", phone, code);
            return true;
        } catch (Exception e) {
            log.error("向手机号 {} 发送验证码失败：{}", phone, e.getMessage());
            log.error("详细错误信息：", e);
            
            // 如果短信服务不可用，可以记录错误并返回false而不是抛出异常
            // 或者根据业务需求决定是否继续
            Assert.isTrue(false, "发送验证码失败：" + e.getMessage());
        }
        return false;
    }
    
    /**
     * 生成6位随机验证码
     * 
     * @return 6位随机验证码
     */
    private String generateSmsCode() {
        Random random = new Random();
        return String.format("%06d", random.nextInt(1000000));
    }
    
    /**
     * 手机号登录
     * 
     * @param phoneLoginDto 手机号登录信息
     * @return 登录结果
     */
    @Override
    public LoginUserDto phoneLogin(PhoneLoginDto phoneLoginDto) {
        String phone = phoneLoginDto.getPhone();
        String code = phoneLoginDto.getCode();
        
        Assert.notBlank(phone, "手机号不能为空");
        Assert.notBlank(code, "验证码不能为空");
        
        // 验证验证码
        String cachedCode = (String) redissonClient.getBucket("sms_code:" + phone).get();
        Assert.notNull(cachedCode, "验证码已过期");
        Assert.equals(cachedCode, code, "验证码错误");
        
        // 验证成功后，删除验证码
        redissonClient.getBucket("sms_code:" + phone).delete();
        
        // 查询用户是否存在
        SysUser sysUser = sysUserMapper.getByTel(phone);
        
        // 如果用户不存在，自动创建账号
        if (sysUser == null) {
            sysUser = createUserByPhone(phone);
        }
        
        // 检查用户是否已有token，如果有则使旧token失效
        String oldToken = CacheManager.getTokenFromCache(sysUser.getUserName());
        if (oldToken != null && !oldToken.isEmpty()) {
            // 从Redis中删除旧token对应的登录信息
            redissonClient.getBucket("loginUser:" + oldToken).delete();
            // 使旧token失效
            cn.dev33.satoken.stp.StpUtil.logoutByTokenValue(oldToken);
            // 从缓存中移除旧token
            CacheManager.removeTokenFromCache(sysUser.getUserName());
            log.info("用户：{}的旧token已失效", sysUser.getUserName());
        }
        
        // 查询用户角色列表
        List<String> roles = queryUserRoles(sysUser.getUserId());
        log.info("用户{}的角色列表：{}", sysUser.getUserName(), String.join(",", roles));
        
        // 生成token并设置角色
        String token = generateToken(sysUser.getUserId(), roles);
        log.info("设置后Sa-Token中的角色列表：{}", String.join(",", cn.dev33.satoken.stp.StpUtil.getRoleList()));
        
        // 创建LoginUser对象
        LoginUser loginUser = new LoginUser();
        
        // 设置用户基本信息
        sysUser.setPwd(null);
        loginUser.setUserInfo(sysUser);
        loginUser.setRoles(roles);
        
        // 查询用户已购买的内容ID列表
        List<Long> purchasedContentIds = new java.util.ArrayList<>();
        try {
            // 创建查询条件，查询用户的购买记录
            ContentPurchaseDto purchaseDto = new ContentPurchaseDto();
            purchaseDto.setUserId(sysUser.getUserId());
            List<ContentPurchase> purchases = contentPurchaseMapper.getList(purchaseDto);
            
            // 遍历购买记录，获取内容ID
            for (ContentPurchase purchase : purchases) {
                purchasedContentIds.add(purchase.getContentId());
            }
        } catch (Exception e) {
            log.error("查询用户已购买内容列表失败：{}", e.getMessage());
            purchasedContentIds = java.util.Collections.emptyList();
        }
        loginUser.setPurchasedContentIds(purchasedContentIds);
        
        // 查询用户账户信息
        Account account = accountMapper.queryByUserId(sysUser.getUserId());
        loginUser.setAccount(account);
        
        // 查询并设置用户最近点赞记录
        loginUser.setFavorites(queryRecentFavorites(sysUser.getUserId()));
        
        // 查询并设置用户最近查看记录
        loginUser.setHistory(queryRecentViews(sysUser.getUserId()));
        
        // 查询并设置用户最近创建记录
        loginUser.setCreate(queryRecentCreatedContent(sysUser.getUserId()));
        
        // 查询并设置用户最近购买记录
        loginUser.setHas(queryRecentPurchases(sysUser.getUserId()));
        
        // 设置token
        loginUser.setToken(token);
        
        // 将LoginUser对象存储到Redis缓存中，使用token作为key
        redissonClient.getBucket("loginUser:" + token).set(cn.hutool.json.JSONUtil.toJsonStr(loginUser), 86400, java.util.concurrent.TimeUnit.SECONDS);
        
        // 存储token与用户名的映射关系到CacheManager
        CacheManager.putTokenToCache(sysUser.getUserName(), token);
        
        // 创建LoginUserDto返回对象
        LoginUserDto sysUserDto = buildLoginUserDto(loginUser);

        log.info("用户：{} 手机号登录成功，token：{}", phone, token);

        return sysUserDto;
    }
    
    /**
     * 根据手机号创建用户
     * 
     * @param phone 手机号
     * @return 创建的用户对象
     */
    private SysUser createUserByPhone(String phone) {
        SysUser sysUser = new SysUser();
        
        // 设置用户名（使用手机号作为用户名）
        sysUser.setUserName(phone);
        sysUser.setTel(phone);
        
        // 设置固定salt为666666
        String salt = "666666";
        sysUser.setSalt(salt);
        
        // 设置默认密码为000000，按照验证逻辑计算存储值
        // 验证逻辑：MD5Util.doubleEncrypt(password, salt) = MD5Util.encryptToHex(clientEncryptedPassword + salt)
        // 客户端会发送MD5("000000")，服务端存储MD5(MD5("000000") + salt)
        try {
            // 计算客户端加密后的密码：MD5("000000")
            String clientEncryptedPassword = MD5Util.encryptToHex("000000");
            // 计算数据库存储的密码：MD5(clientEncryptedPassword + salt)
            String storedPassword = MD5Util.encryptToHex(clientEncryptedPassword + salt);
            sysUser.setPwd(storedPassword);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        
        // 直接调用mapper插入，跳过insert方法的加密逻辑
        sysUser.setEnable(Const.USER_STATUS_NORMAL);
        sysUser.setDeleteFlag(Const.DELETE_FLAG_NO);
        sysUserMapper.insert(sysUser);
        
        // 初始化账户（余额账户）
        Account account = new Account();
        account.setUserId(sysUser.getUserId());
        account.setUserName(sysUser.getUserName());
        account.setBalance(BigDecimal.ZERO);
        account.setFrozenAmount(BigDecimal.ZERO);
        account.setDeleteFlag(Const.DELETE_FLAG_NO);
        accountMapper.insert(account);
        
        return sysUser;
    }
    
    /**
     * 生成随机密码
     * 
     * @return 随机密码
     */
    private String generateRandomPassword() {
        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder(12);
        for (int i = 0; i < 12; i++) {
            sb.append(characters.charAt(random.nextInt(characters.length())));
        }
        return sb.toString();
    }
    
    @Override
    public SessionDto createSession(Long userId) {
        SessionDto sessionDto = new SessionDto();
        
        // 查询用户信息
        SysUser sysUser = sysUserMapper.queryById(userId);
        if (sysUser == null) {
            throw new RuntimeException("用户不存在");
        }
        
        String sessionId = sysUser.getSessionId();
        if (sessionId != null && !sessionId.isEmpty()) {
            // 已有会话，直接返回
            sessionDto.setSessionId(sessionId);
            sessionDto.setNew(false);
        } else {
            // 没有会话，创建新的会话ID
            sessionId = java.util.UUID.randomUUID().toString();
            sysUser.setSessionId(sessionId);
            sysUserMapper.update(sysUser);
            sessionDto.setSessionId(sessionId);
            sessionDto.setNew(true);
        }
        
        return sessionDto;
    }
}
