提交swagger 对象接口补充

This commit is contained in:
wh
2026-04-15 15:28:11 +08:00
parent 73a13fd16d
commit f6ba09521a
41 changed files with 674 additions and 139 deletions

View File

@@ -396,4 +396,6 @@ docker build -t label-backend:latest .
- 固定结构请求体禁止继续使用匿名 `Map<String, Object>``Map<String, String>`,必须定义 DTO 并补齐 `@Schema` 字段说明 - 固定结构请求体禁止继续使用匿名 `Map<String, Object>``Map<String, String>`,必须定义 DTO 并补齐 `@Schema` 字段说明
- 固定结构响应应优先使用明确 DTO或至少为 Swagger 暴露对象补齐字段级 `@Schema` 注解 - 固定结构响应应优先使用明确 DTO或至少为 Swagger 暴露对象补齐字段级 `@Schema` 注解
- 路径参数、查询参数、请求体、分页包装和通用返回体都必须维护可读的 OpenAPI 文档说明 - 路径参数、查询参数、请求体、分页包装和通用返回体都必须维护可读的 OpenAPI 文档说明
- 需要保持历史兼容的原始 JSON 字符串请求体可以继续使用 `String`,但必须在 Swagger `@RequestBody` 中说明完整 JSON body 的提交方式和兼容原因
- 修改 Controller 参数、请求 DTO、响应 DTO 或对外实体后,必须运行 `mvn -Dtest=OpenApiAnnotationTest test`,确保 Swagger 参数名称、类型和含义没有回退
- 目录、配置、打包方式变化后README、设计文档和部署说明必须同步更新 - 目录、配置、打包方式变化后README、设计文档和部署说明必须同步更新

View File

