From 8e3e8fc8b86f5fe355fd8b4934965260a117aaa5 Mon Sep 17 00:00:00 2001 From: FalingCliff Date: Sat, 24 May 2025 15:20:57 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin-server):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=91=98=E7=9B=B8=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 Admin 实体类 - 添加 AdminMapper 接口及 XML 配置 - 实现 AdminService 接口及其实现类 - 创建 AdminController 控制器 - 增加全局异常处理器和统一返回结果封装 - 集成 MyBatis-Plus 和 Swagger --- pom.xml | 10 +++ .../admin_server/AdminServerApplication.java | 2 + .../common/GlobalExceptionHandler.java | 79 +++++++++++++++++ .../example/admin_server/common/Result.java | 73 ++++++++++++++++ .../controller/admin/AdminController.java | 19 ++++ .../admin_server/enums/ResultCode.java | 20 +++++ .../admin_server/mapper/AdminMapper.java | 16 ++++ .../admin_server/model/entity/Admin.java | 86 +++++++++++++++++++ .../admin_server/service/IAdminService.java | 16 ++++ .../service/impl/AdminServiceImpl.java | 21 +++++ src/main/resources/mapper/AdminMapper.xml | 22 +++++ 11 files changed, 364 insertions(+) create mode 100644 src/main/java/com/example/admin_server/common/GlobalExceptionHandler.java create mode 100644 src/main/java/com/example/admin_server/common/Result.java create mode 100644 src/main/java/com/example/admin_server/controller/admin/AdminController.java create mode 100644 src/main/java/com/example/admin_server/enums/ResultCode.java create mode 100644 src/main/java/com/example/admin_server/mapper/AdminMapper.java create mode 100644 src/main/java/com/example/admin_server/model/entity/Admin.java create mode 100644 src/main/java/com/example/admin_server/service/IAdminService.java create mode 100644 src/main/java/com/example/admin_server/service/impl/AdminServiceImpl.java create mode 100644 src/main/resources/mapper/AdminMapper.xml 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 @@ + + + + + + + + + + + + + + + + + + + + + +