diff --git a/backend/src/main/java/com/innovation/platform/config/SaTokenConfig.java b/backend/src/main/java/com/innovation/platform/config/SaTokenConfig.java new file mode 100644 index 0000000..594626d --- /dev/null +++ b/backend/src/main/java/com/innovation/platform/config/SaTokenConfig.java @@ -0,0 +1,25 @@ +package com.innovation.platform.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * Sa-Token 配置类 + */ +@Configuration +public class SaTokenConfig implements WebMvcConfigurer { + + /** + * 跨域配置 + */ + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns("*") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(true) + .maxAge(3600); + } +} diff --git a/backend/src/main/java/com/innovation/platform/controller/AuthController.java b/backend/src/main/java/com/innovation/platform/controller/AuthController.java new file mode 100644 index 0000000..ed8f1d4 --- /dev/null +++ b/backend/src/main/java/com/innovation/platform/controller/AuthController.java @@ -0,0 +1,48 @@ +package com.innovation.platform.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.innovation.platform.common.Result; +import com.innovation.platform.dto.LoginRequest; +import com.innovation.platform.dto.LoginResponse; +import com.innovation.platform.dto.RegisterRequest; +import com.innovation.platform.service.SysUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +/** + * 认证控制器 + */ +@Tag(name = "认证管理", description = "登录、注册、登出接口") +@RestController +@RequestMapping("/api/auth") +@RequiredArgsConstructor +public class AuthController { + + private final SysUserService sysUserService; + + @SaIgnore + @Operation(summary = "用户登录") + @PostMapping("/login") + public Result login(@Valid @RequestBody LoginRequest request) { + LoginResponse response = sysUserService.login(request); + return Result.success("登录成功", response); + } + + @SaIgnore + @Operation(summary = "用户注册") + @PostMapping("/register") + public Result register(@Valid @RequestBody RegisterRequest request) { + sysUserService.register(request); + return Result.success("注册成功", null); + } + + @Operation(summary = "用户登出") + @PostMapping("/logout") + public Result logout() { + sysUserService.logout(); + return Result.success("登出成功", null); + } +} diff --git a/backend/src/main/java/com/innovation/platform/dto/LoginRequest.java b/backend/src/main/java/com/innovation/platform/dto/LoginRequest.java new file mode 100644 index 0000000..4dcd0a7 --- /dev/null +++ b/backend/src/main/java/com/innovation/platform/dto/LoginRequest.java @@ -0,0 +1,16 @@ +package com.innovation.platform.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * 登录请求 DTO + */ +@Data +public class LoginRequest { + @NotBlank(message = "用户名不能为空") + private String username; + + @NotBlank(message = "密码不能为空") + private String password; +} diff --git a/backend/src/main/java/com/innovation/platform/dto/LoginResponse.java b/backend/src/main/java/com/innovation/platform/dto/LoginResponse.java new file mode 100644 index 0000000..e7b147a --- /dev/null +++ b/backend/src/main/java/com/innovation/platform/dto/LoginResponse.java @@ -0,0 +1,22 @@ +package com.innovation.platform.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 登录响应 DTO + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LoginResponse { + private String token; + private Long userId; + private String username; + private String realName; + private String avatar; + private Integer roleType; +} diff --git a/backend/src/main/java/com/innovation/platform/dto/RegisterRequest.java b/backend/src/main/java/com/innovation/platform/dto/RegisterRequest.java new file mode 100644 index 0000000..7126569 --- /dev/null +++ b/backend/src/main/java/com/innovation/platform/dto/RegisterRequest.java @@ -0,0 +1,30 @@ +package com.innovation.platform.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +/** + * 注册请求 DTO + */ +@Data +public class RegisterRequest { + @NotBlank(message = "用户名不能为空") + private String username; + + @NotBlank(message = "密码不能为空") + private String password; + + @NotBlank(message = "真实姓名不能为空") + private String realName; + + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") + private String phone; + + @Pattern(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", message = "邮箱格式不正确") + private String email; + + private Integer gender; + + private Integer roleType; +} diff --git a/backend/src/main/java/com/innovation/platform/exception/GlobalExceptionHandler.java b/backend/src/main/java/com/innovation/platform/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..699e562 --- /dev/null +++ b/backend/src/main/java/com/innovation/platform/exception/GlobalExceptionHandler.java @@ -0,0 +1,61 @@ +package com.innovation.platform.exception; + +import com.innovation.platform.common.Result; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.stream.Collectors; + +/** + * 全局异常处理器 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 业务异常 + */ + @ExceptionHandler(RuntimeException.class) + public Result handleRuntimeException(RuntimeException e) { + log.error("业务异常: {}", e.getMessage(), e); + return Result.error(400, e.getMessage()); + } + + /** + * 参数校验异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Result handleValidationException(MethodArgumentNotValidException e) { + String message = e.getBindingResult().getFieldErrors().stream() + .map(FieldError::getDefaultMessage) + .collect(Collectors.joining(", ")); + log.error("参数校验失败: {}", message); + return Result.error(400, message); + } + + /** + * 绑定异常 + */ + @ExceptionHandler(BindException.class) + public Result handleBindException(BindException e) { + String message = e.getBindingResult().getFieldErrors().stream() + .map(FieldError::getDefaultMessage) + .collect(Collectors.joining(", ")); + log.error("参数绑定失败: {}", message); + return Result.error(400, message); + } + + /** + * 其他异常 + */ + @ExceptionHandler(Exception.class) + public Result handleException(Exception e) { + log.error("系统异常: {}", e.getMessage(), e); + return Result.error(500, "系统异常,请稍后重试"); + } +} diff --git a/backend/src/main/java/com/innovation/platform/mapper/SysUserMapper.java b/backend/src/main/java/com/innovation/platform/mapper/SysUserMapper.java new file mode 100644 index 0000000..e7040dd --- /dev/null +++ b/backend/src/main/java/com/innovation/platform/mapper/SysUserMapper.java @@ -0,0 +1,12 @@ +package com.innovation.platform.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.innovation.platform.entity.SysUser; +import org.apache.ibatis.annotations.Mapper; + +/** + * 用户 Mapper 接口 + */ +@Mapper +public interface SysUserMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/innovation/platform/service/SysUserService.java b/backend/src/main/java/com/innovation/platform/service/SysUserService.java new file mode 100644 index 0000000..ddc1bcf --- /dev/null +++ b/backend/src/main/java/com/innovation/platform/service/SysUserService.java @@ -0,0 +1,37 @@ +package com.innovation.platform.service; + +import com.innovation.platform.dto.LoginRequest; +import com.innovation.platform.dto.LoginResponse; +import com.innovation.platform.dto.RegisterRequest; +import com.innovation.platform.entity.SysUser; + +/** + * 用户服务接口 + */ +public interface SysUserService { + + /** + * 用户登录 + */ + LoginResponse login(LoginRequest request); + + /** + * 用户注册 + */ + void register(RegisterRequest request); + + /** + * 用户登出 + */ + void logout(); + + /** + * 根据用户名查询用户 + */ + SysUser getByUsername(String username); + + /** + * 根据ID查询用户 + */ + SysUser getById(Long id); +} diff --git a/backend/src/main/java/com/innovation/platform/service/impl/SysUserServiceImpl.java b/backend/src/main/java/com/innovation/platform/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..aa39168 --- /dev/null +++ b/backend/src/main/java/com/innovation/platform/service/impl/SysUserServiceImpl.java @@ -0,0 +1,96 @@ +package com.innovation.platform.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.crypto.digest.BCrypt; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.innovation.platform.dto.LoginRequest; +import com.innovation.platform.dto.LoginResponse; +import com.innovation.platform.dto.RegisterRequest; +import com.innovation.platform.entity.SysUser; +import com.innovation.platform.mapper.SysUserMapper; +import com.innovation.platform.service.SysUserService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 用户服务实现类 + */ +@Service +@RequiredArgsConstructor +public class SysUserServiceImpl implements SysUserService { + + private final SysUserMapper sysUserMapper; + + @Override + public LoginResponse login(LoginRequest request) { + // 查询用户 + SysUser user = getByUsername(request.getUsername()); + if (user == null) { + throw new RuntimeException("用户名或密码错误"); + } + + // 验证密码 + if (!BCrypt.checkpw(request.getPassword(), user.getPassword())) { + throw new RuntimeException("用户名或密码错误"); + } + + // 检查用户状态 + if (user.getStatus() != null && user.getStatus() == 0) { + throw new RuntimeException("账号已被禁用"); + } + + // 登录 + StpUtil.login(user.getId()); + + // 返回登录信息 + return LoginResponse.builder() + .token(StpUtil.getTokenValue()) + .userId(user.getId()) + .username(user.getUsername()) + .realName(user.getRealName()) + .avatar(user.getAvatar()) + .roleType(user.getRoleType()) + .build(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void register(RegisterRequest request) { + // 检查用户名是否已存在 + if (getByUsername(request.getUsername()) != null) { + throw new RuntimeException("用户名已存在"); + } + + // 创建用户 + SysUser user = new SysUser(); + user.setUsername(request.getUsername()); + user.setPassword(BCrypt.hashpw(request.getPassword())); + user.setRealName(request.getRealName()); + user.setPhone(request.getPhone()); + user.setEmail(request.getEmail()); + user.setGender(request.getGender()); + user.setRoleType(request.getRoleType() != null ? request.getRoleType() : 0); + user.setStatus(1); + + sysUserMapper.insert(user); + } + + @Override + public void logout() { + StpUtil.logout(); + } + + @Override + public SysUser getByUsername(String username) { + return sysUserMapper.selectOne( + new LambdaQueryWrapper() + .eq(SysUser::getUsername, username) + ); + } + + @Override + public SysUser getById(Long id) { + return sysUserMapper.selectById(id); + } +}