9 Komitmen 23cbe4ab8d ... 9d1b0819ef

Pembuat SHA1 Pesan Tanggal
  yang yi 9d1b0819ef # feat:完善参数校验 1 Minggu lalu
  yang yi 62bf89ee9d # feat:参数校验;校验失败的全局异常处理器 1 Minggu lalu
  yang yi 7ccb7178bf # feat:用户相关实体的接口添加数据校验 1 Minggu lalu
  yang yi 1446520a46 # feat:用户相关实体的接口文档信息完善 1 Minggu lalu
  yang yi 0c9937022c # feat:完善用户管理相关的接口和模型 1 Minggu lalu
  yang yi f4b4acc39a # feat:用户管理相关的接口和模型 1 Minggu lalu
  yang yi 5c1251af2e # feat:跨域配置 1 Minggu lalu
  yang yi 5e5a98d4fb # feat:用户模块的增删改查 1 Minggu lalu
  yang yi 58fa54884f # sql:数据库 1 Minggu lalu

+ 4 - 2
build.gradle.kts

@@ -26,8 +26,10 @@ repositories {
 
 dependencies {
     implementation("org.springframework.boot:spring-boot-starter-actuator")
-    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
-    implementation("org.springframework.boot:spring-boot-starter-security")
+    // Source: https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter
+    implementation ("com.baomidou:mybatis-plus-spring-boot3-starter:3.5.15")
+//    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
+//    implementation("org.springframework.boot:spring-boot-starter-security")
     implementation("org.springframework.boot:spring-boot-starter-validation")
     implementation("org.springframework.boot:spring-boot-starter-web")
     implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.16")

+ 50 - 0
sql/postgersql.sql

@@ -0,0 +1,50 @@
+-- CREATE DATABASE  serve;
+-- USE serve;
+
+-- 创建 schema(如果不存在)
+CREATE SCHEMA IF NOT EXISTS dev;
+
+
+-- 删除序列(如果存在)
+-- DROP SEQUENCE IF EXISTS dev.user_id_seq;
+
+-- 创建新序列
+-- CREATE SEQUENCE dev.user_id_seq
+--     START WITH 1
+--     INCREMENT BY 1
+--     NO MINVALUE
+--     NO MAXVALUE
+--     CACHE 1;
+
+-- 添加注释
+-- COMMENT ON SEQUENCE dev.user_seq IS '用户表ID序列';
+
+DROP TABLE IF EXISTS dev.user;
+-- 在 dev schema 下创建用户表
+CREATE TABLE dev.user (
+                          id SERIAL PRIMARY KEY,
+                          account VARCHAR(50) NOT NULL UNIQUE,
+                          password VARCHAR(255) NOT NULL,
+                          username VARCHAR(100) NOT NULL,
+                          role VARCHAR(20) NOT NULL DEFAULT 'user',
+                          avatar TEXT,
+                          enable INT NOT NULL DEFAULT 1,
+                          delete_flag INT NOT NULL DEFAULT 0
+);
+
+-- 添加注释
+COMMENT ON TABLE dev.user IS '用户表';
+COMMENT ON COLUMN dev.user.id IS '用户ID,自增主键';
+COMMENT ON COLUMN dev.user.account IS '账号,唯一';
+COMMENT ON COLUMN dev.user.password IS '密码';
+COMMENT ON COLUMN dev.user.username IS '用户名';
+COMMENT ON COLUMN dev.user.role IS '角色';
+COMMENT ON COLUMN dev.user.avatar IS '头像URL';
+COMMENT ON COLUMN dev.user.enable IS '是否启用';
+COMMENT ON COLUMN dev.user.delete_flag IS '删除标记';
+
+-- 创建索引
+CREATE INDEX idx_user_account ON dev.user(account);
+CREATE INDEX idx_user_role ON dev.user(role);
+CREATE INDEX idx_user_enable ON dev.user(enable);
+CREATE INDEX idx_user_delete_flag ON dev.user(delete_flag);

+ 27 - 0
src/main/java/space/anyi/serve/config/WebConfig.java

@@ -0,0 +1,27 @@
+package space.anyi.serve.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @fileName: WebConfig
+ * @projectName: serve
+ * @package: space.anyi.serve.config
+ * @author: 杨逸
+ * @date:2026/4/28 10:51
+ * @description:
+ */
+@EnableWebMvc
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**")  // 允许所有路径
+                .allowedOrigins("http://localhost:5173")  // 允许的来源(可修改为前端地址)
+                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的请求方法
+                .allowedHeaders("*") // 允许所有请求头
+                .allowCredentials(true); // 允许携带 Cookie
+    }
+}