@@ -30,7 +30,6 @@
- `src/main/java/com/label/dto/SysConfigUpdateRequest.java` - `src/main/java/com/label/dto/SysConfigUpdateRequest.java`
- `src/main/java/com/label/dto/SysConfigItemResponse.java` - `src/main/java/com/label/dto/SysConfigItemResponse.java`
- `src/main/java/com/label/dto/DynamicJsonResponse.java` - `src/main/java/com/label/dto/DynamicJsonResponse.java`
- `src/main/java/com/label/dto/RawJsonUpdateRequest.java`
- `src/main/java/com/label/dto/FinetuneJobResponse.java` - `src/main/java/com/label/dto/FinetuneJobResponse.java`
### Modify ### Modify
@@ -142,7 +141,6 @@ private static final List<Class<?>> DTOS = List.of(
SysConfigUpdateRequest.class, SysConfigUpdateRequest.class,
SysConfigItemResponse.class, SysConfigItemResponse.class,
DynamicJsonResponse.class, DynamicJsonResponse.class,
RawJsonUpdateRequest.class,
FinetuneJobResponse.class FinetuneJobResponse.class
); );
@@ -302,14 +300,6 @@ public class RejectRequest {
```java ```java
@Data @Data
@Schema(description = "原始 JSON 更新请求")
public class RawJsonUpdateRequest {
@Schema(description = "完整 JSON 字符串;用于整体覆盖提取或问答标注结果", example = "{\"items\":[]}")
private String body;
}
```
```java
@Data @Data
@Schema(description = "动态业务 JSON 响应") @Schema(description = "动态业务 JSON 响应")
public class DynamicJsonResponse { public class DynamicJsonResponse {
@@ -710,7 +700,7 @@ return Result.success(response);
Use the same pattern for QA. Use the same pattern for QA.
- [ ] **Step 3: Use `RawJsonUpdateRequest` for raw JSON update bodies only if existing clients can send `{"body":"..."}`** - [ ] **Step 3: Keep raw JSON update request bodies as raw strings**
Do not change `PUT /api/extraction/{taskId}` or `PUT /api/qa/{taskId}` from raw string request body unless compatibility is explicitly approved. Do not change `PUT /api/extraction/{taskId}` or `PUT /api/qa/{taskId}` from raw string request body unless compatibility is explicitly approved.
@@ -805,13 +795,14 @@ git diff -- src/main/java src/test/java README.md docs/superpowers
Expected: diff only contains Swagger annotations, DTO additions, DTO request binding updates, and matching tests. Expected: diff only contains Swagger annotations, DTO additions, DTO request binding updates, and matching tests.
- [ ] **Step 4: Commit implementation** - [ ] **Step 4: Keep implementation uncommitted for review**
Do not commit unless the user explicitly asks for it.
Run: Run:
```bash ```bash
git add src/main/java src/test/java README.md docs/superpowers git status --short
git commit -m "docs: document backend api parameters in swagger"
``` ```
Expected: commit succeeds. Expected: implementation remains available in the worktree for review.

View File

@@ -1,14 +1,23 @@
package com.label.common.result; package com.label.common.result;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.List;
@Data @Data
@Schema(description = "分页响应")
public class PageResult<T> { public class PageResult<T> {
@Schema(description = "当前页数据列表")
private List<T> items; private List<T> items;
@Schema(description = "总条数", example = "123")
private long total; private long total;
@Schema(description = "页码(从 1 开始)", example = "1")
private int page; private int page;
@Schema(description = "每页条数", example = "20")
private int pageSize; private int pageSize;
public static <T> PageResult<T> of(List<T> items, long total, int page, int pageSize) { public static <T> PageResult<T> of(List<T> items, long total, int page, int pageSize) {

View File

@@ -1,11 +1,18 @@
package com.label.common.result; package com.label.common.result;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@Data @Data
@Schema(description = "通用响应包装")
public class Result<T> { public class Result<T> {
@Schema(description = "业务状态码", example = "SUCCESS")
private String code; private String code;
@Schema(description = "响应数据")
private T data; private T data;
@Schema(description = "提示信息", example = "操作成功")
private String message; private String message;
public static <T> Result<T> success(T data) { public static <T> Result<T> success(T data) {

View File

@@ -34,7 +34,11 @@ public class AuthController {
*/ */
@Operation(summary = "用户登录,返回 Bearer Token") @Operation(summary = "用户登录,返回 Bearer Token")
@PostMapping("/login") @PostMapping("/login")
public Result<LoginResponse> login(@RequestBody LoginRequest request) { public Result<LoginResponse> login(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "用户登录请求体",
required = true)
@RequestBody LoginRequest request) {
return Result.success(authService.login(request)); return Result.success(authService.login(request));
} }

View File

@@ -3,9 +3,13 @@ package com.label.controller;
import com.label.annotation.RequireRole; import com.label.annotation.RequireRole;
import com.label.common.result.PageResult; import com.label.common.result.PageResult;
import com.label.common.result.Result; import com.label.common.result.Result;
import com.label.dto.CompanyCreateRequest;
import com.label.dto.CompanyStatusUpdateRequest;
import com.label.dto.CompanyUpdateRequest;
import com.label.entity.SysCompany; import com.label.entity.SysCompany;
import com.label.service.CompanyService; import com.label.service.CompanyService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -20,8 +24,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@Tag(name = "公司管理", description = "租户公司增删改查") @Tag(name = "公司管理", description = "租户公司增删改查")
@RestController @RestController
@RequestMapping("/api/companies") @RequestMapping("/api/companies")
@@ -34,8 +36,11 @@ public class CompanyController {
@GetMapping @GetMapping
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<PageResult<SysCompany>> list( public Result<PageResult<SysCompany>> list(
@Parameter(description = "页码,从 1 开始", example = "1")
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@Parameter(description = "每页条数", example = "20")
@RequestParam(defaultValue = "20") int pageSize, @RequestParam(defaultValue = "20") int pageSize,
@Parameter(description = "公司状态过滤可选值ACTIVE、DISABLED", example = "ACTIVE")
@RequestParam(required = false) String status) { @RequestParam(required = false) String status) {
return Result.success(companyService.list(page, pageSize, status)); return Result.success(companyService.list(page, pageSize, status));
} }
@@ -44,29 +49,47 @@ public class CompanyController {
@PostMapping @PostMapping
@RequireRole("ADMIN") @RequireRole("ADMIN")
@ResponseStatus(HttpStatus.CREATED) @ResponseStatus(HttpStatus.CREATED)
public Result<SysCompany> create(@RequestBody Map<String, String> body) { public Result<SysCompany> create(
return Result.success(companyService.create(body.get("companyName"), body.get("companyCode"))); @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "创建公司请求体",
required = true)
@RequestBody CompanyCreateRequest body) {
return Result.success(companyService.create(body.getCompanyName(), body.getCompanyCode()));
} }
@Operation(summary = "更新公司信息") @Operation(summary = "更新公司信息")
@PutMapping("/{id}") @PutMapping("/{id}")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<SysCompany> update(@PathVariable Long id, @RequestBody Map<String, String> body) { public Result<SysCompany> update(
return Result.success(companyService.update(id, body.get("companyName"), body.get("companyCode"))); @Parameter(description = "公司 ID", example = "100")
@PathVariable Long id,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "更新公司信息请求体",
required = true)
@RequestBody CompanyUpdateRequest body) {
return Result.success(companyService.update(id, body.getCompanyName(), body.getCompanyCode()));
} }
@Operation(summary = "更新公司状态") @Operation(summary = "更新公司状态")
@PutMapping("/{id}/status") @PutMapping("/{id}/status")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<Void> updateStatus(@PathVariable Long id, @RequestBody Map<String, String> body) { public Result<Void> updateStatus(
companyService.updateStatus(id, body.get("status")); @Parameter(description = "公司 ID", example = "100")
@PathVariable Long id,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "更新公司状态请求体",
required = true)
@RequestBody CompanyStatusUpdateRequest body) {
companyService.updateStatus(id, body.getStatus());
return Result.success(null); return Result.success(null);
} }
@Operation(summary = "删除公司") @Operation(summary = "删除公司")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<Void> delete(@PathVariable Long id) { public Result<Void> delete(
@Parameter(description = "公司 ID", example = "100")
@PathVariable Long id) {
companyService.delete(id); companyService.delete(id);
return Result.success(null); return Result.success(null);
} }

View File

@@ -4,11 +4,14 @@ import com.label.annotation.RequireRole;
import com.label.common.auth.TokenPrincipal; import com.label.common.auth.TokenPrincipal;
import com.label.common.result.PageResult; import com.label.common.result.PageResult;
import com.label.common.result.Result; import com.label.common.result.Result;
import com.label.dto.ExportBatchCreateRequest;
import com.label.dto.FinetuneJobResponse;
import com.label.entity.TrainingDataset; import com.label.entity.TrainingDataset;
import com.label.entity.ExportBatch; import com.label.entity.ExportBatch;
import com.label.service.ExportService; import com.label.service.ExportService;
import com.label.service.FinetuneService; import com.label.service.FinetuneService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -34,9 +37,13 @@ public class ExportController {
@GetMapping("/api/training/samples") @GetMapping("/api/training/samples")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<PageResult<TrainingDataset>> listSamples( public Result<PageResult<TrainingDataset>> listSamples(
@Parameter(description = "页码,从 1 开始", example = "1")
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@Parameter(description = "每页条数", example = "20")
@RequestParam(defaultValue = "20") int pageSize, @RequestParam(defaultValue = "20") int pageSize,
@Parameter(description = "样本类型过滤可选值EXTRACTION、QA_GENERATION", example = "EXTRACTION")
@RequestParam(required = false) String sampleType, @RequestParam(required = false) String sampleType,
@Parameter(description = "是否已导出过滤", example = "false")
@RequestParam(required = false) Boolean exported, @RequestParam(required = false) Boolean exported,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(exportService.listSamples(page, pageSize, sampleType, exported, principal(request))); return Result.success(exportService.listSamples(page, pageSize, sampleType, exported, principal(request)));
@@ -47,32 +54,35 @@ public class ExportController {
@PostMapping("/api/export/batch") @PostMapping("/api/export/batch")
@RequireRole("ADMIN") @RequireRole("ADMIN")
@ResponseStatus(HttpStatus.CREATED) @ResponseStatus(HttpStatus.CREATED)
public Result<ExportBatch> createBatch(@RequestBody Map<String, Object> body, public Result<ExportBatch> createBatch(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "创建训练数据导出批次请求体",
required = true)
@RequestBody ExportBatchCreateRequest body,
HttpServletRequest request) { HttpServletRequest request) {
@SuppressWarnings("unchecked") return Result.success(exportService.createBatch(body.getSampleIds(), principal(request)));
List<Object> rawIds = (List<Object>) body.get("sampleIds");
List<Long> sampleIds = rawIds.stream()
.map(id -> Long.parseLong(id.toString()))
.toList();
return Result.success(exportService.createBatch(sampleIds, principal(request)));
} }
/** POST /api/export/{batchId}/finetune — 提交微调任务 */ /** POST /api/export/{batchId}/finetune — 提交微调任务 */
@Operation(summary = "提交微调任务") @Operation(summary = "提交微调任务")
@PostMapping("/api/export/{batchId}/finetune") @PostMapping("/api/export/{batchId}/finetune")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<Map<String, Object>> triggerFinetune(@PathVariable Long batchId, public Result<FinetuneJobResponse> triggerFinetune(
@Parameter(description = "导出批次 ID", example = "501")
@PathVariable Long batchId,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(finetuneService.trigger(batchId, principal(request))); return Result.success(toFinetuneJobResponse(finetuneService.trigger(batchId, principal(request))));
} }
/** GET /api/export/{batchId}/status — 查询微调状态 */ /** GET /api/export/{batchId}/status — 查询微调状态 */
@Operation(summary = "查询微调状态") @Operation(summary = "查询微调状态")
@GetMapping("/api/export/{batchId}/status") @GetMapping("/api/export/{batchId}/status")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<Map<String, Object>> getFinetuneStatus(@PathVariable Long batchId, public Result<FinetuneJobResponse> getFinetuneStatus(
@Parameter(description = "导出批次 ID", example = "501")
@PathVariable Long batchId,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(finetuneService.getStatus(batchId, principal(request))); return Result.success(toFinetuneJobResponse(finetuneService.getStatus(batchId, principal(request))));
} }
/** GET /api/export/list — 分页查询导出批次列表 */ /** GET /api/export/list — 分页查询导出批次列表 */
@@ -80,12 +90,36 @@ public class ExportController {
@GetMapping("/api/export/list") @GetMapping("/api/export/list")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<PageResult<ExportBatch>> listBatches( public Result<PageResult<ExportBatch>> listBatches(
@Parameter(description = "页码,从 1 开始", example = "1")
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@Parameter(description = "每页条数", example = "20")
@RequestParam(defaultValue = "20") int pageSize, @RequestParam(defaultValue = "20") int pageSize,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(exportService.listBatches(page, pageSize, principal(request))); return Result.success(exportService.listBatches(page, pageSize, principal(request)));
} }
private FinetuneJobResponse toFinetuneJobResponse(Map<String, Object> values) {
FinetuneJobResponse response = new FinetuneJobResponse();
response.setBatchId(asLong(values.get("batchId")));
response.setGlmJobId(asString(values.get("glmJobId")));
response.setFinetuneStatus(asString(values.get("finetuneStatus")));
response.setProgress(asInteger(values.get("progress")));
response.setErrorMessage(asString(values.get("errorMessage")));
return response;
}
private Long asLong(Object value) {
return value == null ? null : Long.parseLong(value.toString());
}
private Integer asInteger(Object value) {
return value == null ? null : Integer.parseInt(value.toString());
}
private String asString(Object value) {
return value == null ? null : value.toString();
}
private TokenPrincipal principal(HttpServletRequest request) { private TokenPrincipal principal(HttpServletRequest request) {
return (TokenPrincipal) request.getAttribute("__token_principal__"); return (TokenPrincipal) request.getAttribute("__token_principal__");
} }

View File

@@ -3,8 +3,10 @@ package com.label.controller;
import com.label.annotation.RequireRole; import com.label.annotation.RequireRole;
import com.label.common.auth.TokenPrincipal; import com.label.common.auth.TokenPrincipal;
import com.label.common.result.Result; import com.label.common.result.Result;
import com.label.dto.RejectRequest;
import com.label.service.ExtractionService; import com.label.service.ExtractionService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -27,7 +29,9 @@ public class ExtractionController {
@Operation(summary = "获取提取标注结果") @Operation(summary = "获取提取标注结果")
@GetMapping("/{taskId}") @GetMapping("/{taskId}")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<Map<String, Object>> getResult(@PathVariable Long taskId, public Result<Map<String, Object>> getResult(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long taskId,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(extractionService.getResult(taskId, principal(request))); return Result.success(extractionService.getResult(taskId, principal(request)));
} }
@@ -36,7 +40,12 @@ public class ExtractionController {
@Operation(summary = "更新提取标注结果") @Operation(summary = "更新提取标注结果")
@PutMapping("/{taskId}") @PutMapping("/{taskId}")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<Void> updateResult(@PathVariable Long taskId, public Result<Void> updateResult(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long taskId,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "完整提取标注结果 JSON 字符串,保持原始 JSON body 直接提交",
required = true)
@RequestBody String resultJson, @RequestBody String resultJson,
HttpServletRequest request) { HttpServletRequest request) {
extractionService.updateResult(taskId, resultJson, principal(request)); extractionService.updateResult(taskId, resultJson, principal(request));
@@ -47,7 +56,9 @@ public class ExtractionController {
@Operation(summary = "提交提取标注结果") @Operation(summary = "提交提取标注结果")
@PostMapping("/{taskId}/submit") @PostMapping("/{taskId}/submit")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<Void> submit(@PathVariable Long taskId, public Result<Void> submit(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long taskId,
HttpServletRequest request) { HttpServletRequest request) {
extractionService.submit(taskId, principal(request)); extractionService.submit(taskId, principal(request));
return Result.success(null); return Result.success(null);
@@ -57,7 +68,9 @@ public class ExtractionController {
@Operation(summary = "审批通过提取结果") @Operation(summary = "审批通过提取结果")
@PostMapping("/{taskId}/approve") @PostMapping("/{taskId}/approve")
@RequireRole("REVIEWER") @RequireRole("REVIEWER")
public Result<Void> approve(@PathVariable Long taskId, public Result<Void> approve(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long taskId,
HttpServletRequest request) { HttpServletRequest request) {
extractionService.approve(taskId, principal(request)); extractionService.approve(taskId, principal(request));
return Result.success(null); return Result.success(null);
@@ -67,10 +80,15 @@ public class ExtractionController {
@Operation(summary = "驳回提取结果") @Operation(summary = "驳回提取结果")
@PostMapping("/{taskId}/reject") @PostMapping("/{taskId}/reject")
@RequireRole("REVIEWER") @RequireRole("REVIEWER")
public Result<Void> reject(@PathVariable Long taskId, public Result<Void> reject(
@RequestBody Map<String, String> body, @Parameter(description = "任务 ID", example = "1001")
@PathVariable Long taskId,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "驳回提取结果请求体",
required = true)
@RequestBody RejectRequest body,
HttpServletRequest request) { HttpServletRequest request) {
String reason = body != null ? body.get("reason") : null; String reason = body != null ? body.getReason() : null;
extractionService.reject(taskId, reason, principal(request)); extractionService.reject(taskId, reason, principal(request));
return Result.success(null); return Result.success(null);
} }

View File

@@ -3,8 +3,10 @@ package com.label.controller;
import com.label.annotation.RequireRole; import com.label.annotation.RequireRole;
import com.label.common.auth.TokenPrincipal; import com.label.common.auth.TokenPrincipal;
import com.label.common.result.Result; import com.label.common.result.Result;
import com.label.dto.RejectRequest;
import com.label.service.QaService; import com.label.service.QaService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -27,7 +29,9 @@ public class QaController {
@Operation(summary = "获取候选问答对") @Operation(summary = "获取候选问答对")
@GetMapping("/{taskId}") @GetMapping("/{taskId}")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<Map<String, Object>> getResult(@PathVariable Long taskId, public Result<Map<String, Object>> getResult(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long taskId,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(qaService.getResult(taskId, principal(request))); return Result.success(qaService.getResult(taskId, principal(request)));
} }
@@ -36,7 +40,12 @@ public class QaController {
@Operation(summary = "更新候选问答对") @Operation(summary = "更新候选问答对")
@PutMapping("/{taskId}") @PutMapping("/{taskId}")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<Void> updateResult(@PathVariable Long taskId, public Result<Void> updateResult(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long taskId,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "完整问答标注结果 JSON 字符串,保持原始 JSON body 直接提交",
required = true)
@RequestBody String body, @RequestBody String body,
HttpServletRequest request) { HttpServletRequest request) {
qaService.updateResult(taskId, body, principal(request)); qaService.updateResult(taskId, body, principal(request));
@@ -47,7 +56,9 @@ public class QaController {
@Operation(summary = "提交问答对") @Operation(summary = "提交问答对")
@PostMapping("/{taskId}/submit") @PostMapping("/{taskId}/submit")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<Void> submit(@PathVariable Long taskId, public Result<Void> submit(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long taskId,
HttpServletRequest request) { HttpServletRequest request) {
qaService.submit(taskId, principal(request)); qaService.submit(taskId, principal(request));
return Result.success(null); return Result.success(null);
@@ -57,7 +68,9 @@ public class QaController {
@Operation(summary = "审批通过问答对") @Operation(summary = "审批通过问答对")
@PostMapping("/{taskId}/approve") @PostMapping("/{taskId}/approve")
@RequireRole("REVIEWER") @RequireRole("REVIEWER")
public Result<Void> approve(@PathVariable Long taskId, public Result<Void> approve(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long taskId,
HttpServletRequest request) { HttpServletRequest request) {
qaService.approve(taskId, principal(request)); qaService.approve(taskId, principal(request));
return Result.success(null); return Result.success(null);
@@ -67,10 +80,15 @@ public class QaController {
@Operation(summary = "驳回答案对") @Operation(summary = "驳回答案对")
@PostMapping("/{taskId}/reject") @PostMapping("/{taskId}/reject")
@RequireRole("REVIEWER") @RequireRole("REVIEWER")
public Result<Void> reject(@PathVariable Long taskId, public Result<Void> reject(
@RequestBody Map<String, String> body, @Parameter(description = "任务 ID", example = "1001")
@PathVariable Long taskId,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "驳回问答结果请求体",
required = true)
@RequestBody RejectRequest body,
HttpServletRequest request) { HttpServletRequest request) {
String reason = body != null ? body.get("reason") : null; String reason = body != null ? body.getReason() : null;
qaService.reject(taskId, reason, principal(request)); qaService.reject(taskId, reason, principal(request));
return Result.success(null); return Result.success(null);
} }

View File

@@ -7,6 +7,7 @@ import com.label.common.result.Result;
import com.label.dto.SourceResponse; import com.label.dto.SourceResponse;
import com.label.service.SourceService; import com.label.service.SourceService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -38,7 +39,9 @@ public class SourceController {
@RequireRole("UPLOADER") @RequireRole("UPLOADER")
@ResponseStatus(HttpStatus.CREATED) @ResponseStatus(HttpStatus.CREATED)
public Result<SourceResponse> upload( public Result<SourceResponse> upload(
@Parameter(description = "上传文件,支持文本、图片、视频", required = true)
@RequestParam("file") MultipartFile file, @RequestParam("file") MultipartFile file,
@Parameter(description = "资料类型可选值text、image、video", example = "text", required = true)
@RequestParam("dataType") String dataType, @RequestParam("dataType") String dataType,
HttpServletRequest request) { HttpServletRequest request) {
TokenPrincipal principal = (TokenPrincipal) request.getAttribute("__token_principal__"); TokenPrincipal principal = (TokenPrincipal) request.getAttribute("__token_principal__");
@@ -53,9 +56,13 @@ public class SourceController {
@GetMapping("/list") @GetMapping("/list")
@RequireRole("UPLOADER") @RequireRole("UPLOADER")
public Result<PageResult<SourceResponse>> list( public Result<PageResult<SourceResponse>> list(
@Parameter(description = "页码,从 1 开始", example = "1")
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@Parameter(description = "每页条数", example = "20")
@RequestParam(defaultValue = "20") int pageSize, @RequestParam(defaultValue = "20") int pageSize,
@Parameter(description = "资料类型过滤可选值text、image、video", example = "text")
@RequestParam(required = false) String dataType, @RequestParam(required = false) String dataType,
@Parameter(description = "资料状态过滤", example = "PENDING")
@RequestParam(required = false) String status, @RequestParam(required = false) String status,
HttpServletRequest request) { HttpServletRequest request) {
TokenPrincipal principal = (TokenPrincipal) request.getAttribute("__token_principal__"); TokenPrincipal principal = (TokenPrincipal) request.getAttribute("__token_principal__");
@@ -68,7 +75,9 @@ public class SourceController {
@Operation(summary = "查询资料详情") @Operation(summary = "查询资料详情")
@GetMapping("/{id}") @GetMapping("/{id}")
@RequireRole("UPLOADER") @RequireRole("UPLOADER")
public Result<SourceResponse> findById(@PathVariable Long id) { public Result<SourceResponse> findById(
@Parameter(description = "资料 ID", example = "1001")
@PathVariable Long id) {
return Result.success(sourceService.findById(id)); return Result.success(sourceService.findById(id));
} }
@@ -79,7 +88,10 @@ public class SourceController {
@Operation(summary = "删除资料") @Operation(summary = "删除资料")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<Void> delete(@PathVariable Long id, HttpServletRequest request) { public Result<Void> delete(
@Parameter(description = "资料 ID", example = "1001")
@PathVariable Long id,
HttpServletRequest request) {
TokenPrincipal principal = (TokenPrincipal) request.getAttribute("__token_principal__"); TokenPrincipal principal = (TokenPrincipal) request.getAttribute("__token_principal__");
sourceService.delete(id, principal.getCompanyId()); sourceService.delete(id, principal.getCompanyId());
return Result.success(null); return Result.success(null);

View File

@@ -3,9 +3,12 @@ package com.label.controller;
import com.label.annotation.RequireRole; import com.label.annotation.RequireRole;
import com.label.common.auth.TokenPrincipal; import com.label.common.auth.TokenPrincipal;
import com.label.common.result.Result; import com.label.common.result.Result;
import com.label.dto.SysConfigItemResponse;
import com.label.dto.SysConfigUpdateRequest;
import com.label.entity.SysConfig; import com.label.entity.SysConfig;
import com.label.service.SysConfigService; import com.label.service.SysConfigService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -37,9 +40,11 @@ public class SysConfigController {
@Operation(summary = "查询合并后的系统配置") @Operation(summary = "查询合并后的系统配置")
@GetMapping("/api/config") @GetMapping("/api/config")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<List<Map<String, Object>>> listConfig(HttpServletRequest request) { public Result<List<SysConfigItemResponse>> listConfig(HttpServletRequest request) {
TokenPrincipal principal = principal(request); TokenPrincipal principal = principal(request);
return Result.success(sysConfigService.list(principal.getCompanyId())); return Result.success(sysConfigService.list(principal.getCompanyId()).stream()
.map(this::toConfigItemResponse)
.toList());
} }
/** /**
@@ -50,14 +55,36 @@ public class SysConfigController {
@Operation(summary = "更新或创建公司专属配置") @Operation(summary = "更新或创建公司专属配置")
@PutMapping("/api/config/{key}") @PutMapping("/api/config/{key}")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<SysConfig> updateConfig(@PathVariable String key, public Result<SysConfig> updateConfig(
@RequestBody Map<String, String> body, @Parameter(description = "系统配置键可选值token_ttl_seconds、model_default、video_frame_interval", example = "model_default")
@PathVariable String key,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "系统配置更新请求体",
required = true)
@RequestBody SysConfigUpdateRequest body,
HttpServletRequest request) { HttpServletRequest request) {
String value = body.get("value");
String description = body.get("description");
TokenPrincipal principal = principal(request); TokenPrincipal principal = principal(request);
return Result.success( return Result.success(
sysConfigService.update(key, value, description, principal.getCompanyId())); sysConfigService.update(key, body.getValue(), body.getDescription(), principal.getCompanyId()));
}
private SysConfigItemResponse toConfigItemResponse(Map<String, Object> item) {
SysConfigItemResponse response = new SysConfigItemResponse();
response.setId(asLong(item.get("id")));
response.setConfigKey(asString(item.get("configKey")));
response.setConfigValue(asString(item.get("configValue")));
response.setDescription(asString(item.get("description")));
response.setScope(asString(item.get("scope")));
response.setCompanyId(asLong(item.get("companyId")));
return response;
}
private Long asLong(Object value) {
return value == null ? null : Long.parseLong(value.toString());
}
private String asString(Object value) {
return value == null ? null : value.toString();
} }
private TokenPrincipal principal(HttpServletRequest request) { private TokenPrincipal principal(HttpServletRequest request) {

View File

@@ -4,17 +4,18 @@ import com.label.annotation.RequireRole;
import com.label.common.auth.TokenPrincipal; import com.label.common.auth.TokenPrincipal;
import com.label.common.result.PageResult; import com.label.common.result.PageResult;
import com.label.common.result.Result; import com.label.common.result.Result;
import com.label.dto.CreateTaskRequest;
import com.label.dto.TaskReassignRequest;
import com.label.dto.TaskResponse; import com.label.dto.TaskResponse;
import com.label.service.TaskClaimService; import com.label.service.TaskClaimService;
import com.label.service.TaskService; import com.label.service.TaskService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Map;
/** /**
* 任务管理接口10 个端点)。 * 任务管理接口10 个端点)。
*/ */
@@ -32,7 +33,9 @@ public class TaskController {
@GetMapping("/pool") @GetMapping("/pool")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<PageResult<TaskResponse>> getPool( public Result<PageResult<TaskResponse>> getPool(
@Parameter(description = "页码,从 1 开始", example = "1")
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@Parameter(description = "每页条数", example = "20")
@RequestParam(defaultValue = "20") int pageSize, @RequestParam(defaultValue = "20") int pageSize,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(taskService.getPool(page, pageSize, principal(request))); return Result.success(taskService.getPool(page, pageSize, principal(request)));
@@ -43,8 +46,11 @@ public class TaskController {
@GetMapping("/mine") @GetMapping("/mine")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<PageResult<TaskResponse>> getMine( public Result<PageResult<TaskResponse>> getMine(
@Parameter(description = "页码,从 1 开始", example = "1")
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@Parameter(description = "每页条数", example = "20")
@RequestParam(defaultValue = "20") int pageSize, @RequestParam(defaultValue = "20") int pageSize,
@Parameter(description = "任务状态过滤可选值UNCLAIMED、IN_PROGRESS、SUBMITTED、APPROVED、REJECTED", example = "IN_PROGRESS")
@RequestParam(required = false) String status, @RequestParam(required = false) String status,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(taskService.getMine(page, pageSize, status, principal(request))); return Result.success(taskService.getMine(page, pageSize, status, principal(request)));
@@ -55,8 +61,11 @@ public class TaskController {
@GetMapping("/pending-review") @GetMapping("/pending-review")
@RequireRole("REVIEWER") @RequireRole("REVIEWER")
public Result<PageResult<TaskResponse>> getPendingReview( public Result<PageResult<TaskResponse>> getPendingReview(
@Parameter(description = "页码,从 1 开始", example = "1")
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@Parameter(description = "每页条数", example = "20")
@RequestParam(defaultValue = "20") int pageSize, @RequestParam(defaultValue = "20") int pageSize,
@Parameter(description = "任务类型过滤可选值EXTRACTION、QA_GENERATION", example = "EXTRACTION")
@RequestParam(required = false) String taskType) { @RequestParam(required = false) String taskType) {
return Result.success(taskService.getPendingReview(page, pageSize, taskType)); return Result.success(taskService.getPendingReview(page, pageSize, taskType));
} }
@@ -66,9 +75,13 @@ public class TaskController {
@GetMapping @GetMapping
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<PageResult<TaskResponse>> getAll( public Result<PageResult<TaskResponse>> getAll(
@Parameter(description = "页码,从 1 开始", example = "1")
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@Parameter(description = "每页条数", example = "20")
@RequestParam(defaultValue = "20") int pageSize, @RequestParam(defaultValue = "20") int pageSize,
@Parameter(description = "任务状态过滤可选值UNCLAIMED、IN_PROGRESS、SUBMITTED、APPROVED、REJECTED", example = "SUBMITTED")
@RequestParam(required = false) String status, @RequestParam(required = false) String status,
@Parameter(description = "任务类型过滤可选值EXTRACTION、QA_GENERATION", example = "QA_GENERATION")
@RequestParam(required = false) String taskType) { @RequestParam(required = false) String taskType) {
return Result.success(taskService.getAll(page, pageSize, status, taskType)); return Result.success(taskService.getAll(page, pageSize, status, taskType));
} }
@@ -77,20 +90,24 @@ public class TaskController {
@Operation(summary = "管理员创建任务") @Operation(summary = "管理员创建任务")
@PostMapping @PostMapping
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<TaskResponse> createTask(@RequestBody Map<String, Object> body, public Result<TaskResponse> createTask(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "创建标注任务请求体",
required = true)
@RequestBody CreateTaskRequest body,
HttpServletRequest request) { HttpServletRequest request) {
Long sourceId = Long.parseLong(body.get("sourceId").toString());
String taskType = body.get("taskType").toString();
TokenPrincipal principal = principal(request); TokenPrincipal principal = principal(request);
return Result.success(taskService.toPublicResponse( return Result.success(taskService.toPublicResponse(
taskService.createTask(sourceId, taskType, principal.getCompanyId()))); taskService.createTask(body.getSourceId(), body.getTaskType(), principal.getCompanyId())));
} }
/** GET /api/tasks/{id} — 查询任务详情 */ /** GET /api/tasks/{id} — 查询任务详情 */
@Operation(summary = "查询任务详情") @Operation(summary = "查询任务详情")
@GetMapping("/{id}") @GetMapping("/{id}")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<TaskResponse> getById(@PathVariable Long id) { public Result<TaskResponse> getById(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long id) {
return Result.success(taskService.toPublicResponse(taskService.getById(id))); return Result.success(taskService.toPublicResponse(taskService.getById(id)));
} }
@@ -98,7 +115,10 @@ public class TaskController {
@Operation(summary = "领取任务") @Operation(summary = "领取任务")
@PostMapping("/{id}/claim") @PostMapping("/{id}/claim")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<Void> claim(@PathVariable Long id, HttpServletRequest request) { public Result<Void> claim(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long id,
HttpServletRequest request) {
taskClaimService.claim(id, principal(request)); taskClaimService.claim(id, principal(request));
return Result.success(null); return Result.success(null);
} }
@@ -107,7 +127,10 @@ public class TaskController {
@Operation(summary = "放弃任务") @Operation(summary = "放弃任务")
@PostMapping("/{id}/unclaim") @PostMapping("/{id}/unclaim")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<Void> unclaim(@PathVariable Long id, HttpServletRequest request) { public Result<Void> unclaim(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long id,
HttpServletRequest request) {
taskClaimService.unclaim(id, principal(request)); taskClaimService.unclaim(id, principal(request));
return Result.success(null); return Result.success(null);
} }
@@ -116,7 +139,10 @@ public class TaskController {
@Operation(summary = "重领被驳回的任务") @Operation(summary = "重领被驳回的任务")
@PostMapping("/{id}/reclaim") @PostMapping("/{id}/reclaim")
@RequireRole("ANNOTATOR") @RequireRole("ANNOTATOR")
public Result<Void> reclaim(@PathVariable Long id, HttpServletRequest request) { public Result<Void> reclaim(
@Parameter(description = "任务 ID", example = "1001")
@PathVariable Long id,
HttpServletRequest request) {
taskClaimService.reclaim(id, principal(request)); taskClaimService.reclaim(id, principal(request));
return Result.success(null); return Result.success(null);
} }
@@ -125,11 +151,15 @@ public class TaskController {
@Operation(summary = "管理员强制指派任务") @Operation(summary = "管理员强制指派任务")
@PutMapping("/{id}/reassign") @PutMapping("/{id}/reassign")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<Void> reassign(@PathVariable Long id, public Result<Void> reassign(
@RequestBody Map<String, Object> body, @Parameter(description = "任务 ID", example = "1001")
@PathVariable Long id,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "管理员强制改派任务请求体",
required = true)
@RequestBody TaskReassignRequest body,
HttpServletRequest request) { HttpServletRequest request) {
Long targetUserId = Long.parseLong(body.get("userId").toString()); taskService.reassign(id, body.getUserId(), principal(request));
taskService.reassign(id, targetUserId, principal(request));
return Result.success(null); return Result.success(null);
} }

View File

@@ -1,7 +1,5 @@
package com.label.controller; package com.label.controller;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -15,10 +13,15 @@ import com.label.annotation.RequireRole;
import com.label.common.auth.TokenPrincipal; import com.label.common.auth.TokenPrincipal;
import com.label.common.result.PageResult; import com.label.common.result.PageResult;
import com.label.common.result.Result; import com.label.common.result.Result;
import com.label.dto.UserCreateRequest;
import com.label.dto.UserRoleUpdateRequest;
import com.label.dto.UserStatusUpdateRequest;
import com.label.dto.UserUpdateRequest;
import com.label.entity.SysUser; import com.label.entity.SysUser;
import com.label.service.UserService; import com.label.service.UserService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -39,7 +42,9 @@ public class UserController {
@GetMapping @GetMapping
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<PageResult<SysUser>> listUsers( public Result<PageResult<SysUser>> listUsers(
@Parameter(description = "页码,从 1 开始", example = "1")
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@Parameter(description = "每页条数", example = "20")
@RequestParam(defaultValue = "20") int pageSize, @RequestParam(defaultValue = "20") int pageSize,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(userService.listUsers(page, pageSize, principal(request))); return Result.success(userService.listUsers(page, pageSize, principal(request)));
@@ -49,13 +54,17 @@ public class UserController {
@Operation(summary = "创建用户") @Operation(summary = "创建用户")
@PostMapping @PostMapping
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<SysUser> createUser(@RequestBody Map<String, String> body, public Result<SysUser> createUser(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "创建用户请求体",
required = true)
@RequestBody UserCreateRequest body,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(userService.createUser( return Result.success(userService.createUser(
body.get("username"), body.getUsername(),
body.get("password"), body.getPassword(),
body.get("realName"), body.getRealName(),
body.get("role"), body.getRole(),
principal(request))); principal(request)));
} }
@@ -63,13 +72,18 @@ public class UserController {
@Operation(summary = "更新用户基本信息") @Operation(summary = "更新用户基本信息")
@PutMapping("/{id}") @PutMapping("/{id}")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<SysUser> updateUser(@PathVariable Long id, public Result<SysUser> updateUser(
@RequestBody Map<String, String> body, @Parameter(description = "用户 ID", example = "2001")
@PathVariable Long id,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "更新用户基本信息请求体",
required = true)
@RequestBody UserUpdateRequest body,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(userService.updateUser( return Result.success(userService.updateUser(
id, id,
body.get("realName"), body.getRealName(),
body.get("password"), body.getPassword(),
principal(request))); principal(request)));
} }
@@ -77,10 +91,15 @@ public class UserController {
@Operation(summary = "变更用户状态", description = "statusACTIVE、DISABLED") @Operation(summary = "变更用户状态", description = "statusACTIVE、DISABLED")
@PutMapping("/{id}/status") @PutMapping("/{id}/status")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<Void> updateStatus(@PathVariable Long id, public Result<Void> updateStatus(
@RequestBody Map<String, String> body, @Parameter(description = "用户 ID", example = "2001")
@PathVariable Long id,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "更新用户状态请求体",
required = true)
@RequestBody UserStatusUpdateRequest body,
HttpServletRequest request) { HttpServletRequest request) {
userService.updateStatus(id, body.get("status"), principal(request)); userService.updateStatus(id, body.getStatus(), principal(request));
return Result.success(null); return Result.success(null);
} }
@@ -88,10 +107,15 @@ public class UserController {
@Operation(summary = "变更用户角色", description = "roleADMIN、UPLOADER、VIEWER") @Operation(summary = "变更用户角色", description = "roleADMIN、UPLOADER、VIEWER")
@PutMapping("/{id}/role") @PutMapping("/{id}/role")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<Void> updateRole(@PathVariable Long id, public Result<Void> updateRole(
@RequestBody Map<String, String> body, @Parameter(description = "用户 ID", example = "2001")
@PathVariable Long id,
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "更新用户角色请求体",
required = true)
@RequestBody UserRoleUpdateRequest body,
HttpServletRequest request) { HttpServletRequest request) {
userService.updateRole(id, body.get("role"), principal(request)); userService.updateRole(id, body.getRole(), principal(request));
return Result.success(null); return Result.success(null);
} }

View File

@@ -3,9 +3,12 @@ package com.label.controller;
import com.label.annotation.RequireRole; import com.label.annotation.RequireRole;
import com.label.common.auth.TokenPrincipal; import com.label.common.auth.TokenPrincipal;
import com.label.common.result.Result; import com.label.common.result.Result;
import com.label.dto.VideoProcessCallbackRequest;
import com.label.dto.VideoProcessCreateRequest;
import com.label.entity.VideoProcessJob; import com.label.entity.VideoProcessJob;
import com.label.service.VideoProcessService; import com.label.service.VideoProcessService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -13,8 +16,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Map;
/** /**
* 视频处理接口4 个端点)。 * 视频处理接口4 个端点)。
* *
@@ -38,16 +39,18 @@ public class VideoController {
@Operation(summary = "触发视频处理任务") @Operation(summary = "触发视频处理任务")
@PostMapping("/api/video/process") @PostMapping("/api/video/process")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<VideoProcessJob> createJob(@RequestBody Map<String, Object> body, public Result<VideoProcessJob> createJob(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "创建视频处理任务请求体",
required = true)
@RequestBody VideoProcessCreateRequest body,
HttpServletRequest request) { HttpServletRequest request) {
Object sourceIdVal = body.get("sourceId"); Long sourceId = body.getSourceId();
Object jobTypeVal = body.get("jobType"); String jobType = body.getJobType();
if (sourceIdVal == null || jobTypeVal == null) { if (sourceId == null || jobType == null) {
return Result.failure("INVALID_PARAMS", "sourceId 和 jobType 不能为空"); return Result.failure("INVALID_PARAMS", "sourceId 和 jobType 不能为空");
} }
Long sourceId = Long.parseLong(sourceIdVal.toString()); String params = body.getParams();
String jobType = jobTypeVal.toString();
String params = body.containsKey("params") ? body.get("params").toString() : null;
TokenPrincipal principal = principal(request); TokenPrincipal principal = principal(request);
return Result.success( return Result.success(
@@ -58,7 +61,9 @@ public class VideoController {
@Operation(summary = "查询视频处理任务状态") @Operation(summary = "查询视频处理任务状态")
@GetMapping("/api/video/jobs/{jobId}") @GetMapping("/api/video/jobs/{jobId}")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<VideoProcessJob> getJob(@PathVariable Long jobId, public Result<VideoProcessJob> getJob(
@Parameter(description = "视频处理任务 ID", example = "9001")
@PathVariable Long jobId,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(videoProcessService.getJob(jobId, principal(request).getCompanyId())); return Result.success(videoProcessService.getJob(jobId, principal(request).getCompanyId()));
} }
@@ -67,7 +72,9 @@ public class VideoController {
@Operation(summary = "重置失败的视频处理任务") @Operation(summary = "重置失败的视频处理任务")
@PostMapping("/api/video/jobs/{jobId}/reset") @PostMapping("/api/video/jobs/{jobId}/reset")
@RequireRole("ADMIN") @RequireRole("ADMIN")
public Result<VideoProcessJob> resetJob(@PathVariable Long jobId, public Result<VideoProcessJob> resetJob(
@Parameter(description = "视频处理任务 ID", example = "9001")
@PathVariable Long jobId,
HttpServletRequest request) { HttpServletRequest request) {
return Result.success(videoProcessService.reset(jobId, principal(request).getCompanyId())); return Result.success(videoProcessService.reset(jobId, principal(request).getCompanyId()));
} }
@@ -84,7 +91,11 @@ public class VideoController {
*/ */
@Operation(summary = "接收 AI 服务视频处理回调") @Operation(summary = "接收 AI 服务视频处理回调")
@PostMapping("/api/video/callback") @PostMapping("/api/video/callback")
public Result<Void> handleCallback(@RequestBody Map<String, Object> body, public Result<Void> handleCallback(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "AI 服务视频处理回调请求体",
required = true)
@RequestBody VideoProcessCallbackRequest body,
HttpServletRequest request) { HttpServletRequest request) {
// 共享密钥校验(配置了 VIDEO_CALLBACK_SECRET 时强制校验) // 共享密钥校验(配置了 VIDEO_CALLBACK_SECRET 时强制校验)
if (callbackSecret != null && !callbackSecret.isBlank()) { if (callbackSecret != null && !callbackSecret.isBlank()) {
@@ -94,10 +105,10 @@ public class VideoController {
} }
} }
Long jobId = Long.parseLong(body.get("jobId").toString()); Long jobId = body.getJobId();
String status = (String) body.get("status"); String status = body.getStatus();
String outputPath = body.containsKey("outputPath") ? (String) body.get("outputPath") : null; String outputPath = body.getOutputPath();
String errorMessage = body.containsKey("errorMessage") ? (String) body.get("errorMessage") : null; String errorMessage = body.getErrorMessage();
log.info("视频处理回调jobId={}, status={}", jobId, status); log.info("视频处理回调jobId={}, status={}", jobId, status);
videoProcessService.handleCallback(jobId, status, outputPath, errorMessage); videoProcessService.handleCallback(jobId, status, outputPath, errorMessage);

View File

@@ -0,0 +1,14 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "创建公司请求")
public class CompanyCreateRequest {
@Schema(description = "公司名称", example = "示例科技")
private String companyName;
@Schema(description = "公司代码(英文简写)", example = "DEMO")
private String companyCode;
}

View File

@@ -0,0 +1,11 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "公司状态变更请求")
public class CompanyStatusUpdateRequest {
@Schema(description = "公司状态可选值ACTIVE / DISABLED", example = "ACTIVE")
private String status;
}

View File

@@ -0,0 +1,14 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "更新公司请求")
public class CompanyUpdateRequest {
@Schema(description = "公司名称", example = "示例科技(升级版)")
private String companyName;
@Schema(description = "公司代码(英文简写)", example = "DEMO")
private String companyCode;
}

View File

@@ -0,0 +1,14 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "创建任务请求")
public class CreateTaskRequest {
@Schema(description = "资料 ID", example = "1001")
private Long sourceId;
@Schema(description = "任务类型可选值EXTRACTION / QA_GENERATION", example = "EXTRACTION")
private String taskType;
}

View File

@@ -0,0 +1,13 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Map;
@Data
@Schema(description = "动态 JSON 响应")
public class DynamicJsonResponse {
@Schema(description = "动态 JSON 内容", example = "{\"label\":\"cat\",\"score\":0.98}")
private Map<String, Object> content;
}

View File

@@ -0,0 +1,13 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
@Schema(description = "创建导出批次请求")
public class ExportBatchCreateRequest {
@Schema(description = "样本 ID 列表", example = "[101, 102, 103]")
private List<Long> sampleIds;
}

View File

@@ -0,0 +1,23 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "微调任务响应")
public class FinetuneJobResponse {
@Schema(description = "导出批次 ID", example = "501")
private Long batchId;
@Schema(description = "GLM 微调任务 ID", example = "glm-ft-001")
private String glmJobId;
@Schema(description = "微调状态", example = "RUNNING")
private String finetuneStatus;
@Schema(description = "进度百分比", example = "35")
private Integer progress;
@Schema(description = "错误信息", example = "")
private String errorMessage;
}

View File

@@ -15,10 +15,10 @@ public class LoginResponse {
@Schema(description = "Bearer Token", example = "550e8400-e29b-41d4-a716-446655440000") @Schema(description = "Bearer Token", example = "550e8400-e29b-41d4-a716-446655440000")
private String token; private String token;
/** 用户主键 */ /** 用户主键 */
@Schema(description = "用户主键") @Schema(description = "用户主键", example = "1")
private Long userId; private Long userId;
/** 登录用户名 */ /** 登录用户名 */
@Schema(description = "登录用户名") @Schema(description = "登录用户名", example = "admin")
private String username; private String username;
/** 角色UPLOADER / ANNOTATOR / REVIEWER / ADMIN */ /** 角色UPLOADER / ANNOTATOR / REVIEWER / ADMIN */
@Schema(description = "角色", example = "ADMIN") @Schema(description = "角色", example = "ADMIN")

View File

@@ -0,0 +1,11 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "驳回请求")
public class RejectRequest {
@Schema(description = "驳回原因", example = "标注结果缺少关键字段")
private String reason;
}

View File

@@ -14,25 +14,25 @@ import java.time.LocalDateTime;
@Builder @Builder
@Schema(description = "原始资料响应") @Schema(description = "原始资料响应")
public class SourceResponse { public class SourceResponse {
@Schema(description = "资料主键") @Schema(description = "资料主键", example = "2001")
private Long id; private Long id;
@Schema(description = "文件名") @Schema(description = "文件名", example = "demo.txt")
private String fileName; private String fileName;
@Schema(description = "资料类型", example = "TEXT") @Schema(description = "资料类型", example = "TEXT")
private String dataType; private String dataType;
@Schema(description = "文件大小(字节)") @Schema(description = "文件大小(字节)", example = "1024")
private Long fileSize; private Long fileSize;
@Schema(description = "资料状态", example = "PENDING") @Schema(description = "资料状态", example = "PENDING")
private String status; private String status;
/** 上传用户 ID列表端点返回 */ /** 上传用户 ID列表端点返回 */
@Schema(description = "上传用户 ID") @Schema(description = "上传用户 ID", example = "1")
private Long uploaderId; private Long uploaderId;
/** 15 分钟预签名下载链接(详情端点返回) */ /** 15 分钟预签名下载链接(详情端点返回) */
@Schema(description = "预签名下载链接") @Schema(description = "预签名下载链接", example = "https://example.com/presigned-url")
private String presignedUrl; private String presignedUrl;
/** 父资料 ID视频帧 / 文本片段;详情端点返回) */ /** 父资料 ID视频帧 / 文本片段;详情端点返回) */
@Schema(description = "父资料 ID") @Schema(description = "父资料 ID", example = "1001")
private Long parentSourceId; private Long parentSourceId;
@Schema(description = "创建时间") @Schema(description = "创建时间", example = "2026-04-15T12:34:56")
private LocalDateTime createdAt; private LocalDateTime createdAt;
} }

View File

@@ -0,0 +1,26 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "系统配置项响应")
public class SysConfigItemResponse {
@Schema(description = "配置主键", example = "1")
private Long id;
@Schema(description = "配置键", example = "model_default")
private String configKey;
@Schema(description = "配置值", example = "glm-4-flash")
private String configValue;
@Schema(description = "配置说明", example = "默认文本模型")
private String description;
@Schema(description = "配置来源作用域可选值COMPANY、GLOBAL", example = "COMPANY")
private String scope;
@Schema(description = "所属公司 IDGLOBAL 配置为空COMPANY 配置为当前公司 ID", example = "100")
private Long companyId;
}

View File

@@ -0,0 +1,14 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "系统配置更新请求")
public class SysConfigUpdateRequest {
@Schema(description = "配置值", example = "https://api.example.com")
private String value;
@Schema(description = "配置说明", example = "AI 服务基础地址")
private String description;
}

View File

@@ -0,0 +1,11 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "任务改派请求")
public class TaskReassignRequest {
@Schema(description = "目标用户 ID", example = "2001")
private Long userId;
}

View File

@@ -13,26 +13,26 @@ import java.time.LocalDateTime;
@Builder @Builder
@Schema(description = "标注任务响应") @Schema(description = "标注任务响应")
public class TaskResponse { public class TaskResponse {
@Schema(description = "任务主键") @Schema(description = "任务主键", example = "1001")
private Long id; private Long id;
@Schema(description = "关联资料 ID") @Schema(description = "关联资料 ID", example = "2001")
private Long sourceId; private Long sourceId;
/** 任务类型(对应 taskType 字段EXTRACTION / QA_GENERATION */ /** 任务类型(对应 taskType 字段EXTRACTION / QA_GENERATION */
@Schema(description = "任务类型", example = "EXTRACTION") @Schema(description = "任务类型", example = "EXTRACTION")
private String taskType; private String taskType;
@Schema(description = "任务状态", example = "UNCLAIMED") @Schema(description = "任务状态", example = "UNCLAIMED")
private String status; private String status;
@Schema(description = "领取人用户 ID") @Schema(description = "领取人用户 ID", example = "1")
private Long claimedBy; private Long claimedBy;
@Schema(description = "领取时间") @Schema(description = "领取时间", example = "2026-04-15T12:34:56")
private LocalDateTime claimedAt; private LocalDateTime claimedAt;
@Schema(description = "提交时间") @Schema(description = "提交时间", example = "2026-04-15T12:34:56")
private LocalDateTime submittedAt; private LocalDateTime submittedAt;
@Schema(description = "完成时间") @Schema(description = "完成时间", example = "2026-04-15T12:34:56")
private LocalDateTime completedAt; private LocalDateTime completedAt;
/** 驳回原因REJECTED 状态时非空) */ /** 驳回原因REJECTED 状态时非空) */
@Schema(description = "驳回原因") @Schema(description = "驳回原因")
private String rejectReason; private String rejectReason;
@Schema(description = "创建时间") @Schema(description = "创建时间", example = "2026-04-15T12:34:56")
private LocalDateTime createdAt; private LocalDateTime createdAt;
} }

View File

@@ -0,0 +1,20 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "创建用户请求")
public class UserCreateRequest {
@Schema(description = "登录用户名", example = "reviewer01")
private String username;
@Schema(description = "明文密码", example = "Pass@123")
private String password;
@Schema(description = "真实姓名", example = "张三")
private String realName;
@Schema(description = "角色可选值ADMIN / REVIEWER / ANNOTATOR / UPLOADER", example = "REVIEWER")
private String role;
}

View File

@@ -11,16 +11,16 @@ import lombok.Data;
@AllArgsConstructor @AllArgsConstructor
@Schema(description = "当前登录用户信息") @Schema(description = "当前登录用户信息")
public class UserInfoResponse { public class UserInfoResponse {
@Schema(description = "用户主键") @Schema(description = "用户主键", example = "1")
private Long id; private Long id;
@Schema(description = "用户名") @Schema(description = "用户名", example = "admin")
private String username; private String username;
@Schema(description = "真实姓名") @Schema(description = "真实姓名", example = "张三")
private String realName; private String realName;
@Schema(description = "角色", example = "ADMIN") @Schema(description = "角色", example = "ADMIN")
private String role; private String role;
@Schema(description = "所属公司 ID") @Schema(description = "所属公司 ID", example = "1")
private Long companyId; private Long companyId;
@Schema(description = "所属公司名称") @Schema(description = "所属公司名称", example = "示例科技有限公司")
private String companyName; private String companyName;
} }

View File

@@ -0,0 +1,11 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "用户角色变更请求")
public class UserRoleUpdateRequest {
@Schema(description = "用户角色可选值ADMIN / REVIEWER / ANNOTATOR / UPLOADER", example = "ANNOTATOR")
private String role;
}

View File

@@ -0,0 +1,11 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "用户状态变更请求")
public class UserStatusUpdateRequest {
@Schema(description = "用户状态可选值ACTIVE / DISABLED", example = "DISABLED")
private String status;
}

View File

@@ -0,0 +1,14 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "更新用户请求")
public class UserUpdateRequest {
@Schema(description = "真实姓名", example = "李四")
private String realName;
@Schema(description = "新密码,可为空或 null 表示保持不变", example = "")
private String password;
}

View File

@@ -0,0 +1,20 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "视频处理回调请求")
public class VideoProcessCallbackRequest {
@Schema(description = "视频处理任务 ID", example = "9001")
private Long jobId;
@Schema(description = "处理状态", example = "SUCCESS")
private String status;
@Schema(description = "输出文件路径", example = "/data/output/video-9001.json")
private String outputPath;
@Schema(description = "失败时的错误信息", example = "ffmpeg error")
private String errorMessage;
}

View File

@@ -0,0 +1,17 @@
package com.label.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "创建视频处理任务请求")
public class VideoProcessCreateRequest {
@Schema(description = "资料 ID", example = "3001")
private Long sourceId;
@Schema(description = "处理任务类型可选值FRAME_EXTRACT、VIDEO_TO_TEXT", example = "FRAME_EXTRACT")
private String jobType;
@Schema(description = "任务参数 JSON 字符串", example = "{\"frameInterval\":5}")
private String params;
}

View File

@@ -3,6 +3,7 @@ package com.label.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -15,30 +16,40 @@ import java.util.UUID;
*/ */
@Data @Data
@TableName("export_batch") @TableName("export_batch")
@Schema(description = "导出批次")
public class ExportBatch { public class ExportBatch {
@TableId(type = IdType.AUTO) @TableId(type = IdType.AUTO)
@Schema(description = "导出批次主键", example = "1")
private Long id; private Long id;
/** 所属公司(多租户键) */ /** 所属公司(多租户键) */
@Schema(description = "所属公司 ID", example = "1")
private Long companyId; private Long companyId;
/** 批次唯一标识UUIDDB 默认 gen_random_uuid() */ /** 批次唯一标识UUIDDB 默认 gen_random_uuid() */
@Schema(description = "批次 UUID", example = "550e8400-e29b-41d4-a716-446655440000")
private UUID batchUuid; private UUID batchUuid;
/** 本批次样本数量 */ /** 本批次样本数量 */
@Schema(description = "样本数量", example = "1000")
private Integer sampleCount; private Integer sampleCount;
/** 导出 JSONL 的 RustFS 路径 */ /** 导出 JSONL 的 RustFS 路径 */
@Schema(description = "数据集文件路径JSONL", example = "datasets/export/2026-04-15/batch.jsonl")
private String datasetFilePath; private String datasetFilePath;
/** GLM fine-tune 任务 ID提交微调后填写 */ /** GLM fine-tune 任务 ID提交微调后填写 */
@Schema(description = "GLM 微调任务 ID", example = "glm-job-123456")
private String glmJobId; private String glmJobId;
/** 微调任务状态NOT_STARTED / RUNNING / COMPLETED / FAILED */ /** 微调任务状态NOT_STARTED / RUNNING / COMPLETED / FAILED */
@Schema(description = "微调任务状态", example = "NOT_STARTED")
private String finetuneStatus; private String finetuneStatus;
@Schema(description = "创建时间", example = "2026-04-15T12:34:56")
private LocalDateTime createdAt; private LocalDateTime createdAt;
@Schema(description = "更新时间", example = "2026-04-15T12:34:56")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
} }

View File

@@ -3,6 +3,7 @@ package com.label.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -13,22 +14,29 @@ import java.time.LocalDateTime;
*/ */
@Data @Data
@TableName("sys_company") @TableName("sys_company")
@Schema(description = "租户公司")
public class SysCompany { public class SysCompany {
/** 公司主键,自增 */ /** 公司主键,自增 */
@TableId(type = IdType.AUTO) @TableId(type = IdType.AUTO)
@Schema(description = "公司主键", example = "1")
private Long id; private Long id;
/** 公司全称,全局唯一 */ /** 公司全称,全局唯一 */
@Schema(description = "公司全称", example = "示例科技有限公司")
private String companyName; private String companyName;
/** 公司代码(英文简写),全局唯一 */ /** 公司代码(英文简写),全局唯一 */
@Schema(description = "公司代码(英文简写)", example = "DEMO")
private String companyCode; private String companyCode;
/** 状态ACTIVE / DISABLED */ /** 状态ACTIVE / DISABLED */
@Schema(description = "状态", example = "ACTIVE")
private String status; private String status;
@Schema(description = "创建时间", example = "2026-04-15T12:34:56")
private LocalDateTime createdAt; private LocalDateTime createdAt;
@Schema(description = "更新时间", example = "2026-04-15T12:34:56")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
} }

View File

@@ -3,6 +3,7 @@ package com.label.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -15,27 +16,35 @@ import java.time.LocalDateTime;
*/ */
@Data @Data
@TableName("sys_config") @TableName("sys_config")
@Schema(description = "系统配置")
public class SysConfig { public class SysConfig {
@TableId(type = IdType.AUTO) @TableId(type = IdType.AUTO)
@Schema(description = "配置主键", example = "1")
private Long id; private Long id;
/** /**
* 所属公司 IDNULL = 全局默认配置;非 NULL = 租户专属配置)。 * 所属公司 IDNULL = 全局默认配置;非 NULL = 租户专属配置)。
* 注意:不能用 @TableField(exist = false) 排除,必须保留以支持 company_id IS NULL 查询。 * 注意:不能用 @TableField(exist = false) 排除,必须保留以支持 company_id IS NULL 查询。
*/ */
@Schema(description = "所属公司 IDNULL 表示全局默认配置)", example = "1")
private Long companyId; private Long companyId;
/** 配置键 */ /** 配置键 */
@Schema(description = "配置键", example = "STORAGE_BUCKET")
private String configKey; private String configKey;
/** 配置值 */ /** 配置值 */
@Schema(description = "配置值", example = "label-bucket")
private String configValue; private String configValue;
/** 配置说明 */ /** 配置说明 */
@Schema(description = "配置说明", example = "对象存储桶名称")
private String description; private String description;
@Schema(description = "创建时间", example = "2026-04-15T12:34:56")
private LocalDateTime createdAt; private LocalDateTime createdAt;
@Schema(description = "更新时间", example = "2026-04-15T12:34:56")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
} }

View File

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -15,16 +16,20 @@ import java.time.LocalDateTime;
*/ */
@Data @Data
@TableName("sys_user") @TableName("sys_user")
@Schema(description = "系统用户")
public class SysUser { public class SysUser {
/** 用户主键,自增 */ /** 用户主键,自增 */
@TableId(type = IdType.AUTO) @TableId(type = IdType.AUTO)
@Schema(description = "用户主键", example = "1")
private Long id; private Long id;
/** 所属公司 ID多租户键 */ /** 所属公司 ID多租户键 */
@Schema(description = "所属公司 ID", example = "1")
private Long companyId; private Long companyId;
/** 登录用户名(同公司内唯一) */ /** 登录用户名(同公司内唯一) */
@Schema(description = "登录用户名", example = "admin")
private String username; private String username;
/** /**
@@ -32,18 +37,24 @@ public class SysUser {
* 序列化时排除,防止密码哈希泄漏到 API 响应。 * 序列化时排除,防止密码哈希泄漏到 API 响应。
*/ */
@JsonIgnore @JsonIgnore
@Schema(description = "密码哈希(不会在响应中返回)")
private String passwordHash; private String passwordHash;
/** 真实姓名 */ /** 真实姓名 */
@Schema(description = "真实姓名", example = "张三")
private String realName; private String realName;
/** 角色UPLOADER / ANNOTATOR / REVIEWER / ADMIN */ /** 角色UPLOADER / ANNOTATOR / REVIEWER / ADMIN */
@Schema(description = "角色", example = "ADMIN")
private String role; private String role;
/** 状态ACTIVE / DISABLED */ /** 状态ACTIVE / DISABLED */
@Schema(description = "状态", example = "ACTIVE")
private String status; private String status;
@Schema(description = "创建时间", example = "2026-04-15T12:34:56")
private LocalDateTime createdAt; private LocalDateTime createdAt;
@Schema(description = "更新时间", example = "2026-04-15T12:34:56")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
} }

View File

@@ -3,6 +3,7 @@ package com.label.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -15,32 +16,44 @@ import java.time.LocalDateTime;
*/ */
@Data @Data
@TableName("training_dataset") @TableName("training_dataset")
@Schema(description = "训练数据集样本")
public class TrainingDataset { public class TrainingDataset {
@TableId(type = IdType.AUTO) @TableId(type = IdType.AUTO)
@Schema(description = "样本主键", example = "1")
private Long id; private Long id;
/** 所属公司(多租户键) */ /** 所属公司(多租户键) */
@Schema(description = "所属公司 ID", example = "1")
private Long companyId; private Long companyId;
@Schema(description = "关联任务 ID", example = "1001")
private Long taskId; private Long taskId;
@Schema(description = "关联资料 ID", example = "2001")
private Long sourceId; private Long sourceId;
/** 样本类型TEXT / IMAGE / VIDEO_FRAME */ /** 样本类型TEXT / IMAGE / VIDEO_FRAME */
@Schema(description = "样本类型", example = "TEXT")
private String sampleType; private String sampleType;
/** GLM fine-tune 格式的 JSON 字符串JSONB */ /** GLM fine-tune 格式的 JSON 字符串JSONB */
@Schema(description = "GLM 微调格式 JSON", example = "{\"messages\":[{\"role\":\"user\",\"content\":\"...\"},{\"role\":\"assistant\",\"content\":\"...\"}]}")
private String glmFormatJson; private String glmFormatJson;
/** 状态PENDING_REVIEW / APPROVED / REJECTED */ /** 状态PENDING_REVIEW / APPROVED / REJECTED */
@Schema(description = "状态", example = "APPROVED")
private String status; private String status;
@Schema(description = "导出批次 ID", example = "3001")
private Long exportBatchId; private Long exportBatchId;
@Schema(description = "导出时间", example = "2026-04-15T12:34:56")
private LocalDateTime exportedAt; private LocalDateTime exportedAt;
@Schema(description = "创建时间", example = "2026-04-15T12:34:56")
private LocalDateTime createdAt; private LocalDateTime createdAt;
@Schema(description = "更新时间", example = "2026-04-15T12:34:56")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
} }

View File

@@ -3,6 +3,7 @@ package com.label.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -15,43 +16,58 @@ import java.time.LocalDateTime;
*/ */
@Data @Data
@TableName("video_process_job") @TableName("video_process_job")
@Schema(description = "视频处理任务")
public class VideoProcessJob { public class VideoProcessJob {
@TableId(type = IdType.AUTO) @TableId(type = IdType.AUTO)
@Schema(description = "任务主键", example = "1")
private Long id; private Long id;
/** 所属公司(多租户键) */ /** 所属公司(多租户键) */
@Schema(description = "所属公司 ID", example = "1")
private Long companyId; private Long companyId;
/** 关联资料 ID */ /** 关联资料 ID */
@Schema(description = "关联资料 ID", example = "2001")
private Long sourceId; private Long sourceId;
/** 任务类型FRAME_EXTRACT / VIDEO_TO_TEXT */ /** 任务类型FRAME_EXTRACT / VIDEO_TO_TEXT */
@Schema(description = "任务类型", example = "FRAME_EXTRACT")
private String jobType; private String jobType;
/** 任务状态PENDING / RUNNING / SUCCESS / FAILED / RETRYING */ /** 任务状态PENDING / RUNNING / SUCCESS / FAILED / RETRYING */
@Schema(description = "任务状态", example = "PENDING")
private String status; private String status;
/** 任务参数JSONB例如 {"frameInterval": 30} */ /** 任务参数JSONB例如 {"frameInterval": 30} */
@Schema(description = "任务参数JSON", example = "{\"frameInterval\":30}")
private String params; private String params;
/** AI 处理输出路径(成功后填写) */ /** AI 处理输出路径(成功后填写) */
@Schema(description = "输出路径", example = "outputs/video/2026-04-15/result.json")
private String outputPath; private String outputPath;
/** 已重试次数 */ /** 已重试次数 */
@Schema(description = "已重试次数", example = "0")
private Integer retryCount; private Integer retryCount;
/** 最大重试次数(默认 3 */ /** 最大重试次数(默认 3 */
@Schema(description = "最大重试次数", example = "3")
private Integer maxRetries; private Integer maxRetries;
/** 错误信息 */ /** 错误信息 */
@Schema(description = "错误信息")
private String errorMessage; private String errorMessage;
@Schema(description = "开始时间", example = "2026-04-15T12:34:56")
private LocalDateTime startedAt; private LocalDateTime startedAt;
@Schema(description = "完成时间", example = "2026-04-15T12:34:56")
private LocalDateTime completedAt; private LocalDateTime completedAt;
@Schema(description = "创建时间", example = "2026-04-15T12:34:56")
private LocalDateTime createdAt; private LocalDateTime createdAt;
@Schema(description = "更新时间", example = "2026-04-15T12:34:56")
private LocalDateTime updatedAt; private LocalDateTime updatedAt;
} }