package shop.alien.dining.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import shop.alien.dining.config.BaseRedisService;
import shop.alien.dining.dto.ChangePhoneDto;
import shop.alien.dining.dto.UserProfileUpdateDto;
import shop.alien.dining.feign.AlienStoreFeign;
import shop.alien.dining.service.DiningUserService;
import shop.alien.dining.util.TokenUtil;
import shop.alien.dining.util.WeChatMiniProgramUtil;
import shop.alien.dining.util.WeChatMiniProgramUtil.WeChatSessionInfo;
import shop.alien.entity.result.R;
import shop.alien.dining.vo.DiningUserVo;
import shop.alien.dining.vo.TokenVerifyVo;
import shop.alien.entity.store.LifeUser;
import shop.alien.mapper.LifeUserMapper;
import shop.alien.util.common.JwtUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 点餐用户服务实现类
 * 标准流程：通过 wx.login() 获取 code，调用 jscode2session 获取 openid
 *
 * @author ssk
 * @version 2.0
 * @date 2024/12/4
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DiningUserServiceImpl implements DiningUserService {

    private final LifeUserMapper lifeUserMapper;
    private final BaseRedisService baseRedisService;
    private final WeChatMiniProgramUtil weChatMiniProgramUtil;
    private final AlienStoreFeign alienStoreFeign;

    @Value("${jwt.expiration-time}")
    private String effectiveTime;

    /**
     * Redis key 常量（小程序专用，避免与 APP 端冲突）
     */
    private static final String REDIS_KEY_OPENID_PREFIX = "wechat:openid:";
    private static final String REDIS_KEY_TOKEN_PREFIX = "miniprogram_user_token:";
    private static final String REDIS_KEY_USER_PHONE_PREFIX = "miniprogram_user_";
    private static final long OPENID_MAPPING_EXPIRE_SECONDS = 30 * 24 * 60 * 60L; // 30天
    private static final long TOKEN_EXPIRE_SECONDS = 7 * 24 * 60 * 60L; // 7天

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DiningUserVo wechatLogin(String code, String phoneCode, String macIp) {
        // 1. 通过 code2session 获取 openid 和 session_key
        String openid = getOpenidFromCode(code);
        if (StringUtils.isBlank(openid)) {
            return null;
        }

        // 2. 如果提供了 phoneCode，先解析手机号（用于返回给前端和更新用户）
        String parsedPhone = null;
        if (StringUtils.isNotBlank(phoneCode)) {
            parsedPhone = weChatMiniProgramUtil.getPhoneNumberByCode(phoneCode);
            if (StringUtils.isNotBlank(parsedPhone)) {
                log.info("成功解析手机号: {}", maskString(parsedPhone, 7));
            } else {
                log.warn("解析手机号失败，phoneCode可能已过期或无效");
            }
        }
        if (StringUtils.isBlank(parsedPhone)) {
            log.info("微信登录未传手机号或解析失败: openid={}, phoneCode={}", maskString(openid, 8), StringUtils.isNotBlank(phoneCode) ? "已传" : "未传");
        }

        // 3. 查找或创建用户（传入解析后的手机号，避免重复解析）
        LifeUser user = findOrCreateUser(openid, parsedPhone);
        if (user == null) {
            return null;
        }

        // 4. 检查用户状态（提前检查，避免不必要的操作）
        if (!isUserValid(user)) {
            return null;
        }

        // 5. 生成并存储 token（传入本次解析的手机号，确保有手机号时一定写入 miniprogram_user_{phone}）
        String token = generateAndStoreToken(openid, user, parsedPhone);

        // 6. 构建返回对象，优先使用解析后的手机号返回给前端
        return buildDiningUserVo(user, token, openid, parsedPhone);
    }

    /**
     * 通过 code 获取 openid
     */
    private String getOpenidFromCode(String code) {
        WeChatSessionInfo sessionInfo = weChatMiniProgramUtil.code2Session(code);
        if (sessionInfo == null || sessionInfo.getErrcode() != null) {
            Integer errcode = sessionInfo != null ? sessionInfo.getErrcode() : null;
            String errmsg = sessionInfo != null ? sessionInfo.getErrmsg() : "调用微信接口失败";
            log.error("登录失败：code2session 失败, errcode={}, errmsg={}", errcode, errmsg);
            return null;
        }

        String openid = sessionInfo.getOpenid();
        if (StringUtils.isBlank(openid)) {
            log.error("登录失败：无法获取 openid");
            return null;
        }
        log.info("成功获取 openid: {}", maskString(openid, 8));
        return openid;
    }

    /**
     * 查找或创建用户
     * 规则：只要 user_phone 存在，就代表存在账号，不创建新账号
     * 
     * @param openid 微信OpenID
     * @param phone 解析后的手机号（如果为null，说明没有提供phoneCode或解析失败）
     */
    private LifeUser findOrCreateUser(String openid, String phone) {
        LifeUser user = null;
        
        // 1. 如果提供了手机号，优先通过手机号查找（只要手机号存在就代表账号存在）
        if (StringUtils.isNotBlank(phone)) {
            user = findUserByPhone(phone);
            if (user != null) {
                // 账号已存在，建立 openid 和 userId 的映射关系
                saveOpenidMapping(openid, user.getId());
                log.info("通过手机号找到已存在账号: phone={}, userId={}, openid={}", 
                        maskString(phone, 7), user.getId(), maskString(openid, 8));
                // 如果用户已有手机号，但新解析的手机号不同，更新手机号
                if (StringUtils.isNotBlank(user.getUserPhone()) && !user.getUserPhone().equals(phone)) {
                    log.info("检测到手机号变更，更新手机号: userId={}, oldPhone={}, newPhone={}", 
                            user.getId(), maskString(user.getUserPhone(), 7), maskString(phone, 7));
                    updateUserPhone(user, phone);
                }
                return user;
            }
        }
        
        // 2. 如果通过手机号找不到（或未提供手机号），尝试通过 openid 查找
        user = findUserByOpenid(openid);
        
        // 3. 如果通过 openid 找到了用户，且提供了手机号，更新手机号
        if (user != null) {
            if (StringUtils.isNotBlank(phone) && StringUtils.isBlank(user.getUserPhone())) {
                // 用户存在但没有手机号，更新手机号
                updateUserPhone(user, phone);
            } else if (StringUtils.isNotBlank(phone) && StringUtils.isNotBlank(user.getUserPhone()) 
                    && !user.getUserPhone().equals(phone)) {
                // 用户已有手机号，但新解析的手机号不同，也更新
                log.info("检测到手机号变更，更新手机号: userId={}, oldPhone={}, newPhone={}", 
                        user.getId(), maskString(user.getUserPhone(), 7), maskString(phone, 7));
                updateUserPhone(user, phone);
            }
            return user;
        }

        // 4. 如果都找不到，且未提供手机号（或手机号不存在），才创建新账号
        // 注意：如果提供了手机号但找不到，说明这个手机号没有注册过，可以创建新账号
        user = createNewUser(openid, phone);
        if (user != null) {
            saveOpenidMapping(openid, user.getId());
        }

        return user;
    }

    /**
     * 通过 openid 查找用户
     */
    private LifeUser findUserByOpenid(String openid) {
        String openidKey = REDIS_KEY_OPENID_PREFIX + openid;
        String userIdStr = baseRedisService.getString(openidKey);
        
        if (StringUtils.isBlank(userIdStr)) {
            return null;
        }

        try {
            Integer userId = Integer.parseInt(userIdStr);
            LifeUser user = lifeUserMapper.selectById(userId);
            if (user != null) {
                log.info("通过 openid 找到用户: openid={}, userId={}", maskString(openid, 8), userId);
            }
            return user;
        } catch (NumberFormatException e) {
            log.warn("Redis 中的 userId 格式错误: {}", userIdStr);
            return null;
        }
    }

    /**
     * 通过手机号查找用户
     */
    private LifeUser findUserByPhone(String phone) {
        LambdaQueryWrapper<LifeUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LifeUser::getUserPhone, phone);
        LifeUser user = lifeUserMapper.selectOne(queryWrapper);
        if (user != null) {
            log.info("通过手机号找到用户: phone={}, userId={}", maskString(phone, 7), user.getId());
        }
        return user;
    }

    /**
     * 创建新用户
     */
    private LifeUser createNewUser(String openid, String phone) {
        LifeUser user = new LifeUser();
        if (StringUtils.isNotBlank(phone)) {
            user.setUserPhone(phone);
            user.setUserName(phone);
            user.setRealName(phone);
        } else {
            // 没有手机号时，使用 openid 后8位作为临时用户名
            String tempName = "微信用户" + openid.substring(Math.max(0, openid.length() - 8));
            user.setUserName(tempName);
            user.setRealName(tempName);
        }
        user.setCreatedTime(new Date());
        
        int ret = lifeUserMapper.insert(user);
        if (ret != 1) {
            log.error("创建用户失败");
            return null;
        }
        
        // MyBatis-Plus 的 insert 会自动填充 ID，不需要重新查询
        log.info("创建新用户: openid={}, userId={}, phone={}", maskString(openid, 8), user.getId(), maskString(phone, 7));
        return user;
    }

    /**
     * 更新用户手机号
     */
    private void updateUserPhone(LifeUser user, String phone) {
        user.setUserPhone(phone);
        if (StringUtils.isBlank(user.getUserName()) || user.getUserName().startsWith("微信用户")) {
            user.setUserName(phone);
        }
        if (StringUtils.isBlank(user.getRealName()) || user.getRealName().startsWith("微信用户")) {
            user.setRealName(phone);
        }
        user.setUpdatedTime(new Date());
        lifeUserMapper.updateById(user);
        log.info("更新用户手机号: userId={}, phone={}", user.getId(), maskString(phone, 7));
    }

    /**
     * 保存 openid 和 userId 的映射关系
     */
    private void saveOpenidMapping(String openid, Integer userId) {
        String openidKey = REDIS_KEY_OPENID_PREFIX + openid;
        baseRedisService.setString(openidKey, userId.toString(), OPENID_MAPPING_EXPIRE_SECONDS);
    }

    /**
     * 检查用户状态是否有效
     */
    private boolean isUserValid(LifeUser user) {
        if (user.getIsBanned() != null && user.getIsBanned() == 1) {
            log.warn("用户已被封禁: userId={}", user.getId());
            return false;
        }
        if (user.getLogoutFlag() != null && user.getLogoutFlag() == 1) {
            log.warn("用户已注销: userId={}", user.getId());
            return false;
        }
        return true;
    }

    /**
     * 生成并存储 token
     * @param openid 微信 openid
     * @param user 用户实体（可能尚未持久化手机号）
     * @param currentLoginPhone 本次登录解析到的手机号（可选），有则优先用其写入 miniprogram_user_{phone}，避免只存 openid 不存 phone 的情况
     */
    private String generateAndStoreToken(String openid, LifeUser user, String currentLoginPhone) {
        // 构建 token 信息
        Map<String, String> tokenMap = buildTokenMap(openid, user);
        String userName = StringUtils.isNotBlank(user.getUserName()) ? user.getUserName() : "用户";
        String token = generateToken(openid, userName, tokenMap);

        // 存入 Redis（使用 openid 作为 key，始终写入）
        baseRedisService.setString(REDIS_KEY_TOKEN_PREFIX + openid, token, TOKEN_EXPIRE_SECONDS);

        // 兼容旧版本：有手机号则同时按手机号存一份（优先用本次登录解析到的手机号，否则用用户实体中的手机号）
        String phoneToStore = StringUtils.isNotBlank(currentLoginPhone) ? currentLoginPhone : user.getUserPhone();
        if (StringUtils.isNotBlank(phoneToStore)) {
            baseRedisService.setString(REDIS_KEY_USER_PHONE_PREFIX + phoneToStore, token, TOKEN_EXPIRE_SECONDS);
        }

        return token;
    }

    /**
     * 构建 token 信息 Map
     */
    private Map<String, String> buildTokenMap(String openid, LifeUser user) {
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("openid", openid);
        tokenMap.put("phone", StringUtils.isNotBlank(user.getUserPhone()) ? user.getUserPhone() : "");
        tokenMap.put("userName", StringUtils.isNotBlank(user.getUserName()) ? user.getUserName() : "用户");
        tokenMap.put("userId", user.getId().toString());
        tokenMap.put("userType", "miniprogram_user");
        return tokenMap;
    }

    /**
     * 构建返回对象
     */
    private DiningUserVo buildDiningUserVo(LifeUser user, String token, String openid) {
        DiningUserVo diningUserVo = new DiningUserVo();
        diningUserVo.setId(user.getId().longValue());
        // 优先使用 user 对象中的手机号（如果通过 phoneCode 解析并更新了，这里会是最新的）
        diningUserVo.setPhone(user.getUserPhone());
        diningUserVo.setNickName(user.getUserName());
        diningUserVo.setAvatarUrl(user.getUserImage());
        diningUserVo.setStatus(0);
        diningUserVo.setCreatedTime(user.getCreatedTime());
        diningUserVo.setToken(token);
        diningUserVo.setOpenId(openid);
        return diningUserVo;
    }
    
    /**
     * 构建返回对象（带解析后的手机号）
     */
    private DiningUserVo buildDiningUserVo(LifeUser user, String token, String openid, String parsedPhone) {
        DiningUserVo diningUserVo = buildDiningUserVo(user, token, openid);
        // 如果解析到了手机号，优先使用解析后的手机号返回给前端
        if (StringUtils.isNotBlank(parsedPhone)) {
            diningUserVo.setPhone(parsedPhone);
        }
        return diningUserVo;
    }

    /**
     * 掩码字符串（用于日志脱敏）
     */
    private String maskString(String str, int visibleLength) {
        if (StringUtils.isBlank(str) || str.length() <= visibleLength) {
            return "****";
        }
        return str.substring(0, Math.min(visibleLength, str.length())) + "****";
    }

    private String generateToken(String userId, String userName, Map<String, String> tokenMap) {
        int effectiveTimeInt = Integer.parseInt(effectiveTime.substring(0, effectiveTime.length() - 1));
        String effectiveTimeUnit = effectiveTime.substring(effectiveTime.length() - 1);
        long effectiveTimeIntLong = 0L;
        switch (effectiveTimeUnit) {
            case "s":
                effectiveTimeIntLong = effectiveTimeInt * 1000L;
                break;
            case "m":
                effectiveTimeIntLong = effectiveTimeInt * 60L * 1000L;
                break;
            case "h":
                effectiveTimeIntLong = effectiveTimeInt * 60L * 60L * 1000L;
                break;
            case "d":
                effectiveTimeIntLong = effectiveTimeInt * 24L * 60L * 60L * 1000L;
                break;
            default:
                effectiveTimeIntLong = effectiveTimeInt * 24L * 60L * 60L * 1000L;
        }
        return JwtUtil.createJWT("user_" + userId, userName, JSONObject.toJSONString(tokenMap), effectiveTimeIntLong);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DiningUserVo updateProfile(UserProfileUpdateDto dto) {
        // 1. 查询用户是否存在
        LifeUser user = lifeUserMapper.selectById(dto.getUserId().intValue());
        if (user == null) {
            log.warn("更新个人信息失败：用户不存在, userId={}", dto.getUserId());
            return null;
        }

        // 2. 获取当前用户ID（用于设置 updatedUserId）
        Integer currentUserId = TokenUtil.getCurrentUserId();
        if (currentUserId == null) {
            currentUserId = dto.getUserId().intValue(); // 如果没有 token，使用被更新的用户ID
        }

        // 3. 使用 LambdaUpdateWrapper 只更新需要更新的字段，避免更新不应该更新的字段
        LambdaUpdateWrapper<LifeUser> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(LifeUser::getId, dto.getUserId().intValue());

        // 只更新非空字段
        if (StringUtils.isNotBlank(dto.getNickName())) {
            updateWrapper.set(LifeUser::getUserName, dto.getNickName());
        }
        if (StringUtils.isNotBlank(dto.getAvatarUrl())) {
            updateWrapper.set(LifeUser::getUserImage, dto.getAvatarUrl());
        }
        if (StringUtils.isNotBlank(dto.getGender())) {
            updateWrapper.set(LifeUser::getUserSex, dto.getGender());
        }
        if (dto.getBirthday() != null) {
            updateWrapper.set(LifeUser::getUserBirthday, dto.getBirthday());
        }
        if (StringUtils.isNotBlank(dto.getRealName())) {
            updateWrapper.set(LifeUser::getRealName, dto.getRealName());
        }
        if (StringUtils.isNotBlank(dto.getProvince())) {
            updateWrapper.set(LifeUser::getProvince, dto.getProvince());
        }
        if (StringUtils.isNotBlank(dto.getCity())) {
            updateWrapper.set(LifeUser::getCity, dto.getCity());
        }
        if (StringUtils.isNotBlank(dto.getDistrict())) {
            updateWrapper.set(LifeUser::getDistrict, dto.getDistrict());
        }
        if (StringUtils.isNotBlank(dto.getAddress())) {
            updateWrapper.set(LifeUser::getAddress, dto.getAddress());
        }
        if (StringUtils.isNotBlank(dto.getJianjie())) {
            updateWrapper.set(LifeUser::getJianjie, dto.getJianjie());
        }

        // 设置更新时间和更新人ID
        updateWrapper.set(LifeUser::getUpdatedTime, new Date());
        updateWrapper.set(LifeUser::getUpdatedUserId, currentUserId);

        // 4. 执行更新
        int result = lifeUserMapper.update(null, updateWrapper);
        if (result != 1) {
            log.error("更新用户信息失败, userId={}", dto.getUserId());
            return null;
        }
        log.info("用户信息更新成功, userId={}, updatedUserId={}", dto.getUserId(), currentUserId);

        // 5. 重新查询用户信息（因为使用 updateWrapper 后，user 对象不会自动更新）
        LifeUser updatedUser = lifeUserMapper.selectById(dto.getUserId().intValue());
        if (updatedUser == null) {
            log.error("更新后查询用户信息失败, userId={}", dto.getUserId());
            return null;
        }

        // 6. 返回更新后的用户信息
        return buildDiningUserVo(updatedUser);
    }

    @Override
    public DiningUserVo getUserInfo(Long userId) {
        // 1. 查询用户
        LifeUser user = lifeUserMapper.selectById(userId.intValue());
        if (user == null) {
            log.warn("获取用户信息失败：用户不存在, userId={}", userId);
            return null;
        }

        // 2. 检查用户状态
        if (user.getIsBanned() != null && user.getIsBanned() == 1) {
            log.warn("用户已被封禁: userId={}", userId);
            return null;
        }
        if (user.getLogoutFlag() != null && user.getLogoutFlag() == 1) {
            log.warn("用户已注销: userId={}", userId);
            return null;
        }

        // 3. 返回用户信息
        return buildDiningUserVo(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DiningUserVo changePhone(ChangePhoneDto dto) {
        String newPhone = dto.getNewPhone().trim();
        String codeStr = dto.getCode().trim();

        // 1. 校验验证码（Feign 调 store ali/checkSmsCode，appType=0 用户端，businessType=3 修改手机号）
        int codeInt;
        try {
            codeInt = Integer.parseInt(codeStr);
        } catch (NumberFormatException e) {
            log.warn("更换手机号失败：验证码格式错误, userId={}, newPhone={}", dto.getUserId(), newPhone);
            return null;
        }
        R<?> checkRes = alienStoreFeign.checkSmsCode(newPhone, 0, 3, codeInt);
        if (!R.isSuccess(checkRes)) {
            log.warn("更换手机号失败：验证码错误或已过期, userId={}, newPhone={}", dto.getUserId(), newPhone);
            return null;
        }

        // 2. 查询用户
        LifeUser user = lifeUserMapper.selectById(dto.getUserId().intValue());
        if (user == null) {
            log.warn("更换手机号失败：用户不存在, userId={}", dto.getUserId());
            return null;
        }
        if (user.getIsBanned() != null && user.getIsBanned() == 1) {
            log.warn("更换手机号失败：用户已被封禁, userId={}", dto.getUserId());
            return null;
        }
        if (user.getLogoutFlag() != null && user.getLogoutFlag() == 1) {
            log.warn("更换手机号失败：用户已注销, userId={}", dto.getUserId());
            return null;
        }

        // 3. 新手机号与当前相同则无需更新
        if (newPhone.equals(user.getUserPhone())) {
            log.info("新手机号与当前相同，无需更换, userId={}", dto.getUserId());
            return buildDiningUserVo(user);
        }

        // 4. 新手机号是否已被其他用户使用
        LambdaQueryWrapper<LifeUser> q = new LambdaQueryWrapper<>();
        q.eq(LifeUser::getUserPhone, newPhone);
        LifeUser existing = lifeUserMapper.selectOne(q);
        if (existing != null && !existing.getId().equals(user.getId())) {
            log.warn("更换手机号失败：新手机号已被其他用户使用, newPhone={}", newPhone);
            return null;
        }

        // 5. 更新 user_phone
        String oldPhone = user.getUserPhone();
        user.setUserPhone(newPhone);
        user.setUpdatedTime(new Date());
        int n = lifeUserMapper.updateById(user);
        if (n != 1) {
            log.error("更换手机号失败：更新数据库异常, userId={}", dto.getUserId());
            return null;
        }

        // 6. 只清除小程序旧手机号对应的 token，APP 的 token 保持不变
        // 因为小程序更换手机号只影响小程序平台，APP 可以继续使用旧手机号登录
        baseRedisService.delete(REDIS_KEY_USER_PHONE_PREFIX + oldPhone);
        log.info("更换手机号成功, userId={}, oldPhone={}, newPhone={}（仅清除小程序 token，APP token 保持不变）", dto.getUserId(), oldPhone, newPhone);

        return buildDiningUserVo(user);
    }

    @Override
    public TokenVerifyVo verifyToken(String token) {
        TokenVerifyVo verifyVo = new TokenVerifyVo();
        verifyVo.setValid(false);

        // 1. 检查 token 是否为空
        if (StringUtils.isBlank(token)) {
            verifyVo.setReason("Token 不能为空");
            return verifyVo;
        }

        // 去除 "Bearer " 前缀（不区分大小写）
        token = token.trim();
        if (token.length() > 7 && token.substring(0, 7).equalsIgnoreCase("Bearer ")) {
            token = token.substring(7).trim();
        }
        
        if (StringUtils.isBlank(token)) {
            verifyVo.setReason("Token 去除前缀后为空");
            return verifyVo;
        }

        try {
            // 2. 解析 token，验证格式和签名
            Claims claims = JwtUtil.parseJWT(token);
            
            // 3. 检查 token 是否过期
            Date expiration = claims.getExpiration();
            if (expiration != null && expiration.before(new Date())) {
                verifyVo.setReason("Token 已过期");
                verifyVo.setExpirationTime(expiration);
                return verifyVo;
            }

            // 4. 从 token 中提取用户信息
            String sub = claims.get("sub").toString();
            JSONObject tokenInfo = JSONObject.parseObject(sub);
            String openid = tokenInfo.getString("openid");
            String phone = tokenInfo.getString("phone");
            String userIdStr = tokenInfo.getString("userId");
            String userName = tokenInfo.getString("userName");

            // 5. 验证 Redis 中是否存在该 token（使用小程序专用的 key 前缀）
            boolean tokenExists = false;
            if (StringUtils.isNotBlank(openid)) {
                // 优先使用 openid 查找
                String redisToken = baseRedisService.getString(REDIS_KEY_TOKEN_PREFIX + openid);
                if (token.equals(redisToken)) {
                    tokenExists = true;
                }
            }
            
            // 兼容旧版本：通过手机号查找
            if (!tokenExists && StringUtils.isNotBlank(phone)) {
                String redisToken = baseRedisService.getString(REDIS_KEY_USER_PHONE_PREFIX + phone);
                if (token.equals(redisToken)) {
                    tokenExists = true;
                }
            }

            if (!tokenExists) {
                verifyVo.setReason("Token 不存在或已失效（可能已退出登录）");
                return verifyVo;
            }

            // 6. 验证用户状态（如果提供了 userId）
            if (StringUtils.isNotBlank(userIdStr)) {
                try {
                    Integer userId = Integer.parseInt(userIdStr);
                    LifeUser user = lifeUserMapper.selectById(userId);
                    if (user == null) {
                        verifyVo.setReason("用户不存在");
                        return verifyVo;
                    }
                    if (user.getIsBanned() != null && user.getIsBanned() == 1) {
                        verifyVo.setReason("用户已被封禁");
                        return verifyVo;
                    }
                    if (user.getLogoutFlag() != null && user.getLogoutFlag() == 1) {
                        verifyVo.setReason("用户已注销");
                        return verifyVo;
                    }
                } catch (NumberFormatException e) {
                    log.warn("Token 中的 userId 格式错误: {}", userIdStr);
                }
            }

            // 7. Token 验证通过，返回用户信息
            verifyVo.setValid(true);
            if (StringUtils.isNotBlank(userIdStr)) {
                try {
                    verifyVo.setUserId(Long.parseLong(userIdStr));
                } catch (NumberFormatException e) {
                    log.warn("无法解析 userId: {}", userIdStr);
                }
            }
            verifyVo.setPhone(phone);
            verifyVo.setNickName(userName);
            verifyVo.setOpenid(openid);
            verifyVo.setExpirationTime(expiration);

            log.info("Token 验证成功: userId={}, openid={}, phone={}", userIdStr, openid, phone);
            return verifyVo;

        } catch (ExpiredJwtException e) {
            log.warn("Token 已过期: {}", e.getMessage());
            verifyVo.setReason("Token 已过期: " + e.getMessage());
            return verifyVo;
        } catch (MalformedJwtException e) {
            log.warn("Token 格式错误: {}", e.getMessage());
            verifyVo.setReason("Token 格式错误: " + e.getMessage());
            return verifyVo;
        } catch (SignatureException e) {
            log.warn("Token 签名验证失败: {}", e.getMessage());
            verifyVo.setReason("Token 签名验证失败: " + e.getMessage());
            return verifyVo;
        } catch (Exception e) {
            log.error("Token 验证异常: {}", e.getMessage(), e);
            verifyVo.setReason("Token 验证异常: " + e.getMessage());
            return verifyVo;
        }
    }

    /**
     * 构建 DiningUserVo
     */
    private DiningUserVo buildDiningUserVo(LifeUser user) {
        DiningUserVo diningUserVo = new DiningUserVo();
        diningUserVo.setId(user.getId().longValue());
        diningUserVo.setPhone(user.getUserPhone());
        diningUserVo.setNickName(user.getUserName());
        diningUserVo.setAvatarUrl(user.getUserImage());
        diningUserVo.setStatus(0);
        diningUserVo.setCreatedTime(user.getCreatedTime());
        // 补充更多字段
        diningUserVo.setGender(user.getUserSex());
        diningUserVo.setBirthday(user.getUserBirthday());
        diningUserVo.setRealName(user.getRealName());
        diningUserVo.setProvince(user.getProvince());
        diningUserVo.setCity(user.getCity());
        diningUserVo.setDistrict(user.getDistrict());
        diningUserVo.setAddress(user.getAddress());
        diningUserVo.setJianjie(user.getJianjie());
        return diningUserVo;
    }
}