+ 153 - 0
src/main/java/space/anyi/serve/controller/UserController.java

@@ -0,0 +1,153 @@
+package space.anyi.serve.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import org.springframework.beans.BeanUtils;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.serve.entity.PageVo;
+import space.anyi.serve.entity.Response;
+import space.anyi.serve.entity.user.*;
+import space.anyi.serve.service.UserService;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * (User)表控制层
+ *
+ * @author 杨逸
+ * @since 2026-03-31 13:17:55
+ */
+@RestController
+@RequestMapping("user")
+public class UserController {
+    /**
+     * 服务对象
+     */
+    @Resource
+    private UserService userService;
+
+    /**
+     * 分页查询
+     * @param account
+     * @param username
+     * @param role
+     * @param enable
+     * @param pageNum
+     * @param pageSize
+     * @return
+     */
+    @GetMapping
+    public Response<PageVo<List<UserVo>>> queryByPage(
+            @NotNull @RequestParam(defaultValue = "") String account,
+            @NotNull @RequestParam(defaultValue = "") String username,
+            @NotNull @RequestParam(defaultValue = "") String role,
+            @NotNull @RequestParam(defaultValue = "1") Integer enable,
+            @NotNull(message = "页码不能为null") @RequestParam(defaultValue = "1") Integer pageNum,
+            @NotNull(message = "分页大小不能为null")@RequestParam(defaultValue = "10") Integer pageSize) {
+        User user = new User();
+        user.setAccount(account);
+        user.setUsername(username);
+        user.setRole(role);
+        user.setEnable(enable);
+        Page<User> page = this.userService.queryByPage(user, Page.of(pageNum, pageSize));
+        return Response.ok(new PageVo<List<UserVo>>(page.getTotal(),UserVo.from(page.getRecords())));
+    }
+
+    /**
+     * 通过主键查询单条数据
+     *
+     * @param id 主键
+     * @return 单条数据
+     */
+    @GetMapping("{id}")
+    public Response<UserVo> queryById(@NotBlank(message = "用户ID不能为空") @PathVariable String id) {
+        User user = this.userService.queryById(Long.valueOf(id));
+        return Response.ok(UserVo.form(user));
+    }
+
+    /**
+     * 新增数据
+     *
+     * @param userDto 实体
+     * @return 新增结果
+     */
+    @PostMapping
+    public Response<Boolean> add(@Valid@RequestBody UserDto userDto) {
+        User user = new User();
+        BeanUtils.copyProperties(userDto,user);
+        return Response.ok(this.userService.insert(user));
+    }
+
+    /**
+     * 编辑数据
+     *
+     * @param userDto 实体
+     * @return 编辑结果
+     */
+    @PutMapping
+    public Response<Boolean> edit(@Valid@RequestBody UserDto userDto) {
+        User user = new User();
+        BeanUtils.copyProperties(userDto,user);
+        user.setId(Long.valueOf(userDto.getId()));
+        return Response.ok(this.userService.update(user));
+    }
+
+    /**
+     * 删除数据
+     *
+     * @param ids 主键
+     * @return 删除是否成功
+     */
+    @DeleteMapping
+    public Response<Boolean> deleteById(@NotEmpty(message = "ID列表不能为空") @RequestParam List<String> ids) {
+        List<Long> list = ids.stream().map(Long::valueOf).toList();
+        return Response.ok(this.userService.deleteById(list));
+    }
+
+    /**
+     * 更新用户状态
+     * @param dto
+     * @return
+     */
+    @PutMapping("/updateStatus")
+    public Response updateUserStatus(@Valid@RequestBody UpdateUserStatusDto dto){
+        User user = new User();
+        BeanUtils.copyProperties(dto,user);
+        user.setId(Long.valueOf(dto.getId()));
+        return Response.ok(userService.updateUserStatus(user));
+    }
+
+    /**
+     * 更新用户头像
+     * @param dto
+     * @return
+     */
+    @PutMapping("/updateAvatar")
+    public Response updateUserAvatar(@Valid@RequestBody UpdateUserAvatarDto dto){
+        User user = new User();
+        BeanUtils.copyProperties(dto,user);
+        user.setId(Long.valueOf(dto.getId()));
+        return Response.ok(userService.updateUserAvatar(user));
+    }
+
+    /**
+     * 更新用户密码
+     * @param dto
+     * @return
+     */
+    @PutMapping("/updatePassword")
+    public Response updatePassword(@Valid @RequestBody UpdateUserPasswordDto dto){
+        User user = new User();
+        BeanUtils.copyProperties(dto,user);
+        user.setId(Long.valueOf(dto.getId()));
+        return Response.ok(userService.updatePassword(user,dto.getOldPassword()));
+    }
+
+}
+

