lrj
昨天 9f8395fab13ca4b230a0f7d62636e209745c91d4
backend/src/main/java/com/rongyichuang/auth/service/AuthService.java
@@ -2,7 +2,15 @@
import com.rongyichuang.auth.dto.LoginRequest;
import com.rongyichuang.auth.dto.LoginResponse;
import com.rongyichuang.auth.dto.PhoneDecryptResponse;
import com.rongyichuang.auth.dto.WxLoginRequest;
import com.rongyichuang.auth.dto.WxLoginResponse;
import com.rongyichuang.auth.entity.WxLoginRecord;
import com.rongyichuang.auth.util.JwtUtil;
import com.rongyichuang.service.WechatApiService;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.rongyichuang.employee.entity.Employee;
import com.rongyichuang.employee.repository.EmployeeRepository;
import com.rongyichuang.judge.entity.Judge;
@@ -19,6 +27,11 @@
import org.springframework.stereotype.Service;
import java.util.Optional;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
 * 认证服务类
@@ -45,6 +58,12 @@
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private WxLoginRecordService wxLoginRecordService;
    @Autowired
    private WechatApiService wechatApiService;
    /**
     * 用户登录
@@ -85,10 +104,10 @@
            throw new BadCredentialsException("没有权限");
        }
        // 6. 生成JWT token
        // 7. 生成JWT token
        String token = jwtUtil.generateToken(user.getId(), user.getPhone());
        // 7. 确定主要角色类型(优先级:employee > judge > player)
        // 8. 确定主要角色类型(优先级:employee > judge > player)
        String userType;
        if (employeeOpt.isPresent()) {
            userType = "employee";
@@ -98,7 +117,7 @@
            userType = "player";
        }
        // 8. 构建用户信息
        // 9. 构建用户信息
        LoginResponse.UserInfo userInfo = new LoginResponse.UserInfo(
                user.getId(),
                user.getName(),
@@ -106,7 +125,7 @@
                userType
        );
        // 9. 设置所有关联的角色信息
        // 10. 设置所有关联的角色信息
        if (employeeOpt.isPresent()) {
            Employee employee = employeeOpt.get();
            userInfo.setEmployee(new LoginResponse.EmployeeInfo(
@@ -138,8 +157,7 @@
                    player.getId(),
                    player.getName(),
                    player.getPhone(),
                    player.getDescription(),
                    player.getAuditState()
                    player.getDescription()
            ));
            if (employeeOpt.isEmpty() && judgeOpt.isEmpty()) {
                logger.info("学员登录成功,ID: {}, 姓名: {}", player.getId(), player.getName());
@@ -148,4 +166,520 @@
        return new LoginResponse(token, userInfo);
    }
    /**
     * 微信登录
     */
    public WxLoginResponse wxLogin(WxLoginRequest wxLoginRequest) throws JsonProcessingException, JsonMappingException {
        logger.info("=== 开始微信登录流程 ===");
        logger.info("登录时间: {}", java.time.LocalDateTime.now());
        logger.info("请求参数详情:");
        logger.info("- code: {}", wxLoginRequest.getCode());
        logger.info("- code长度: {}", wxLoginRequest.getCode() != null ? wxLoginRequest.getCode().length() : 0);
        logger.info("- wxOpenid: {}", wxLoginRequest.getWxOpenid());
        logger.info("- wxUnionid: {}", wxLoginRequest.getWxUnionid());
        logger.info("- phone: {}", wxLoginRequest.getPhone());
        logger.info("- loginIp: {}", wxLoginRequest.getLoginIp());
        logger.info("- deviceInfo: {}", wxLoginRequest.getDeviceInfo());
        logger.info("- phoneAuthorized: {}", wxLoginRequest.getPhoneAuthorized());
        // 1. 通过code调用微信API获取openid和unionid
        WechatApiService.WechatUserInfo wechatUserInfo = null;
        if (wxLoginRequest.getCode() != null && !wxLoginRequest.getCode().trim().isEmpty()) {
            logger.info("步骤1: 调用微信API获取用户信息");
            logger.info("使用code: {}", wxLoginRequest.getCode());
            try {
                wechatUserInfo = wechatApiService.getWechatUserInfo(wxLoginRequest.getCode());
                // 将从微信API获取的信息设置到请求对象中
                wxLoginRequest.setWxOpenid(wechatUserInfo.getOpenid());
                wxLoginRequest.setWxUnionid(wechatUserInfo.getUnionid());
                wxLoginRequest.setSessionKey(wechatUserInfo.getSessionKey());
                logger.info("✅ 从微信API获取用户信息成功");
                logger.info("- openid: {}", wechatUserInfo.getOpenid());
                logger.info("- unionid: {}", wechatUserInfo.getUnionid());
                logger.info("- sessionKey长度: {}", wechatUserInfo.getSessionKey() != null ? wechatUserInfo.getSessionKey().length() : 0);
            } catch (Exception e) {
                logger.error("❌ 调用微信API失败");
                logger.error("异常信息: {}", e.getMessage());
                logger.error("异常堆栈:", e);
                throw e;
            }
        } else {
            logger.warn("⚠️ 微信登录code为空,将使用请求中的openid和unionid");
            logger.info("请求中的openid: {}", wxLoginRequest.getWxOpenid());
            logger.info("请求中的unionid: {}", wxLoginRequest.getWxUnionid());
        }
        User user = null;
        boolean isNewUser = false;
        logger.info("步骤2: 查找或创建用户");
        // 2. 首先尝试通过openid查找用户
        if (wxLoginRequest.getWxOpenid() != null && !wxLoginRequest.getWxOpenid().trim().isEmpty()) {
            logger.info("尝试通过openid查找用户: {}", wxLoginRequest.getWxOpenid());
            try {
                Optional<User> userOpt = userRepository.findByWxOpenid(wxLoginRequest.getWxOpenid());
                if (userOpt.isPresent()) {
                    user = userOpt.get();
                    logger.info("✅ 通过openid找到用户");
                    logger.info("- 用户ID: {}", user.getId());
                    logger.info("- 用户姓名: {}", user.getName());
                    logger.info("- 用户手机号: {}", user.getPhone());
                    logger.info("- 用户unionid: {}", user.getWxUnionid());
                } else {
                    logger.info("❌ 通过openid未找到用户");
                }
            } catch (Exception e) {
                logger.error("❌ 通过openid查找用户时发生异常: {}", e.getMessage());
                logger.error("异常堆栈:", e);
            }
        } else {
            logger.warn("⚠️ openid为空,跳过openid查找");
        }
        // 3. 如果通过openid没找到,尝试通过unionid查找
        if (user == null && wxLoginRequest.getWxUnionid() != null && !wxLoginRequest.getWxUnionid().trim().isEmpty()) {
            logger.info("尝试通过unionid查找用户: {}", wxLoginRequest.getWxUnionid());
            try {
                Optional<User> userOpt = userRepository.findByWxUnionid(wxLoginRequest.getWxUnionid());
                if (userOpt.isPresent()) {
                    user = userOpt.get();
                    logger.info("✅ 通过unionid找到用户");
                    logger.info("- 用户ID: {}", user.getId());
                    logger.info("- 用户姓名: {}", user.getName());
                    logger.info("- 用户手机号: {}", user.getPhone());
                    logger.info("- 用户openid: {}", user.getWxOpenid());
                    // 更新用户的openid(如果之前没有)
                    if (user.getWxOpenid() == null || user.getWxOpenid().trim().isEmpty()) {
                        logger.info("用户openid为空,需要更新");
                        user.setWxOpenid(wxLoginRequest.getWxOpenid());
                        try {
                            userRepository.save(user);
                            logger.info("✅ 成功更新用户openid,用户ID: {}", user.getId());
                        } catch (Exception e) {
                            logger.error("❌ 更新用户openid失败: {}", e.getMessage());
                            logger.error("异常堆栈:", e);
                        }
                    } else {
                        logger.info("用户openid已存在,无需更新");
                    }
                } else {
                    logger.info("❌ 通过unionid未找到用户");
                }
            } catch (Exception e) {
                logger.error("❌ 通过unionid查找用户时发生异常: {}", e.getMessage());
                logger.error("异常堆栈:", e);
            }
        } else {
            logger.warn("⚠️ unionid为空或用户已找到,跳过unionid查找");
        }
        // 4. 如果都没找到,创建新用户
        if (user == null) {
            logger.info("未找到现有用户,开始创建新用户");
            logger.info("新用户信息:");
            logger.info("- openid: {}", wxLoginRequest.getWxOpenid());
            logger.info("- unionid: {}", wxLoginRequest.getWxUnionid());
            logger.info("- phone: {}", wxLoginRequest.getPhone());
            try {
                user = new User();
                user.setWxOpenid(wxLoginRequest.getWxOpenid());
                user.setWxUnionid(wxLoginRequest.getWxUnionid());
                user.setName("微信用户"); // 默认名称,后续可以更新
                user.setPhone(wxLoginRequest.getPhone()); // 如果有授权手机号
                user = userRepository.save(user);
                isNewUser = true;
                logger.info("✅ 成功创建新的微信用户");
                logger.info("- 新用户ID: {}", user.getId());
                logger.info("- 新用户姓名: {}", user.getName());
                logger.info("- 新用户手机号: {}", user.getPhone());
            } catch (Exception e) {
                logger.error("❌ 创建新用户失败");
                logger.error("异常信息: {}", e.getMessage());
                logger.error("异常堆栈:", e);
                throw new RuntimeException("创建新用户失败: " + e.getMessage(), e);
            }
        }
        // 5. 记录登录信息到新表
        logger.info("步骤3: 记录登录信息");
        WxLoginRecord loginRecord = null;
        try {
            loginRecord = wxLoginRecordService.createLoginRecord(
                    wxLoginRequest.getWxOpenid(),
                    wxLoginRequest.getWxUnionid(),
                    user.getId(),
                    wxLoginRequest.getLoginIp(),
                    wxLoginRequest.getDeviceInfo(),
                    wxLoginRequest.getSessionKey(),
                    wxLoginRequest.getPhoneAuthorized()
            );
            logger.info("✅ 成功创建登录记录,记录ID: {}", loginRecord.getId());
        } catch (Exception e) {
            logger.error("❌ 创建登录记录失败: {}", e.getMessage());
            logger.error("异常堆栈:", e);
            throw new RuntimeException("创建登录记录失败: " + e.getMessage(), e);
        }
        // 6. 查找关联的员工、评委和学员信息
        logger.info("步骤4: 查找用户关联的角色信息");
        logger.info("查找用户ID: {}", user.getId());
        Optional<Employee> employeeOpt = Optional.empty();
        Optional<Judge> judgeOpt = Optional.empty();
        Optional<Player> playerOpt = Optional.empty();
        try {
            employeeOpt = employeeRepository.findByUserId(user.getId());
            logger.info("员工角色查找结果: {}", employeeOpt.isPresent() ? "找到" : "未找到");
            if (employeeOpt.isPresent()) {
                Employee employee = employeeOpt.get();
                logger.info("- 员工ID: {}", employee.getId());
                logger.info("- 员工姓名: {}", employee.getName());
                logger.info("- 员工角色ID: {}", employee.getRoleId());
            }
        } catch (Exception e) {
            logger.error("❌ 查找员工角色时发生异常: {}", e.getMessage());
            logger.error("异常堆栈:", e);
        }
        try {
            judgeOpt = judgeRepository.findByUserId(user.getId());
            logger.info("评委角色查找结果: {}", judgeOpt.isPresent() ? "找到" : "未找到");
            if (judgeOpt.isPresent()) {
                Judge judge = judgeOpt.get();
                logger.info("- 评委ID: {}", judge.getId());
                logger.info("- 评委姓名: {}", judge.getName());
                logger.info("- 评委职称: {}", judge.getTitle());
                logger.info("- 评委公司: {}", judge.getCompany());
            }
        } catch (Exception e) {
            logger.error("❌ 查找评委角色时发生异常: {}", e.getMessage());
            logger.error("异常堆栈:", e);
        }
        try {
            playerOpt = playerRepository.findByUserId(user.getId());
            logger.info("学员角色查找结果: {}", playerOpt.isPresent() ? "找到" : "未找到");
            if (playerOpt.isPresent()) {
                Player player = playerOpt.get();
                logger.info("- 学员ID: {}", player.getId());
                logger.info("- 学员姓名: {}", player.getName());
                logger.info("- 学员手机号: {}", player.getPhone());
            }
        } catch (Exception e) {
            logger.error("❌ 查找学员角色时发生异常: {}", e.getMessage());
            logger.error("异常堆栈:", e);
        }
        // 6. 生成JWT token
        logger.info("步骤5: 生成JWT token");
        String tokenIdentifier = user.getPhone() != null ? user.getPhone() : user.getWxOpenid();
        logger.info("Token标识符: {}", tokenIdentifier);
        String token = null;
        try {
            token = jwtUtil.generateToken(user.getId(), tokenIdentifier);
            logger.info("✅ 成功生成JWT token,长度: {}", token != null ? token.length() : 0);
        } catch (Exception e) {
            logger.error("❌ 生成JWT token失败: {}", e.getMessage());
            logger.error("异常堆栈:", e);
            throw new RuntimeException("生成JWT token失败: " + e.getMessage(), e);
        }
        // 7. 确定主要角色类型(优先级:employee > judge > player)
        logger.info("步骤6: 确定用户主要角色类型");
        String userType;
        if (employeeOpt.isPresent()) {
            userType = "employee";
            logger.info("用户主要角色: 员工");
        } else if (judgeOpt.isPresent()) {
            userType = "judge";
            logger.info("用户主要角色: 评委");
        } else if (playerOpt.isPresent()) {
            userType = "player";
            logger.info("用户主要角色: 学员");
        } else {
            userType = "user"; // 普通微信用户
            logger.info("用户主要角色: 普通微信用户");
        }
        // 8. 构建用户信息
        logger.info("步骤7: 构建用户信息响应");
        LoginResponse.UserInfo userInfo = null;
        try {
            userInfo = new LoginResponse.UserInfo(
                    user.getId(),
                    user.getName(),
                    user.getPhone(),
                    userType
            );
            logger.info("✅ 成功构建基础用户信息");
            logger.info("- 用户ID: {}", user.getId());
            logger.info("- 用户姓名: {}", user.getName());
            logger.info("- 用户手机号: {}", user.getPhone());
            logger.info("- 用户类型: {}", userType);
        } catch (Exception e) {
            logger.error("❌ 构建基础用户信息失败: {}", e.getMessage());
            logger.error("异常堆栈:", e);
            throw new RuntimeException("构建基础用户信息失败: " + e.getMessage(), e);
        }
        // 9. 设置所有关联的角色信息
        logger.info("步骤8: 设置角色详细信息");
        if (employeeOpt.isPresent()) {
            try {
                Employee employee = employeeOpt.get();
                userInfo.setEmployee(new LoginResponse.EmployeeInfo(
                        employee.getId(),
                        employee.getName(),
                        employee.getRoleId(),
                        employee.getDescription()
                ));
                logger.info("✅ 设置员工信息成功");
                logger.info("- 员工ID: {}", employee.getId());
                logger.info("- 员工姓名: {}", employee.getName());
                logger.info("- 员工角色ID: {}", employee.getRoleId());
                logger.info("员工微信登录成功,ID: {}, 姓名: {}", employee.getId(), employee.getName());
            } catch (Exception e) {
                logger.error("❌ 设置员工信息失败: {}", e.getMessage());
                logger.error("异常堆栈:", e);
            }
        }
        if (judgeOpt.isPresent()) {
            try {
                Judge judge = judgeOpt.get();
                userInfo.setJudge(new LoginResponse.JudgeInfo(
                        judge.getId(),
                        judge.getName(),
                        judge.getTitle(),
                        judge.getCompany(),
                        judge.getDescription()
                ));
                logger.info("✅ 设置评委信息成功");
                logger.info("- 评委ID: {}", judge.getId());
                logger.info("- 评委姓名: {}", judge.getName());
                logger.info("- 评委职称: {}", judge.getTitle());
                logger.info("- 评委公司: {}", judge.getCompany());
                if (employeeOpt.isEmpty()) {
                    logger.info("评委微信登录成功,ID: {}, 姓名: {}", judge.getId(), judge.getName());
                }
            } catch (Exception e) {
                logger.error("❌ 设置评委信息失败: {}", e.getMessage());
                logger.error("异常堆栈:", e);
            }
        }
        if (playerOpt.isPresent()) {
            try {
                Player player = playerOpt.get();
                userInfo.setPlayer(new LoginResponse.PlayerInfo(
                        player.getId(),
                        player.getName(),
                        player.getPhone(),
                        player.getDescription()
                ));
                logger.info("✅ 设置学员信息成功");
                logger.info("- 学员ID: {}", player.getId());
                logger.info("- 学员姓名: {}", player.getName());
                logger.info("- 学员手机号: {}", player.getPhone());
                if (employeeOpt.isEmpty() && judgeOpt.isEmpty()) {
                    logger.info("学员微信登录成功,ID: {}, 姓名: {}", player.getId(), player.getName());
                }
            } catch (Exception e) {
                logger.error("❌ 设置学员信息失败: {}", e.getMessage());
                logger.error("异常堆栈:", e);
            }
        }
        logger.info("=== 微信登录流程完成 ===");
        logger.info("登录结果:");
        logger.info("- 是否新用户: {}", isNewUser);
        logger.info("- 登录记录ID: {}", loginRecord != null ? loginRecord.getId() : "null");
        logger.info("- Token长度: {}", token != null ? token.length() : 0);
        logger.info("- 用户类型: {}", userType);
        logger.info("完成时间: {}", java.time.LocalDateTime.now());
        return new WxLoginResponse(token, userInfo, isNewUser, loginRecord.getId(), wxLoginRequest.getSessionKey());
    }
    /**
     * 解密微信手机号
     */
    public PhoneDecryptResponse decryptPhoneNumber(String encryptedData, String iv, String sessionKey) {
        logger.info("=== 开始解密微信手机号 ===");
        logger.info("解密时间: {}", java.time.LocalDateTime.now());
        logger.info("encryptedData长度: {}", encryptedData != null ? encryptedData.length() : 0);
        logger.info("iv长度: {}", iv != null ? iv.length() : 0);
        logger.info("sessionKey长度: {}", sessionKey != null ? sessionKey.length() : 0);
        try {
            // 1. Base64解码
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
            byte[] ivBytes = Base64.getDecoder().decode(iv);
            byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey);
            logger.info("Base64解码成功");
            logger.info("encryptedBytes长度: {}", encryptedBytes.length);
            logger.info("ivBytes长度: {}", ivBytes.length);
            logger.info("sessionKeyBytes长度: {}", sessionKeyBytes.length);
            // 2. AES解密
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec secretKeySpec = new SecretKeySpec(sessionKeyBytes, "AES");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            String decryptedData = new String(decryptedBytes, "UTF-8");
            logger.info("AES解密成功");
            logger.info("解密后数据长度: {}", decryptedData.length());
            logger.info("解密后数据: {}", decryptedData);
            // 3. 解析JSON
            ObjectMapper objectMapper = new ObjectMapper();
            WechatPhoneInfo phoneInfo = objectMapper.readValue(decryptedData, WechatPhoneInfo.class);
            logger.info("JSON解析成功");
            logger.info("手机号: {}", phoneInfo.getPhoneNumber());
            logger.info("纯手机号: {}", phoneInfo.getPurePhoneNumber());
            logger.info("国家代码: {}", phoneInfo.getCountryCode());
            PhoneDecryptResponse response = new PhoneDecryptResponse(
                phoneInfo.getPhoneNumber(),
                phoneInfo.getPurePhoneNumber(),
                phoneInfo.getCountryCode()
            );
            logger.info("✅ 微信手机号解密成功");
            return response;
        } catch (Exception e) {
            logger.error("❌ 微信手机号解密失败", e);
            String friendlyMessage = getDecryptFriendlyErrorMessage(e);
            throw new RuntimeException(friendlyMessage, e);
        }
    }
    /**
     * 使用新版API通过code直接获取手机号
     */
    public PhoneDecryptResponse getPhoneNumberByCode(String code) {
        logger.info("=== 开始使用新版API获取手机号 ===");
        logger.info("获取时间: {}", java.time.LocalDateTime.now());
        logger.info("输入code: {}", code);
        try {
            // 调用微信API服务获取手机号
            WechatApiService.WechatPhoneInfo phoneInfo = wechatApiService.getPhoneNumberByCode(code);
            logger.info("✅ 新版API获取手机号成功");
            logger.info("手机号: {}", phoneInfo.getPhoneNumber());
            logger.info("纯手机号: {}", phoneInfo.getPurePhoneNumber());
            logger.info("国家代码: {}", phoneInfo.getCountryCode());
            PhoneDecryptResponse response = new PhoneDecryptResponse(
                phoneInfo.getPhoneNumber(),
                phoneInfo.getPurePhoneNumber(),
                phoneInfo.getCountryCode()
            );
            return response;
        } catch (Exception e) {
            logger.error("❌ 新版API获取手机号失败", e);
            // 直接传递WechatApiService的友好错误信息
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * 微信手机号信息
     */
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class WechatPhoneInfo {
        private String phoneNumber;
        private String purePhoneNumber;
        private String countryCode;
        public String getPhoneNumber() {
            return phoneNumber;
        }
        public void setPhoneNumber(String phoneNumber) {
            this.phoneNumber = phoneNumber;
        }
        public String getPurePhoneNumber() {
            return purePhoneNumber;
        }
        public void setPurePhoneNumber(String purePhoneNumber) {
            this.purePhoneNumber = purePhoneNumber;
        }
        public String getCountryCode() {
            return countryCode;
        }
        public void setCountryCode(String countryCode) {
            this.countryCode = countryCode;
        }
    }
    /**
     * 根据解密异常提供友好的错误信息
     */
    private String getDecryptFriendlyErrorMessage(Exception e) {
        String errorMessage = e.getMessage();
        if (e instanceof IllegalArgumentException) {
            if (errorMessage.contains("Illegal base64 character")) {
                return "授权数据格式错误,请重新获取手机号授权";
            }
        }
        if (e instanceof javax.crypto.BadPaddingException) {
            return "授权数据已过期或无效,请重新获取手机号授权";
        }
        if (e instanceof javax.crypto.IllegalBlockSizeException) {
            return "授权数据长度错误,请重新获取手机号授权";
        }
        if (e instanceof java.security.InvalidKeyException) {
            return "会话密钥无效,请重新登录后再试";
        }
        if (e instanceof com.fasterxml.jackson.core.JsonParseException) {
            return "授权数据解析失败,请重新获取手机号授权";
        }
        if (errorMessage.contains("sessionKey")) {
            return "会话已过期,请重新登录后再试";
        }
        if (errorMessage.contains("encryptedData")) {
            return "授权数据无效,请重新获取手机号授权";
        }
        return "手机号解密失败,请重新获取手机号授权";
    }
}