package com.designhub.controller; import com.designhub.common.Result; import com.designhub.dto.LoginDTO; import com.designhub.dto.RegisterDTO; import com.designhub.entity.User; import com.designhub.exception.BusinessException; import com.designhub.service.UserService; import com.designhub.util.JwtUtil; import com.designhub.vo.LoginVO; import com.designhub.vo.UserInfoVO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; /** * 认证控制器 */ @Slf4j @Tag(name = "用户认证", description = "用户登录、登出、刷新令牌等接口") @RestController @RequestMapping("/api/auth") @RequiredArgsConstructor public class AuthController { private final UserService userService; private final JwtUtil jwtUtil; @Operation(summary = "用户注册", description = "新用户注册") @PostMapping("/register") public Result register(@Valid @RequestBody RegisterDTO registerDTO) { log.info("用户注册请求: {}", registerDTO.getUsername()); User user = userService.register(registerDTO); return Result.success(user); } @Operation(summary = "用户登录", description = "支持密码登录、钉钉登录、Casdoor登录") @PostMapping("/login") public Result login(@Valid @RequestBody LoginDTO loginDTO) { log.info("用户登录请求: {}", loginDTO.getUsername()); LoginVO loginVO = userService.login(loginDTO); return Result.success(loginVO); } @Operation(summary = "获取当前用户信息", description = "根据Token获取当前登录用户信息") @GetMapping("/userinfo") public Result getUserInfo(@RequestHeader(value = "Authorization", required = false) String authHeader) { log.info("获取用户信息"); String token = jwtUtil.extractTokenFromHeader(authHeader); UserInfoVO userInfo = userService.getUserInfoByToken(token); return Result.success(userInfo); } @Operation(summary = "用户登出", description = "退出登录,清除Token") @PostMapping("/logout") public Result logout(@RequestHeader(value = "Authorization", required = false) String authHeader) { log.info("用户登出"); // Token是无状态的,前端删除即可 // 如果需要黑名单机制,可以在Redis中记录 return Result.success("登出成功"); } @Operation(summary = "刷新令牌", description = "使用刷新令牌获取新的访问令牌") @PostMapping("/refresh") public Result refresh(@RequestHeader("Authorization") String authHeader) { log.info("刷新令牌"); LoginVO loginVO = userService.refreshToken(authHeader); return Result.success(loginVO); } @Operation(summary = "钉钉扫码登录回调", description = "接收钉钉扫码登录的回调 - 使用OAuth 2.0流程") @GetMapping("/dingtalk/callback") @SuppressWarnings("unchecked") public Result dingtalkCallback( @RequestParam String code, @RequestParam(required = false) String state) { log.info("钉钉登录回调 - code: {}, state: {}", code.substring(0, Math.min(10, code.length())) + "...", state); // 使用新的OAuth 2.0流程登录 LoginDTO loginDTO = new LoginDTO(); loginDTO.setLoginType("dingtalk"); loginDTO.setAuthCode(code); try { LoginVO loginVO = userService.login(loginDTO); log.info("钉钉登录成功: {}", loginVO.getUsername()); return Result.success(loginVO); } catch (BusinessException e) { // 检查是否需要选择部门 if (e.getMessage() != null && e.getMessage().startsWith("NEED_SELECT_DEPT:")) { String[] parts = e.getMessage().split(":"); Map data = new HashMap<>(); data.put("needSelectDept", true); data.put("userId", parts[1]); data.put("dingDepartments", parts.length > 2 ? parts[2] : ""); Result> result = Result.error(1901, "需要选择部门", data); return (Result) (Result) result; } log.error("钉钉登录失败", e); return Result.error(e.getMessage()); } catch (Exception e) { log.error("钉钉登录失败", e); return Result.error("钉钉登录失败: " + e.getMessage()); } } @Operation(summary = "钉钉用户选择部门", description = "首次登录时选择系统部门") @PostMapping("/dingtalk/select-department") public Result selectDepartment( @RequestParam Long userId, @RequestParam Long deptId) { log.info("钉钉用户选择部门: userId={}, deptId={}", userId, deptId); try { LoginVO loginVO = userService.completeDingTalkUserSetup(userId, deptId); return Result.success(loginVO); } catch (Exception e) { log.error("选择部门失败", e); return Result.error("选择部门失败: " + e.getMessage()); } } }