Merge branch 'dev54'
This commit is contained in:
8
pom.xml
8
pom.xml
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.labelsys</groupId>
|
<groupId>com.labelsys</groupId>
|
||||||
<artifactId>label-backend</artifactId>
|
<artifactId>label-backend</artifactId>
|
||||||
@@ -15,6 +15,8 @@
|
|||||||
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
|
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
|
||||||
<springdoc-openapi.version>2.3.0</springdoc-openapi.version>
|
<springdoc-openapi.version>2.3.0</springdoc-openapi.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@@ -102,7 +104,7 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package com.labelsys.backend.config;
|
package com.labelsys.backend.config;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.DbType;
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class MybatisPlusConfig {
|
public class MybatisPlusConfig {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public class AnnotationResultController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "查询标注结果比对信息")
|
@Operation(summary = "查询标注结果比对信息")
|
||||||
@RequirePosition(UserPosition.REVIEWER)
|
//@RequirePosition(UserPosition.REVIEWER)
|
||||||
@GetMapping("/{id}/compare")
|
@GetMapping("/{id}/compare")
|
||||||
public Result<AnnotationResultCompareResponse> compare(
|
public Result<AnnotationResultCompareResponse> compare(
|
||||||
@Parameter(description = "结果ID", example = "191000000000000401")
|
@Parameter(description = "结果ID", example = "191000000000000401")
|
||||||
@@ -60,7 +60,7 @@ public class AnnotationResultController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "提交合并审核结果")
|
@Operation(summary = "提交合并审核结果")
|
||||||
@RequirePosition(UserPosition.REVIEWER)
|
//@RequirePosition(UserPosition.REVIEWER)
|
||||||
@PostMapping("/{id}/merge-review")
|
@PostMapping("/{id}/merge-review")
|
||||||
public Result<MergeReviewResultResponse> mergeReview(
|
public Result<MergeReviewResultResponse> mergeReview(
|
||||||
@Parameter(description = "结果ID", example = "191000000000000401")
|
@Parameter(description = "结果ID", example = "191000000000000401")
|
||||||
|
|||||||
@@ -3,21 +3,28 @@ package com.labelsys.backend.controller;
|
|||||||
import com.labelsys.backend.common.Result;
|
import com.labelsys.backend.common.Result;
|
||||||
import com.labelsys.backend.context.UserContext;
|
import com.labelsys.backend.context.UserContext;
|
||||||
import com.labelsys.backend.dto.common.PageResult;
|
import com.labelsys.backend.dto.common.PageResult;
|
||||||
|
import com.labelsys.backend.dto.request.SaveImageBboxRequest;
|
||||||
import com.labelsys.backend.dto.request.SourceResourcePageQuery;
|
import com.labelsys.backend.dto.request.SourceResourcePageQuery;
|
||||||
import com.labelsys.backend.dto.request.SourceUploadRequest;
|
import com.labelsys.backend.dto.request.SourceUploadRequest;
|
||||||
|
import com.labelsys.backend.dto.response.ImageBboxResponse;
|
||||||
import com.labelsys.backend.dto.response.SourceResourceResponse;
|
import com.labelsys.backend.dto.response.SourceResourceResponse;
|
||||||
import com.labelsys.backend.dto.response.SourceUploadResponse;
|
import com.labelsys.backend.dto.response.SourceUploadResponse;
|
||||||
|
import com.labelsys.backend.entity.SourceResource;
|
||||||
import com.labelsys.backend.service.SourceResourceService;
|
import com.labelsys.backend.service.SourceResourceService;
|
||||||
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.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springdoc.core.annotations.ParameterObject;
|
import org.springdoc.core.annotations.ParameterObject;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
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;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@@ -37,15 +44,16 @@ public class SourceResourceController {
|
|||||||
|
|
||||||
@Operation(summary = "分页查询资源")
|
@Operation(summary = "分页查询资源")
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public Result<PageResult<SourceResourceResponse>> page(@ParameterObject @ModelAttribute SourceResourcePageQuery query) {
|
public Result<PageResult<SourceResourceResponse>> page(
|
||||||
|
@ParameterObject @ModelAttribute SourceResourcePageQuery query) {
|
||||||
return Result.success(sourceResourceService.pageResources(UserContext.requireUser(), query));
|
return Result.success(sourceResourceService.pageResources(UserContext.requireUser(), query));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "查询资源详情")
|
@Operation(summary = "查询资源详情")
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public Result<SourceResourceResponse> detail(
|
public Result<SourceResourceResponse> detail(
|
||||||
@Parameter(description = "资源ID", example = "191000000000000101")
|
@Parameter(description = "资源ID", example = "191000000000000101")
|
||||||
@PathVariable Long id
|
@PathVariable Long id
|
||||||
) {
|
) {
|
||||||
return Result.success(sourceResourceService.getResource(UserContext.requireUser(), id));
|
return Result.success(sourceResourceService.getResource(UserContext.requireUser(), id));
|
||||||
}
|
}
|
||||||
@@ -53,10 +61,59 @@ public class SourceResourceController {
|
|||||||
@Operation(summary = "删除资源")
|
@Operation(summary = "删除资源")
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public Result<Void> delete(
|
public Result<Void> delete(
|
||||||
@Parameter(description = "资源ID", example = "191000000000000101")
|
@Parameter(description = "资源ID", example = "191000000000000101")
|
||||||
@PathVariable Long id
|
@PathVariable Long id
|
||||||
) {
|
) {
|
||||||
sourceResourceService.deleteResource(UserContext.requireUser(), id);
|
sourceResourceService.deleteResource(UserContext.requireUser(), id);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Operation(summary = "下载图片资源")
|
||||||
|
@GetMapping("/{id}/download")
|
||||||
|
public ResponseEntity<byte[]> downloadImage(
|
||||||
|
@Parameter(description = "资源ID", example = "191000000000000101")
|
||||||
|
@PathVariable Long id
|
||||||
|
) {
|
||||||
|
var currentUser = UserContext.requireUser();
|
||||||
|
byte[] imageData = sourceResourceService.downloadImage(currentUser, id);
|
||||||
|
|
||||||
|
// 获取资源信息以确定Content-Type
|
||||||
|
SourceResource resource = sourceResourceService.getResourceEntity(id);
|
||||||
|
String contentType = sourceResourceService.getImageContentType(resource);
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header(HttpHeaders.CONTENT_TYPE, contentType)
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + resource.getResourceName() + "\"")
|
||||||
|
.body(imageData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新接口
|
||||||
|
@Operation(summary = "查询图片资源BBOX标注")
|
||||||
|
@GetMapping("/{id}/bbox")
|
||||||
|
public Result<ImageBboxResponse> getImageBbox(
|
||||||
|
@Parameter(description = "资源ID", example = "191000000000000101")
|
||||||
|
@PathVariable Long id
|
||||||
|
) {
|
||||||
|
return Result.success(sourceResourceService.getImageBbox(UserContext.requireUser(), id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "保存图片资源BBOX标注")
|
||||||
|
@PostMapping("/{id}/bbox")
|
||||||
|
public Result<ImageBboxResponse> saveImageBbox(
|
||||||
|
@Parameter(description = "资源ID", example = "191000000000000101")
|
||||||
|
@PathVariable Long id,
|
||||||
|
@Valid @RequestBody SaveImageBboxRequest request
|
||||||
|
) {
|
||||||
|
return Result.success(sourceResourceService.saveImageBbox(UserContext.requireUser(), id, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除图片资源BBOX标注")
|
||||||
|
@DeleteMapping("/{id}/bbox")
|
||||||
|
public Result<Void> deleteImageBbox(
|
||||||
|
@Parameter(description = "资源ID", example = "191000000000000101")
|
||||||
|
@PathVariable Long id
|
||||||
|
) {
|
||||||
|
sourceResourceService.deleteImageBbox(UserContext.requireUser(), id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.labelsys.backend.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Schema(description = "BBOX坐标对象")
|
||||||
|
public record BboxCoordinate(
|
||||||
|
@Schema(description = "BBOX标识", example = "bbox_001") String id,
|
||||||
|
@NotNull @Schema(description = "左上角X坐标", example = "100") Integer x,
|
||||||
|
@NotNull @Schema(description = "左上角Y坐标", example = "50") Integer y,
|
||||||
|
@NotNull @Schema(description = "宽度", example = "200") Integer width,
|
||||||
|
@NotNull @Schema(description = "高度", example = "150") Integer height,
|
||||||
|
@Schema(description = "标注标签", example = "车辆") String label
|
||||||
|
//@Schema(description = "置信度", example = "0.95") Double confidence
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -1,24 +1,18 @@
|
|||||||
package com.labelsys.backend.dto.request;
|
package com.labelsys.backend.dto.request;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.labelsys.backend.enums.IndustryType;
|
import com.labelsys.backend.enums.IndustryType;
|
||||||
import com.labelsys.backend.enums.TaskType;
|
import com.labelsys.backend.enums.TaskType;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotEmpty;
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(description = "创建标注任务请求")
|
@Schema(description = "创建标注任务请求")
|
||||||
public record CreateAnnotationTaskRequest(
|
public record CreateAnnotationTaskRequest(
|
||||||
@Schema(description = "任务名称", example = "运输文档问答抽取任务") @NotBlank(message = "任务名称不能为空") String taskName,
|
@Schema(description = "任务名称", example = "运输文档问答抽取任务") @NotBlank(message = "任务名称不能为空") String taskName,
|
||||||
@Schema(description = "行业类型", defaultValue = "TRANSPORT", example = "TRANSPORT") @NotNull(message = "行业类型不能为空") IndustryType industryType,
|
@Schema(description = "行业类型", defaultValue = "TRANSPORT", example = "TRANSPORT") @NotNull(message = "行业类型不能为空") IndustryType industryType,
|
||||||
@Schema(description = "任务类型", defaultValue = "EXTRACT_QA", example = "EXTRACT_QA") @NotNull(message = "任务类型不能为空") TaskType taskType,
|
@Schema(description = "任务类型", defaultValue = "EXTRACT_QA", example = "EXTRACT_QA") @NotNull(message = "任务类型不能为空") TaskType taskType,
|
||||||
@Schema(description = "资源ID列表", example = "[191000000000000101,191000000000000102]") @NotEmpty(message = "资源列表不能为空") List<Long> resourceIds,
|
@Schema(description = "资源ID列表", example = "[191000000000000101,191000000000000102]") @NotEmpty(message = "资源列表不能为空") List<Long> resourceIds) {
|
||||||
@Schema(description = "抽取模型配置", example = "{\"mode\":\"SELECT\",\"selectedConfigName\":\"qwen-plus-extract\"}") @Valid TaskModelConfigRequest extractModel,
|
}
|
||||||
@Schema(description = "校验模型配置", example = "{\"mode\":\"MANUAL\",\"manualConfig\":{\"modelName\":\"qwen-max\",\"modelUrl\":\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\"apiKey\":\"sk-demo5678\"}}") @Valid TaskModelConfigRequest verifyModel,
|
|
||||||
@Schema(description = "抽取提示词配置", example = "{\"selectedConfigName\":\"qa-extract-v1\"}") @Valid PromptConfigOptionRequest extractPrompt,
|
|
||||||
@Schema(description = "校验提示词配置", example = "{\"promptText\":\"请对抽取结果进行逐项校验,并输出差异说明。\"}") @Valid PromptConfigOptionRequest verifyPrompt) {
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package com.labelsys.backend.dto.request;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
|
|
||||||
@Schema(description = "手动模型配置请求")
|
|
||||||
public record ManualModelConfigRequest(
|
|
||||||
@Schema(description = "模型名称", example = "qwen-plus") @NotBlank(message = "模型名称不能为空") String modelName,
|
|
||||||
@Schema(description = "模型地址", example = "https://dashscope.aliyuncs.com/compatible-mode/v1") @NotBlank(message = "模型地址不能为空") String modelUrl,
|
|
||||||
@Schema(description = "模型密钥", example = "sk-demo1234") @NotBlank(message = "模型密钥不能为空") String apiKey
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.labelsys.backend.dto.request;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
|
|
||||||
@Schema(description = "任务提示词配置请求")
|
|
||||||
public record PromptConfigOptionRequest(
|
|
||||||
@Schema(description = "已选择的提示词配置名称", example = "qa-extract-v1") String selectedConfigName,
|
|
||||||
@Schema(description = "手动输入的提示词内容", example = "请抽取文档中的问答对,并按 JSON 数组输出。") String promptText
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.labelsys.backend.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Schema(description = "保存图片BBOX请求")
|
||||||
|
public record SaveImageBboxRequest(
|
||||||
|
@NotEmpty @Schema(description = "BBOX坐标列表")
|
||||||
|
@Valid List<BboxCoordinate> bboxes,
|
||||||
|
@Schema(description = "备注") String remark
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package com.labelsys.backend.dto.request;
|
|
||||||
|
|
||||||
import com.labelsys.backend.enums.ConfigMode;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
|
|
||||||
@Schema(description = "任务模型配置请求")
|
|
||||||
public record TaskModelConfigRequest(
|
|
||||||
@Schema(description = "配置模式:SELECT 或 MANUAL", example = "SELECT") @NotNull(message = "配置模式不能为空") ConfigMode mode,
|
|
||||||
|
|
||||||
@Schema(description = "已选择的配置名称", example = "qwen-plus-extract") String selectedConfigName,
|
|
||||||
|
|
||||||
@Schema(description = "手动录入的模型配置", example = "{\"modelName\":\"qwen-plus\",\"modelUrl\":\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\"apiKey\":\"sk-demo1234\"}") @Valid ManualModelConfigRequest manualConfig) {
|
|
||||||
}
|
|
||||||
@@ -6,15 +6,10 @@ import com.labelsys.backend.enums.IndustryType;
|
|||||||
import com.labelsys.backend.enums.TaskType;
|
import com.labelsys.backend.enums.TaskType;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.Valid;
|
|
||||||
|
|
||||||
@Schema(description = "更新标注任务请求")
|
@Schema(description = "更新标注任务请求")
|
||||||
public record UpdateAnnotationTaskRequest(
|
public record UpdateAnnotationTaskRequest(
|
||||||
@Schema(description = "行业类型", example = "TRANSPORT") IndustryType industryType,
|
@Schema(description = "行业类型", example = "TRANSPORT") IndustryType industryType,
|
||||||
@Schema(description = "任务类型", example = "EXTRACT_QA") TaskType taskType,
|
@Schema(description = "任务类型", example = "EXTRACT_QA") TaskType taskType,
|
||||||
@Schema(description = "资源ID列表", example = "[191000000000000101,191000000000000102]") List<Long> resourceIds,
|
@Schema(description = "资源ID列表", example = "[191000000000000101,191000000000000102]") List<Long> resourceIds) {
|
||||||
@Schema(description = "抽取模型配置", example = "{\"mode\":\"SELECT\",\"selectedConfigName\":\"qwen-plus-extract\"}") @Valid TaskModelConfigRequest extractModel,
|
}
|
||||||
@Schema(description = "校验模型配置", example = "{\"mode\":\"MANUAL\",\"manualConfig\":{\"modelName\":\"qwen-max\",\"modelUrl\":\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\"apiKey\":\"sk-demo5678\"}}") @Valid TaskModelConfigRequest verifyModel,
|
|
||||||
@Schema(description = "抽取提示词配置", example = "{\"selectedConfigName\":\"qa-extract-v1\"}") @Valid PromptConfigOptionRequest extractPrompt,
|
|
||||||
@Schema(description = "校验提示词配置", example = "{\"promptText\":\"请对抽取结果进行逐项校验,并输出差异说明。\"}") @Valid PromptConfigOptionRequest verifyPrompt) {
|
|
||||||
}
|
|
||||||
@@ -3,12 +3,14 @@ package com.labelsys.backend.dto.response;
|
|||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import com.labelsys.backend.enums.AnnotationResultStatus;
|
||||||
|
|
||||||
@Schema(description = "标注结果响应")
|
@Schema(description = "标注结果响应")
|
||||||
public record AnnotationResultResponse(
|
public record AnnotationResultResponse(
|
||||||
@Schema(description = "结果ID", example = "191000000000000401") Long id,
|
@Schema(description = "结果ID", example = "191000000000000401") Long id,
|
||||||
@Schema(description = "任务ID", example = "191000000000000301") Long taskId,
|
@Schema(description = "任务ID", example = "191000000000000301") Long taskId,
|
||||||
@Schema(description = "资源ID", example = "191000000000000101") Long resourceId,
|
@Schema(description = "资源ID", example = "191000000000000101") Long resourceId,
|
||||||
@Schema(description = "运行态状态", example = "MANUAL_REVIEW_PENDING") String runtimeStatus,
|
@Schema(description = "标注结果状态", example = "MANUAL_REVIEW_PENDING") AnnotationResultStatus runtimeStatus,
|
||||||
@Schema(description = "是否需要人工审核", example = "true") Boolean requiresManualReview,
|
@Schema(description = "是否需要人工审核", example = "true") Boolean requiresManualReview,
|
||||||
@Schema(description = "是否已删除", example = "false") Boolean isDeleted,
|
@Schema(description = "是否已删除", example = "false") Boolean isDeleted,
|
||||||
@Schema(description = "问答存储模式", example = "INLINE") String qaContentStorageMode,
|
@Schema(description = "问答存储模式", example = "INLINE") String qaContentStorageMode,
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
package com.labelsys.backend.dto.response;
|
package com.labelsys.backend.dto.response;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.labelsys.backend.enums.IndustryType;
|
import com.labelsys.backend.enums.IndustryType;
|
||||||
import com.labelsys.backend.enums.TaskType;
|
import com.labelsys.backend.enums.TaskType;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(description = "标注任务响应")
|
@Schema(description = "标注任务响应")
|
||||||
public record AnnotationTaskResponse(
|
public record AnnotationTaskResponse(
|
||||||
@Schema(description = "任务ID", example = "191000000000000301") Long id,
|
@Schema(description = "任务ID", example = "191000000000000301") Long id,
|
||||||
@Schema(description = "任务名称", example = "运输文档问答抽取任务") String taskName,
|
@Schema(description = "任务名称", example = "运输文档问答抽取任务") String taskName,
|
||||||
@Schema(description = "行业类型:默认值transport,暂不显示", example = "transport") IndustryType industryType,
|
@Schema(description = "行业类型", example = "transport") IndustryType industryType,
|
||||||
@Schema(description = "任务类型:暂不显示", example = "EXTRACT_QA") TaskType taskType,
|
@Schema(description = "任务类型", example = "EXTRACT_QA") TaskType taskType,
|
||||||
@Schema(description = "任务状态", example = "PENDING") String taskStatus,
|
@Schema(description = "任务状态", example = "PENDING") String taskStatus,
|
||||||
@Schema(description = "资源ID列表", example = "[191000000000000101,191000000000000102]") List<Long> resourceIds,
|
@Schema(description = "资源ID列表", example = "[191000000000000101,191000000000000102]") List<Long> resourceIds,
|
||||||
@Schema(description = "抽取模型配置", example = "{\"configId\":191000000000000511,\"configName\":\"qwen-plus-extract\",\"modelName\":\"qwen-plus\",\"modelUrl\":\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\"maskedApiKey\":\"****1234\"}") TaskModelConfigResponse extractModel,
|
@Schema(description = "创建时间", example = "2026-04-27T10:20:00") LocalDateTime createdAt,
|
||||||
@Schema(description = "校验模型配置", example = "{\"configId\":191000000000000512,\"configName\":\"qwen-max-verify\",\"modelName\":\"qwen-max\",\"modelUrl\":\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\"maskedApiKey\":\"****5678\"}") TaskModelConfigResponse verifyModel,
|
@Schema(description = "更新时间", example = "2026-04-27T10:30:00") LocalDateTime updatedAt
|
||||||
@Schema(description = "抽取提示词配置", example = "{\"configId\":191000000000000521,\"configName\":\"qa-extract-v1\",\"promptText\":\"请抽取文档中的问答对,并按 JSON 数组输出。\"}") TaskPromptConfigResponse extractPrompt,
|
|
||||||
@Schema(description = "校验提示词配置", example = "{\"configId\":191000000000000522,\"configName\":\"qa-verify-v1\",\"promptText\":\"请对抽取结果进行逐项校验,并输出差异说明。\"}") TaskPromptConfigResponse verifyPrompt,
|
|
||||||
@Schema(description = "创建时间", example = "2026-04-27T10:20:00") LocalDateTime createdAt,
|
|
||||||
@Schema(description = "更新时间", example = "2026-04-27T10:30:00") LocalDateTime updatedAt
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.labelsys.backend.dto.response;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Schema(description = "图片BBOX响应")
|
||||||
|
public record ImageBboxResponse(
|
||||||
|
@Schema(description = "bbox标识ID", example = "191000000000000101") Long id,
|
||||||
|
@Schema(description = "资源ID", example = "191000000000000102") Long resourceId,
|
||||||
|
@Schema(description = "BBOX坐标列表") List<BboxCoordinateResponse> bboxes,
|
||||||
|
@Schema(description = "备注", example = "车辆检测标注") String remark,
|
||||||
|
@Schema(description = "创建人名称", example = "张审核") String creatorName,
|
||||||
|
@Schema(description = "创建时间", example = "2026-04-27T10:00:00") LocalDateTime createdAt,
|
||||||
|
@Schema(description = "更新时间", example = "2026-04-27T10:05:00") LocalDateTime updatedAt
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Schema(description = "BBOX坐标响应对象")
|
||||||
|
public record BboxCoordinateResponse(
|
||||||
|
@Schema(description = "BBOX标识", example = "bbox_001") String id,
|
||||||
|
@Schema(description = "左上角X坐标", example = "100") Integer x,
|
||||||
|
@Schema(description = "左上角Y坐标", example = "50") Integer y,
|
||||||
|
@Schema(description = "宽度", example = "200") Integer width,
|
||||||
|
@Schema(description = "高度", example = "150") Integer height,
|
||||||
|
@Schema(description = "标注标签", example = "车辆") String label
|
||||||
|
// @Schema(description = "置信度", example = "0.95") Double confidence
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package com.labelsys.backend.dto.response;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
|
|
||||||
@Schema(description = "任务模型配置响应")
|
|
||||||
public record TaskModelConfigResponse(
|
|
||||||
@Schema(description = "配置ID", example = "191000000000000511") Long configId,
|
|
||||||
@Schema(description = "配置名称", example = "qwen-plus-extract") String configName,
|
|
||||||
@Schema(description = "模型名称", example = "qwen-plus") String modelName,
|
|
||||||
@Schema(description = "模型地址", example = "https://dashscope.aliyuncs.com/compatible-mode/v1") String modelUrl,
|
|
||||||
@Schema(description = "脱敏后的模型密钥", example = "****1234") String maskedApiKey
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.labelsys.backend.dto.response;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
|
|
||||||
@Schema(description = "任务提示词配置响应")
|
|
||||||
public record TaskPromptConfigResponse(
|
|
||||||
@Schema(description = "配置ID", example = "191000000000000521") Long configId,
|
|
||||||
@Schema(description = "配置名称", example = "qa-extract-v1") String configName,
|
|
||||||
@Schema(description = "提示词内容", example = "请抽取文档中的问答对,并按 JSON 数组输出。") String promptText
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
@@ -6,12 +6,13 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
|||||||
import com.labelsys.backend.enums.IndustryType;
|
import com.labelsys.backend.enums.IndustryType;
|
||||||
import com.labelsys.backend.enums.TaskType;
|
import com.labelsys.backend.enums.TaskType;
|
||||||
import com.labelsys.backend.enums.UserRole;
|
import com.labelsys.backend.enums.UserRole;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@@ -19,30 +20,18 @@ import lombok.NoArgsConstructor;
|
|||||||
@TableName("annotation_task")
|
@TableName("annotation_task")
|
||||||
public class AnnotationTask {
|
public class AnnotationTask {
|
||||||
@TableId(type = IdType.INPUT)
|
@TableId(type = IdType.INPUT)
|
||||||
private Long id;
|
private Long id;
|
||||||
private Long companyId;
|
private Long companyId;
|
||||||
private Long creatorId;
|
private Long creatorId;
|
||||||
private UserRole creatorRole;
|
private UserRole creatorRole;
|
||||||
private String taskName;
|
private String taskName;
|
||||||
private IndustryType industryType;
|
private IndustryType industryType;
|
||||||
private TaskType taskType;
|
private TaskType taskType;
|
||||||
private Long extractModelConfigId;
|
private String taskStatus;
|
||||||
private String extractModelName;
|
private Boolean isDeleted;
|
||||||
private String extractModelUrl;
|
|
||||||
private String extractModelApiKey;
|
|
||||||
private Long verifyModelConfigId;
|
|
||||||
private String verifyModelName;
|
|
||||||
private String verifyModelUrl;
|
|
||||||
private String verifyModelApiKey;
|
|
||||||
private Long extractPromptConfigId;
|
|
||||||
private String extractPrompt;
|
|
||||||
private Long verifyPromptConfigId;
|
|
||||||
private String verifyPrompt;
|
|
||||||
private String taskStatus;
|
|
||||||
private Boolean isDeleted;
|
|
||||||
private LocalDateTime startedAt;
|
private LocalDateTime startedAt;
|
||||||
private LocalDateTime finishedAt;
|
private LocalDateTime finishedAt;
|
||||||
private String errorMessage;
|
private String errorMessage;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.labelsys.backend.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.labelsys.backend.enums.UserRole;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片BBOX标注表实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@TableName("image_bbox_annotation")
|
||||||
|
public class ImageBboxAnnotation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.INPUT)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所属公司ID
|
||||||
|
*/
|
||||||
|
private Long companyId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联的图片资源ID
|
||||||
|
*/
|
||||||
|
private Long resourceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bbox坐标信息JSON数组
|
||||||
|
*/
|
||||||
|
private String bboxJson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注说明
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人用户ID
|
||||||
|
*/
|
||||||
|
private Long creatorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人数据权限角色,默认 EMPLOYEE
|
||||||
|
*/
|
||||||
|
private UserRole creatorRole;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.labelsys.backend.enums;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
@Schema(description = "标注结果状态枚举值:MANUAL_REVIEW_PENDING待人工审核、MANUAL_REVIEW_PENDING待自动归档、ARCHIVED已归档")
|
||||||
|
public enum AnnotationResultStatus {
|
||||||
|
MANUAL_REVIEW_PENDING,
|
||||||
|
AUTO_ARCHIVE_PENDING,
|
||||||
|
ARCHIVED
|
||||||
|
}
|
||||||
@@ -2,11 +2,8 @@ package com.labelsys.backend.enums;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
@Schema(description = "模型配置模式,枚举值:SELECT:选择已有模型配置、MANUAL:手动配置新模型")
|
@Schema(description = "模型配置模式,枚举值:SELECT:选择已有模型配置、MANUAL:手动录入新模型")
|
||||||
public enum ConfigMode {
|
public enum ConfigMode {
|
||||||
@Schema(description = "从已有配置中选择")
|
|
||||||
SELECT,
|
SELECT,
|
||||||
|
|
||||||
@Schema(description = "手动录入配置")
|
|
||||||
MANUAL
|
MANUAL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.labelsys.backend.enums;
|
package com.labelsys.backend.enums;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
@Schema(description = "系统配置类型,枚举值:model:大模型配置、prompt:提示词配置, system:其他配置项")
|
||||||
public enum ConfigType {
|
public enum ConfigType {
|
||||||
MODEL,
|
MODEL,
|
||||||
PROMPT,
|
PROMPT,
|
||||||
|
|||||||
@@ -2,20 +2,11 @@ package com.labelsys.backend.enums;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
@Schema(description = "行业类型")
|
@Schema(description = "行业类型,枚举值:TRANSPORT:交通、ELECTRICITY:电力、FINANCE:金融、MEDICAL:医疗、EDUCATION:教育")
|
||||||
public enum IndustryType {
|
public enum IndustryType {
|
||||||
@Schema(description = "交通运输")
|
|
||||||
TRANSPORT,
|
TRANSPORT,
|
||||||
|
|
||||||
@Schema(description = "电力")
|
|
||||||
ELECTRICITY,
|
ELECTRICITY,
|
||||||
|
|
||||||
@Schema(description = "金融")
|
|
||||||
FINANCE,
|
FINANCE,
|
||||||
|
|
||||||
@Schema(description = "医疗")
|
|
||||||
MEDICAL,
|
MEDICAL,
|
||||||
|
|
||||||
@Schema(description = "教育")
|
|
||||||
EDUCATION
|
EDUCATION
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package com.labelsys.backend.enums;
|
package com.labelsys.backend.enums;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
@Schema(description = "问答对存储形式,枚举值:INLINE:记录中保存、EXTERNAL:外部存储器rustfs")
|
||||||
public enum QaContentStorageMode {
|
public enum QaContentStorageMode {
|
||||||
INLINE,
|
INLINE,
|
||||||
EXTERNAL;
|
EXTERNAL;
|
||||||
|
|
||||||
public static boolean isValid(String value) {
|
public static boolean isValid(String value) {
|
||||||
return Arrays.stream(values()).anyMatch(mode -> mode.name().equals(value));
|
return Arrays.stream(values()).anyMatch(mode -> mode.name().equals(value));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package com.labelsys.backend.enums;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
@Schema(description = "资源类型,枚举值:TEXT:文本、IMAGE:图片、VIDEO:视频")
|
||||||
public enum ResourceType {
|
public enum ResourceType {
|
||||||
TEXT,
|
TEXT,
|
||||||
IMAGE,
|
IMAGE,
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.labelsys.backend.enums;
|
|
||||||
|
|
||||||
public enum RuntimeResultStatus {
|
|
||||||
MANUAL_REVIEW_PENDING,
|
|
||||||
AUTO_ARCHIVE_PENDING,
|
|
||||||
ARCHIVED
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,9 @@ package com.labelsys.backend.enums;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
@Schema(description = "资源状态,暂不使用,枚举值:UPLOADED:已上传、PROCESSING:处理中、READY:已完成、ARCHIVED:已归档")
|
||||||
public enum SourceStatus {
|
public enum SourceStatus {
|
||||||
UPLOADED,
|
UPLOADED,
|
||||||
PROCESSING,
|
PROCESSING,
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package com.labelsys.backend.enums;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
@Schema(description = "任务状态枚举值:PENDING:待执行、RUNNING:处理中、COMPLETED:已完成、FAILED:失败")
|
||||||
public enum TaskStatus {
|
public enum TaskStatus {
|
||||||
PENDING,
|
PENDING,
|
||||||
RUNNING,
|
RUNNING,
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ package com.labelsys.backend.enums;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
@Schema(description = "标注任务类型")
|
@Schema(description = "标注任务类型枚举值:EXTRACT_QA:抽取任务、FINE_TUNE:微调任务")
|
||||||
public enum TaskType {
|
public enum TaskType {
|
||||||
@Schema(description = "抽取问答对")
|
|
||||||
EXTRACT_QA,
|
EXTRACT_QA,
|
||||||
|
|
||||||
@Schema(description = "大模型微调")
|
|
||||||
FINE_TUNE
|
FINE_TUNE
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.labelsys.backend.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.labelsys.backend.entity.ImageBboxAnnotation;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ImageBboxAnnotationMapper extends BaseMapper<ImageBboxAnnotation> {
|
||||||
|
ImageBboxAnnotation selectByResourceId(@Param("resourceId") Long resourceId);
|
||||||
|
|
||||||
|
int deleteByResourceId(@Param("resourceId") Long resourceId);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.labelsys.backend.service;
|
package com.labelsys.backend.service;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -15,7 +16,8 @@ import com.labelsys.backend.dto.response.AnnotationResultCompareResponse;
|
|||||||
import com.labelsys.backend.dto.response.AnnotationResultResponse;
|
import com.labelsys.backend.dto.response.AnnotationResultResponse;
|
||||||
import com.labelsys.backend.entity.AnnotationResult;
|
import com.labelsys.backend.entity.AnnotationResult;
|
||||||
import com.labelsys.backend.entity.SourceResource;
|
import com.labelsys.backend.entity.SourceResource;
|
||||||
import com.labelsys.backend.enums.RuntimeResultStatus;
|
import com.labelsys.backend.enums.AnnotationResultStatus;
|
||||||
|
import com.labelsys.backend.enums.QaContentStorageMode;
|
||||||
import com.labelsys.backend.mapper.AnnotationResultMapper;
|
import com.labelsys.backend.mapper.AnnotationResultMapper;
|
||||||
import com.labelsys.backend.mapper.SourceResourceMapper;
|
import com.labelsys.backend.mapper.SourceResourceMapper;
|
||||||
|
|
||||||
@@ -30,17 +32,18 @@ public class AnnotationResultService {
|
|||||||
private final AnnotationResultMapper annotationResultMapper;
|
private final AnnotationResultMapper annotationResultMapper;
|
||||||
private final SourceResourceMapper sourceResourceMapper;
|
private final SourceResourceMapper sourceResourceMapper;
|
||||||
private final DataPermissionService dataPermissionService;
|
private final DataPermissionService dataPermissionService;
|
||||||
|
private final ObjectStorageService objectStorageService;
|
||||||
|
|
||||||
public PageResult<AnnotationResultResponse> pageResults(LoginUser currentUser, AnnotationResultPageQuery query) {
|
public PageResult<AnnotationResultResponse> pageResults(LoginUser currentUser, AnnotationResultPageQuery query) {
|
||||||
List<String> allowedRoles = dataPermissionService.getAllowedRoles(currentUser);
|
List<String> allowedRoles = dataPermissionService.getAllowedRoles(currentUser);
|
||||||
boolean shouldFilterByUserId = dataPermissionService.shouldFilterByUserId(currentUser);
|
boolean shouldFilterByUserId = dataPermissionService.shouldFilterByUserId(currentUser);
|
||||||
|
|
||||||
LambdaQueryWrapper<AnnotationResult> wrapper =
|
LambdaQueryWrapper<AnnotationResult> wrapper = new LambdaQueryWrapper<AnnotationResult>()
|
||||||
new LambdaQueryWrapper<AnnotationResult>().eq(AnnotationResult::getCompanyId, currentUser.companyId())
|
.eq(AnnotationResult::getCompanyId, currentUser.companyId())
|
||||||
.eq(query.taskId() != null, AnnotationResult::getTaskId, query.taskId())
|
.eq(query.taskId() != null, AnnotationResult::getTaskId, query.taskId())
|
||||||
.eq(query.resourceId() != null, AnnotationResult::getResourceId, query.resourceId())
|
.eq(query.resourceId() != null, AnnotationResult::getResourceId, query.resourceId())
|
||||||
.eq(query.requiresManualReview() != null, AnnotationResult::getRequiresManualReview,
|
.eq(query.requiresManualReview() != null, AnnotationResult::getRequiresManualReview,
|
||||||
query.requiresManualReview());
|
query.requiresManualReview());
|
||||||
|
|
||||||
if (shouldFilterByUserId) {
|
if (shouldFilterByUserId) {
|
||||||
wrapper.eq(AnnotationResult::getCreatorId, currentUser.userId());
|
wrapper.eq(AnnotationResult::getCreatorId, currentUser.userId());
|
||||||
@@ -54,49 +57,104 @@ public class AnnotationResultService {
|
|||||||
Page<AnnotationResult> resultPage = annotationResultMapper.selectPage(page, wrapper);
|
Page<AnnotationResult> resultPage = annotationResultMapper.selectPage(page, wrapper);
|
||||||
|
|
||||||
List<AnnotationResultResponse> records = resultPage.getRecords().stream().map(this::toResponse)
|
List<AnnotationResultResponse> records = resultPage.getRecords().stream().map(this::toResponse)
|
||||||
.filter(response -> query.runtimeStatus() == null || query.runtimeStatus().equals(response.runtimeStatus()))
|
.filter(response -> query.runtimeStatus() == null
|
||||||
.toList();
|
|| query.runtimeStatus().equals(response.runtimeStatus()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
return new PageResult<>(records, resultPage.getTotal(), (int)resultPage.getCurrent(),
|
return new PageResult<>(records, resultPage.getTotal(), (int) resultPage.getCurrent(),
|
||||||
(int)resultPage.getSize());
|
(int) resultPage.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnnotationResultResponse getResult(LoginUser currentUser, Long resultId) {
|
public AnnotationResultResponse getResult(LoginUser currentUser, Long resultId) {
|
||||||
AnnotationResult result = annotationResultMapper.findActiveByIdAndCompanyId(resultId, currentUser.companyId());
|
AnnotationResult result = annotationResultMapper.findActiveByIdAndCompanyId(resultId, currentUser.companyId());
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
log.warn("Result not found or cross-tenant access attempt: resultId={}, companyId={}, userId={}", resultId,
|
log.warn("Result not found or cross-tenant access attempt: resultId={}, companyId={}, userId={}", resultId,
|
||||||
currentUser.companyId(), currentUser.userId());
|
currentUser.companyId(), currentUser.userId());
|
||||||
throw new BusinessException(ResultCode.NOT_FOUND, "结果不存在");
|
throw new BusinessException(ResultCode.NOT_FOUND, "结果不存在");
|
||||||
}
|
}
|
||||||
return toResponse(result);
|
return toResponse(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AnnotationResultResponse toResponse(AnnotationResult result) {
|
||||||
|
return new AnnotationResultResponse(result.getId(), result.getTaskId(), result.getResourceId(),
|
||||||
|
deriveStatus(result), result.getRequiresManualReview(), result.getIsDeleted(),
|
||||||
|
result.getQaContentStorageMode(), result.getReviewComment(), result.getReviewedAt(),
|
||||||
|
result.getCreatedAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnnotationResultStatus deriveStatus(AnnotationResult result) {
|
||||||
|
if (Boolean.TRUE.equals(result.getIsDeleted())) {
|
||||||
|
return AnnotationResultStatus.ARCHIVED;
|
||||||
|
}
|
||||||
|
if (Boolean.TRUE.equals(result.getRequiresManualReview())) {
|
||||||
|
return AnnotationResultStatus.MANUAL_REVIEW_PENDING;
|
||||||
|
}
|
||||||
|
return AnnotationResultStatus.AUTO_ARCHIVE_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
public AnnotationResultCompareResponse compareResult(LoginUser currentUser, Long resultId) {
|
public AnnotationResultCompareResponse compareResult(LoginUser currentUser, Long resultId) {
|
||||||
AnnotationResult result = annotationResultMapper.findActiveByIdAndCompanyId(resultId, currentUser.companyId());
|
AnnotationResult result = annotationResultMapper.findActiveByIdAndCompanyId(resultId, currentUser.companyId());
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
log.warn("Result not found or cross-tenant access attempt: resultId={}, companyId={}, userId={}", resultId,
|
log.warn("Result not found or cross-tenant access attempt: resultId={}, companyId={}, userId={}", resultId,
|
||||||
currentUser.companyId(), currentUser.userId());
|
currentUser.companyId(), currentUser.userId());
|
||||||
throw new BusinessException(ResultCode.NOT_FOUND, "结果不存在");
|
throw new BusinessException(ResultCode.NOT_FOUND, "结果不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String qaContentJson = resolveQaContent(result);
|
||||||
|
|
||||||
SourceResource resource = sourceResourceMapper.selectById(result.getResourceId());
|
SourceResource resource = sourceResourceMapper.selectById(result.getResourceId());
|
||||||
return new AnnotationResultCompareResponse(result.getId(), result.getTaskId(), result.getResourceId(),
|
return new AnnotationResultCompareResponse(
|
||||||
result.getQaContentJson(), result.getDiffSummary(), result.getQaContentStorageMode(),
|
result.getId(),
|
||||||
result.getQaContentFilePath(), resource == null ? null : resource.getFilePath());
|
result.getTaskId(),
|
||||||
|
result.getResourceId(),
|
||||||
|
qaContentJson,
|
||||||
|
result.getDiffSummary(),
|
||||||
|
result.getQaContentStorageMode(),
|
||||||
|
result.getQaContentFilePath(),
|
||||||
|
resource == null ? null : resource.getFilePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnnotationResultResponse toResponse(AnnotationResult result) {
|
private String resolveQaContent(AnnotationResult result) {
|
||||||
return new AnnotationResultResponse(result.getId(), result.getTaskId(), result.getResourceId(),
|
if (QaContentStorageMode.EXTERNAL.name().equals(result.getQaContentStorageMode())) {
|
||||||
deriveStatus(result), result.getRequiresManualReview(), result.getIsDeleted(),
|
if (result.getQaContentFilePath() == null || result.getQaContentFilePath().isBlank()) {
|
||||||
result.getQaContentStorageMode(), result.getReviewComment(), result.getReviewedAt(), result.getCreatedAt());
|
log.warn("External storage mode but file path is empty, resultId={}", result.getId());
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String filePath = result.getQaContentFilePath();
|
||||||
|
String bucketName = extractBucketName(filePath);
|
||||||
|
String objectKey = extractObjectKey(filePath);
|
||||||
|
byte[] content = objectStorageService.download(bucketName, objectKey);
|
||||||
|
return new String(content, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to download external qa content, resultId={}, filePath={}",
|
||||||
|
result.getId(), result.getQaContentFilePath(), e);
|
||||||
|
throw new BusinessException(ResultCode.ERROR, "下载问答内容失败");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return result.getQaContentJson() != null ? result.getQaContentJson() : "{}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String deriveStatus(AnnotationResult result) {
|
private String extractBucketName(String filePath) {
|
||||||
if (Boolean.TRUE.equals(result.getIsDeleted())) {
|
if (filePath.startsWith("/")) {
|
||||||
return RuntimeResultStatus.ARCHIVED.name();
|
filePath = filePath.substring(1);
|
||||||
}
|
}
|
||||||
if (Boolean.TRUE.equals(result.getRequiresManualReview())) {
|
int firstSlash = filePath.indexOf("/");
|
||||||
return RuntimeResultStatus.MANUAL_REVIEW_PENDING.name();
|
if (firstSlash > 0) {
|
||||||
|
return filePath.substring(0, firstSlash);
|
||||||
}
|
}
|
||||||
return RuntimeResultStatus.AUTO_ARCHIVE_PENDING.name();
|
throw new BusinessException(ResultCode.BAD_REQUEST, "无效的文件路径格式");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractObjectKey(String filePath) {
|
||||||
|
if (filePath.startsWith("/")) {
|
||||||
|
filePath = filePath.substring(1);
|
||||||
|
}
|
||||||
|
int firstSlash = filePath.indexOf("/");
|
||||||
|
if (firstSlash > 0 && firstSlash < filePath.length() - 1) {
|
||||||
|
return filePath.substring(firstSlash + 1);
|
||||||
|
}
|
||||||
|
throw new BusinessException(ResultCode.BAD_REQUEST, "无效的文件路径格式");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,5 @@
|
|||||||
package com.labelsys.backend.service;
|
package com.labelsys.backend.service;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.labelsys.backend.common.ResultCode;
|
import com.labelsys.backend.common.ResultCode;
|
||||||
@@ -20,12 +10,9 @@ import com.labelsys.backend.dto.request.AnnotationTaskPageQuery;
|
|||||||
import com.labelsys.backend.dto.request.CreateAnnotationTaskRequest;
|
import com.labelsys.backend.dto.request.CreateAnnotationTaskRequest;
|
||||||
import com.labelsys.backend.dto.request.UpdateAnnotationTaskRequest;
|
import com.labelsys.backend.dto.request.UpdateAnnotationTaskRequest;
|
||||||
import com.labelsys.backend.dto.response.AnnotationTaskResponse;
|
import com.labelsys.backend.dto.response.AnnotationTaskResponse;
|
||||||
import com.labelsys.backend.dto.response.TaskModelConfigResponse;
|
|
||||||
import com.labelsys.backend.dto.response.TaskPromptConfigResponse;
|
|
||||||
import com.labelsys.backend.entity.AnnotationTask;
|
import com.labelsys.backend.entity.AnnotationTask;
|
||||||
import com.labelsys.backend.entity.AnnotationTaskResource;
|
import com.labelsys.backend.entity.AnnotationTaskResource;
|
||||||
import com.labelsys.backend.entity.SourceResource;
|
import com.labelsys.backend.entity.SourceResource;
|
||||||
import com.labelsys.backend.entity.SysConfig;
|
|
||||||
import com.labelsys.backend.enums.IndustryType;
|
import com.labelsys.backend.enums.IndustryType;
|
||||||
import com.labelsys.backend.enums.SourceStatus;
|
import com.labelsys.backend.enums.SourceStatus;
|
||||||
import com.labelsys.backend.enums.TaskStatus;
|
import com.labelsys.backend.enums.TaskStatus;
|
||||||
@@ -33,51 +20,52 @@ import com.labelsys.backend.enums.TaskType;
|
|||||||
import com.labelsys.backend.mapper.AnnotationTaskMapper;
|
import com.labelsys.backend.mapper.AnnotationTaskMapper;
|
||||||
import com.labelsys.backend.mapper.AnnotationTaskResourceMapper;
|
import com.labelsys.backend.mapper.AnnotationTaskResourceMapper;
|
||||||
import com.labelsys.backend.mapper.SourceResourceMapper;
|
import com.labelsys.backend.mapper.SourceResourceMapper;
|
||||||
import com.labelsys.backend.mapper.SysConfigMapper;
|
|
||||||
import com.labelsys.backend.util.IdGenerator;
|
import com.labelsys.backend.util.IdGenerator;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class AnnotationTaskService {
|
public class AnnotationTaskService {
|
||||||
|
|
||||||
private final AnnotationTaskMapper annotationTaskMapper;
|
private final AnnotationTaskMapper annotationTaskMapper;
|
||||||
private final AnnotationTaskResourceMapper annotationTaskResourceMapper;
|
private final AnnotationTaskResourceMapper annotationTaskResourceMapper;
|
||||||
private final SourceResourceMapper sourceResourceMapper;
|
private final SourceResourceMapper sourceResourceMapper;
|
||||||
private final SysConfigMapper sysConfigMapper;
|
private final DataPermissionService dataPermissionService;
|
||||||
private final SysConfigService sysConfigService;
|
|
||||||
private final DataPermissionService dataPermissionService;
|
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public AnnotationTaskResponse createTask(LoginUser currentUser, CreateAnnotationTaskRequest request) {
|
public AnnotationTaskResponse createTask(LoginUser currentUser, CreateAnnotationTaskRequest request) {
|
||||||
List<SourceResource> resources = loadAndValidateResources(currentUser, request.resourceIds());
|
List<SourceResource> resources = loadAndValidateResources(currentUser, request.resourceIds());
|
||||||
SysConfigService.ResolvedModelConfig extractModel = sysConfigService.resolveModelConfig(currentUser,
|
|
||||||
request.extractModel());
|
|
||||||
SysConfigService.ResolvedModelConfig verifyModel = sysConfigService.resolveModelConfig(currentUser,
|
|
||||||
request.verifyModel());
|
|
||||||
SysConfigService.ResolvedPromptConfig extractPrompt = sysConfigService.resolvePromptConfig(currentUser,
|
|
||||||
request.extractPrompt());
|
|
||||||
SysConfigService.ResolvedPromptConfig verifyPrompt = sysConfigService.resolvePromptConfig(currentUser,
|
|
||||||
request.verifyPrompt());
|
|
||||||
|
|
||||||
AnnotationTask task = AnnotationTask.builder().id(IdGenerator.nextId()).companyId(currentUser.companyId())
|
AnnotationTask task = AnnotationTask.builder()
|
||||||
.creatorId(currentUser.userId()).creatorRole(currentUser.role()).taskName(request.taskName())
|
.id(IdGenerator.nextId())
|
||||||
.industryType(defaultIndustryType(request.industryType())).taskType(defaultTaskType(request.taskType()))
|
.companyId(currentUser.companyId())
|
||||||
.extractModelConfigId(extractModel.configId()).extractModelName(extractModel.modelName())
|
.creatorId(currentUser.userId())
|
||||||
.extractModelUrl(extractModel.modelUrl()).extractModelApiKey(extractModel.apiKey())
|
.creatorRole(currentUser.role())
|
||||||
.verifyModelConfigId(verifyModel.configId()).verifyModelName(verifyModel.modelName())
|
.taskName(request.taskName())
|
||||||
.verifyModelUrl(verifyModel.modelUrl()).verifyModelApiKey(verifyModel.apiKey())
|
.industryType(defaultIndustryType(request.industryType()))
|
||||||
.extractPromptConfigId(extractPrompt.configId()).extractPrompt(extractPrompt.promptText())
|
.taskType(defaultTaskType(request.taskType()))
|
||||||
.verifyPromptConfigId(verifyPrompt.configId()).verifyPrompt(verifyPrompt.promptText())
|
.taskStatus(TaskStatus.PENDING.name())
|
||||||
.taskStatus(TaskStatus.PENDING.name()).isDeleted(false).build();
|
.isDeleted(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
annotationTaskMapper.insert(task);
|
annotationTaskMapper.insert(task);
|
||||||
saveTaskBindings(task.getId(), currentUser.companyId(), resources);
|
saveTaskBindings(task.getId(), currentUser.companyId(), resources);
|
||||||
|
|
||||||
log.info("created annotation task, companyId={}, userId={}, taskId={}, resourceCount={}",
|
log.info("created annotation task, companyId={}, userId={}, taskId={}, resourceCount={}",
|
||||||
currentUser.companyId(), currentUser.userId(), task.getId(), resources.size());
|
currentUser.companyId(), currentUser.userId(), task.getId(), resources.size());
|
||||||
return buildTaskResponse(task, resourceIds(resources), extractModel, verifyModel, extractPrompt, verifyPrompt);
|
|
||||||
|
return buildTaskResponse(task, resourceIds(resources));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -95,45 +83,13 @@ public class AnnotationTaskService {
|
|||||||
List<Long> currentResourceIds = normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(taskId));
|
List<Long> currentResourceIds = normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(taskId));
|
||||||
List<Long> targetResourceIds = normalizeIds(request.resourceIds());
|
List<Long> targetResourceIds = normalizeIds(request.resourceIds());
|
||||||
resourcesChanged = !currentResourceIds.equals(targetResourceIds);
|
resourcesChanged = !currentResourceIds.equals(targetResourceIds);
|
||||||
|
|
||||||
if (TaskStatus.RUNNING.name().equals(task.getTaskStatus()) && resourcesChanged) {
|
if (TaskStatus.RUNNING.name().equals(task.getTaskStatus()) && resourcesChanged) {
|
||||||
throw new BusinessException(ResultCode.CONFLICT, "运行中的任务不允许修改资源");
|
throw new BusinessException(ResultCode.CONFLICT, "运行中的任务不允许修改资源");
|
||||||
}
|
}
|
||||||
resources = loadAndValidateResources(currentUser, request.resourceIds());
|
resources = loadAndValidateResources(currentUser, request.resourceIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
SysConfigService.ResolvedModelConfig extractModel = null;
|
|
||||||
SysConfigService.ResolvedModelConfig verifyModel = null;
|
|
||||||
SysConfigService.ResolvedPromptConfig extractPrompt = null;
|
|
||||||
SysConfigService.ResolvedPromptConfig verifyPrompt = null;
|
|
||||||
|
|
||||||
if (request.extractModel() != null) {
|
|
||||||
extractModel = sysConfigService.resolveModelConfig(currentUser, request.extractModel());
|
|
||||||
task.setExtractModelConfigId(extractModel.configId());
|
|
||||||
task.setExtractModelName(extractModel.modelName());
|
|
||||||
task.setExtractModelUrl(extractModel.modelUrl());
|
|
||||||
task.setExtractModelApiKey(extractModel.apiKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.verifyModel() != null) {
|
|
||||||
verifyModel = sysConfigService.resolveModelConfig(currentUser, request.verifyModel());
|
|
||||||
task.setVerifyModelConfigId(verifyModel.configId());
|
|
||||||
task.setVerifyModelName(verifyModel.modelName());
|
|
||||||
task.setVerifyModelUrl(verifyModel.modelUrl());
|
|
||||||
task.setVerifyModelApiKey(verifyModel.apiKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.extractPrompt() != null) {
|
|
||||||
extractPrompt = sysConfigService.resolvePromptConfig(currentUser, request.extractPrompt());
|
|
||||||
task.setExtractPromptConfigId(extractPrompt.configId());
|
|
||||||
task.setExtractPrompt(extractPrompt.promptText());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.verifyPrompt() != null) {
|
|
||||||
verifyPrompt = sysConfigService.resolvePromptConfig(currentUser, request.verifyPrompt());
|
|
||||||
task.setVerifyPromptConfigId(verifyPrompt.configId());
|
|
||||||
task.setVerifyPrompt(verifyPrompt.promptText());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.industryType() != null) {
|
if (request.industryType() != null) {
|
||||||
task.setIndustryType(request.industryType());
|
task.setIndustryType(request.industryType());
|
||||||
}
|
}
|
||||||
@@ -155,10 +111,7 @@ public class AnnotationTaskService {
|
|||||||
List<Long> finalResourceIds = resources != null ? resourceIds(resources)
|
List<Long> finalResourceIds = resources != null ? resourceIds(resources)
|
||||||
: normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(taskId));
|
: normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(taskId));
|
||||||
|
|
||||||
if (extractModel == null || verifyModel == null || extractPrompt == null || verifyPrompt == null) {
|
return buildTaskResponse(task, finalResourceIds);
|
||||||
return buildTaskResponse(task, finalResourceIds);
|
|
||||||
}
|
|
||||||
return buildTaskResponse(task, finalResourceIds, extractModel, verifyModel, extractPrompt, verifyPrompt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnnotationTaskResponse getTask(LoginUser currentUser, Long taskId) {
|
public AnnotationTaskResponse getTask(LoginUser currentUser, Long taskId) {
|
||||||
@@ -195,7 +148,7 @@ public class AnnotationTaskService {
|
|||||||
List<AnnotationTaskResponse> records = resultPage.getRecords().stream()
|
List<AnnotationTaskResponse> records = resultPage.getRecords().stream()
|
||||||
.filter(task -> query.resourceId() == null
|
.filter(task -> query.resourceId() == null
|
||||||
|| annotationTaskResourceMapper.listResourceIdsByTaskId(task.getId())
|
|| annotationTaskResourceMapper.listResourceIdsByTaskId(task.getId())
|
||||||
.contains(query.resourceId()))
|
.contains(query.resourceId()))
|
||||||
.map(task -> buildTaskResponse(task,
|
.map(task -> buildTaskResponse(task,
|
||||||
normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(task.getId()))))
|
normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(task.getId()))))
|
||||||
.toList();
|
.toList();
|
||||||
@@ -245,39 +198,26 @@ public class AnnotationTaskService {
|
|||||||
|
|
||||||
private void saveTaskBindings(Long taskId, Long companyId, List<SourceResource> resources) {
|
private void saveTaskBindings(Long taskId, Long companyId, List<SourceResource> resources) {
|
||||||
for (SourceResource resource : resources) {
|
for (SourceResource resource : resources) {
|
||||||
annotationTaskResourceMapper.insert(AnnotationTaskResource.builder().id(IdGenerator.nextId())
|
annotationTaskResourceMapper.insert(AnnotationTaskResource.builder()
|
||||||
.companyId(companyId).taskId(taskId).resourceId(resource.getId()).build());
|
.id(IdGenerator.nextId())
|
||||||
|
.companyId(companyId)
|
||||||
|
.taskId(taskId)
|
||||||
|
.resourceId(resource.getId())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnnotationTaskResponse buildTaskResponse(AnnotationTask task, List<Long> resourceIds,
|
|
||||||
SysConfigService.ResolvedModelConfig extractModel, SysConfigService.ResolvedModelConfig verifyModel,
|
|
||||||
SysConfigService.ResolvedPromptConfig extractPrompt, SysConfigService.ResolvedPromptConfig verifyPrompt) {
|
|
||||||
return new AnnotationTaskResponse(task.getId(), task.getTaskName(), task.getIndustryType(), task.getTaskType(),
|
|
||||||
task.getTaskStatus(), resourceIds, sysConfigService.toResponse(extractModel),
|
|
||||||
sysConfigService.toResponse(verifyModel), sysConfigService.toResponse(extractPrompt),
|
|
||||||
sysConfigService.toResponse(verifyPrompt), task.getCreatedAt(), task.getUpdatedAt());
|
|
||||||
}
|
|
||||||
|
|
||||||
private AnnotationTaskResponse buildTaskResponse(AnnotationTask task, List<Long> resourceIds) {
|
private AnnotationTaskResponse buildTaskResponse(AnnotationTask task, List<Long> resourceIds) {
|
||||||
String extractModelConfigName = resolveConfigName(task.getExtractModelConfigId());
|
return new AnnotationTaskResponse(
|
||||||
String verifyModelConfigName = resolveConfigName(task.getVerifyModelConfigId());
|
task.getId(),
|
||||||
String extractPromptConfigName = resolveConfigName(task.getExtractPromptConfigId());
|
task.getTaskName(),
|
||||||
String verifyPromptConfigName = resolveConfigName(task.getVerifyPromptConfigId());
|
task.getIndustryType(),
|
||||||
|
task.getTaskType(),
|
||||||
return new AnnotationTaskResponse(task.getId(), task.getTaskName(), task.getIndustryType(), task.getTaskType(),
|
task.getTaskStatus(),
|
||||||
task.getTaskStatus(), resourceIds,
|
resourceIds,
|
||||||
new TaskModelConfigResponse(task.getExtractModelConfigId(), extractModelConfigName,
|
task.getCreatedAt(),
|
||||||
task.getExtractModelName(),
|
task.getUpdatedAt()
|
||||||
task.getExtractModelUrl(), maskSecret(task.getExtractModelApiKey())),
|
);
|
||||||
new TaskModelConfigResponse(task.getVerifyModelConfigId(), verifyModelConfigName,
|
|
||||||
task.getVerifyModelName(),
|
|
||||||
task.getVerifyModelUrl(), maskSecret(task.getVerifyModelApiKey())),
|
|
||||||
new TaskPromptConfigResponse(task.getExtractPromptConfigId(), extractPromptConfigName,
|
|
||||||
task.getExtractPrompt()),
|
|
||||||
new TaskPromptConfigResponse(task.getVerifyPromptConfigId(), verifyPromptConfigName,
|
|
||||||
task.getVerifyPrompt()),
|
|
||||||
task.getCreatedAt(), task.getUpdatedAt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Long> resourceIds(List<SourceResource> resources) {
|
private List<Long> resourceIds(List<SourceResource> resources) {
|
||||||
@@ -304,22 +244,4 @@ public class AnnotationTaskService {
|
|||||||
private TaskType defaultTaskType(TaskType taskType) {
|
private TaskType defaultTaskType(TaskType taskType) {
|
||||||
return taskType != null ? taskType : TaskType.EXTRACT_QA;
|
return taskType != null ? taskType : TaskType.EXTRACT_QA;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private String maskSecret(String secret) {
|
|
||||||
if (!StringUtils.hasText(secret)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (secret.length() <= 4) {
|
|
||||||
return "****";
|
|
||||||
}
|
|
||||||
return "****" + secret.substring(secret.length() - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolveConfigName(Long configId) {
|
|
||||||
if (configId == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
SysConfig config = sysConfigMapper.selectById(configId);
|
|
||||||
return config != null ? config.getConfigName() : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,4 +5,6 @@ public interface ObjectStorageService {
|
|||||||
String upload(String bucketName, String objectKey, byte[] content, String contentType);
|
String upload(String bucketName, String objectKey, byte[] content, String contentType);
|
||||||
|
|
||||||
void delete(String bucketName, String objectKey);
|
void delete(String bucketName, String objectKey);
|
||||||
|
|
||||||
|
byte[] download(String bucketName, String objectKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import com.labelsys.backend.common.exception.BusinessException;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import software.amazon.awssdk.core.sync.RequestBody;
|
import software.amazon.awssdk.core.sync.RequestBody;
|
||||||
|
import software.amazon.awssdk.core.sync.ResponseTransformer;
|
||||||
import software.amazon.awssdk.services.s3.S3Client;
|
import software.amazon.awssdk.services.s3.S3Client;
|
||||||
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
|
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
|
||||||
|
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
|
||||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -42,4 +44,17 @@ public class RustfsObjectStorageService implements ObjectStorageService {
|
|||||||
throw new BusinessException(ResultCode.ERROR, "对象存储删除失败");
|
throw new BusinessException(ResultCode.ERROR, "对象存储删除失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] download(String bucketName, String objectKey) {
|
||||||
|
try {
|
||||||
|
GetObjectRequest request = GetObjectRequest.builder()
|
||||||
|
.bucket(bucketName)
|
||||||
|
.key(objectKey)
|
||||||
|
.build();
|
||||||
|
return s3Client.getObject(request, ResponseTransformer.toBytes()).asByteArray();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new BusinessException(ResultCode.ERROR, "对象存储下载失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,56 @@
|
|||||||
package com.labelsys.backend.service;
|
package com.labelsys.backend.service;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.labelsys.backend.common.ResultCode;
|
import com.labelsys.backend.common.ResultCode;
|
||||||
import com.labelsys.backend.common.exception.BusinessException;
|
import com.labelsys.backend.common.exception.BusinessException;
|
||||||
|
import com.labelsys.backend.config.ObjectStorageProperties;
|
||||||
import com.labelsys.backend.context.LoginUser;
|
import com.labelsys.backend.context.LoginUser;
|
||||||
import com.labelsys.backend.dto.common.PageResult;
|
import com.labelsys.backend.dto.common.PageResult;
|
||||||
|
import com.labelsys.backend.dto.request.SaveImageBboxRequest;
|
||||||
import com.labelsys.backend.dto.request.SourceResourcePageQuery;
|
import com.labelsys.backend.dto.request.SourceResourcePageQuery;
|
||||||
import com.labelsys.backend.dto.request.SourceUploadRequest;
|
import com.labelsys.backend.dto.request.SourceUploadRequest;
|
||||||
|
import com.labelsys.backend.dto.response.ImageBboxResponse;
|
||||||
import com.labelsys.backend.dto.response.SourceResourceResponse;
|
import com.labelsys.backend.dto.response.SourceResourceResponse;
|
||||||
import com.labelsys.backend.dto.response.SourceUploadResponse;
|
import com.labelsys.backend.dto.response.SourceUploadResponse;
|
||||||
|
import com.labelsys.backend.entity.ImageBboxAnnotation;
|
||||||
import com.labelsys.backend.entity.SourceResource;
|
import com.labelsys.backend.entity.SourceResource;
|
||||||
import com.labelsys.backend.entity.SysUser;
|
import com.labelsys.backend.entity.SysUser;
|
||||||
import com.labelsys.backend.enums.ResourceType;
|
import com.labelsys.backend.enums.ResourceType;
|
||||||
import com.labelsys.backend.enums.SourceStatus;
|
import com.labelsys.backend.enums.SourceStatus;
|
||||||
import com.labelsys.backend.mapper.AnnotationTaskResourceMapper;
|
import com.labelsys.backend.mapper.AnnotationTaskResourceMapper;
|
||||||
|
import com.labelsys.backend.mapper.ImageBboxAnnotationMapper;
|
||||||
import com.labelsys.backend.mapper.SourceResourceMapper;
|
import com.labelsys.backend.mapper.SourceResourceMapper;
|
||||||
import com.labelsys.backend.mapper.SysUserMapper;
|
import com.labelsys.backend.mapper.SysUserMapper;
|
||||||
import com.labelsys.backend.util.IdGenerator;
|
import com.labelsys.backend.util.IdGenerator;
|
||||||
import com.labelsys.backend.util.ObjectStoragePathBuilder;
|
import com.labelsys.backend.util.ObjectStoragePathBuilder;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SourceResourceService {
|
public class SourceResourceService {
|
||||||
|
|
||||||
private final SourceResourceMapper sourceResourceMapper;
|
private final SourceResourceMapper sourceResourceMapper;
|
||||||
private final AnnotationTaskResourceMapper annotationTaskResourceMapper;
|
private final AnnotationTaskResourceMapper annotationTaskResourceMapper;
|
||||||
private final SysUserMapper sysUserMapper;
|
private final SysUserMapper sysUserMapper;
|
||||||
private final DataPermissionService dataPermissionService;
|
private final DataPermissionService dataPermissionService;
|
||||||
private final ObjectStorageService objectStorageService;
|
private final ObjectStorageService objectStorageService;
|
||||||
private final com.labelsys.backend.config.ObjectStorageProperties objectStorageProperties;
|
private final ObjectStorageProperties objectStorageProperties;
|
||||||
|
private final ImageBboxAnnotationMapper imageBboxAnnotationMapper;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public SourceUploadResponse upload(LoginUser currentUser, SourceUploadRequest request) {
|
public SourceUploadResponse upload(LoginUser currentUser, SourceUploadRequest request) {
|
||||||
@@ -53,37 +62,39 @@ public class SourceResourceService {
|
|||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "资源类型非法");
|
throw new BusinessException(ResultCode.BAD_REQUEST, "资源类型非法");
|
||||||
}
|
}
|
||||||
String resourceName =
|
String resourceName =
|
||||||
StringUtils.hasText(request.getResourceName()) ? request.getResourceName() : file.getOriginalFilename();
|
StringUtils.hasText(request.getResourceName()) ? request.getResourceName() : file.getOriginalFilename();
|
||||||
SourceResource existingResource =
|
SourceResource existingResource =
|
||||||
sourceResourceMapper.selectByCompanyIdAndResourceName(currentUser.companyId(), resourceName);
|
sourceResourceMapper.selectByCompanyIdAndResourceName(currentUser.companyId(), resourceName);
|
||||||
if (existingResource != null) {
|
if (existingResource != null) {
|
||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "资源名称已存在:" + resourceName);
|
throw new BusinessException(ResultCode.BAD_REQUEST, "资源名称已存在:" + resourceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
long resourceId = IdGenerator.nextId();
|
long resourceId = IdGenerator.nextId();
|
||||||
String extension = resolveExtension(file.getOriginalFilename(), request.getResourceType());
|
String extension = resolveExtension(file.getOriginalFilename(), request.getResourceType());
|
||||||
String objectKey = ObjectStoragePathBuilder.sourceObjectKey(currentUser.companyId(), request.getResourceType(),
|
String objectKey = ObjectStoragePathBuilder.sourceObjectKey(currentUser.companyId(), request.getResourceType(),
|
||||||
resourceId, extension);
|
resourceId, extension);
|
||||||
try {
|
try {
|
||||||
objectStorageService.upload(objectStorageProperties.getSourceBucket(), objectKey, file.getBytes(),
|
objectStorageService.upload(objectStorageProperties.getSourceBucket(), objectKey, file.getBytes(),
|
||||||
file.getContentType());
|
file.getContentType());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "读取上传文件失败");
|
throw new BusinessException(ResultCode.BAD_REQUEST, "读取上传文件失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceResource resource = SourceResource.builder().id(resourceId).companyId(currentUser.companyId())
|
SourceResource resource = SourceResource.builder().id(resourceId).companyId(currentUser.companyId())
|
||||||
.creatorId(currentUser.userId()).creatorRole(currentUser.role())
|
.creatorId(currentUser.userId()).creatorRole(currentUser.role())
|
||||||
.resourceName(
|
.resourceName(
|
||||||
StringUtils.hasText(request.getResourceName()) ? request.getResourceName() : file.getOriginalFilename())
|
StringUtils.hasText(request.getResourceName()) ?
|
||||||
.resourceType(request.getResourceType()).bucketName(objectStorageProperties.getSourceBucket())
|
request.getResourceName() :
|
||||||
.filePath(objectKey).fileSize(file.getSize()).sourceStatus(SourceStatus.READY.name())
|
file.getOriginalFilename())
|
||||||
.storageProvider("rustfs").remark(request.getRemark()).build();
|
.resourceType(request.getResourceType()).bucketName(objectStorageProperties.getSourceBucket())
|
||||||
|
.filePath(objectKey).fileSize(file.getSize()).sourceStatus(SourceStatus.READY.name())
|
||||||
|
.storageProvider("rustfs").remark(request.getRemark()).build();
|
||||||
sourceResourceMapper.insert(resource);
|
sourceResourceMapper.insert(resource);
|
||||||
log.info("uploaded source resource, companyId={}, userId={}, resourceId={}", currentUser.companyId(),
|
log.info("uploaded source resource, companyId={}, userId={}, resourceId={}", currentUser.companyId(),
|
||||||
currentUser.userId(), resourceId);
|
currentUser.userId(), resourceId);
|
||||||
return new SourceUploadResponse(resource.getId(), resource.getResourceName(), resource.getResourceType(),
|
return new SourceUploadResponse(resource.getId(), resource.getResourceName(), resource.getResourceType(),
|
||||||
resource.getBucketName(), resource.getFilePath(), resource.getFileSize(), resource.getSourceStatus(),
|
resource.getBucketName(), resource.getFilePath(), resource.getFileSize(), resource.getSourceStatus(),
|
||||||
resource.getCreatedAt());
|
resource.getCreatedAt());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PageResult<SourceResourceResponse> pageResources(LoginUser currentUser, SourceResourcePageQuery query) {
|
public PageResult<SourceResourceResponse> pageResources(LoginUser currentUser, SourceResourcePageQuery query) {
|
||||||
@@ -91,10 +102,12 @@ public class SourceResourceService {
|
|||||||
boolean shouldFilterByUserId = dataPermissionService.shouldFilterByUserId(currentUser);
|
boolean shouldFilterByUserId = dataPermissionService.shouldFilterByUserId(currentUser);
|
||||||
|
|
||||||
LambdaQueryWrapper<SourceResource> wrapper =
|
LambdaQueryWrapper<SourceResource> wrapper =
|
||||||
new LambdaQueryWrapper<SourceResource>().eq(SourceResource::getCompanyId, currentUser.companyId())
|
new LambdaQueryWrapper<SourceResource>().eq(SourceResource::getCompanyId, currentUser.companyId())
|
||||||
.eq(StringUtils.hasText(query.resourceType()), SourceResource::getResourceType, query.resourceType())
|
.eq(StringUtils.hasText(query.resourceType()), SourceResource::getResourceType,
|
||||||
.eq(StringUtils.hasText(query.sourceStatus()), SourceResource::getSourceStatus, query.sourceStatus())
|
query.resourceType())
|
||||||
.like(StringUtils.hasText(query.keyword()), SourceResource::getResourceName, query.keyword());
|
.eq(StringUtils.hasText(query.sourceStatus()), SourceResource::getSourceStatus,
|
||||||
|
query.sourceStatus())
|
||||||
|
.like(StringUtils.hasText(query.keyword()), SourceResource::getResourceName, query.keyword());
|
||||||
|
|
||||||
if (shouldFilterByUserId) {
|
if (shouldFilterByUserId) {
|
||||||
wrapper.eq(SourceResource::getCreatorId, currentUser.userId());
|
wrapper.eq(SourceResource::getCreatorId, currentUser.userId());
|
||||||
@@ -109,20 +122,30 @@ public class SourceResourceService {
|
|||||||
|
|
||||||
List<SourceResourceResponse> records = resultPage.getRecords().stream().map(this::toResponse).toList();
|
List<SourceResourceResponse> records = resultPage.getRecords().stream().map(this::toResponse).toList();
|
||||||
|
|
||||||
return new PageResult<>(records, resultPage.getTotal(), (int)resultPage.getCurrent(),
|
return new PageResult<>(records, resultPage.getTotal(), (int) resultPage.getCurrent(),
|
||||||
(int)resultPage.getSize());
|
(int) resultPage.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceResourceResponse getResource(LoginUser currentUser, Long resourceId) {
|
public SourceResourceResponse getResource(LoginUser currentUser, Long resourceId) {
|
||||||
SourceResource resource = sourceResourceMapper.selectById(resourceId);
|
SourceResource resource = sourceResourceMapper.selectById(resourceId);
|
||||||
if (resource == null || !currentUser.companyId().equals(resource.getCompanyId())) {
|
if (resource == null || !currentUser.companyId().equals(resource.getCompanyId())) {
|
||||||
log.warn("Resource not found or cross-tenant access attempt: resourceId={}, companyId={}, userId={}",
|
log.warn("Resource not found or cross-tenant access attempt: resourceId={}, companyId={}, userId={}",
|
||||||
resourceId, currentUser.companyId(), currentUser.userId());
|
resourceId, currentUser.companyId(), currentUser.userId());
|
||||||
throw new BusinessException(ResultCode.NOT_FOUND, "资源不存在");
|
throw new BusinessException(ResultCode.NOT_FOUND, "资源不存在");
|
||||||
}
|
}
|
||||||
return toResponse(resource);
|
return toResponse(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取资源实体(内部使用)
|
||||||
|
*
|
||||||
|
* @param resourceId 资源ID
|
||||||
|
* @return 资源实体
|
||||||
|
*/
|
||||||
|
public SourceResource getResourceEntity(Long resourceId) {
|
||||||
|
return sourceResourceMapper.selectById(resourceId);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteResource(LoginUser currentUser, Long resourceId) {
|
public void deleteResource(LoginUser currentUser, Long resourceId) {
|
||||||
SourceResource resource = sourceResourceMapper.selectById(resourceId);
|
SourceResource resource = sourceResourceMapper.selectById(resourceId);
|
||||||
@@ -137,21 +160,21 @@ public class SourceResourceService {
|
|||||||
resource.setSourceStatus(SourceStatus.ARCHIVED.name());
|
resource.setSourceStatus(SourceStatus.ARCHIVED.name());
|
||||||
sourceResourceMapper.updateById(resource);
|
sourceResourceMapper.updateById(resource);
|
||||||
log.info("archived referenced source resource, companyId={}, userId={}, resourceId={}",
|
log.info("archived referenced source resource, companyId={}, userId={}, resourceId={}",
|
||||||
currentUser.companyId(), currentUser.userId(), resourceId);
|
currentUser.companyId(), currentUser.userId(), resourceId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
objectStorageService.delete(resource.getBucketName(), resource.getFilePath());
|
objectStorageService.delete(resource.getBucketName(), resource.getFilePath());
|
||||||
sourceResourceMapper.deleteById(resourceId);
|
sourceResourceMapper.deleteById(resourceId);
|
||||||
log.info("deleted source resource, companyId={}, userId={}, resourceId={}", currentUser.companyId(),
|
log.info("deleted source resource, companyId={}, userId={}, resourceId={}", currentUser.companyId(),
|
||||||
currentUser.userId(), resourceId);
|
currentUser.userId(), resourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SourceResourceResponse toResponse(SourceResource resource) {
|
private SourceResourceResponse toResponse(SourceResource resource) {
|
||||||
SysUser creator = sysUserMapper.selectById(resource.getCreatorId());
|
SysUser creator = sysUserMapper.selectById(resource.getCreatorId());
|
||||||
return new SourceResourceResponse(resource.getId(), resource.getResourceName(), resource.getResourceType(),
|
return new SourceResourceResponse(resource.getId(), resource.getResourceName(), resource.getResourceType(),
|
||||||
resource.getBucketName(), resource.getFilePath(), resource.getFileSize(), resource.getSourceStatus(),
|
resource.getBucketName(), resource.getFilePath(), resource.getFileSize(), resource.getSourceStatus(),
|
||||||
resource.getStorageProvider(), resource.getRemark(), creator == null ? null : creator.getRealName(),
|
resource.getStorageProvider(), resource.getRemark(), creator == null ? null : creator.getRealName(),
|
||||||
resource.getCreatedAt(), resource.getUpdatedAt());
|
resource.getCreatedAt(), resource.getUpdatedAt());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveExtension(String originalFilename, String resourceType) {
|
private String resolveExtension(String originalFilename, String resourceType) {
|
||||||
@@ -165,4 +188,140 @@ public class SourceResourceService {
|
|||||||
default -> "bin";
|
default -> "bin";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* 下载图片资源
|
||||||
|
*
|
||||||
|
* @param currentUser 当前用户
|
||||||
|
* @param resourceId 资源ID
|
||||||
|
* @return 图片字节数组
|
||||||
|
*/
|
||||||
|
public byte[] downloadImage(LoginUser currentUser, Long resourceId) {
|
||||||
|
SourceResource resource = sourceResourceMapper.selectById(resourceId);
|
||||||
|
if (resource == null || !currentUser.companyId().equals(resource.getCompanyId())) {
|
||||||
|
log.warn("Resource not found or cross-tenant access attempt: resourceId={}, companyId={}, userId={}",
|
||||||
|
resourceId, currentUser.companyId(), currentUser.userId());
|
||||||
|
throw new BusinessException(ResultCode.NOT_FOUND, "资源不存在");
|
||||||
|
}
|
||||||
|
if (!"IMAGE".equals(resource.getResourceType())) {
|
||||||
|
throw new BusinessException(ResultCode.BAD_REQUEST, "仅图片资源支持下载");
|
||||||
|
}
|
||||||
|
if (!"READY".equals(resource.getSourceStatus())) {
|
||||||
|
throw new BusinessException(ResultCode.BAD_REQUEST, "资源未就绪");
|
||||||
|
}
|
||||||
|
return objectStorageService.download(resource.getBucketName(), resource.getFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图片资源的Content-Type
|
||||||
|
*
|
||||||
|
* @param resource 资源实体
|
||||||
|
* @return Content-Type
|
||||||
|
*/
|
||||||
|
public String getImageContentType(SourceResource resource) {
|
||||||
|
String filePath = resource.getFilePath();
|
||||||
|
if (filePath != null && filePath.contains(".")) {
|
||||||
|
String extension = filePath.substring(filePath.lastIndexOf('.') + 1).toLowerCase();
|
||||||
|
return switch (extension) {
|
||||||
|
case "jpg", "jpeg" -> "image/jpeg";
|
||||||
|
case "png" -> "image/png";
|
||||||
|
case "gif" -> "image/gif";
|
||||||
|
case "webp" -> "image/webp";
|
||||||
|
default -> "application/octet-stream";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageBboxResponse getImageBbox(LoginUser currentUser, Long resourceId) {
|
||||||
|
SourceResource resource = sourceResourceMapper.selectById(resourceId);
|
||||||
|
if (resource == null || !currentUser.companyId().equals(resource.getCompanyId())) {
|
||||||
|
throw new BusinessException(ResultCode.NOT_FOUND, "资源不存在");
|
||||||
|
}
|
||||||
|
if (!"IMAGE".equals(resource.getResourceType())) {
|
||||||
|
throw new BusinessException(ResultCode.BAD_REQUEST, "仅图片资源支持BBOX标注");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageBboxAnnotation annotation = imageBboxAnnotationMapper.selectByResourceId(resourceId);
|
||||||
|
if (annotation == null) {
|
||||||
|
return new ImageBboxResponse(null, resourceId, List.of(), null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ImageBboxResponse.BboxCoordinateResponse> bboxes = parseBboxJson(annotation.getBboxJson());
|
||||||
|
SysUser creator = sysUserMapper.selectById(annotation.getCreatorId());
|
||||||
|
|
||||||
|
return new ImageBboxResponse(
|
||||||
|
annotation.getId(),
|
||||||
|
annotation.getResourceId(),
|
||||||
|
bboxes,
|
||||||
|
annotation.getRemark(),
|
||||||
|
creator == null ? null : creator.getRealName(),
|
||||||
|
annotation.getCreatedAt(),
|
||||||
|
annotation.getUpdatedAt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public ImageBboxResponse saveImageBbox(LoginUser currentUser, Long resourceId, SaveImageBboxRequest request) {
|
||||||
|
SourceResource resource = sourceResourceMapper.selectById(resourceId);
|
||||||
|
if (resource == null || !currentUser.companyId().equals(resource.getCompanyId())) {
|
||||||
|
throw new BusinessException(ResultCode.NOT_FOUND, "资源不存在");
|
||||||
|
}
|
||||||
|
if (!"IMAGE".equals(resource.getResourceType())) {
|
||||||
|
throw new BusinessException(ResultCode.BAD_REQUEST, "仅图片资源支持BBOX标注");
|
||||||
|
}
|
||||||
|
|
||||||
|
String bboxJson;
|
||||||
|
try {
|
||||||
|
bboxJson = objectMapper.writeValueAsString(request.bboxes());
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new BusinessException(ResultCode.BAD_REQUEST, "BBOX数据序列化失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageBboxAnnotation existing = imageBboxAnnotationMapper.selectByResourceId(resourceId);
|
||||||
|
if (existing != null) {
|
||||||
|
existing.setBboxJson(bboxJson);
|
||||||
|
existing.setRemark(request.remark());
|
||||||
|
existing.setUpdatedAt(LocalDateTime.now());
|
||||||
|
imageBboxAnnotationMapper.updateById(existing);
|
||||||
|
} else {
|
||||||
|
long annotationId = IdGenerator.nextId();
|
||||||
|
ImageBboxAnnotation annotation = ImageBboxAnnotation.builder()
|
||||||
|
.id(annotationId)
|
||||||
|
.companyId(currentUser.companyId())
|
||||||
|
.resourceId(resourceId)
|
||||||
|
.bboxJson(bboxJson)
|
||||||
|
.remark(request.remark())
|
||||||
|
.creatorId(currentUser.userId())
|
||||||
|
.creatorRole(currentUser.role())
|
||||||
|
.build();
|
||||||
|
imageBboxAnnotationMapper.insert(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getImageBbox(currentUser, resourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteImageBbox(LoginUser currentUser, Long resourceId) {
|
||||||
|
SourceResource resource = sourceResourceMapper.selectById(resourceId);
|
||||||
|
if (resource == null || !currentUser.companyId().equals(resource.getCompanyId())) {
|
||||||
|
throw new BusinessException(ResultCode.NOT_FOUND, "资源不存在");
|
||||||
|
}
|
||||||
|
imageBboxAnnotationMapper.deleteByResourceId(resourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ImageBboxResponse.BboxCoordinateResponse> parseBboxJson(String bboxJson) {
|
||||||
|
if (!StringUtils.hasText(bboxJson)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(bboxJson,
|
||||||
|
new TypeReference<List<ImageBboxResponse.BboxCoordinateResponse>>() {
|
||||||
|
});
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.warn("Failed to parse bbox json: {}", e.getMessage());
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,46 +1,34 @@
|
|||||||
package com.labelsys.backend.service;
|
package com.labelsys.backend.service;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.labelsys.backend.common.ResultCode;
|
import com.labelsys.backend.common.ResultCode;
|
||||||
import com.labelsys.backend.common.exception.BusinessException;
|
import com.labelsys.backend.common.exception.BusinessException;
|
||||||
import com.labelsys.backend.context.LoginUser;
|
import com.labelsys.backend.context.LoginUser;
|
||||||
import com.labelsys.backend.dto.common.PageResult;
|
import com.labelsys.backend.dto.common.PageResult;
|
||||||
import com.labelsys.backend.dto.request.ManualModelConfigRequest;
|
|
||||||
import com.labelsys.backend.dto.request.PromptConfigOptionRequest;
|
|
||||||
import com.labelsys.backend.dto.request.SaveSysConfigRequest;
|
import com.labelsys.backend.dto.request.SaveSysConfigRequest;
|
||||||
import com.labelsys.backend.dto.request.SysConfigPageQuery;
|
import com.labelsys.backend.dto.request.SysConfigPageQuery;
|
||||||
import com.labelsys.backend.dto.request.TaskModelConfigRequest;
|
|
||||||
import com.labelsys.backend.dto.request.UpdateSysConfigRequest;
|
import com.labelsys.backend.dto.request.UpdateSysConfigRequest;
|
||||||
import com.labelsys.backend.dto.response.SysConfigResponse;
|
import com.labelsys.backend.dto.response.SysConfigResponse;
|
||||||
import com.labelsys.backend.dto.response.TaskModelConfigResponse;
|
|
||||||
import com.labelsys.backend.dto.response.TaskPromptConfigResponse;
|
|
||||||
import com.labelsys.backend.entity.SysConfig;
|
import com.labelsys.backend.entity.SysConfig;
|
||||||
import com.labelsys.backend.enums.ConfigMode;
|
|
||||||
import com.labelsys.backend.enums.ConfigType;
|
import com.labelsys.backend.enums.ConfigType;
|
||||||
import com.labelsys.backend.enums.UserRole;
|
import com.labelsys.backend.enums.UserRole;
|
||||||
import com.labelsys.backend.mapper.SysConfigMapper;
|
import com.labelsys.backend.mapper.SysConfigMapper;
|
||||||
import com.labelsys.backend.util.IdGenerator;
|
import com.labelsys.backend.util.IdGenerator;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SysConfigService {
|
public class SysConfigService {
|
||||||
|
|
||||||
private final SysConfigMapper sysConfigMapper;
|
private final SysConfigMapper sysConfigMapper;
|
||||||
private final ObjectMapper objectMapper;
|
|
||||||
private final DataPermissionService dataPermissionService;
|
private final DataPermissionService dataPermissionService;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -51,14 +39,20 @@ public class SysConfigService {
|
|||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
throw new BusinessException(ResultCode.CONFLICT, "配置名称已存在");
|
throw new BusinessException(ResultCode.CONFLICT, "配置名称已存在");
|
||||||
}
|
}
|
||||||
SysConfig config = SysConfig.builder().id(IdGenerator.nextId()).companyId(currentUser.companyId())
|
SysConfig config = SysConfig.builder()
|
||||||
.configType(request.configType()).configName(request.configName()).configValue(request.configValue())
|
.id(IdGenerator.nextId())
|
||||||
.status(request.status()).creatorId(currentUser.userId()).creatorRole(currentUser.role().name())
|
.companyId(currentUser.companyId())
|
||||||
|
.configType(request.configType())
|
||||||
|
.configName(request.configName())
|
||||||
|
.configValue(request.configValue())
|
||||||
|
.status(request.status())
|
||||||
|
.creatorId(currentUser.userId())
|
||||||
|
.creatorRole(currentUser.role().name())
|
||||||
.build();
|
.build();
|
||||||
sysConfigMapper.insert(config);
|
sysConfigMapper.insert(config);
|
||||||
log.info("saved sys config, companyId={}, userId={}, userRole={}, configName={}, configType={}",
|
log.info("saved sys config, companyId={}, userId={}, userRole={}, configName={}, configType={}",
|
||||||
currentUser.companyId(), currentUser.userId(), currentUser.role().name(), request.configName(),
|
currentUser.companyId(), currentUser.userId(), currentUser.role().name(),
|
||||||
request.configType());
|
request.configName(), request.configType());
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,12 +60,7 @@ public class SysConfigService {
|
|||||||
public SysConfig updateConfig(LoginUser currentUser, Long configId, UpdateSysConfigRequest request) {
|
public SysConfig updateConfig(LoginUser currentUser, Long configId, UpdateSysConfigRequest request) {
|
||||||
validateConfigType(request.configType());
|
validateConfigType(request.configType());
|
||||||
SysConfig existing = getConfigEntity(currentUser, configId);
|
SysConfig existing = getConfigEntity(currentUser, configId);
|
||||||
// SysConfig duplicate =
|
|
||||||
// sysConfigMapper.findByCompanyIdAndConfigName(currentUser.companyId(),
|
|
||||||
// request.configName());
|
|
||||||
// if (duplicate != null && !duplicate.getId().equals(configId)) {
|
|
||||||
// throw new BusinessException(ResultCode.CONFLICT, "配置名称已存在");
|
|
||||||
// }
|
|
||||||
if (StringUtils.hasText(request.configName())) {
|
if (StringUtils.hasText(request.configName())) {
|
||||||
existing.setConfigName(request.configName());
|
existing.setConfigName(request.configName());
|
||||||
}
|
}
|
||||||
@@ -85,8 +74,8 @@ public class SysConfigService {
|
|||||||
existing.setStatus(request.status());
|
existing.setStatus(request.status());
|
||||||
}
|
}
|
||||||
sysConfigMapper.updateById(existing);
|
sysConfigMapper.updateById(existing);
|
||||||
log.info("updated sys config, companyId={}, userId={}, configId={}", currentUser.companyId(),
|
log.info("updated sys config, companyId={}, userId={}, configId={}",
|
||||||
currentUser.userId(), configId);
|
currentUser.companyId(), currentUser.userId(), configId);
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,96 +109,25 @@ public class SysConfigService {
|
|||||||
Page<SysConfig> page = new Page<>(query.pageNo(), query.pageSize());
|
Page<SysConfig> page = new Page<>(query.pageNo(), query.pageSize());
|
||||||
Page<SysConfig> resultPage = sysConfigMapper.selectPage(page, wrapper);
|
Page<SysConfig> resultPage = sysConfigMapper.selectPage(page, wrapper);
|
||||||
|
|
||||||
List<SysConfigResponse> records = resultPage.getRecords().stream().map(this::toResponse).toList();
|
List<SysConfigResponse> records = resultPage.getRecords().stream()
|
||||||
|
.map(this::toResponse)
|
||||||
|
.toList();
|
||||||
|
|
||||||
return new PageResult<>(records, resultPage.getTotal(), (int) resultPage.getCurrent(),
|
return new PageResult<>(records, resultPage.getTotal(), (int) resultPage.getCurrent(),
|
||||||
(int) resultPage.getSize());
|
(int) resultPage.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
|
||||||
public ResolvedModelConfig resolveModelConfig(LoginUser currentUser, TaskModelConfigRequest request) {
|
|
||||||
if (request == null || request.mode() == null) {
|
|
||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "模型配置不能为空");
|
|
||||||
}
|
|
||||||
if (request.mode() == ConfigMode.SELECT) {
|
|
||||||
return resolveSelectedModel(currentUser, request.selectedConfigName());
|
|
||||||
}
|
|
||||||
if (request.mode() == ConfigMode.MANUAL) {
|
|
||||||
return resolveManualModel(currentUser, request.manualConfig());
|
|
||||||
}
|
|
||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "不支持的模型配置模式");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResolvedPromptConfig resolvePromptConfig(LoginUser currentUser, PromptConfigOptionRequest request) {
|
|
||||||
if (request == null) {
|
|
||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "提示词配置不能为空");
|
|
||||||
}
|
|
||||||
if (StringUtils.hasText(request.selectedConfigName())) {
|
|
||||||
SysConfig config = sysConfigMapper.findByCompanyIdAndConfigNameAndType(currentUser.companyId(),
|
|
||||||
request.selectedConfigName(), ConfigType.PROMPT.name());
|
|
||||||
if (config == null) {
|
|
||||||
throw new BusinessException(ResultCode.NOT_FOUND, "提示词配置不存在");
|
|
||||||
}
|
|
||||||
return new ResolvedPromptConfig(config.getId(), config.getConfigName(), config.getConfigValue());
|
|
||||||
}
|
|
||||||
if (!StringUtils.hasText(request.promptText())) {
|
|
||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "提示词内容不能为空");
|
|
||||||
}
|
|
||||||
return new ResolvedPromptConfig(null, null, request.promptText());
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskModelConfigResponse toResponse(ResolvedModelConfig config) {
|
|
||||||
return new TaskModelConfigResponse(config.configId(), config.configName(), config.modelName(),
|
|
||||||
config.modelUrl(), maskSecret(config.apiKey()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskPromptConfigResponse toResponse(ResolvedPromptConfig config) {
|
|
||||||
return new TaskPromptConfigResponse(config.configId(), config.configName(), config.promptText());
|
|
||||||
}
|
|
||||||
|
|
||||||
public SysConfigResponse toResponse(SysConfig config) {
|
public SysConfigResponse toResponse(SysConfig config) {
|
||||||
return new SysConfigResponse(config.getId(), config.getConfigType(), config.getConfigName(),
|
return new SysConfigResponse(
|
||||||
config.getConfigValue(), config.getStatus(), config.getCreatorId(), config.getCreatedAt(),
|
config.getId(),
|
||||||
config.getUpdatedAt());
|
config.getConfigType(),
|
||||||
}
|
config.getConfigName(),
|
||||||
|
config.getConfigValue(),
|
||||||
private ResolvedModelConfig resolveSelectedModel(LoginUser currentUser, String configName) {
|
config.getStatus(),
|
||||||
if (!StringUtils.hasText(configName)) {
|
config.getCreatorId(),
|
||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "模型配置名称不能为空");
|
config.getCreatedAt(),
|
||||||
}
|
config.getUpdatedAt()
|
||||||
SysConfig config = sysConfigMapper.findByCompanyIdAndConfigNameAndType(currentUser.companyId(), configName,
|
);
|
||||||
ConfigType.MODEL.name());
|
|
||||||
if (config == null) {
|
|
||||||
throw new BusinessException(ResultCode.NOT_FOUND, "模型配置不存在");
|
|
||||||
}
|
|
||||||
ModelConfigValue configValue = parseModelConfig(config.getConfigValue());
|
|
||||||
return new ResolvedModelConfig(config.getId(), config.getConfigName(), configValue.modelName(),
|
|
||||||
configValue.modelUrl(), configValue.apiKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResolvedModelConfig resolveManualModel(LoginUser currentUser, ManualModelConfigRequest request) {
|
|
||||||
if (request == null || !StringUtils.hasText(request.modelName()) || !StringUtils.hasText(request.modelUrl())
|
|
||||||
|| !StringUtils.hasText(request.apiKey())) {
|
|
||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "手动模型配置不完整");
|
|
||||||
}
|
|
||||||
SysConfig existing = sysConfigMapper.findByCompanyIdAndConfigName(currentUser.companyId(), request.modelName());
|
|
||||||
if (existing == null) {
|
|
||||||
String configValue = writeModelConfig(request);
|
|
||||||
SysConfig config = SysConfig.builder().id(IdGenerator.nextId()).companyId(currentUser.companyId())
|
|
||||||
.configType(ConfigType.MODEL.name()).configName(request.modelName()).configValue(configValue)
|
|
||||||
.status("ENABLED").creatorId(currentUser.userId()).build();
|
|
||||||
sysConfigMapper.insert(config);
|
|
||||||
log.info("auto created model config, companyId={}, userId={}, configName={}", currentUser.companyId(),
|
|
||||||
currentUser.userId(), request.modelName());
|
|
||||||
return new ResolvedModelConfig(config.getId(), config.getConfigName(), request.modelName(),
|
|
||||||
request.modelUrl(), request.apiKey());
|
|
||||||
}
|
|
||||||
if (!ConfigType.MODEL.name().equals(existing.getConfigType())) {
|
|
||||||
throw new BusinessException(ResultCode.CONFLICT, "同名配置已被其他类型占用");
|
|
||||||
}
|
|
||||||
ModelConfigValue configValue = parseModelConfig(existing.getConfigValue());
|
|
||||||
return new ResolvedModelConfig(existing.getId(), existing.getConfigName(), configValue.modelName(),
|
|
||||||
configValue.modelUrl(), configValue.apiKey());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SysConfig getConfigEntity(LoginUser currentUser, Long configId) {
|
private SysConfig getConfigEntity(LoginUser currentUser, Long configId) {
|
||||||
@@ -225,54 +143,4 @@ public class SysConfigService {
|
|||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "配置类型非法");
|
throw new BusinessException(ResultCode.BAD_REQUEST, "配置类型非法");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private ModelConfigValue parseModelConfig(String value) {
|
|
||||||
try {
|
|
||||||
return objectMapper.readValue(value, ModelConfigValue.class);
|
|
||||||
} catch (JsonProcessingException ex) {
|
|
||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "模型配置值格式非法");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String writeModelConfig(ManualModelConfigRequest request) {
|
|
||||||
try {
|
|
||||||
return objectMapper.writeValueAsString(
|
|
||||||
Map.of("modelName", request.modelName(), "modelUrl", request.modelUrl(), "apiKey",
|
|
||||||
request.apiKey()));
|
|
||||||
} catch (JsonProcessingException ex) {
|
|
||||||
throw new BusinessException(ResultCode.BAD_REQUEST, "模型配置值生成失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String maskSecret(String secret) {
|
|
||||||
if (!StringUtils.hasText(secret)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (secret.length() <= 4) {
|
|
||||||
return "****";
|
|
||||||
}
|
|
||||||
return "****" + secret.substring(secret.length() - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// private <T> PageResult<T> paginate(List<T> records, Integer pageNo, Integer
|
|
||||||
// pageSize) {
|
|
||||||
// int actualPageNo = pageNo == null || pageNo < 1 ? 1 : pageNo;
|
|
||||||
// int actualPageSize = pageSize == null || pageSize < 1 ? 10 : pageSize;
|
|
||||||
// int fromIndex = Math.min((actualPageNo - 1) * actualPageSize,
|
|
||||||
// records.size());
|
|
||||||
// int toIndex = Math.min(fromIndex + actualPageSize, records.size());
|
|
||||||
// return new PageResult<>(records.subList(fromIndex, toIndex),
|
|
||||||
// (long)records.size(), actualPageNo,
|
|
||||||
// actualPageSize);
|
|
||||||
// }
|
|
||||||
|
|
||||||
private record ModelConfigValue(String modelName, String modelUrl, String apiKey) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public record ResolvedModelConfig(Long configId, String configName, String modelName, String modelUrl,
|
|
||||||
String apiKey) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public record ResolvedPromptConfig(Long configId, String configName, String promptText) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,18 +9,6 @@
|
|||||||
<result column="task_name" property="taskName"/>
|
<result column="task_name" property="taskName"/>
|
||||||
<result column="industry_type" property="industryType"/>
|
<result column="industry_type" property="industryType"/>
|
||||||
<result column="task_type" property="taskType"/>
|
<result column="task_type" property="taskType"/>
|
||||||
<result column="extract_model_config_id" property="extractModelConfigId"/>
|
|
||||||
<result column="extract_model_name" property="extractModelName"/>
|
|
||||||
<result column="extract_model_url" property="extractModelUrl"/>
|
|
||||||
<result column="extract_model_api_key" property="extractModelApiKey"/>
|
|
||||||
<result column="verify_model_config_id" property="verifyModelConfigId"/>
|
|
||||||
<result column="verify_model_name" property="verifyModelName"/>
|
|
||||||
<result column="verify_model_url" property="verifyModelUrl"/>
|
|
||||||
<result column="verify_model_api_key" property="verifyModelApiKey"/>
|
|
||||||
<result column="extract_prompt_config_id" property="extractPromptConfigId"/>
|
|
||||||
<result column="extract_prompt" property="extractPrompt"/>
|
|
||||||
<result column="verify_prompt_config_id" property="verifyPromptConfigId"/>
|
|
||||||
<result column="verify_prompt" property="verifyPrompt"/>
|
|
||||||
<result column="task_status" property="taskStatus"/>
|
<result column="task_status" property="taskStatus"/>
|
||||||
<result column="is_deleted" property="isDeleted"/>
|
<result column="is_deleted" property="isDeleted"/>
|
||||||
<result column="started_at" property="startedAt"/>
|
<result column="started_at" property="startedAt"/>
|
||||||
@@ -32,18 +20,16 @@
|
|||||||
|
|
||||||
<sql id="AnnotationTaskColumns">
|
<sql id="AnnotationTaskColumns">
|
||||||
id, company_id, creator_id, creator_role, task_name, industry_type, task_type,
|
id, company_id, creator_id, creator_role, task_name, industry_type, task_type,
|
||||||
extract_model_config_id, extract_model_name, extract_model_url, extract_model_api_key,
|
|
||||||
verify_model_config_id, verify_model_name, verify_model_url, verify_model_api_key,
|
|
||||||
extract_prompt_config_id, extract_prompt, verify_prompt_config_id, verify_prompt,
|
|
||||||
task_status, is_deleted, started_at, finished_at, error_message, created_at, updated_at
|
task_status, is_deleted, started_at, finished_at, error_message, created_at, updated_at
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="findByIdAndCompanyId" resultMap="AnnotationTaskResultMap">
|
<select id="findByIdAndCompanyId" resultMap="AnnotationTaskResultMap">
|
||||||
select <include refid="AnnotationTaskColumns"/>
|
select
|
||||||
|
<include refid="AnnotationTaskColumns"/>
|
||||||
from annotation_task
|
from annotation_task
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
and company_id = #{companyId}
|
and company_id = #{companyId}
|
||||||
and is_deleted = false
|
and is_deleted = false
|
||||||
limit 1
|
limit 1
|
||||||
</select>
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
24
src/main/resources/mapper/ImageBboxAnnotationMapper.xml
Normal file
24
src/main/resources/mapper/ImageBboxAnnotationMapper.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.labelsys.backend.mapper.ImageBboxAnnotationMapper">
|
||||||
|
<resultMap id="ImageBboxAnnotationResultMap" type="com.labelsys.backend.entity.ImageBboxAnnotation">
|
||||||
|
<id column="id" property="id"/>
|
||||||
|
<result column="company_id" property="companyId"/>
|
||||||
|
<result column="resource_id" property="resourceId"/>
|
||||||
|
<result column="bbox_json" property="bboxJson"/>
|
||||||
|
<result column="remark" property="remark"/>
|
||||||
|
<result column="creator_id" property="creatorId"/>
|
||||||
|
<result column="creator_role" property="creatorRole"/>
|
||||||
|
<result column="created_at" property="createdAt"/>
|
||||||
|
<result column="updated_at" property="updatedAt"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="selectByResourceId" resultMap="ImageBboxAnnotationResultMap">
|
||||||
|
SELECT id, company_id, resource_id, bbox_json, remark, creator_id, creator_role, created_at, updated_at
|
||||||
|
FROM image_bbox_annotation WHERE resource_id = #{resourceId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<delete id="deleteByResourceId">
|
||||||
|
DELETE FROM image_bbox_annotation WHERE resource_id = #{resourceId}
|
||||||
|
</delete>
|
||||||
|
</mapper>
|
||||||
@@ -67,31 +67,13 @@ ON CONFLICT DO NOTHING;
|
|||||||
|
|
||||||
INSERT INTO annotation_task (
|
INSERT INTO annotation_task (
|
||||||
id, company_id, creator_id, creator_role, task_name, industry_type, task_type,
|
id, company_id, creator_id, creator_role, task_name, industry_type, task_type,
|
||||||
extract_model_config_id,
|
|
||||||
extract_model_name, extract_model_url, extract_model_api_key,
|
|
||||||
verify_model_config_id,
|
|
||||||
verify_model_name, verify_model_url, verify_model_api_key,
|
|
||||||
extract_prompt_config_id, extract_prompt,
|
|
||||||
verify_prompt_config_id, verify_prompt,
|
|
||||||
task_status, is_deleted, started_at, finished_at, error_message
|
task_status, is_deleted, started_at, finished_at, error_message
|
||||||
) VALUES
|
) VALUES
|
||||||
(701, 2, 4, 'EMPLOYEE', '多资源问答抽取任务', 'electricity', 'EXTRACT_QA',
|
(701, 2, 4, 'EMPLOYEE', '多资源问答抽取任务', 'electricity', 'EXTRACT_QA',
|
||||||
401,
|
'PENDING', FALSE, NULL, NULL, NULL),
|
||||||
'qwen-max', 'https://api.example.com/extract', 'extract-api-key-demo',
|
(702, 2, 4, 'EMPLOYEE', '图片问答抽取任务', 'transport', 'EXTRACT_QA',
|
||||||
402,
|
'COMPLETED', FALSE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL)
|
||||||
'glm-4.5', 'https://api.example.com/verify', 'verify-api-key-demo',
|
ON CONFLICT DO NOTHING;
|
||||||
403, '请根据输入文本提取结构化问答对。',
|
|
||||||
404, '请核验生成答案是否与原始内容一致。',
|
|
||||||
'PENDING', FALSE, NULL, NULL, NULL),
|
|
||||||
(702, 2, 4, 'EMPLOYEE', '图片问答抽取任务', 'transport', 'EXTRACT_QA',
|
|
||||||
406,
|
|
||||||
'qwen-vl-max', 'https://api.example.com/extract-vl', 'extract-vl-api-key-demo',
|
|
||||||
407,
|
|
||||||
'glm-4.5v', 'https://api.example.com/verify-vl', 'verify-vl-api-key-demo',
|
|
||||||
408, '请根据输入图片内容提取结构化问答对。',
|
|
||||||
409, '请核验图片问答结果是否准确。',
|
|
||||||
'COMPLETED', FALSE, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL)
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
|
|
||||||
INSERT INTO annotation_task_resource (
|
INSERT INTO annotation_task_resource (
|
||||||
id, company_id, task_id, resource_id
|
id, company_id, task_id, resource_id
|
||||||
@@ -129,6 +111,14 @@ INSERT INTO annotation_result_history (
|
|||||||
'审核通过后归档', 5, CURRENT_TIMESTAMP)
|
'审核通过后归档', 5, CURRENT_TIMESTAMP)
|
||||||
ON CONFLICT DO NOTHING;
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO image_bbox_annotation (
|
||||||
|
id, company_id, resource_id, bbox_json, remark, creator_id, creator_role
|
||||||
|
) VALUES
|
||||||
|
(1301, 2, 602,
|
||||||
|
'[{"id":"bbox_001","x":50,"y":30,"width":120,"height":80,"label":"指示灯"},{"id":"bbox_002","x":180,"y":40,"width":100,"height":90,"label":"开关按钮"}]',
|
||||||
|
'控制柜图片BBOX标注示例', 3, 'EMPLOYEE')
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
INSERT INTO training_dataset (
|
INSERT INTO training_dataset (
|
||||||
id, company_id, creator_id, creator_role, result_history_id, sample_type, glm_format_json, dataset_status
|
id, company_id, creator_id, creator_role, result_history_id, sample_type, glm_format_json, dataset_status
|
||||||
) VALUES
|
) VALUES
|
||||||
@@ -147,4 +137,4 @@ INSERT INTO export_batch_item (id, batch_id, dataset_id) VALUES
|
|||||||
(1201, 1101, 1001)
|
(1201, 1101, 1001)
|
||||||
ON CONFLICT DO NOTHING;
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
-- Active: 1775801470429@@39.107.112.174@5432@lablesystem
|
-- Active: 1775801470429@@39.107.112.174@5432@lablesystem_test
|
||||||
|
|
||||||
begin;
|
begin;
|
||||||
|
|
||||||
-- Drop Tables (按依赖关系倒序删除)
|
-- Drop Tables (按依赖关系倒序删除)
|
||||||
@@ -15,14 +16,14 @@ DROP TABLE IF EXISTS sys_menu CASCADE;
|
|||||||
DROP TABLE IF EXISTS sys_user CASCADE;
|
DROP TABLE IF EXISTS sys_user CASCADE;
|
||||||
DROP TABLE IF EXISTS sys_company CASCADE;
|
DROP TABLE IF EXISTS sys_company CASCADE;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS sys_company
|
||||||
CREATE TABLE IF NOT EXISTS sys_company (
|
(
|
||||||
id BIGINT PRIMARY KEY,
|
id BIGINT PRIMARY KEY,
|
||||||
company_code VARCHAR(64) NOT NULL UNIQUE,
|
company_code VARCHAR(64) NOT NULL UNIQUE,
|
||||||
company_name VARCHAR(128) NOT NULL,
|
company_name VARCHAR(128) NOT NULL,
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'ENABLED',
|
status VARCHAR(32) NOT NULL DEFAULT 'ENABLED',
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE sys_company IS '公司表。';
|
COMMENT ON TABLE sys_company IS '公司表。';
|
||||||
@@ -33,22 +34,23 @@ COMMENT ON COLUMN sys_company.status IS '公司状态,默认 ENABLED,可按
|
|||||||
COMMENT ON COLUMN sys_company.created_at IS '创建时间。';
|
COMMENT ON COLUMN sys_company.created_at IS '创建时间。';
|
||||||
COMMENT ON COLUMN sys_company.updated_at IS '更新时间。';
|
COMMENT ON COLUMN sys_company.updated_at IS '更新时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS sys_user (
|
CREATE TABLE IF NOT EXISTS sys_user
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
company_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
phone VARCHAR(32) NOT NULL,
|
company_id BIGINT NOT NULL,
|
||||||
username VARCHAR(64),
|
phone VARCHAR(32) NOT NULL,
|
||||||
role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
username VARCHAR(64),
|
||||||
position VARCHAR(32) NOT NULL DEFAULT 'ANNOTATOR',
|
role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
||||||
real_name VARCHAR(64) NOT NULL,
|
position VARCHAR(32) NOT NULL DEFAULT 'ANNOTATOR',
|
||||||
password_hash VARCHAR(255) NOT NULL,
|
real_name VARCHAR(64) NOT NULL,
|
||||||
must_change_password BOOLEAN NOT NULL DEFAULT TRUE,
|
password_hash VARCHAR(255) NOT NULL,
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'ENABLED',
|
must_change_password BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
session_version INTEGER NOT NULL DEFAULT 1,
|
status VARCHAR(32) NOT NULL DEFAULT 'ENABLED',
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
session_version INTEGER NOT NULL DEFAULT 1,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT uq_sys_user_company_phone UNIQUE (company_id, phone),
|
CONSTRAINT uq_sys_user_company_phone UNIQUE (company_id, phone),
|
||||||
CONSTRAINT fk_sys_user_company FOREIGN KEY (company_id) REFERENCES sys_company(id)
|
CONSTRAINT fk_sys_user_company FOREIGN KEY (company_id) REFERENCES sys_company (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE sys_user IS '用户表。role 表示数据权限角色,position 表示岗位。';
|
COMMENT ON TABLE sys_user IS '用户表。role 表示数据权限角色,position 表示岗位。';
|
||||||
@@ -66,17 +68,18 @@ COMMENT ON COLUMN sys_user.session_version IS '会话版本号,用于强制旧
|
|||||||
COMMENT ON COLUMN sys_user.created_at IS '创建时间。';
|
COMMENT ON COLUMN sys_user.created_at IS '创建时间。';
|
||||||
COMMENT ON COLUMN sys_user.updated_at IS '更新时间。';
|
COMMENT ON COLUMN sys_user.updated_at IS '更新时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS sys_menu (
|
CREATE TABLE IF NOT EXISTS sys_menu
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
company_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
menu_code VARCHAR(64) NOT NULL,
|
company_id BIGINT NOT NULL,
|
||||||
menu_name VARCHAR(128) NOT NULL,
|
menu_code VARCHAR(64) NOT NULL,
|
||||||
path VARCHAR(255) NOT NULL,
|
menu_name VARCHAR(128) NOT NULL,
|
||||||
|
path VARCHAR(255) NOT NULL,
|
||||||
visible_positions VARCHAR(255) NOT NULL DEFAULT 'ADMIN',
|
visible_positions VARCHAR(255) NOT NULL DEFAULT 'ADMIN',
|
||||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT fk_sys_menu_company FOREIGN KEY (company_id) REFERENCES sys_company(id)
|
CONSTRAINT fk_sys_menu_company FOREIGN KEY (company_id) REFERENCES sys_company (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE sys_menu IS '菜单表。';
|
COMMENT ON TABLE sys_menu IS '菜单表。';
|
||||||
@@ -90,20 +93,21 @@ COMMENT ON COLUMN sys_menu.sort_order IS '菜单排序号,默认 0。';
|
|||||||
COMMENT ON COLUMN sys_menu.created_at IS '创建时间。';
|
COMMENT ON COLUMN sys_menu.created_at IS '创建时间。';
|
||||||
COMMENT ON COLUMN sys_menu.updated_at IS '更新时间。';
|
COMMENT ON COLUMN sys_menu.updated_at IS '更新时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS sys_config (
|
CREATE TABLE IF NOT EXISTS sys_config
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
company_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
config_type VARCHAR(32) NOT NULL DEFAULT 'SYSTEM',
|
company_id BIGINT NOT NULL,
|
||||||
config_name VARCHAR(128) NOT NULL,
|
config_type VARCHAR(32) NOT NULL DEFAULT 'SYSTEM',
|
||||||
config_value TEXT NOT NULL,
|
config_name VARCHAR(128) NOT NULL,
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'ENABLED',
|
config_value TEXT NOT NULL,
|
||||||
creator_id BIGINT NOT NULL,
|
status VARCHAR(32) NOT NULL DEFAULT 'ENABLED',
|
||||||
creator_role VARCHAR(50) NOT NULL DEFAULT 'EMPLOYEE',
|
creator_id BIGINT NOT NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
creator_role VARCHAR(50) NOT NULL DEFAULT 'EMPLOYEE',
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT uq_sys_config_company_name UNIQUE (company_id, config_name),
|
CONSTRAINT uq_sys_config_company_name UNIQUE (company_id, config_name),
|
||||||
CONSTRAINT fk_sys_config_company FOREIGN KEY (company_id) REFERENCES sys_company(id),
|
CONSTRAINT fk_sys_config_company FOREIGN KEY (company_id) REFERENCES sys_company (id),
|
||||||
CONSTRAINT fk_sys_config_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id)
|
CONSTRAINT fk_sys_config_creator FOREIGN KEY (creator_id) REFERENCES sys_user (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE sys_config IS '系统配置表,保存模型配置、Prompt 配置和系统参数配置。';
|
COMMENT ON TABLE sys_config IS '系统配置表,保存模型配置、Prompt 配置和系统参数配置。';
|
||||||
@@ -118,23 +122,24 @@ COMMENT ON COLUMN sys_config.creator_role IS '创建人角色.默认 EMPLOYEE。
|
|||||||
COMMENT ON COLUMN sys_config.created_at IS '创建时间。';
|
COMMENT ON COLUMN sys_config.created_at IS '创建时间。';
|
||||||
COMMENT ON COLUMN sys_config.updated_at IS '更新时间。';
|
COMMENT ON COLUMN sys_config.updated_at IS '更新时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS source_resource (
|
CREATE TABLE IF NOT EXISTS source_resource
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
company_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
creator_id BIGINT NOT NULL,
|
company_id BIGINT NOT NULL,
|
||||||
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
creator_id BIGINT NOT NULL,
|
||||||
resource_name VARCHAR(255) NOT NULL,
|
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
||||||
resource_type VARCHAR(32) NOT NULL DEFAULT 'TEXT',
|
resource_name VARCHAR(255) NOT NULL,
|
||||||
bucket_name VARCHAR(128) NOT NULL,
|
resource_type VARCHAR(32) NOT NULL DEFAULT 'TEXT',
|
||||||
file_path VARCHAR(512) NOT NULL,
|
bucket_name VARCHAR(128) NOT NULL,
|
||||||
file_size BIGINT NOT NULL DEFAULT 0,
|
file_path VARCHAR(512) NOT NULL,
|
||||||
source_status VARCHAR(32) NOT NULL DEFAULT 'UPLOADED',
|
file_size BIGINT NOT NULL DEFAULT 0,
|
||||||
storage_provider VARCHAR(64) NOT NULL DEFAULT 'rustfs',
|
source_status VARCHAR(32) NOT NULL DEFAULT 'UPLOADED',
|
||||||
remark VARCHAR(255),
|
storage_provider VARCHAR(64) NOT NULL DEFAULT 'rustfs',
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
remark VARCHAR(255),
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT fk_source_resource_company FOREIGN KEY (company_id) REFERENCES sys_company(id),
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT fk_source_resource_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id)
|
CONSTRAINT fk_source_resource_company FOREIGN KEY (company_id) REFERENCES sys_company (id),
|
||||||
|
CONSTRAINT fk_source_resource_creator FOREIGN KEY (creator_id) REFERENCES sys_user (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE source_resource IS '资源表,保存文本、图片、视频资源元数据。';
|
COMMENT ON TABLE source_resource IS '资源表,保存文本、图片、视频资源元数据。';
|
||||||
@@ -153,61 +158,62 @@ COMMENT ON COLUMN source_resource.remark IS '备注说明。';
|
|||||||
COMMENT ON COLUMN source_resource.created_at IS '创建时间。';
|
COMMENT ON COLUMN source_resource.created_at IS '创建时间。';
|
||||||
COMMENT ON COLUMN source_resource.updated_at IS '更新时间。';
|
COMMENT ON COLUMN source_resource.updated_at IS '更新时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS annotation_task (
|
CREATE TABLE IF NOT EXISTS image_bbox_annotation
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
company_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
creator_id BIGINT NOT NULL,
|
company_id BIGINT NOT NULL,
|
||||||
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
resource_id BIGINT NOT NULL,
|
||||||
task_name VARCHAR(255) NOT NULL,
|
bbox_json TEXT,
|
||||||
industry_type VARCHAR(32) NOT NULL DEFAULT 'transport',
|
remark VARCHAR(500),
|
||||||
task_type VARCHAR(32) NOT NULL DEFAULT 'EXTRACT_QA',
|
creator_id BIGINT NOT NULL,
|
||||||
extract_model_config_id BIGINT,
|
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
||||||
extract_model_name VARCHAR(128),
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
extract_model_url VARCHAR(255),
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
extract_model_api_key VARCHAR(255),
|
CONSTRAINT fk_image_bbox_annotation_company FOREIGN KEY (company_id) REFERENCES sys_company (id),
|
||||||
verify_model_config_id BIGINT,
|
CONSTRAINT fk_image_bbox_annotation_resource FOREIGN KEY (resource_id) REFERENCES source_resource (id),
|
||||||
verify_model_name VARCHAR(128),
|
CONSTRAINT fk_image_bbox_annotation_creator FOREIGN KEY (creator_id) REFERENCES sys_user (id)
|
||||||
verify_model_url VARCHAR(255),
|
|
||||||
verify_model_api_key VARCHAR(255),
|
|
||||||
extract_prompt_config_id BIGINT,
|
|
||||||
extract_prompt TEXT,
|
|
||||||
verify_prompt_config_id BIGINT,
|
|
||||||
verify_prompt TEXT,
|
|
||||||
task_status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
|
|
||||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
started_at TIMESTAMP,
|
|
||||||
finished_at TIMESTAMP,
|
|
||||||
error_message TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
CONSTRAINT fk_annotation_task_company FOREIGN KEY (company_id) REFERENCES sys_company(id),
|
|
||||||
CONSTRAINT fk_annotation_task_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id),
|
|
||||||
CONSTRAINT fk_annotation_task_extract_model_config FOREIGN KEY (extract_model_config_id) REFERENCES sys_config(id),
|
|
||||||
CONSTRAINT fk_annotation_task_verify_model_config FOREIGN KEY (verify_model_config_id) REFERENCES sys_config(id),
|
|
||||||
CONSTRAINT fk_annotation_task_extract_prompt_config FOREIGN KEY (extract_prompt_config_id) REFERENCES sys_config(id),
|
|
||||||
CONSTRAINT fk_annotation_task_verify_prompt_config FOREIGN KEY (verify_prompt_config_id) REFERENCES sys_config(id)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE annotation_task IS '任务表,保存任务、配置引用与执行快照。';
|
COMMENT ON TABLE image_bbox_annotation IS '图片BBOX标注表。';
|
||||||
|
COMMENT ON COLUMN image_bbox_annotation.id IS '主键ID。';
|
||||||
|
COMMENT ON COLUMN image_bbox_annotation.company_id IS '所属公司ID。';
|
||||||
|
COMMENT ON COLUMN image_bbox_annotation.resource_id IS '关联的图片资源ID。';
|
||||||
|
COMMENT ON COLUMN image_bbox_annotation.bbox_json IS 'bbox坐标信息JSON数组。';
|
||||||
|
COMMENT ON COLUMN image_bbox_annotation.remark IS '备注说明。';
|
||||||
|
COMMENT ON COLUMN image_bbox_annotation.creator_id IS '创建人用户ID。';
|
||||||
|
COMMENT ON COLUMN image_bbox_annotation.creator_role IS '创建人数据权限角色,默认 EMPLOYEE。';
|
||||||
|
COMMENT ON COLUMN image_bbox_annotation.created_at IS '创建时间。';
|
||||||
|
COMMENT ON COLUMN image_bbox_annotation.updated_at IS '更新时间。';
|
||||||
|
|
||||||
|
-- 修改 annotation_task 表,删除模型和提示词相关字段
|
||||||
|
CREATE TABLE IF NOT EXISTS annotation_task
|
||||||
|
(
|
||||||
|
id BIGINT PRIMARY KEY,
|
||||||
|
company_id BIGINT NOT NULL,
|
||||||
|
creator_id BIGINT NOT NULL,
|
||||||
|
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
||||||
|
task_name VARCHAR(255) NOT NULL,
|
||||||
|
industry_type VARCHAR(32) NOT NULL DEFAULT 'transport',
|
||||||
|
task_type VARCHAR(32) NOT NULL DEFAULT 'EXTRACT_QA',
|
||||||
|
task_status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
|
||||||
|
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
started_at TIMESTAMP,
|
||||||
|
finished_at TIMESTAMP,
|
||||||
|
error_message TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT fk_annotation_task_company FOREIGN KEY (company_id) REFERENCES sys_company (id),
|
||||||
|
CONSTRAINT fk_annotation_task_creator FOREIGN KEY (creator_id) REFERENCES sys_user (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE annotation_task IS '任务表,保存任务配置与执行快照。';
|
||||||
COMMENT ON COLUMN annotation_task.id IS '任务主键ID。';
|
COMMENT ON COLUMN annotation_task.id IS '任务主键ID。';
|
||||||
COMMENT ON COLUMN annotation_task.company_id IS '所属公司ID。';
|
COMMENT ON COLUMN annotation_task.company_id IS '所属公司ID。';
|
||||||
COMMENT ON COLUMN annotation_task.creator_id IS '任务创建人用户ID。';
|
COMMENT ON COLUMN annotation_task.creator_id IS '任务创建人用户ID。';
|
||||||
COMMENT ON COLUMN annotation_task.creator_role IS '任务创建人数据权限角色,默认 EMPLOYEE。';
|
COMMENT ON COLUMN annotation_task.creator_role IS '任务创建人数据权限角色,默认 EMPLOYEE。';
|
||||||
COMMENT ON COLUMN annotation_task.task_name IS '任务名称。';
|
COMMENT ON COLUMN annotation_task.task_name IS '任务名称。';
|
||||||
COMMENT ON COLUMN annotation_task.industry_type IS '行业类型简写,默认 transport,可选值按业务扩展,例如 electricity。';
|
COMMENT ON COLUMN annotation_task.industry_type IS '行业类型简写,默认 transport,可选值按业务扩展。';
|
||||||
COMMENT ON COLUMN annotation_task.task_type IS '任务类型,默认 EXTRACT_QA。';
|
COMMENT ON COLUMN annotation_task.task_type IS '任务类型,默认 EXTRACT_QA。';
|
||||||
COMMENT ON COLUMN annotation_task.extract_model_config_id IS '抽取模型配置ID,关联 sys_config.id。';
|
|
||||||
COMMENT ON COLUMN annotation_task.extract_model_name IS '抽取模型名称。';
|
|
||||||
COMMENT ON COLUMN annotation_task.extract_model_url IS '抽取模型调用地址。';
|
|
||||||
COMMENT ON COLUMN annotation_task.extract_model_api_key IS '抽取模型调用密钥。';
|
|
||||||
COMMENT ON COLUMN annotation_task.verify_model_config_id IS '校验模型配置ID,关联 sys_config.id。';
|
|
||||||
COMMENT ON COLUMN annotation_task.verify_model_name IS '校验模型名称。';
|
|
||||||
COMMENT ON COLUMN annotation_task.verify_model_url IS '校验模型调用地址。';
|
|
||||||
COMMENT ON COLUMN annotation_task.verify_model_api_key IS '校验模型调用密钥。';
|
|
||||||
COMMENT ON COLUMN annotation_task.extract_prompt_config_id IS '抽取Prompt配置ID,关联 sys_config.id。';
|
|
||||||
COMMENT ON COLUMN annotation_task.extract_prompt IS '抽取 Prompt 文本。';
|
|
||||||
COMMENT ON COLUMN annotation_task.verify_prompt_config_id IS '校验Prompt配置ID,关联 sys_config.id。';
|
|
||||||
COMMENT ON COLUMN annotation_task.verify_prompt IS '校验 Prompt 文本。';
|
|
||||||
COMMENT ON COLUMN annotation_task.task_status IS '任务状态,默认 PENDING,可选 RUNNING、COMPLETED、FAILED。';
|
COMMENT ON COLUMN annotation_task.task_status IS '任务状态,默认 PENDING,可选 RUNNING、COMPLETED、FAILED。';
|
||||||
COMMENT ON COLUMN annotation_task.is_deleted IS '任务软删除标记,默认 FALSE。';
|
COMMENT ON COLUMN annotation_task.is_deleted IS '任务软删除标记,默认 FALSE。';
|
||||||
COMMENT ON COLUMN annotation_task.started_at IS '任务开始时间。';
|
COMMENT ON COLUMN annotation_task.started_at IS '任务开始时间。';
|
||||||
@@ -216,16 +222,17 @@ COMMENT ON COLUMN annotation_task.error_message IS '任务失败错误信息。'
|
|||||||
COMMENT ON COLUMN annotation_task.created_at IS '创建时间。';
|
COMMENT ON COLUMN annotation_task.created_at IS '创建时间。';
|
||||||
COMMENT ON COLUMN annotation_task.updated_at IS '更新时间。';
|
COMMENT ON COLUMN annotation_task.updated_at IS '更新时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS annotation_task_resource (
|
CREATE TABLE IF NOT EXISTS annotation_task_resource
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
company_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
task_id BIGINT NOT NULL,
|
company_id BIGINT NOT NULL,
|
||||||
|
task_id BIGINT NOT NULL,
|
||||||
resource_id BIGINT NOT NULL,
|
resource_id BIGINT NOT NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT uq_annotation_task_resource UNIQUE (task_id, resource_id),
|
CONSTRAINT uq_annotation_task_resource UNIQUE (task_id, resource_id),
|
||||||
CONSTRAINT fk_annotation_task_resource_company FOREIGN KEY (company_id) REFERENCES sys_company(id),
|
CONSTRAINT fk_annotation_task_resource_company FOREIGN KEY (company_id) REFERENCES sys_company (id),
|
||||||
CONSTRAINT fk_annotation_task_resource_task FOREIGN KEY (task_id) REFERENCES annotation_task(id),
|
CONSTRAINT fk_annotation_task_resource_task FOREIGN KEY (task_id) REFERENCES annotation_task (id),
|
||||||
CONSTRAINT fk_annotation_task_resource_resource FOREIGN KEY (resource_id) REFERENCES source_resource(id)
|
CONSTRAINT fk_annotation_task_resource_resource FOREIGN KEY (resource_id) REFERENCES source_resource (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE annotation_task_resource IS '任务与资源关联表,一个任务可绑定多个资源。';
|
COMMENT ON TABLE annotation_task_resource IS '任务与资源关联表,一个任务可绑定多个资源。';
|
||||||
@@ -235,29 +242,30 @@ COMMENT ON COLUMN annotation_task_resource.task_id IS '任务ID。';
|
|||||||
COMMENT ON COLUMN annotation_task_resource.resource_id IS '资源ID。';
|
COMMENT ON COLUMN annotation_task_resource.resource_id IS '资源ID。';
|
||||||
COMMENT ON COLUMN annotation_task_resource.created_at IS '创建时间。';
|
COMMENT ON COLUMN annotation_task_resource.created_at IS '创建时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS annotation_result (
|
CREATE TABLE IF NOT EXISTS annotation_result
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
company_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
creator_id BIGINT NOT NULL,
|
company_id BIGINT NOT NULL,
|
||||||
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
creator_id BIGINT NOT NULL,
|
||||||
task_id BIGINT NOT NULL,
|
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
||||||
resource_id BIGINT NOT NULL,
|
task_id BIGINT NOT NULL,
|
||||||
qa_content_json TEXT NOT NULL DEFAULT '{}',
|
resource_id BIGINT NOT NULL,
|
||||||
|
qa_content_json TEXT NOT NULL DEFAULT '{}',
|
||||||
qa_content_storage_mode VARCHAR(32) NOT NULL DEFAULT 'INLINE',
|
qa_content_storage_mode VARCHAR(32) NOT NULL DEFAULT 'INLINE',
|
||||||
qa_content_file_path VARCHAR(512),
|
qa_content_file_path VARCHAR(512),
|
||||||
diff_summary TEXT NOT NULL DEFAULT '{}',
|
diff_summary TEXT NOT NULL DEFAULT '{}',
|
||||||
requires_manual_review BOOLEAN NOT NULL DEFAULT FALSE,
|
requires_manual_review BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
reviewer_id BIGINT,
|
reviewer_id BIGINT,
|
||||||
review_comment TEXT,
|
review_comment TEXT,
|
||||||
reviewed_at TIMESTAMP,
|
reviewed_at TIMESTAMP,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT fk_annotation_result_company FOREIGN KEY (company_id) REFERENCES sys_company(id),
|
CONSTRAINT fk_annotation_result_company FOREIGN KEY (company_id) REFERENCES sys_company (id),
|
||||||
CONSTRAINT fk_annotation_result_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id),
|
CONSTRAINT fk_annotation_result_creator FOREIGN KEY (creator_id) REFERENCES sys_user (id),
|
||||||
CONSTRAINT fk_annotation_result_task FOREIGN KEY (task_id) REFERENCES annotation_task(id),
|
CONSTRAINT fk_annotation_result_task FOREIGN KEY (task_id) REFERENCES annotation_task (id),
|
||||||
CONSTRAINT fk_annotation_result_resource FOREIGN KEY (resource_id) REFERENCES source_resource(id),
|
CONSTRAINT fk_annotation_result_resource FOREIGN KEY (resource_id) REFERENCES source_resource (id),
|
||||||
CONSTRAINT fk_annotation_result_reviewer FOREIGN KEY (reviewer_id) REFERENCES sys_user(id)
|
CONSTRAINT fk_annotation_result_reviewer FOREIGN KEY (reviewer_id) REFERENCES sys_user (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE annotation_result IS '当前标注结果表。';
|
COMMENT ON TABLE annotation_result IS '当前标注结果表。';
|
||||||
@@ -279,27 +287,28 @@ COMMENT ON COLUMN annotation_result.reviewed_at IS '审核时间。';
|
|||||||
COMMENT ON COLUMN annotation_result.created_at IS '创建时间。';
|
COMMENT ON COLUMN annotation_result.created_at IS '创建时间。';
|
||||||
COMMENT ON COLUMN annotation_result.updated_at IS '更新时间。';
|
COMMENT ON COLUMN annotation_result.updated_at IS '更新时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS annotation_result_history (
|
CREATE TABLE IF NOT EXISTS annotation_result_history
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
company_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
creator_id BIGINT NOT NULL,
|
company_id BIGINT NOT NULL,
|
||||||
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
creator_id BIGINT NOT NULL,
|
||||||
source_result_id BIGINT,
|
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
||||||
task_id BIGINT NOT NULL,
|
source_result_id BIGINT,
|
||||||
resource_id BIGINT NOT NULL,
|
task_id BIGINT NOT NULL,
|
||||||
qa_content_json TEXT NOT NULL DEFAULT '{}',
|
resource_id BIGINT NOT NULL,
|
||||||
|
qa_content_json TEXT NOT NULL DEFAULT '{}',
|
||||||
qa_content_storage_mode VARCHAR(32) NOT NULL DEFAULT 'INLINE',
|
qa_content_storage_mode VARCHAR(32) NOT NULL DEFAULT 'INLINE',
|
||||||
qa_content_file_path VARCHAR(512),
|
qa_content_file_path VARCHAR(512),
|
||||||
archive_reason VARCHAR(255),
|
archive_reason VARCHAR(255),
|
||||||
archived_by BIGINT,
|
archived_by BIGINT,
|
||||||
archived_at TIMESTAMP,
|
archived_at TIMESTAMP,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT fk_annotation_result_history_company FOREIGN KEY (company_id) REFERENCES sys_company(id),
|
CONSTRAINT fk_annotation_result_history_company FOREIGN KEY (company_id) REFERENCES sys_company (id),
|
||||||
CONSTRAINT fk_annotation_result_history_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id),
|
CONSTRAINT fk_annotation_result_history_creator FOREIGN KEY (creator_id) REFERENCES sys_user (id),
|
||||||
CONSTRAINT fk_annotation_result_history_result FOREIGN KEY (source_result_id) REFERENCES annotation_result(id),
|
CONSTRAINT fk_annotation_result_history_result FOREIGN KEY (source_result_id) REFERENCES annotation_result (id),
|
||||||
CONSTRAINT fk_annotation_result_history_task FOREIGN KEY (task_id) REFERENCES annotation_task(id),
|
CONSTRAINT fk_annotation_result_history_task FOREIGN KEY (task_id) REFERENCES annotation_task (id),
|
||||||
CONSTRAINT fk_annotation_result_history_resource FOREIGN KEY (resource_id) REFERENCES source_resource(id),
|
CONSTRAINT fk_annotation_result_history_resource FOREIGN KEY (resource_id) REFERENCES source_resource (id),
|
||||||
CONSTRAINT fk_annotation_result_history_archived_by FOREIGN KEY (archived_by) REFERENCES sys_user(id)
|
CONSTRAINT fk_annotation_result_history_archived_by FOREIGN KEY (archived_by) REFERENCES sys_user (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE annotation_result_history IS '历史归档结果表。';
|
COMMENT ON TABLE annotation_result_history IS '历史归档结果表。';
|
||||||
@@ -318,20 +327,21 @@ COMMENT ON COLUMN annotation_result_history.archived_by IS '归档操作人用
|
|||||||
COMMENT ON COLUMN annotation_result_history.archived_at IS '归档时间。';
|
COMMENT ON COLUMN annotation_result_history.archived_at IS '归档时间。';
|
||||||
COMMENT ON COLUMN annotation_result_history.created_at IS '创建时间。';
|
COMMENT ON COLUMN annotation_result_history.created_at IS '创建时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS training_dataset (
|
CREATE TABLE IF NOT EXISTS training_dataset
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
company_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
creator_id BIGINT NOT NULL,
|
company_id BIGINT NOT NULL,
|
||||||
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
creator_id BIGINT NOT NULL,
|
||||||
result_history_id BIGINT NOT NULL,
|
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
||||||
sample_type VARCHAR(32) NOT NULL DEFAULT 'TEXT',
|
result_history_id BIGINT NOT NULL,
|
||||||
glm_format_json TEXT NOT NULL,
|
sample_type VARCHAR(32) NOT NULL DEFAULT 'TEXT',
|
||||||
dataset_status VARCHAR(32) NOT NULL DEFAULT 'DRAFT',
|
glm_format_json TEXT NOT NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
dataset_status VARCHAR(32) NOT NULL DEFAULT 'DRAFT',
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT fk_training_dataset_company FOREIGN KEY (company_id) REFERENCES sys_company(id),
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT fk_training_dataset_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id),
|
CONSTRAINT fk_training_dataset_company FOREIGN KEY (company_id) REFERENCES sys_company (id),
|
||||||
CONSTRAINT fk_training_dataset_result_history FOREIGN KEY (result_history_id) REFERENCES annotation_result_history(id)
|
CONSTRAINT fk_training_dataset_creator FOREIGN KEY (creator_id) REFERENCES sys_user (id),
|
||||||
|
CONSTRAINT fk_training_dataset_result_history FOREIGN KEY (result_history_id) REFERENCES annotation_result_history (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE training_dataset IS '训练样本表。';
|
COMMENT ON TABLE training_dataset IS '训练样本表。';
|
||||||
@@ -346,20 +356,21 @@ COMMENT ON COLUMN training_dataset.dataset_status IS '样本状态,默认 DRAF
|
|||||||
COMMENT ON COLUMN training_dataset.created_at IS '创建时间。';
|
COMMENT ON COLUMN training_dataset.created_at IS '创建时间。';
|
||||||
COMMENT ON COLUMN training_dataset.updated_at IS '更新时间。';
|
COMMENT ON COLUMN training_dataset.updated_at IS '更新时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS export_batch (
|
CREATE TABLE IF NOT EXISTS export_batch
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
company_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
creator_id BIGINT NOT NULL,
|
company_id BIGINT NOT NULL,
|
||||||
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
creator_id BIGINT NOT NULL,
|
||||||
batch_no VARCHAR(64) NOT NULL UNIQUE,
|
creator_role VARCHAR(32) NOT NULL DEFAULT 'EMPLOYEE',
|
||||||
|
batch_no VARCHAR(64) NOT NULL UNIQUE,
|
||||||
dataset_file_path VARCHAR(512),
|
dataset_file_path VARCHAR(512),
|
||||||
sample_count INTEGER NOT NULL DEFAULT 0,
|
sample_count INTEGER NOT NULL DEFAULT 0,
|
||||||
finetune_job_id VARCHAR(128),
|
finetune_job_id VARCHAR(128),
|
||||||
finetune_status VARCHAR(32) NOT NULL DEFAULT 'NOT_STARTED',
|
finetune_status VARCHAR(32) NOT NULL DEFAULT 'NOT_STARTED',
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT fk_export_batch_company FOREIGN KEY (company_id) REFERENCES sys_company(id),
|
CONSTRAINT fk_export_batch_company FOREIGN KEY (company_id) REFERENCES sys_company (id),
|
||||||
CONSTRAINT fk_export_batch_creator FOREIGN KEY (creator_id) REFERENCES sys_user(id)
|
CONSTRAINT fk_export_batch_creator FOREIGN KEY (creator_id) REFERENCES sys_user (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON TABLE export_batch IS '导出批次表。';
|
COMMENT ON TABLE export_batch IS '导出批次表。';
|
||||||
@@ -375,13 +386,14 @@ COMMENT ON COLUMN export_batch.finetune_status IS '微调状态,默认 NOT_STA
|
|||||||
COMMENT ON COLUMN export_batch.created_at IS '创建时间。';
|
COMMENT ON COLUMN export_batch.created_at IS '创建时间。';
|
||||||
COMMENT ON COLUMN export_batch.updated_at IS '更新时间。';
|
COMMENT ON COLUMN export_batch.updated_at IS '更新时间。';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS export_batch_item (
|
CREATE TABLE IF NOT EXISTS export_batch_item
|
||||||
id BIGINT PRIMARY KEY,
|
(
|
||||||
batch_id BIGINT NOT NULL,
|
id BIGINT PRIMARY KEY,
|
||||||
|
batch_id BIGINT NOT NULL,
|
||||||
dataset_id BIGINT NOT NULL,
|
dataset_id BIGINT NOT NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT fk_export_batch_item_batch FOREIGN KEY (batch_id) REFERENCES export_batch(id),
|
CONSTRAINT fk_export_batch_item_batch FOREIGN KEY (batch_id) REFERENCES export_batch (id),
|
||||||
CONSTRAINT fk_export_batch_item_dataset FOREIGN KEY (dataset_id) REFERENCES training_dataset(id),
|
CONSTRAINT fk_export_batch_item_dataset FOREIGN KEY (dataset_id) REFERENCES training_dataset (id),
|
||||||
CONSTRAINT uq_export_batch_item UNIQUE (batch_id, dataset_id)
|
CONSTRAINT uq_export_batch_item UNIQUE (batch_id, dataset_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -391,26 +403,28 @@ COMMENT ON COLUMN export_batch_item.batch_id IS '关联导出批次ID。';
|
|||||||
COMMENT ON COLUMN export_batch_item.dataset_id IS '关联训练样本ID。';
|
COMMENT ON COLUMN export_batch_item.dataset_id IS '关联训练样本ID。';
|
||||||
COMMENT ON COLUMN export_batch_item.created_at IS '创建时间。';
|
COMMENT ON COLUMN export_batch_item.created_at IS '创建时间。';
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_sys_user_company ON sys_user(company_id);
|
CREATE INDEX IF NOT EXISTS idx_sys_user_company ON sys_user (company_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_sys_user_role ON sys_user(company_id, role);
|
CREATE INDEX IF NOT EXISTS idx_sys_user_role ON sys_user (company_id, role);
|
||||||
CREATE INDEX IF NOT EXISTS idx_sys_user_position ON sys_user(company_id, position);
|
CREATE INDEX IF NOT EXISTS idx_sys_user_position ON sys_user (company_id, position);
|
||||||
CREATE INDEX IF NOT EXISTS idx_sys_menu_company_sort ON sys_menu(company_id, sort_order);
|
CREATE INDEX IF NOT EXISTS idx_sys_menu_company_sort ON sys_menu (company_id, sort_order);
|
||||||
CREATE INDEX IF NOT EXISTS idx_sys_config_company_type ON sys_config(company_id, config_type);
|
CREATE INDEX IF NOT EXISTS idx_sys_config_company_type ON sys_config (company_id, config_type);
|
||||||
CREATE INDEX IF NOT EXISTS idx_source_resource_company_type ON source_resource(company_id, resource_type);
|
CREATE INDEX IF NOT EXISTS idx_source_resource_company_type ON source_resource (company_id, resource_type);
|
||||||
CREATE INDEX IF NOT EXISTS idx_source_resource_company_status ON source_resource(company_id, source_status);
|
CREATE INDEX IF NOT EXISTS idx_source_resource_company_status ON source_resource (company_id, source_status);
|
||||||
CREATE INDEX IF NOT EXISTS idx_source_resource_creator ON source_resource(company_id, creator_id);
|
CREATE INDEX IF NOT EXISTS idx_source_resource_creator ON source_resource (company_id, creator_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_task_company_status ON annotation_task(company_id, task_status);
|
CREATE INDEX IF NOT EXISTS idx_annotation_task_company_status ON annotation_task (company_id, task_status);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_task_company_deleted ON annotation_task(company_id, is_deleted);
|
CREATE INDEX IF NOT EXISTS idx_annotation_task_company_deleted ON annotation_task (company_id, is_deleted);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_task_creator ON annotation_task(company_id, creator_id);
|
CREATE INDEX IF NOT EXISTS idx_annotation_task_creator ON annotation_task (company_id, creator_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_task_resource_company_task ON annotation_task_resource(company_id, task_id);
|
CREATE INDEX IF NOT EXISTS idx_annotation_task_resource_company_task ON annotation_task_resource (company_id, task_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_task_resource_company_resource ON annotation_task_resource(company_id, resource_id);
|
CREATE INDEX IF NOT EXISTS idx_annotation_task_resource_company_resource ON annotation_task_resource (company_id, resource_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_result_company_deleted ON annotation_result(company_id, is_deleted);
|
CREATE INDEX IF NOT EXISTS idx_annotation_result_company_deleted ON annotation_result (company_id, is_deleted);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_result_company_manual ON annotation_result(company_id, requires_manual_review);
|
CREATE INDEX IF NOT EXISTS idx_annotation_result_company_manual ON annotation_result (company_id, requires_manual_review);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_result_task ON annotation_result(company_id, task_id);
|
CREATE INDEX IF NOT EXISTS idx_annotation_result_task ON annotation_result (company_id, task_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_result_history_company ON annotation_result_history(company_id);
|
CREATE INDEX IF NOT EXISTS idx_annotation_result_history_company ON annotation_result_history (company_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_result_history_task ON annotation_result_history(company_id, task_id);
|
CREATE INDEX IF NOT EXISTS idx_annotation_result_history_task ON annotation_result_history (company_id, task_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_annotation_result_history_resource ON annotation_result_history(company_id, resource_id);
|
CREATE INDEX IF NOT EXISTS idx_annotation_result_history_resource ON annotation_result_history (company_id, resource_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_training_dataset_company_status ON training_dataset(company_id, dataset_status);
|
CREATE INDEX IF NOT EXISTS idx_training_dataset_company_status ON training_dataset (company_id, dataset_status);
|
||||||
CREATE INDEX IF NOT EXISTS idx_export_batch_company_status ON export_batch(company_id, finetune_status);
|
CREATE INDEX IF NOT EXISTS idx_export_batch_company_status ON export_batch (company_id, finetune_status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_image_bbox_annotation_company ON image_bbox_annotation (company_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_image_bbox_annotation_resource ON image_bbox_annotation (company_id, resource_id);
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
Reference in New Issue
Block a user