+ 40 - 0
src/main/java/space/anyi/serve/entity/PageVo.java

@@ -0,0 +1,40 @@
+package space.anyi.serve.entity;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * @fileName: PageVo
+ * @projectName: serve
+ * @package: space.anyi.serve.entity
+ * @author: 杨逸
+ * @date:2026/3/31 22:35
+ * @description:
+ */
+@Schema(description = "分页数据对象")
+public final class PageVo<T> {
+    @Schema(description = "总数据条数")
+    private final Long total;
+    @Schema(description = "当前页的数据")
+    private final T data;
+
+    public PageVo(Long total, T data) {
+        this.total = total;
+        this.data = data;
+    }
+
+    public static <T> PageVo<T> of(Page<T> page) {
+        return new PageVo(page.getTotal(), page.getRecords());
+    }
+
+
+    public Long getTotal() {
+        return total;
+    }
+
+
+    public T getData() {
+        return data;
+    }
+
+}

+ 114 - 0
src/main/java/space/anyi/serve/entity/Response.java

@@ -0,0 +1,114 @@
+package space.anyi.serve.entity;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * @fileName: Response
+ * @projectName: serve
+ * @package: space.anyi.serve.entity
+ * @author: 杨逸
+ * @date:2026/4/1 9:03
+ * @description:
+ */
+@Schema(description = "后端统一的响应实体")
+public final class Response<T> {
+    public static final Integer SUCCESS_CODE = 200;
+    public static final Integer ERROR_CODE = 400;
+
+    @Schema(description = "状态码;200:成功")
+    private Integer code;
+    @Schema(description = "响应附加信息")
+    private String message;
+    @Schema(description = "响应的具体数据")
+    private T data;
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public void setCode(Integer code) {
+        this.code = code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(T data) {
+        this.data = data;
+    }
+
+    /**
+     * 成功的封装
+     * @param data 数据
+     * @return {@code Response<T> }
+     * @description:
+     * @author: 杨逸
+     * @data:2026/04/01 09:09:45
+     * @since 1.0.0
+     */
+    public static <T> Response<T> ok(T data){
+        Response<T> response = new Response<>();
+        response.setCode(SUCCESS_CODE);
+        response.setMessage("success");
+        response.setData(data);
+        return response;
+    }
+    public static <T> Response<T> ok(){
+        Response<T> response = new Response<>();
+        response.setCode(SUCCESS_CODE);
+        response.setMessage("success");
+        response.setData(null);
+        return response;
+    }
+
+    /**
+     * 失败的封装
+     * @param message 错误信息
+     * @return {@code Response<T> }
+     * @description:
+     * @author: 杨逸
+     * @data:2026/04/01 09:09:51
+     * @since 1.0.0
+     */
+    public static <T> Response<T> error(String message){
+        Response<T> response = new Response<>();
+        response.setCode(ERROR_CODE);
+        response.setMessage(message);
+        return response;
+    }
+    public static <T> Response<T> error(String message,T data){
+        Response<T> response = new Response<>();
+        response.setCode(ERROR_CODE);
+        response.setMessage(message);
+        response.setData(data);
+        return response;
+    }
+
+    /**
+     * 自定义封装
+     * @param code 状态码
+     * @param message 信息
+     * @param data 数据
+     * @return {@code Response<T> }
+     * @description:
+     * @author: 杨逸
+     * @data:2026/04/01 09:12:31
+     * @since 1.0.0
+     */
+    public static <T> Response<T> of(Integer code,String message,T data){
+        Response<T> response = new Response<>();
+        response.setCode(code);
+        response.setMessage(message);
+        response.setData(data);
+        return response;
+    }
+}

+ 46 - 0
src/main/java/space/anyi/serve/entity/user/UpdateUserAvatarDto.java

@@ -0,0 +1,46 @@
+package space.anyi.serve.entity.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+
+/**
+ * @fileName: UpdateUserAvatarDto
+ * @projectName: serve
+ * @package: space.anyi.serve.entity.user
+ * @author: 杨逸
+ * @date:2026/4/29 9:11
+ * @description:
+ */
+@Schema(
+        description = "更新用户头像的实体DTO",
+        requiredProperties = {
+                "id",
+                "avatar",
+        }
+)
+public class UpdateUserAvatarDto {
+    @NotBlank(message = "用户ID不能为空")
+    @Schema(description = "用户的ID")
+    private String id;
+    @Pattern(regexp = "^(https?)://[^\\s/$.?#].[^\\s]*$", message = "URL格式不正确")
+    @NotBlank(message = "头像不能链接地址不能为空")
+    @Schema(description = "头像地址的链接")
+    private String avatar;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAvatar() {
+        return avatar;
+    }
+
+    public void setAvatar(String avatar) {
+        this.avatar = avatar;
+    }
+}

+ 52 - 0
src/main/java/space/anyi/serve/entity/user/UpdateUserPasswordDto.java

@@ -0,0 +1,52 @@
+package space.anyi.serve.entity.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+/**
+ * @fileName: UpdateUserPasswordDto
+ * @projectName: serve
+ * @package: space.anyi.serve.entity.user
+ * @author: 杨逸
+ * @date:2026/4/29 9:15
+ * @description:
+ */
+@Schema(description = "用户密码修改使用的DTO")
+public class UpdateUserPasswordDto {
+    @NotBlank(message = "用户ID不能为空")
+    @Schema(description = "用户ID")
+    private String id;
+    @NotBlank(message = "用户的旧密码不能为空")
+    @Size(min = 4,max = 20,message = "用户密码长度不能小于4且不能大于20")
+    @Schema(description = "用户的当前密码")
+    private String password;
+    @NotBlank(message = "用户的新密码不能为空")
+    @Size(min = 4,max = 20,message = "用户密码长度不能小于4且不能大于20")
+    @Schema(description = "用户的新密码")
+    private String oldPassword;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getOldPassword() {
+        return oldPassword;
+    }
+
+    public void setOldPassword(String oldPassword) {
+        this.oldPassword = oldPassword;
+    }
+}

+ 44 - 0
src/main/java/space/anyi/serve/entity/user/UpdateUserStatusDto.java

@@ -0,0 +1,44 @@
+package space.anyi.serve.entity.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.media.SchemaProperty;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+/**
+ * @fileName: UpdateUserStatusDto
+ * @projectName: serve
+ * @package: space.anyi.serve.entity.user
+ * @author: 杨逸
+ * @date:2026/4/29 9:05
+ * @description:
+ */
+@Schema(description = "用户状态更新使用的DTO")
+public class UpdateUserStatusDto {
+    @NotBlank(message = "用户ID不能为空")
+    @Schema(description = "用户ID")
+    private String id;
+    @NotNull(message = "用户状态不能为空")
+    @Min(value = 0,message = "状态不能小于0")
+    @Max(value = 1,message = "用户状态不能大于1")
+    @Schema(description = "更新后的状态;1:启用,0:禁用")
+    private Integer enable;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Integer getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Integer enable) {
+        this.enable = enable;
+    }
+}

