diff --git a/pom.xml b/pom.xml index ad99c75..59fdacd 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,11 @@ spring-boot-starter-test test + + io.springfox + springfox-boot-starter + 3.0.0 + com.github.xiaoymin knife4j-openapi3-spring-boot-starter @@ -54,6 +59,11 @@ spring-boot-devtools runtime + + com.baomidou + mybatis-plus-boot-starter + 3.5.12 + diff --git a/src/main/java/com/example/admin_server/AdminServerApplication.java b/src/main/java/com/example/admin_server/AdminServerApplication.java index 410dc6d..4b6862f 100644 --- a/src/main/java/com/example/admin_server/AdminServerApplication.java +++ b/src/main/java/com/example/admin_server/AdminServerApplication.java @@ -1,9 +1,11 @@ package com.example.admin_server; +import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication +@MapperScan("com.example.admin_server.mapper") public class AdminServerApplication { public static void main(String[] args) { diff --git a/src/main/java/com/example/admin_server/common/GlobalExceptionHandler.java b/src/main/java/com/example/admin_server/common/GlobalExceptionHandler.java new file mode 100644 index 0000000..4a6cb55 --- /dev/null +++ b/src/main/java/com/example/admin_server/common/GlobalExceptionHandler.java @@ -0,0 +1,79 @@ +package com.example.admin_server.common; + +import com.example.admin_server.enums.ResultCode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; + +/** + * 全局异常处理器,统一将异常转换为 Result 返回 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 参数校验异常(@Valid) + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) { + String message = ex.getBindingResult().getFieldErrors().stream() + .findFirst() + .map(err -> err.getField() + " " + err.getDefaultMessage()) + .orElse(ResultCode.VALIDATE_ERROR.getMsg()); + log.warn("@Valid参数校验失败: {}", message); + return Result.of(ResultCode.VALIDATE_ERROR, message); + } + + /** + * 参数绑定异常(普通对象绑定) + */ + @ExceptionHandler(BindException.class) + public Result handleBindException(BindException ex) { + String message = ex.getFieldErrors().stream() + .findFirst() + .map(err -> err.getField() + " " + err.getDefaultMessage()) + .orElse(ResultCode.VALIDATE_ERROR.getMsg()); + log.warn("参数绑定失败: {}", message); + return Result.of(ResultCode.VALIDATE_ERROR, message); + } + + /** + * 单个参数校验(@Validated) + */ + @ExceptionHandler(ConstraintViolationException.class) + public Result handleConstraintViolationException(ConstraintViolationException ex) { + String message = ex.getConstraintViolations().stream() + .findFirst() + .map(ConstraintViolation::getMessage) + .orElse(ResultCode.VALIDATE_ERROR.getMsg()); + log.warn("@Validated参数校验失败: {}", message); + return Result.of(ResultCode.VALIDATE_ERROR, message); + } + + /** + * 请求体不可读 + */ + @ExceptionHandler(HttpMessageNotReadableException.class) + public Result handleHttpMessageNotReadable(HttpMessageNotReadableException ex) { + log.warn("消息不可读: {}", ex.getMessage()); + return Result.of(ResultCode.VALIDATE_ERROR); + } + + + /** + * 兜底异常 + */ + @ExceptionHandler(Exception.class) + public Result handleException(Exception ex) { + log.error("系统异常", ex); + return Result.of(ResultCode.SERVER_ERROR); + } +} + diff --git a/src/main/java/com/example/admin_server/common/Result.java b/src/main/java/com/example/admin_server/common/Result.java new file mode 100644 index 0000000..16e0972 --- /dev/null +++ b/src/main/java/com/example/admin_server/common/Result.java @@ -0,0 +1,73 @@ +package com.example.admin_server.common; + +import com.example.admin_server.enums.ResultCode; +import lombok.Data; + +@Data +public class Result { + + private Integer code; + private String msg; + private T data; + + private Result() {} + + /** 成功:无数据 */ + public static Result ok() { + return restResult(null, ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg()); + } + + /** 成功:有数据 */ + public static Result ok(T data) { + return restResult(data, ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg()); + } + + /** 成功:指定 msg 和 data */ + public static Result ok(String msg, T data) { + return restResult(data, ResultCode.SUCCESS.getCode(), msg); + } + + /** 失败:默认错误码与信息 */ + public static Result fail() { + return restResult(null, ResultCode.FAIL.getCode(), ResultCode.FAIL.getMsg()); + } + + /** 失败:指定信息 */ + public static Result fail(String msg) { + return restResult(null, ResultCode.FAIL.getCode(), msg); + } + + /** 失败:指定错误码与信息 */ + public static Result fail(Integer code, String msg) { + return restResult(null, code, msg); + } + + /** 通用:使用枚举返回 */ + public static Result of(ResultCode resultCode) { + return restResult(null, resultCode.getCode(), resultCode.getMsg()); + } + + /** 通用:使用枚举 + 自定义提示 */ + public static Result of(ResultCode resultCode, String msg) { + return restResult(null, resultCode.getCode(), msg); + } + + /** 通用:使用枚举 + 返回数据 */ + public static Result of(ResultCode resultCode, T data) { + return restResult(data, resultCode.getCode(), resultCode.getMsg()); + } + + /** 统一创建返回结构 */ + private static Result restResult(T data, int code, String msg) { + Result result = new Result<>(); + result.setCode(code); + result.setMsg(msg); + result.setData(data); + return result; + } + + /** 是否成功 */ + public boolean isSuccess() { + return this.code != null && this.code.equals(ResultCode.SUCCESS.getCode()); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/admin_server/controller/admin/AdminController.java b/src/main/java/com/example/admin_server/controller/admin/AdminController.java new file mode 100644 index 0000000..b6205a2 --- /dev/null +++ b/src/main/java/com/example/admin_server/controller/admin/AdminController.java @@ -0,0 +1,19 @@ +package com.example.admin_server.controller.admin; + + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 后台管理员表 前端控制器 + *

+ * + * @author FallingCliff + * @since 2025-05-24 + */ +@RestController +@RequestMapping("/admin") +public class AdminController { + +} diff --git a/src/main/java/com/example/admin_server/enums/ResultCode.java b/src/main/java/com/example/admin_server/enums/ResultCode.java new file mode 100644 index 0000000..ba5997a --- /dev/null +++ b/src/main/java/com/example/admin_server/enums/ResultCode.java @@ -0,0 +1,20 @@ +package com.example.admin_server.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ResultCode { + + SUCCESS(200, "成功"), + FAIL(500, "失败"), + UNAUTHORIZED(401, "未授权"), + FORBIDDEN(403, "禁止访问"), + NOT_FOUND(404, "资源不存在"), + VALIDATE_ERROR(400, "参数校验失败"), + SERVER_ERROR(503, "服务器内部错误"); + + private final int code; + private final String msg; +} diff --git a/src/main/java/com/example/admin_server/mapper/AdminMapper.java b/src/main/java/com/example/admin_server/mapper/AdminMapper.java new file mode 100644 index 0000000..a0d54f9 --- /dev/null +++ b/src/main/java/com/example/admin_server/mapper/AdminMapper.java @@ -0,0 +1,16 @@ +package com.example.admin_server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.example.admin_server.model.entity.Admin; + +/** + *

+ * 后台管理员表 Mapper 接口 + *

+ * + * @author FallingCliff + * @since 2025-05-24 + */ +public interface AdminMapper extends BaseMapper { + +} diff --git a/src/main/java/com/example/admin_server/model/entity/Admin.java b/src/main/java/com/example/admin_server/model/entity/Admin.java new file mode 100644 index 0000000..886573e --- /dev/null +++ b/src/main/java/com/example/admin_server/model/entity/Admin.java @@ -0,0 +1,86 @@ +package com.example.admin_server.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 后台管理员表 + *

+ * + * @author FallingCliff + * @since 2025-05-24 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("admin") +@ApiModel(value="Admin对象", description="后台管理员表") +public class Admin implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "主键ID") + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @ApiModelProperty(value = "用户名") + @TableField("username") + private String username; + + @ApiModelProperty(value = "密码") + @TableField("password") + private String password; + + @ApiModelProperty(value = "姓名") + @TableField("real_name") + private String realName; + + @ApiModelProperty(value = "邮箱") + @TableField("email") + private String email; + + @ApiModelProperty(value = "手机号") + @TableField("phone") + private String phone; + + @ApiModelProperty(value = "头像") + @TableField("avatar") + private String avatar; + + @ApiModelProperty(value = "状态:0禁用,1正常") + @TableField("status") + private Integer status; + + @ApiModelProperty(value = "是否为超级管理员:1是,0否") + @TableField("is_super") + private Integer isSuper; + + @ApiModelProperty(value = "上次登录IP") + @TableField("last_login_ip") + private String lastLoginIp; + + @ApiModelProperty(value = "上次登录时间") + @TableField("last_login_time") + private LocalDateTime lastLoginTime; + + @ApiModelProperty(value = "创建时间") + @TableField("create_time") + private LocalDateTime createTime; + + @ApiModelProperty(value = "更新时间") + @TableField("update_time") + private LocalDateTime updateTime; + + +} diff --git a/src/main/java/com/example/admin_server/service/IAdminService.java b/src/main/java/com/example/admin_server/service/IAdminService.java new file mode 100644 index 0000000..1db91a8 --- /dev/null +++ b/src/main/java/com/example/admin_server/service/IAdminService.java @@ -0,0 +1,16 @@ +package com.example.admin_server.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.example.admin_server.model.entity.Admin; + +/** + *

+ * 后台管理员表 服务类 + *

+ * + * @author FallingCliff + * @since 2025-05-24 + */ +public interface IAdminService extends IService { + +} diff --git a/src/main/java/com/example/admin_server/service/impl/AdminServiceImpl.java b/src/main/java/com/example/admin_server/service/impl/AdminServiceImpl.java new file mode 100644 index 0000000..e3c0440 --- /dev/null +++ b/src/main/java/com/example/admin_server/service/impl/AdminServiceImpl.java @@ -0,0 +1,21 @@ +package com.example.admin_server.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.example.admin_server.mapper.AdminMapper; +import com.example.admin_server.model.entity.Admin; +import com.example.admin_server.service.IAdminService; +import org.springframework.stereotype.Service; + +/** + *

+ * 后台管理员表 服务实现类 + *

+ * + * @author FallingCliff + * @since 2025-05-24 + */ +@Service +public class AdminServiceImpl extends ServiceImpl implements IAdminService { + + +} diff --git a/src/main/resources/mapper/AdminMapper.xml b/src/main/resources/mapper/AdminMapper.xml new file mode 100644 index 0000000..455a81e --- /dev/null +++ b/src/main/resources/mapper/AdminMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + +