Kaynağa Gözat

# feat: 完善OpenAPI文档注解(鉴权scheme、@Operation/@Parameter/@ApiResponses/@Schema)

yangyi 2 ay önce
ebeveyn
işleme
07f3357d8b

+ 11 - 0
src/main/java/space/anyi/serve/config/OpenApiConfig.java

@@ -1,8 +1,10 @@
 package space.anyi.serve.config;
 
+import io.swagger.v3.oas.models.Components;
 import io.swagger.v3.oas.models.OpenAPI;
 import io.swagger.v3.oas.models.info.Contact;
 import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.security.SecurityScheme;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -12,6 +14,15 @@ public class OpenApiConfig {
     @Bean
     public OpenAPI customOpenAPI() {
         return new OpenAPI()
+                .components(new Components()
+                        // 设置 spring security jwt accessToken 认证的请求头 Authorization: Bearer xxx.xxx.xxx
+                        //参考文章 https://www.cnblogs.com/breezefaith/p/17998698
+                        .addSecuritySchemes("openApiSecurityScheme", new SecurityScheme()
+                                .type(SecurityScheme.Type.HTTP)
+                                .bearerFormat("JWT")
+                                .in(SecurityScheme.In.HEADER)
+                                .name("Authorization")
+                                .scheme("Bearer")))
                 .info(new Info()
                         .title("Serve API")
                         .version("0.0.1-SNAPSHOT")

+ 7 - 2
src/main/java/space/anyi/serve/controller/AuthController.java

@@ -2,6 +2,8 @@ package space.anyi.serve.controller;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
 import org.springframework.security.authentication.AuthenticationManager;
@@ -21,6 +23,9 @@ import space.anyi.serve.handler.security.JwtTokenProvider;
 import space.anyi.serve.service.UserService;
 
 @Tag(name = "AuthController",description = "用户认证相关接口")
+@ApiResponses(value = {
+        @ApiResponse(responseCode = "400", description = "参数校验失败,返回体 code=400")
+})
 @RestController
 @RequestMapping("auth")
 public class AuthController {
@@ -40,7 +45,7 @@ public class AuthController {
         this.passwordEncoder = passwordEncoder;
     }
 
-    @Operation(summary = "用户登录")
+    @Operation(summary = "用户登录", description = "使用账号密码登录,成功后返回 JWT token")
     @PostMapping("login")
     public Response<AuthTokenVo> login(@Valid @RequestBody LoginDto loginDto) {
         var authentication = authenticationManager.authenticate(
@@ -52,7 +57,7 @@ public class AuthController {
         return Response.ok(new AuthTokenVo(token, user.getId(), user.getAccount(), user.getUsername(), user.getRole()));
     }
 
-    @Operation(summary = "用户注册")
+    @Operation(summary = "用户注册", description = "注册新账号,成功后直接返回 JWT token")
     @PostMapping("register")
     public Response<AuthTokenVo> register(@Valid @RequestBody RegisterDto registerDto) {
         LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()

+ 13 - 4
src/main/java/space/anyi/serve/controller/CategoryController.java

@@ -1,6 +1,10 @@
 package space.anyi.serve.controller;
 
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
@@ -15,6 +19,11 @@ import space.anyi.serve.service.CategoryService;
 import java.util.List;
 
 @Tag(name = "CategoryController", description = "题目分类相关接口")
+@ApiResponses(value = {
+        @ApiResponse(responseCode = "400", description = "参数校验失败,返回体 code=400"),
+        @ApiResponse(responseCode = "401", description = "未认证或认证已过期"),
+        @ApiResponse(responseCode = "403", description = "无权限访问")
+})
 @RestController
 @RequestMapping("category")
 public class CategoryController {
@@ -28,24 +37,24 @@ public class CategoryController {
         return Response.ok(categoryService.listAll());
     }
 
-    @Operation(summary = "新增分类")
+    @Operation(summary = "新增分类", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PostMapping
     public Response<Boolean> add(@Valid @RequestBody CategoryDto dto) {
         return Response.ok(categoryService.insert(dto));
     }
 
-    @Operation(summary = "修改分类")
+    @Operation(summary = "修改分类", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PutMapping
     public Response<Boolean> edit(@Valid @RequestBody CategoryDto dto) {
         return Response.ok(categoryService.update(dto));
     }
 
-    @Operation(summary = "删除分类")
+    @Operation(summary = "删除分类", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasRole('ROLE_admin')")
     @DeleteMapping
-    public Response<Boolean> delete(@NotNull(message = "分类ID不能为空") @RequestParam Long id) {
+    public Response<Boolean> delete(@Parameter(description = "分类ID") @NotNull(message = "分类ID不能为空") @RequestParam Long id) {
         return Response.ok(categoryService.deleteById(id));
     }
 }

+ 32 - 19
src/main/java/space/anyi/serve/controller/ContestController.java

@@ -2,6 +2,10 @@ package space.anyi.serve.controller;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
@@ -18,6 +22,11 @@ import space.anyi.serve.util.SecurityUtils;
 import java.util.List;
 
 @Tag(name = "ContestController", description = "比赛相关接口")
+@ApiResponses(value = {
+        @ApiResponse(responseCode = "400", description = "参数校验失败,返回体 code=400"),
+        @ApiResponse(responseCode = "401", description = "未认证或认证已过期"),
+        @ApiResponse(responseCode = "403", description = "无权限访问")
+})
 @RestController
 @RequestMapping("contest")
 public class ContestController {
@@ -28,9 +37,13 @@ public class ContestController {
     @Operation(summary = "分页查询比赛列表")
     @GetMapping
     public Response<PageVo<List<ContestVo>>> queryByPage(
+            @Parameter(description = "比赛标题,模糊查询")
             @RequestParam(defaultValue = "") String title,
+            @Parameter(description = "状态:pending-未开始,running-进行中,ended-已结束")
             @RequestParam(defaultValue = "") String status,
+            @Parameter(description = "页码")
             @NotNull(message = "页码不能为null") @RequestParam(defaultValue = "1") Integer pageNum,
+            @Parameter(description = "分页大小")
             @NotNull(message = "分页大小不能为null") @RequestParam(defaultValue = "10") Integer pageSize) {
         boolean onlyPublic = SecurityUtils.isAnonymous();
         Page<Contest> page = Page.of(pageNum, pageSize);
@@ -38,10 +51,10 @@ public class ContestController {
         return Response.ok(new PageVo<>(result.getTotal(), result.getRecords()));
     }
 
-    @Operation(summary = "查询比赛详情")
+    @Operation(summary = "查询比赛详情", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @GetMapping("{id}")
-    public Response<ContestVo> getDetail(@PathVariable Long id,
-                                         @RequestParam(required = false) String password) {
+    public Response<ContestVo> getDetail(@Parameter(description = "比赛ID") @PathVariable Long id,
+                                         @Parameter(description = "访问密码(密码型比赛需传入)") @RequestParam(required = false) String password) {
         Long userId = SecurityUtils.getCurrentUserId();
         ContestVo base = contestService.getBaseInfo(id);
         if ("password".equals(base.getType())) {
@@ -59,68 +72,68 @@ public class ContestController {
         return Response.ok(contestService.getDetail(id, userId));
     }
 
-    @Operation(summary = "查询比赛排行榜")
+    @Operation(summary = "查询比赛排行榜", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @GetMapping("{id}/ranking")
-    public Response<List<RankVo>> getRanking(@PathVariable Long id) {
+    public Response<List<RankVo>> getRanking(@Parameter(description = "比赛ID") @PathVariable Long id) {
         return Response.ok(contestService.getRanking(id));
     }
 
-    @Operation(summary = "新增比赛")
+    @Operation(summary = "新增比赛", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PostMapping
     public Response<Boolean> add(@Valid @RequestBody ContestDto dto) {
         return Response.ok(contestService.insert(dto, SecurityUtils.getCurrentUserId()));
     }
 
-    @Operation(summary = "修改比赛")
+    @Operation(summary = "修改比赛", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PutMapping
     public Response<Boolean> edit(@Valid @RequestBody ContestDto dto) {
         return Response.ok(contestService.update(dto));
     }
 
-    @Operation(summary = "批量删除比赛")
+    @Operation(summary = "批量删除比赛", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasRole('ROLE_admin')")
     @DeleteMapping
     public Response<Boolean> deleteByIds(@NotEmpty(message = "ID列表不能为空") @RequestParam List<Long> ids) {
         return Response.ok(contestService.deleteByIds(ids));
     }
 
-    @Operation(summary = "比赛添加题目")
+    @Operation(summary = "比赛添加题目", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PostMapping("{id}/problem")
-    public Response<Boolean> addProblem(@PathVariable Long id, @Valid @RequestBody ContestProblemDto dto) {
+    public Response<Boolean> addProblem(@Parameter(description = "比赛ID") @PathVariable Long id, @Valid @RequestBody ContestProblemDto dto) {
         return Response.ok(contestService.addProblem(id, dto));
     }
 
-    @Operation(summary = "修改比赛题目")
+    @Operation(summary = "修改比赛题目", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PutMapping("{id}/problem")
-    public Response<Boolean> updateProblem(@PathVariable Long id, @Valid @RequestBody ContestProblemDto dto) {
+    public Response<Boolean> updateProblem(@Parameter(description = "比赛ID") @PathVariable Long id, @Valid @RequestBody ContestProblemDto dto) {
         return Response.ok(contestService.updateProblem(id, dto));
     }
 
-    @Operation(summary = "比赛移除题目")
+    @Operation(summary = "比赛移除题目", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @DeleteMapping("{id}/problem")
-    public Response<Boolean> removeProblem(@PathVariable Long id,
-                                           @NotNull(message = "题目ID不能为空") @RequestParam Long problemId) {
+    public Response<Boolean> removeProblem(@Parameter(description = "比赛ID") @PathVariable Long id,
+                                           @Parameter(description = "题目ID") @NotNull(message = "题目ID不能为空") @RequestParam Long problemId) {
         return Response.ok(contestService.removeProblem(id, problemId));
     }
 
-    @Operation(summary = "报名比赛")
+    @Operation(summary = "报名比赛", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PostMapping("{id}/register")
-    public Response<Boolean> register(@PathVariable Long id, @Valid @RequestBody ContestRegisterDto dto) {
+    public Response<Boolean> register(@Parameter(description = "比赛ID") @PathVariable Long id, @Valid @RequestBody ContestRegisterDto dto) {
         if (!contestService.checkPassword(id, dto.getPassword())) {
             return Response.error("比赛密码错误");
         }
         return Response.ok(contestService.register(id, SecurityUtils.getCurrentUserId()));
     }
 
-    @Operation(summary = "查询比赛报名名单")
+    @Operation(summary = "查询比赛报名名单", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @GetMapping("{id}/registrations")
-    public Response<List<ContestRegistrationVo>> queryRegistrations(@PathVariable Long id) {
+    public Response<List<ContestRegistrationVo>> queryRegistrations(@Parameter(description = "比赛ID") @PathVariable Long id) {
         return Response.ok(contestService.queryRegistrations(id));
     }
 }

+ 32 - 17
src/main/java/space/anyi/serve/controller/ProblemController.java

@@ -2,6 +2,10 @@ package space.anyi.serve.controller;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
@@ -18,6 +22,11 @@ import space.anyi.serve.util.SecurityUtils;
 import java.util.List;
 
 @Tag(name = "ProblemController", description = "题目相关接口")
+@ApiResponses(value = {
+        @ApiResponse(responseCode = "400", description = "参数校验失败,返回体 code=400"),
+        @ApiResponse(responseCode = "401", description = "未认证或认证已过期"),
+        @ApiResponse(responseCode = "403", description = "无权限访问")
+})
 @RestController
 @RequestMapping("problem")
 public class ProblemController {
@@ -28,11 +37,17 @@ public class ProblemController {
     @Operation(summary = "分页查询题目列表")
     @GetMapping
     public Response<PageVo<List<ProblemVo>>> queryByPage(
+            @Parameter(description = "题目标题,模糊查询")
             @RequestParam(defaultValue = "") String title,
+            @Parameter(description = "难度:easy-简单,medium-中等,hard-困难")
             @RequestParam(defaultValue = "") String difficulty,
+            @Parameter(description = "分类ID")
             @RequestParam(required = false) Long categoryId,
+            @Parameter(description = "是否公开:1-公开,0-未公开")
             @RequestParam(required = false) Integer isPublic,
+            @Parameter(description = "页码")
             @NotNull(message = "页码不能为null") @RequestParam(defaultValue = "1") Integer pageNum,
+            @Parameter(description = "分页大小")
             @NotNull(message = "分页大小不能为null") @RequestParam(defaultValue = "10") Integer pageSize) {
         boolean onlyPublic = SecurityUtils.isAnonymous();
         Page<Problem> page = Page.of(pageNum, pageSize);
@@ -42,7 +57,7 @@ public class ProblemController {
 
     @Operation(summary = "查询题目详情")
     @GetMapping("{id}")
-    public Response<ProblemDetailVo> getDetail(@PathVariable Long id) {
+    public Response<ProblemDetailVo> getDetail(@Parameter(description = "题目ID") @PathVariable Long id) {
         ProblemDetailVo detail = problemService.getDetail(id);
         String role = SecurityUtils.getCurrentRole();
         boolean staff = "coach".equals(role) || "admin".equals(role);
@@ -52,75 +67,75 @@ public class ProblemController {
         return Response.ok(detail);
     }
 
-    @Operation(summary = "新增题目")
+    @Operation(summary = "新增题目", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PostMapping
     public Response<Boolean> add(@Valid @RequestBody ProblemDto dto) {
         return Response.ok(problemService.insert(dto, SecurityUtils.getCurrentUserId()));
     }
 
-    @Operation(summary = "修改题目")
+    @Operation(summary = "修改题目", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PutMapping
     public Response<Boolean> edit(@Valid @RequestBody ProblemDto dto) {
         return Response.ok(problemService.update(dto));
     }
 
-    @Operation(summary = "批量删除题目")
+    @Operation(summary = "批量删除题目", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasRole('ROLE_admin')")
     @DeleteMapping
-    public Response<Boolean> deleteByIds(@NotEmpty(message = "ID列表不能为空") @RequestParam List<Long> ids) {
+    public Response<Boolean> deleteByIds(@Parameter(description = "题目ID列表") @NotEmpty(message = "ID列表不能为空") @RequestParam List<Long> ids) {
         return Response.ok(problemService.deleteByIds(ids));
     }
 
-    @Operation(summary = "新增题目样例")
+    @Operation(summary = "新增题目样例", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PostMapping("{id}/case")
-    public Response<Boolean> addCase(@PathVariable Long id, @Valid @RequestBody ProblemCaseDto dto) {
+    public Response<Boolean> addCase(@Parameter(description = "题目ID") @PathVariable Long id, @Valid @RequestBody ProblemCaseDto dto) {
         dto.setProblemId(id);
         return Response.ok(problemService.addCase(dto));
     }
 
-    @Operation(summary = "修改题目样例")
+    @Operation(summary = "修改题目样例", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PutMapping("case")
     public Response<Boolean> updateCase(@Valid @RequestBody ProblemCaseDto dto) {
         return Response.ok(problemService.updateCase(dto));
     }
 
-    @Operation(summary = "删除题目样例")
+    @Operation(summary = "删除题目样例", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @DeleteMapping("case")
-    public Response<Boolean> deleteCase(@NotNull(message = "样例ID不能为空") @RequestParam Long id) {
+    public Response<Boolean> deleteCase(@Parameter(description = "样例ID") @NotNull(message = "样例ID不能为空") @RequestParam Long id) {
         return Response.ok(problemService.deleteCase(id));
     }
 
-    @Operation(summary = "新增题目评测用例")
+    @Operation(summary = "新增题目评测用例", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PostMapping("{id}/test")
-    public Response<Boolean> addTest(@PathVariable Long id, @Valid @RequestBody ProblemTestDto dto) {
+    public Response<Boolean> addTest(@Parameter(description = "题目ID") @PathVariable Long id, @Valid @RequestBody ProblemTestDto dto) {
         dto.setProblemId(id);
         return Response.ok(problemService.addTest(dto));
     }
 
-    @Operation(summary = "修改题目评测用例")
+    @Operation(summary = "修改题目评测用例", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @PutMapping("test")
     public Response<Boolean> updateTest(@Valid @RequestBody ProblemTestDto dto) {
         return Response.ok(problemService.updateTest(dto));
     }
 
-    @Operation(summary = "删除题目评测用例")
+    @Operation(summary = "删除题目评测用例", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @DeleteMapping("test")
-    public Response<Boolean> deleteTest(@NotNull(message = "评测用例ID不能为空") @RequestParam Long id) {
+    public Response<Boolean> deleteTest(@Parameter(description = "评测用例ID") @NotNull(message = "评测用例ID不能为空") @RequestParam Long id) {
         return Response.ok(problemService.deleteTest(id));
     }
 
-    @Operation(summary = "查询题目评测用例列表")
+    @Operation(summary = "查询题目评测用例列表", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_coach', 'ROLE_admin')")
     @GetMapping("{id}/test")
-    public Response<List<ProblemTestVo>> queryTests(@PathVariable Long id) {
+    public Response<List<ProblemTestVo>> queryTests(@Parameter(description = "题目ID") @PathVariable Long id) {
         return Response.ok(problemService.queryTests(id));
     }
 }

+ 28 - 2
src/main/java/space/anyi/serve/controller/UserController.java

@@ -1,6 +1,12 @@
 package space.anyi.serve.controller;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotBlank;
@@ -24,6 +30,12 @@ import java.util.stream.Collectors;
  * @author 杨逸
  * @since 2026-03-31 13:17:55
  */
+@Tag(name = "UserController", description = "用户管理")
+@ApiResponses(value = {
+        @ApiResponse(responseCode = "400", description = "参数校验失败,返回体 code=400"),
+        @ApiResponse(responseCode = "401", description = "未认证或认证已过期"),
+        @ApiResponse(responseCode = "403", description = "无权限访问")
+})
 @RestController
 @RequestMapping("user")
 public class UserController {
@@ -43,14 +55,21 @@ public class UserController {
      * @param pageSize
      * @return
      */
+    @Operation(summary = "分页查询用户信息", description = "按账号、用户名、角色、状态分页查询用户", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasRole('ROLE_admin')")
     @GetMapping
     public Response<PageVo<List<UserVo>>> queryByPage(
+            @Parameter(description = "账号,模糊查询")
             @NotNull @RequestParam(defaultValue = "") String account,
+            @Parameter(description = "用户名,模糊查询")
             @NotNull @RequestParam(defaultValue = "") String username,
+            @Parameter(description = "角色:user-用户,coach-教练,admin-管理员")
             @NotNull @RequestParam(defaultValue = "") String role,
+            @Parameter(description = "状态:1-启用,0-禁用")
             @NotNull @RequestParam(defaultValue = "1") Integer enable,
+            @Parameter(description = "页码")
             @NotNull(message = "页码不能为null") @RequestParam(defaultValue = "1") Integer pageNum,
+            @Parameter(description = "分页大小")
             @NotNull(message = "分页大小不能为null")@RequestParam(defaultValue = "10") Integer pageSize) {
         User user = new User();
         user.setAccount(account);
@@ -67,9 +86,10 @@ public class UserController {
      * @param id 主键
      * @return 单条数据
      */
+    @Operation(summary = "查询用户详情", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_admin', 'ROLE_user')")
     @GetMapping("{id}")
-    public Response<UserVo> queryById(@NotBlank(message = "用户ID不能为空") @PathVariable String id) {
+    public Response<UserVo> queryById(@Parameter(description = "用户ID") @NotBlank(message = "用户ID不能为空") @PathVariable String id) {
         User user = this.userService.queryById(Long.valueOf(id));
         return Response.ok(UserVo.form(user));
     }
@@ -80,6 +100,7 @@ public class UserController {
      * @param userDto 实体
      * @return 新增结果
      */
+    @Operation(summary = "新增用户", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasRole('ROLE_admin')")
     @PostMapping
     public Response<Boolean> add(@Valid@RequestBody UserDto userDto) {
@@ -94,6 +115,7 @@ public class UserController {
      * @param userDto 实体
      * @return 编辑结果
      */
+    @Operation(summary = "编辑用户", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasRole('ROLE_admin')")
     @PutMapping
     public Response<Boolean> edit(@Valid@RequestBody UserDto userDto) {
@@ -109,9 +131,10 @@ public class UserController {
      * @param ids 主键
      * @return 删除是否成功
      */
+    @Operation(summary = "删除用户", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasRole('ROLE_admin')")
     @DeleteMapping
-    public Response<Boolean> deleteById(@NotEmpty(message = "ID列表不能为空") @RequestParam List<String> ids) {
+    public Response<Boolean> deleteById(@Parameter(description = "用户ID列表") @NotEmpty(message = "ID列表不能为空") @RequestParam List<String> ids) {
         List<Long> list = ids.stream().map(Long::valueOf).toList();
         return Response.ok(this.userService.deleteById(list));
     }
@@ -121,6 +144,7 @@ public class UserController {
      * @param dto
      * @return
      */
+    @Operation(summary = "更新用户状态", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasRole('ROLE_admin')")
     @PutMapping("/updateStatus")
     public Response updateUserStatus(@Valid@RequestBody UpdateUserStatusDto dto){
@@ -135,6 +159,7 @@ public class UserController {
      * @param dto
      * @return
      */
+    @Operation(summary = "更新用户头像", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_admin', 'ROLE_user')")
     @PutMapping("/updateAvatar")
     public Response updateUserAvatar(@Valid@RequestBody UpdateUserAvatarDto dto){
@@ -149,6 +174,7 @@ public class UserController {
      * @param dto
      * @return
      */
+    @Operation(summary = "更新用户密码", security = @SecurityRequirement(name = "openApiSecurityScheme"))
     @PreAuthorize("hasAnyRole('ROLE_admin', 'ROLE_user')")
     @PutMapping("/updatePassword")
     public Response updatePassword(@Valid @RequestBody UpdateUserPasswordDto dto){

+ 1 - 1
src/main/java/space/anyi/serve/entity/auth/AuthTokenVo.java

@@ -17,7 +17,7 @@ public class AuthTokenVo {
     @Schema(description = "用户名")
     private String username;
 
-    @Schema(description = "角色")
+    @Schema(description = "角色:user-用户,coach-教练,admin-管理员", allowableValues = {"user", "coach", "admin"}, example = "user")
     private String role;
 
     public AuthTokenVo(String token, Long userId, String account, String username, String role) {

+ 1 - 1
src/main/java/space/anyi/serve/entity/contest/ContestDto.java

@@ -17,7 +17,7 @@ public class ContestDto {
     @Schema(description = "比赛描述")
     private String description;
     @NotBlank(message = "比赛类型不能为空")
-    @Schema(description = "比赛类型:public-公开,private-私有,password-密码访问")
+    @Schema(description = "比赛类型:public-公开,private-私有,password-密码访问", allowableValues = {"public", "private", "password"}, example = "public")
     private String type;
     @Schema(description = "访问密码(type为password时使用)")
     private String password;

+ 1 - 1
src/main/java/space/anyi/serve/entity/contest/ContestProblemVo.java

@@ -9,7 +9,7 @@ public class ContestProblemVo {
     private Long problemId;
     @Schema(description = "题目标题")
     private String title;
-    @Schema(description = "难度:easy-简单,medium-中等,hard-困难")
+    @Schema(description = "难度:easy-简单,medium-中等,hard-困难", allowableValues = {"easy", "medium", "hard"}, example = "easy")
     private String difficulty;
     @Schema(description = "题目在比赛中的显示序号")
     private Integer displayOrder;

+ 2 - 2
src/main/java/space/anyi/serve/entity/contest/ContestVo.java

@@ -14,13 +14,13 @@ public class ContestVo {
     private Long id;
     @Schema(description = "比赛标题")
     private String title;
-    @Schema(description = "比赛类型:public-公开,private-私有,password-密码访问")
+    @Schema(description = "比赛类型:public-公开,private-私有,password-密码访问", allowableValues = {"public", "private", "password"}, example = "public")
     private String type;
     @Schema(description = "开始时间")
     private LocalDateTime startDatetime;
     @Schema(description = "结束时间")
     private LocalDateTime endDatetime;
-    @Schema(description = "状态:pending-未开始,running-进行中,ended-已结束")
+    @Schema(description = "状态:pending-未开始,running-进行中,ended-已结束", allowableValues = {"pending", "running", "ended"}, example = "pending")
     private String status;
     @Schema(description = "创建时间")
     private LocalDateTime createdAt;

+ 4 - 4
src/main/java/space/anyi/serve/entity/problem/ProblemDto.java

@@ -22,18 +22,18 @@ public class ProblemDto {
     private String description;
     @NotNull(message = "时间限制不能为空")
     @Min(value = 1, message = "时间限制必须大于0")
-    @Schema(description = "时间限制(毫秒)")
+    @Schema(description = "时间限制(毫秒)", example = "1000")
     private Integer timeLimitMs;
     @NotNull(message = "内存限制不能为空")
     @Min(value = 1, message = "内存限制必须大于0")
-    @Schema(description = "内存限制(MB)")
+    @Schema(description = "内存限制(MB)", example = "256")
     private Integer memoryLimitMb;
     @NotNull(message = "是否公开不能为空")
     @Min(value = 0, message = "是否公开取值0或1")
     @Max(value = 1, message = "是否公开取值0或1")
-    @Schema(description = "是否公开题目,0-未公开,1-公开")
+    @Schema(description = "是否公开题目,0-未公开,1-公开", allowableValues = {"0", "1"}, example = "1")
     private Integer isPublic;
-    @Schema(description = "难度:easy-简单,medium-中等,hard-困难")
+    @Schema(description = "难度:easy-简单,medium-中等,hard-困难", allowableValues = {"easy", "medium", "hard"}, example = "easy")
     private String difficulty;
     @Schema(description = "题目分类ID列表")
     private List<Long> categoryIds;

+ 1 - 1
src/main/java/space/anyi/serve/entity/problem/ProblemTestDto.java

@@ -17,7 +17,7 @@ public class ProblemTestDto {
     private String output;
     @Schema(description = "判题顺序")
     private Integer sortOrder;
-    @Schema(description = "是否隐藏用例,0-不隐藏,1-隐藏")
+    @Schema(description = "是否隐藏用例,0-不隐藏,1-隐藏", allowableValues = {"0", "1"}, example = "1")
     private Integer isHidden;
 
     public Long getId() {

+ 1 - 1
src/main/java/space/anyi/serve/entity/problem/ProblemTestVo.java

@@ -19,7 +19,7 @@ public class ProblemTestVo {
     private String output;
     @Schema(description = "判题顺序")
     private Integer sortOrder;
-    @Schema(description = "是否隐藏用例,0-不隐藏,1-隐藏")
+    @Schema(description = "是否隐藏用例,0-不隐藏,1-隐藏", allowableValues = {"0", "1"}, example = "1")
     private Integer isHidden;
 
     public static ProblemTestVo from(ProblemTest problemTest) {

+ 4 - 4
src/main/java/space/anyi/serve/entity/problem/ProblemVo.java

@@ -14,13 +14,13 @@ public class ProblemVo {
     private Long id;
     @Schema(description = "题目标题")
     private String title;
-    @Schema(description = "难度:easy-简单,medium-中等,hard-困难")
+    @Schema(description = "难度:easy-简单,medium-中等,hard-困难", allowableValues = {"easy", "medium", "hard"}, example = "easy")
     private String difficulty;
-    @Schema(description = "时间限制(毫秒)")
+    @Schema(description = "时间限制(毫秒)", example = "1000")
     private Integer timeLimitMs;
-    @Schema(description = "内存限制(MB)")
+    @Schema(description = "内存限制(MB)", example = "256")
     private Integer memoryLimitMb;
-    @Schema(description = "是否公开题目,0-未公开,1-公开")
+    @Schema(description = "是否公开题目,0-未公开,1-公开", allowableValues = {"0", "1"}, example = "1")
     private Integer isPublic;
     @Schema(description = "总提交次数")
     private Long tryCount;

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

@@ -23,7 +23,7 @@ public class UpdateUserStatusDto {
     @NotNull(message = "用户状态不能为空")
     @Min(value = 0,message = "状态不能小于0")
     @Max(value = 1,message = "用户状态不能大于1")
-    @Schema(description = "更新后的状态;1:启用,0:禁用", minLength = 1)
+    @Schema(description = "更新后的状态;1:启用,0:禁用", minimum = "0", maximum = "1", allowableValues = {"0", "1"}, example = "1")
     private Integer enable;
 
     public String getId() {

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

@@ -38,7 +38,7 @@ public class UserDto {
     private String username;
     @NotBlank(message = "用户角色不能为空")
     @Size(min = 4,max = 32,message = "用户角色长度不能小于4且不能大于32")
-    @Schema(description = "用户角色",minLength = 4,maxLength = 32)
+    @Schema(description = "用户角色:user-用户,coach-教练,admin-管理员", minLength = 4, maxLength = 32, allowableValues = {"user", "coach", "admin"}, example = "user")
     private String role;
 
     public String getId() {

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

@@ -23,11 +23,11 @@ public class UserVo {
     private String account;
     @Schema(description = "用户名称")
     private String username;
-    @Schema(description = "用户角色")
+    @Schema(description = "用户角色:user-用户,coach-教练,admin-管理员", allowableValues = {"user", "coach", "admin"}, example = "user")
     private String role;
     @Schema(description = "用户头像地址")
     private String avatar;
-    @Schema(description = "用户状态;0:禁用,1:启用")
+    @Schema(description = "用户状态;0:禁用,1:启用", allowableValues = {"0", "1"}, example = "1")
     private Integer enable = 1;
 
     public String getId() {