+ 99 - 0
src/main/java/space/anyi/serve/entity/user/User.java

@@ -0,0 +1,99 @@
+package space.anyi.serve.entity.user;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+/**
+ * @fileName: User
+ * @projectName: serve
+ * @package: space.anyi.serve.entity.user
+ * @author: 杨逸
+ * @date:2026/3/31 12:50
+ * @description:
+ */
+//指定表名
+@TableName("dev.user")
+public class User {
+    //声明主键
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String account;
+    @TableField(value = "password")
+    private String password;
+    @TableField(value = "username")
+    private String username;
+    @TableField(value = "role")
+    private String role;
+    @TableField(value = "avatar")
+    private String avatar;
+    @TableField(value = "enable")
+    private Integer enable = 1;
+    @TableField(value = "delete_flag")
+    private Integer deleteFlag = 0;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+
+    public String getAvatar() {
+        return avatar;
+    }
+
+    public void setAvatar(String avatar) {
+        this.avatar = avatar;
+    }
+
+    public Integer getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Integer enable) {
+        this.enable = enable;
+    }
+
+    public Integer getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(Integer deleteFlag) {
+        this.deleteFlag = deleteFlag;
+    }
+}

+ 75 - 0
src/main/java/space/anyi/serve/entity/user/UserDto.java

@@ -0,0 +1,75 @@
+package space.anyi.serve.entity.user;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+/**
+ * @fileName: UserDto
+ * @projectName: serve
+ * @package: space.anyi.serve.entity.user
+ * @author: 杨逸
+ * @date:2026/3/31 20:42
+ * @description:
+ */
+@Schema(description = "用户新增和修改使用的DTO")
+public class UserDto {
+    @Schema(description = "用户ID")
+    private String id;
+    @NotBlank(message = "用户账号不能为空")
+    @Size(min = 4,max = 20,message = "用户账号长度不能小于4且不能大于20")
+    @Schema(description = "用户账号")
+    private String account;
+    @NotBlank(message = "用户密码不能为空")
+    @Size(min = 4,max = 20,message = "用户密码长度不能小于4且不能大于20")
+    @Schema(description = "用户密码")
+    private String password;
+    @NotBlank(message = "用户名称不能为空")
+    @Size(min = 2,max = 32,message = "用户名称长度不能小于2且不能大于32")
+    @Schema(description = "用户名称")
+    private String username;
+    @NotBlank(message = "用户角色不能为空")
+    @Size(min = 4,max = 32,message = "用户角色长度不能小于4且不能大于32")
+    @Schema(description = "用户角色")
+    private String role;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+}

+ 100 - 0
src/main/java/space/anyi/serve/entity/user/UserVo.java

@@ -0,0 +1,100 @@
+package space.anyi.serve.entity.user;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @fileName: UserVo
+ * @projectName: serve
+ * @package: space.anyi.serve.entity.user
+ * @author: 杨逸
+ * @date:2026/4/1 8:54
+ * @description:
+ */
+@Schema(description = "用户响应展示对象")
+public class UserVo {
+    @Schema(description = "用户ID")
+    private String id;
+    @Schema(description = "用户账号")
+    private String account;
+    @Schema(description = "用户名称")
+    private String username;
+    @Schema(description = "用户角色")
+    private String role;
+    @Schema(description = "用户头像地址")
+    private String avatar;
+    @Schema(description = "用户状态;0:禁用,1:启用")
+    private Integer enable = 1;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+
+    public String getAvatar() {
+        return avatar;
+    }
+
+    public void setAvatar(String avatar) {
+        this.avatar = avatar;
+    }
+
+    public Integer getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Integer enable) {
+        this.enable = enable;
+    }
+    public static UserVo form(User user){
+        UserVo vo = new UserVo();
+        if (Objects.isNull(user)) {
+            return vo;
+        }
+        vo.setId(user.getId()+"");
+        vo.setAccount(user.getAccount());
+        vo.setUsername(user.getUsername());
+        vo.setRole(user.getRole());
+        vo.setAvatar(user.getAvatar());
+        vo.setEnable(user.getEnable());
+        return vo;
+    }
+    public static List<UserVo> from(List<User> list){
+        List<UserVo> res = new ArrayList<>();
+        for (User user : list) {
+            res.add(UserVo.form(user));
+        }
+        return res;
+    }
+}

+ 69 - 0
src/main/java/space/anyi/serve/handler/GlobalExceptionHandler.java

@@ -0,0 +1,69 @@
+package space.anyi.serve.handler;
+
+import jakarta.validation.ConstraintViolationException;
+import org.springframework.http.HttpStatus;
+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.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.View;
+import space.anyi.serve.entity.Response;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @fileName: GlobalExceptionHandler
+ * @projectName: serve
+ * @package: space.anyi.serve.handler
+ * @author: 杨逸
+ * @date:2026/4/29 17:01
+ * @description: 全局异常处理
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+
+    // 处理参数校验失败异常
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Response<List<FieldError>> handleValidationExceptions(MethodArgumentNotValidException ex) {
+        List<FieldError> fieldErrors = ex.getFieldErrors()
+                .stream()
+                .map(error -> new FieldError(error.getObjectName(),error.getField(),error.getDefaultMessage()))
+                .collect(Collectors.toList());
+
+        return Response.error("参数不合法",fieldErrors);
+    }
+
+    // 处理单个参数校验失败
+    @ExceptionHandler(ConstraintViolationException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Response<List<FieldError>> handleConstraintViolation(ConstraintViolationException ex) {
+        List<FieldError> fieldErrors = ex.getConstraintViolations()
+                .stream()
+                .map(violation -> new FieldError(null,violation.getPropertyPath().toString(),violation.getMessage()))
+                .collect(Collectors.toList());
+
+        return Response.error("参数不合法",fieldErrors);
+    }
+
+    // 处理 @Validated 分组校验异常
+    @ExceptionHandler(BindException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Response<List<FieldError>> handleBindException(BindException ex) {
+        List<FieldError> fieldErrors = ex.getFieldErrors()
+                .stream()
+                .map(error -> new FieldError( error.getObjectName(),error.getField(), error.getDefaultMessage()))
+                .collect(Collectors.toList());
+
+        return Response.error("参数不合法",fieldErrors);
+    }
+
+    @ExceptionHandler(Exception.class)
+    public Response handlerException(Exception e){
+        return Response.error(e.getMessage());
+    }
+}

+ 9 - 0
src/main/java/space/anyi/serve/mapper/UserMapper.java

@@ -0,0 +1,9 @@
+package space.anyi.serve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import space.anyi.serve.entity.user.User;
+
+@Mapper
+public interface UserMapper extends BaseMapper<User> {
+}

+ 63 - 0
src/main/java/space/anyi/serve/service/UserService.java

@@ -0,0 +1,63 @@
+package space.anyi.serve.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import space.anyi.serve.entity.user.User;
+
+import java.util.List;
+
+/**
+ * (User)表服务接口
+ *
+ * @author 杨逸
+ * @since 2026-03-31 13:17:55
+ */
+public interface UserService extends IService<User> {
+
+    /**
+     * 通过ID查询单条数据
+     *
+     * @param id 主键
+     * @return 实例对象
+     */
+    User queryById(Long id);
+
+    /**
+     * 分页查询
+     *
+     * @param user 筛选条件
+     * @param page      分页对象
+     * @return 查询结果
+     */
+    Page<User> queryByPage(User user,Page<User> page);
+
+    /**
+     * 新增数据
+     *
+     * @param user 实例对象
+     * @return 实例对象
+     */
+    Boolean insert(User user);
+
+    /**
+     * 修改数据
+     *
+     * @param user 实例对象
+     * @return 实例对象
+     */
+    Boolean update(User user);
+
+    /**
+     * 通过主键删除数据
+     *
+     * @param ids 主键
+     * @return 是否成功
+     */
+    Boolean deleteById(List<Long> ids);
+
+    Boolean updateUserStatus(User user);
+
+    Boolean updateUserAvatar(User user);
+
+    Boolean updatePassword(User user, String oldPassword);
+}

+ 114 - 0
src/main/java/space/anyi/serve/service/impl/UserServiceImpl.java

@@ -0,0 +1,114 @@
+package space.anyi.serve.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import space.anyi.serve.entity.user.User;
+import space.anyi.serve.mapper.UserMapper;
+import space.anyi.serve.service.UserService;
+
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * (User)表服务实现类
+ *
+ * @author 杨逸
+ * @since 2026-03-31 13:17:55
+ */
+@Service("userService")
+public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
+
+    /**
+     * 通过ID查询单条数据
+     *
+     * @param id 主键
+     * @return 实例对象
+     */
+    @Override
+    public User queryById(Long id) {
+        return getById(id);
+    }
+
+    /**
+     * 分页查询
+     *
+     * @param user 筛选条件
+     * @param page      分页对象
+     * @return 查询结果
+     */
+    @Override
+    public Page<User> queryByPage(User user,Page<User> page) {
+        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>()
+                .eq(!user.getRole().isEmpty(),User::getRole,user.getRole())
+                .eq(!user.getAccount().isEmpty(),User::getAccount,user.getAccount())
+                .eq(User::getEnable,user.getEnable())
+                .like(!user.getUsername().isEmpty(), User::getUsername, user.getUsername())
+                .orderByAsc(User::getId);
+        page(page, lambdaQueryWrapper);
+        return page;
+    }
+
+    @Override
+    public Boolean updateUserStatus(User user) {
+        return this.update(user);
+    }
+
+    @Override
+    public Boolean updateUserAvatar(User user) {
+        return this.update(user);
+    }
+
+    @Override
+    public Boolean updatePassword(User user, String oldPassword) {
+        User user1 = getById(user.getId());
+        if (!oldPassword.equals(user1.getPassword())) {
+            return false;
+        }
+        return update(user);
+    }
+
+    /**
+     * 新增数据
+     *
+     * @param user 实例对象
+     * @return 实例对象
+     */
+    @Override
+    public Boolean insert(User user) {
+        return save(user);
+    }
+
+    /**
+     * 修改数据
+     *
+     * @param user 实例对象
+     * @return 实例对象
+     */
+    @Override
+    public Boolean update(User user) {
+        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<User>()
+                .eq(User::getId, user.getId())
+                .set(Objects.nonNull(user.getAccount()),User::getAccount, user.getAccount())
+                .set(Objects.nonNull(user.getPassword()),User::getPassword,user.getPassword())
+                .set(Objects.nonNull(user.getUsername()),User::getUsername, user.getUsername())
+                .set(Objects.nonNull(user.getAvatar()), User::getAvatar,user.getAvatar())
+                .set(Objects.nonNull(user.getRole()),User::getRole, user.getRole())
+                .set(Objects.nonNull(user.getEnable()),User::getEnable, user.getEnable());
+        return this.update(updateWrapper);
+    }
+
+    /**
+     * 通过主键删除数据
+     *
+     * @param ids 主键
+     * @return 是否成功
+     */
+    @Override
+    public Boolean deleteById(List<Long> ids) {
+        return this.getBaseMapper().deleteByIds(ids) == 1;
+    }
+}

+ 23 - 1
src/main/resources/application.yaml

@@ -4,11 +4,33 @@ server:
 spring:
   application:
     name: serve
+    admin:
+      enabled: true
   datasource:
     type: com.alibaba.druid.pool.DruidDataSource
-    url: jdbc:postgresql://${databaseHost}:5432/postgres?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC+8
+    url: jdbc:postgresql://${databaseHost}:5432/serve?currentSchema=dev&useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC+8
     driver-class-name: org.postgresql.Driver
     username: ${databaseUsername}
     password: ${databasePassword}
     druid:
       enable: true
+  jmx:
+    enabled: true
+#配置全局loglevel 为debug
+logging:
+  level:
+    root: info
+mybatis-plus:
+  configuration:
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+  global-config:
+    db-config:
+      logic-delete-field: deleteFlag
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "*"
+  endpoint:
+    health:
+      show-details: always

+ 4 - 0
src/main/resources/mapper/UserMapper.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="space.anyi.serve.mapper.UserMapper">
+</mapper>