提交代码实现v1.0
This commit is contained in:
10
pom.xml
10
pom.xml
@@ -82,6 +82,14 @@
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>sts</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
@@ -126,4 +134,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -3,9 +3,11 @@ package com.labelsys.backend;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.labelsys.backend.mapper")
|
||||
@EnableScheduling
|
||||
public class LabelsysBackendApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.labelsys.backend.config;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.labelsys.backend.config;
|
||||
|
||||
import java.net.URI;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.S3Configuration;
|
||||
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
@EnableConfigurationProperties(ObjectStorageProperties.class)
|
||||
public class ObjectStorageConfig {
|
||||
|
||||
private final ObjectStorageProperties properties;
|
||||
|
||||
@Bean
|
||||
public S3Client s3Client() {
|
||||
return S3Client.builder()
|
||||
.endpointOverride(URI.create(properties.getEndpoint()))
|
||||
.region(Region.of(properties.getRegion()))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(
|
||||
AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey())))
|
||||
.serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(properties.isPathStyleAccess()).build())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.labelsys.backend.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "labelsys.object-storage")
|
||||
public class ObjectStorageProperties {
|
||||
private String endpoint;
|
||||
private String region;
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
private boolean pathStyleAccess = true;
|
||||
private String sourceBucket;
|
||||
private String artifactBucket;
|
||||
private String exportBucket;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.labelsys.backend.controller;
|
||||
|
||||
import com.labelsys.backend.annotation.RequirePosition;
|
||||
import com.labelsys.backend.common.Result;
|
||||
import com.labelsys.backend.context.UserContext;
|
||||
import com.labelsys.backend.dto.common.PageResult;
|
||||
import com.labelsys.backend.dto.request.AnnotationResultPageQuery;
|
||||
import com.labelsys.backend.dto.request.MergeReviewResultRequest;
|
||||
import com.labelsys.backend.dto.response.AnnotationResultCompareResponse;
|
||||
import com.labelsys.backend.dto.response.AnnotationResultResponse;
|
||||
import com.labelsys.backend.dto.response.MergeReviewResultResponse;
|
||||
import com.labelsys.backend.enums.UserPosition;
|
||||
import com.labelsys.backend.service.AnnotationResultArchiveService;
|
||||
import com.labelsys.backend.service.AnnotationResultService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
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.RestController;
|
||||
|
||||
@Tag(name = "标注结果管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/annotation-results")
|
||||
@RequiredArgsConstructor
|
||||
public class AnnotationResultController {
|
||||
|
||||
private final AnnotationResultService annotationResultService;
|
||||
private final AnnotationResultArchiveService annotationResultArchiveService;
|
||||
|
||||
@Operation(summary = "分页查询标注结果")
|
||||
@GetMapping
|
||||
public Result<PageResult<AnnotationResultResponse>> page(AnnotationResultPageQuery query) {
|
||||
return Result.success(annotationResultService.pageResults(UserContext.requireUser(), query));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询标注结果详情")
|
||||
@GetMapping("/{id}")
|
||||
public Result<AnnotationResultResponse> detail(@PathVariable Long id) {
|
||||
return Result.success(annotationResultService.getResult(UserContext.requireUser(), id));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询标注结果比对信息")
|
||||
@RequirePosition(UserPosition.REVIEWER)
|
||||
@GetMapping("/{id}/compare")
|
||||
public Result<AnnotationResultCompareResponse> compare(@PathVariable Long id) {
|
||||
return Result.success(annotationResultService.compareResult(UserContext.requireUser(), id));
|
||||
}
|
||||
|
||||
@Operation(summary = "提交合并审核结果")
|
||||
@RequirePosition(UserPosition.REVIEWER)
|
||||
@PostMapping("/{id}/merge-review")
|
||||
public Result<MergeReviewResultResponse> mergeReview(@PathVariable Long id,
|
||||
@Valid @RequestBody MergeReviewResultRequest request) {
|
||||
return Result.success(annotationResultArchiveService.mergeReview(UserContext.requireUser(), id, request));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.labelsys.backend.controller;
|
||||
|
||||
import com.labelsys.backend.common.Result;
|
||||
import com.labelsys.backend.context.UserContext;
|
||||
import com.labelsys.backend.dto.common.PageResult;
|
||||
import com.labelsys.backend.dto.request.AnnotationTaskPageQuery;
|
||||
import com.labelsys.backend.dto.request.CreateAnnotationTaskRequest;
|
||||
import com.labelsys.backend.dto.request.UpdateAnnotationTaskRequest;
|
||||
import com.labelsys.backend.dto.response.AnnotationTaskResponse;
|
||||
import com.labelsys.backend.service.AnnotationTaskService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "标注任务管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/annotation-tasks")
|
||||
@RequiredArgsConstructor
|
||||
public class AnnotationTaskController {
|
||||
|
||||
private final AnnotationTaskService annotationTaskService;
|
||||
|
||||
@Operation(summary = "创建标注任务")
|
||||
@PostMapping
|
||||
public Result<AnnotationTaskResponse> create(@Valid @RequestBody CreateAnnotationTaskRequest request) {
|
||||
return Result.success(annotationTaskService.createTask(UserContext.requireUser(), request));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新标注任务")
|
||||
@PutMapping("/{id}")
|
||||
public Result<AnnotationTaskResponse> update(@PathVariable Long id, @Valid @RequestBody UpdateAnnotationTaskRequest request) {
|
||||
return Result.success(annotationTaskService.updateTask(UserContext.requireUser(), id, request));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询标注任务")
|
||||
@GetMapping
|
||||
public Result<PageResult<AnnotationTaskResponse>> page(AnnotationTaskPageQuery query) {
|
||||
return Result.success(annotationTaskService.pageTasks(UserContext.requireUser(), query));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询标注任务详情")
|
||||
@GetMapping("/{id}")
|
||||
public Result<AnnotationTaskResponse> detail(@PathVariable Long id) {
|
||||
return Result.success(annotationTaskService.getTask(UserContext.requireUser(), id));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除标注任务")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@PathVariable Long id) {
|
||||
annotationTaskService.deleteTask(UserContext.requireUser(), id);
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.labelsys.backend.controller;
|
||||
|
||||
import com.labelsys.backend.common.Result;
|
||||
import com.labelsys.backend.context.UserContext;
|
||||
import com.labelsys.backend.dto.common.PageResult;
|
||||
import com.labelsys.backend.dto.request.SourceResourcePageQuery;
|
||||
import com.labelsys.backend.dto.request.SourceUploadRequest;
|
||||
import com.labelsys.backend.dto.response.SourceResourceResponse;
|
||||
import com.labelsys.backend.dto.response.SourceUploadResponse;
|
||||
import com.labelsys.backend.service.SourceResourceService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "资源管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/source-resources")
|
||||
@RequiredArgsConstructor
|
||||
public class SourceResourceController {
|
||||
|
||||
private final SourceResourceService sourceResourceService;
|
||||
|
||||
@Operation(summary = "上传资源")
|
||||
@PostMapping("/upload")
|
||||
public Result<SourceUploadResponse> upload(@ModelAttribute SourceUploadRequest request) {
|
||||
return Result.success(sourceResourceService.upload(UserContext.requireUser(), request));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询资源")
|
||||
@GetMapping
|
||||
public Result<PageResult<SourceResourceResponse>> page(SourceResourcePageQuery query) {
|
||||
return Result.success(sourceResourceService.pageResources(UserContext.requireUser(), query));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询资源详情")
|
||||
@GetMapping("/{id}")
|
||||
public Result<SourceResourceResponse> detail(@PathVariable Long id) {
|
||||
return Result.success(sourceResourceService.getResource(UserContext.requireUser(), id));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除资源")
|
||||
@DeleteMapping("/{id}")
|
||||
public Result<Void> delete(@PathVariable Long id) {
|
||||
sourceResourceService.deleteResource(UserContext.requireUser(), id);
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.labelsys.backend.controller;
|
||||
|
||||
import com.labelsys.backend.annotation.RequirePosition;
|
||||
import com.labelsys.backend.common.Result;
|
||||
import com.labelsys.backend.context.UserContext;
|
||||
import com.labelsys.backend.dto.common.PageResult;
|
||||
import com.labelsys.backend.dto.request.SaveSysConfigRequest;
|
||||
import com.labelsys.backend.dto.request.SysConfigPageQuery;
|
||||
import com.labelsys.backend.dto.response.SysConfigResponse;
|
||||
import com.labelsys.backend.enums.UserPosition;
|
||||
import com.labelsys.backend.service.SysConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "系统配置管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/sys-configs")
|
||||
@RequiredArgsConstructor
|
||||
public class SysConfigController {
|
||||
|
||||
private final SysConfigService sysConfigService;
|
||||
|
||||
@Operation(summary = "创建系统配置")
|
||||
@RequirePosition(UserPosition.ADMIN)
|
||||
@PostMapping
|
||||
public Result<SysConfigResponse> create(@Valid @RequestBody SaveSysConfigRequest request) {
|
||||
return Result.success(sysConfigService.toResponse(sysConfigService.saveConfig(UserContext.requireUser(), request)));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新系统配置")
|
||||
@RequirePosition(UserPosition.ADMIN)
|
||||
@PutMapping("/{id}")
|
||||
public Result<SysConfigResponse> update(@PathVariable Long id, @Valid @RequestBody SaveSysConfigRequest request) {
|
||||
return Result.success(sysConfigService.toResponse(sysConfigService.updateConfig(UserContext.requireUser(), id, request)));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询系统配置")
|
||||
@GetMapping
|
||||
public Result<PageResult<SysConfigResponse>> page(SysConfigPageQuery query) {
|
||||
return Result.success(sysConfigService.pageConfigs(UserContext.requireUser(), query));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询系统配置详情")
|
||||
@GetMapping("/{id}")
|
||||
public Result<SysConfigResponse> detail(@PathVariable Long id) {
|
||||
return Result.success(sysConfigService.getConfig(UserContext.requireUser(), id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.labelsys.backend.dto.common;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "分页结果")
|
||||
public record PageResult<T>(
|
||||
@Schema(description = "当前页记录") List<T> records,
|
||||
@Schema(description = "总记录数") Long total,
|
||||
@Schema(description = "页码") Integer pageNo,
|
||||
@Schema(description = "每页数量") Integer pageSize
|
||||
) {
|
||||
public static <T> PageResult<T> from(IPage<T> page) {
|
||||
return new PageResult<>(page.getRecords(), page.getTotal(), (int) page.getCurrent(), (int) page.getSize());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.labelsys.backend.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "标注结果分页查询请求")
|
||||
public record AnnotationResultPageQuery(
|
||||
@Schema(description = "任务ID") Long taskId,
|
||||
@Schema(description = "资源ID") Long resourceId,
|
||||
@Schema(description = "是否需要人工审核") Boolean requiresManualReview,
|
||||
@Schema(description = "运行态状态") String runtimeStatus,
|
||||
@Schema(description = "页码") Integer pageNo,
|
||||
@Schema(description = "每页数量") Integer pageSize
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.labelsys.backend.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "标注任务分页查询请求")
|
||||
public record AnnotationTaskPageQuery(
|
||||
@Schema(description = "关键字") String keyword,
|
||||
@Schema(description = "任务类型") String taskType,
|
||||
@Schema(description = "任务状态") String taskStatus,
|
||||
@Schema(description = "资源ID") Long resourceId,
|
||||
@Schema(description = "是否已删除") Boolean isDeleted,
|
||||
@Schema(description = "页码") Integer pageNo,
|
||||
@Schema(description = "每页数量") Integer pageSize
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.labelsys.backend.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "创建标注任务请求")
|
||||
public record CreateAnnotationTaskRequest(
|
||||
@Schema(description = "任务名称") @NotBlank(message = "任务名称不能为空") String taskName,
|
||||
@Schema(description = "行业类型") String industryType,
|
||||
@Schema(description = "任务类型") String taskType,
|
||||
@Schema(description = "资源ID列表") @NotEmpty(message = "资源列表不能为空") List<Long> resourceIds,
|
||||
@Schema(description = "抽取模型配置") @Valid TaskModelConfigRequest extractModel,
|
||||
@Schema(description = "校验模型配置") @Valid TaskModelConfigRequest verifyModel,
|
||||
@Schema(description = "抽取提示词配置") @Valid PromptConfigOptionRequest extractPrompt,
|
||||
@Schema(description = "校验提示词配置") @Valid PromptConfigOptionRequest verifyPrompt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
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 = "模型名称") @NotBlank(message = "模型名称不能为空") String modelName,
|
||||
@Schema(description = "模型地址") @NotBlank(message = "模型地址不能为空") String modelUrl,
|
||||
@Schema(description = "模型密钥") @NotBlank(message = "模型密钥不能为空") String apiKey
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.labelsys.backend.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
@Schema(description = "合并审核结果请求")
|
||||
public record MergeReviewResultRequest(
|
||||
@Schema(description = "差异摘要 JSON") @NotBlank(message = "差异摘要不能为空") String diffSummary,
|
||||
@Schema(description = "最终问答内容 JSON") @NotBlank(message = "问答内容不能为空") String qaContentJson,
|
||||
@Schema(description = "审核备注") String reviewComment
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.labelsys.backend.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "任务提示词配置请求")
|
||||
public record PromptConfigOptionRequest(
|
||||
@Schema(description = "已选择的提示词配置名称") String selectedConfigName,
|
||||
@Schema(description = "手动输入的提示词内容") String promptText
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.labelsys.backend.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
@Schema(description = "保存系统配置请求")
|
||||
public record SaveSysConfigRequest(
|
||||
@Schema(description = "配置类型") @NotBlank(message = "配置类型不能为空") String configType,
|
||||
@Schema(description = "配置名称") @NotBlank(message = "配置名称不能为空") String configName,
|
||||
@Schema(description = "配置值") @NotBlank(message = "配置值不能为空") String configValue,
|
||||
@Schema(description = "配置状态") @NotBlank(message = "配置状态不能为空") String status
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.labelsys.backend.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "资源分页查询请求")
|
||||
public record SourceResourcePageQuery(
|
||||
@Schema(description = "关键字") String keyword,
|
||||
@Schema(description = "资源类型") String resourceType,
|
||||
@Schema(description = "资源状态") String sourceStatus,
|
||||
@Schema(description = "页码") Integer pageNo,
|
||||
@Schema(description = "每页数量") Integer pageSize
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.labelsys.backend.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Data
|
||||
@Schema(description = "资源上传请求")
|
||||
public class SourceUploadRequest {
|
||||
|
||||
@Schema(description = "资源名称")
|
||||
private String resourceName;
|
||||
|
||||
@Schema(description = "资源类型:TEXT、IMAGE、VIDEO")
|
||||
private String resourceType;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "上传文件")
|
||||
private MultipartFile file;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.labelsys.backend.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "系统配置分页查询请求")
|
||||
public record SysConfigPageQuery(
|
||||
@Schema(description = "配置类型") String configType,
|
||||
@Schema(description = "配置名称") String configName,
|
||||
@Schema(description = "配置状态") String status,
|
||||
@Schema(description = "页码") Integer pageNo,
|
||||
@Schema(description = "每页数量") Integer pageSize
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.labelsys.backend.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
@Schema(description = "任务模型配置请求")
|
||||
public record TaskModelConfigRequest(
|
||||
@Schema(description = "配置模式:SELECT 或 MANUAL") @NotBlank(message = "配置模式不能为空") String mode,
|
||||
@Schema(description = "已选择的配置名称") String selectedConfigName,
|
||||
@Schema(description = "手动录入的模型配置") @Valid ManualModelConfigRequest manualConfig
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
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 = "更新标注任务请求")
|
||||
public record UpdateAnnotationTaskRequest(
|
||||
@Schema(description = "行业类型") String industryType,
|
||||
@Schema(description = "任务类型") String taskType,
|
||||
@Schema(description = "资源ID列表") @NotEmpty(message = "资源列表不能为空") List<Long> resourceIds,
|
||||
@Schema(description = "抽取模型配置") @Valid TaskModelConfigRequest extractModel,
|
||||
@Schema(description = "校验模型配置") @Valid TaskModelConfigRequest verifyModel,
|
||||
@Schema(description = "抽取提示词配置") @Valid PromptConfigOptionRequest extractPrompt,
|
||||
@Schema(description = "校验提示词配置") @Valid PromptConfigOptionRequest verifyPrompt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.labelsys.backend.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "标注结果比对响应")
|
||||
public record AnnotationResultCompareResponse(
|
||||
@Schema(description = "结果ID") Long id,
|
||||
@Schema(description = "任务ID") Long taskId,
|
||||
@Schema(description = "资源ID") Long resourceId,
|
||||
@Schema(description = "问答内容 JSON") String qaContentJson,
|
||||
@Schema(description = "差异摘要 JSON") String diffSummary,
|
||||
@Schema(description = "问答存储模式") String qaContentStorageMode,
|
||||
@Schema(description = "外置问答文件路径") String qaContentFilePath,
|
||||
@Schema(description = "资源预览路径") String sourcePreviewPath
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.labelsys.backend.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "标注结果响应")
|
||||
public record AnnotationResultResponse(
|
||||
@Schema(description = "结果ID") Long id,
|
||||
@Schema(description = "任务ID") Long taskId,
|
||||
@Schema(description = "资源ID") Long resourceId,
|
||||
@Schema(description = "运行态状态") String runtimeStatus,
|
||||
@Schema(description = "是否需要人工审核") Boolean requiresManualReview,
|
||||
@Schema(description = "是否已删除") Boolean isDeleted,
|
||||
@Schema(description = "问答存储模式") String qaContentStorageMode,
|
||||
@Schema(description = "审核备注") String reviewComment,
|
||||
@Schema(description = "审核时间") LocalDateTime reviewedAt,
|
||||
@Schema(description = "创建时间") LocalDateTime createdAt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.labelsys.backend.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "标注任务响应")
|
||||
public record AnnotationTaskResponse(
|
||||
@Schema(description = "任务ID") Long id,
|
||||
@Schema(description = "任务名称") String taskName,
|
||||
@Schema(description = "行业类型") String industryType,
|
||||
@Schema(description = "任务类型") String taskType,
|
||||
@Schema(description = "任务状态") String taskStatus,
|
||||
@Schema(description = "资源ID列表") List<Long> resourceIds,
|
||||
@Schema(description = "抽取模型配置") TaskModelConfigResponse extractModel,
|
||||
@Schema(description = "校验模型配置") TaskModelConfigResponse verifyModel,
|
||||
@Schema(description = "抽取提示词配置") TaskPromptConfigResponse extractPrompt,
|
||||
@Schema(description = "校验提示词配置") TaskPromptConfigResponse verifyPrompt,
|
||||
@Schema(description = "创建时间") LocalDateTime createdAt,
|
||||
@Schema(description = "更新时间") LocalDateTime updatedAt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.labelsys.backend.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "合并审核结果响应")
|
||||
public record MergeReviewResultResponse(
|
||||
@Schema(description = "运行态结果ID") Long resultId,
|
||||
@Schema(description = "历史结果ID") Long historyId,
|
||||
@Schema(description = "归档原因") String archiveReason,
|
||||
@Schema(description = "归档时间") LocalDateTime archivedAt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.labelsys.backend.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "资源响应")
|
||||
public record SourceResourceResponse(
|
||||
@Schema(description = "资源ID") Long id,
|
||||
@Schema(description = "资源名称") String resourceName,
|
||||
@Schema(description = "资源类型") String resourceType,
|
||||
@Schema(description = "桶名称") String bucketName,
|
||||
@Schema(description = "文件路径") String filePath,
|
||||
@Schema(description = "文件大小") Long fileSize,
|
||||
@Schema(description = "资源状态") String sourceStatus,
|
||||
@Schema(description = "存储提供方") String storageProvider,
|
||||
@Schema(description = "备注") String remark,
|
||||
@Schema(description = "创建人名称") String creatorName,
|
||||
@Schema(description = "创建时间") LocalDateTime createdAt,
|
||||
@Schema(description = "更新时间") LocalDateTime updatedAt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.labelsys.backend.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "资源上传响应")
|
||||
public record SourceUploadResponse(
|
||||
@Schema(description = "资源ID") Long id,
|
||||
@Schema(description = "资源名称") String resourceName,
|
||||
@Schema(description = "资源类型") String resourceType,
|
||||
@Schema(description = "桶名称") String bucketName,
|
||||
@Schema(description = "文件路径") String filePath,
|
||||
@Schema(description = "文件大小") Long fileSize,
|
||||
@Schema(description = "资源状态") String sourceStatus,
|
||||
@Schema(description = "创建时间") LocalDateTime createdAt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.labelsys.backend.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "系统配置响应")
|
||||
public record SysConfigResponse(
|
||||
@Schema(description = "配置ID") Long id,
|
||||
@Schema(description = "配置类型") String configType,
|
||||
@Schema(description = "配置名称") String configName,
|
||||
@Schema(description = "配置值") String configValue,
|
||||
@Schema(description = "配置状态") String status,
|
||||
@Schema(description = "创建人ID") Long creatorId,
|
||||
@Schema(description = "创建时间") LocalDateTime createdAt,
|
||||
@Schema(description = "更新时间") LocalDateTime updatedAt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.labelsys.backend.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "任务模型配置响应")
|
||||
public record TaskModelConfigResponse(
|
||||
@Schema(description = "配置ID") Long configId,
|
||||
@Schema(description = "配置名称") String configName,
|
||||
@Schema(description = "模型名称") String modelName,
|
||||
@Schema(description = "模型地址") String modelUrl,
|
||||
@Schema(description = "脱敏后的模型密钥") String maskedApiKey
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.labelsys.backend.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "任务提示词配置响应")
|
||||
public record TaskPromptConfigResponse(
|
||||
@Schema(description = "配置ID") Long configId,
|
||||
@Schema(description = "配置名称") String configName,
|
||||
@Schema(description = "提示词内容") String promptText
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("annotation_result")
|
||||
public class AnnotationResult {
|
||||
@TableId(type = IdType.INPUT)
|
||||
private Long id;
|
||||
private Long companyId;
|
||||
private Long creatorId;
|
||||
private UserRole creatorRole;
|
||||
private Long taskId;
|
||||
private Long resourceId;
|
||||
private String qaContentJson;
|
||||
private String qaContentStorageMode;
|
||||
private String qaContentFilePath;
|
||||
private String diffSummary;
|
||||
private Boolean requiresManualReview;
|
||||
private Boolean isDeleted;
|
||||
private Long reviewerId;
|
||||
private String reviewComment;
|
||||
private LocalDateTime reviewedAt;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
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;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("annotation_result_history")
|
||||
public class AnnotationResultHistory {
|
||||
@TableId(type = IdType.INPUT)
|
||||
private Long id;
|
||||
private Long companyId;
|
||||
private Long creatorId;
|
||||
private UserRole creatorRole;
|
||||
private Long sourceResultId;
|
||||
private Long taskId;
|
||||
private Long resourceId;
|
||||
private String qaContentJson;
|
||||
private String qaContentStorageMode;
|
||||
private String qaContentFilePath;
|
||||
private String archiveReason;
|
||||
private Long archivedBy;
|
||||
private LocalDateTime archivedAt;
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
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;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("annotation_task")
|
||||
public class AnnotationTask {
|
||||
@TableId(type = IdType.INPUT)
|
||||
private Long id;
|
||||
private Long companyId;
|
||||
private Long creatorId;
|
||||
private UserRole creatorRole;
|
||||
private String taskName;
|
||||
private String industryType;
|
||||
private String taskType;
|
||||
private Long extractModelConfigId;
|
||||
private String extractModelName;
|
||||
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 finishedAt;
|
||||
private String errorMessage;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.labelsys.backend.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("annotation_task_resource")
|
||||
public class AnnotationTaskResource {
|
||||
@TableId(type = IdType.INPUT)
|
||||
private Long id;
|
||||
private Long companyId;
|
||||
private Long taskId;
|
||||
private Long resourceId;
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
22
src/main/java/com/labelsys/backend/entity/BizDataRecord.java
Normal file
22
src/main/java/com/labelsys/backend/entity/BizDataRecord.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.labelsys.backend.entity;
|
||||
|
||||
import com.labelsys.backend.enums.UserRole;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BizDataRecord {
|
||||
private Long id;
|
||||
private Long companyId;
|
||||
private Long creatorId;
|
||||
private UserRole creatorRole;
|
||||
private String recordName;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
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;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("source_resource")
|
||||
public class SourceResource {
|
||||
@TableId(type = IdType.INPUT)
|
||||
private Long id;
|
||||
private Long companyId;
|
||||
private Long creatorId;
|
||||
private UserRole creatorRole;
|
||||
private String resourceName;
|
||||
private String resourceType;
|
||||
private String bucketName;
|
||||
private String filePath;
|
||||
private Long fileSize;
|
||||
private String sourceStatus;
|
||||
private String storageProvider;
|
||||
private String remark;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
28
src/main/java/com/labelsys/backend/entity/SysConfig.java
Normal file
28
src/main/java/com/labelsys/backend/entity/SysConfig.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.labelsys.backend.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("sys_config")
|
||||
public class SysConfig {
|
||||
@TableId(type = IdType.INPUT)
|
||||
private Long id;
|
||||
private Long companyId;
|
||||
private String configType;
|
||||
private String configName;
|
||||
private String configValue;
|
||||
private String status;
|
||||
private Long creatorId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
13
src/main/java/com/labelsys/backend/enums/ConfigType.java
Normal file
13
src/main/java/com/labelsys/backend/enums/ConfigType.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.labelsys.backend.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum ConfigType {
|
||||
MODEL,
|
||||
PROMPT,
|
||||
SYSTEM;
|
||||
|
||||
public static boolean isValid(String value) {
|
||||
return Arrays.stream(values()).anyMatch(type -> type.name().equals(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.labelsys.backend.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum QaContentStorageMode {
|
||||
INLINE,
|
||||
EXTERNAL;
|
||||
|
||||
public static boolean isValid(String value) {
|
||||
return Arrays.stream(values()).anyMatch(mode -> mode.name().equals(value));
|
||||
}
|
||||
}
|
||||
13
src/main/java/com/labelsys/backend/enums/ResourceType.java
Normal file
13
src/main/java/com/labelsys/backend/enums/ResourceType.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.labelsys.backend.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum ResourceType {
|
||||
TEXT,
|
||||
IMAGE,
|
||||
VIDEO;
|
||||
|
||||
public static boolean isValid(String value) {
|
||||
return Arrays.stream(values()).anyMatch(type -> type.name().equals(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.labelsys.backend.enums;
|
||||
|
||||
public enum RuntimeResultStatus {
|
||||
MANUAL_REVIEW_PENDING,
|
||||
AUTO_ARCHIVE_PENDING,
|
||||
ARCHIVED
|
||||
}
|
||||
14
src/main/java/com/labelsys/backend/enums/SourceStatus.java
Normal file
14
src/main/java/com/labelsys/backend/enums/SourceStatus.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.labelsys.backend.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum SourceStatus {
|
||||
UPLOADED,
|
||||
PROCESSING,
|
||||
READY,
|
||||
ARCHIVED;
|
||||
|
||||
public static boolean isValid(String value) {
|
||||
return Arrays.stream(values()).anyMatch(status -> status.name().equals(value));
|
||||
}
|
||||
}
|
||||
14
src/main/java/com/labelsys/backend/enums/TaskStatus.java
Normal file
14
src/main/java/com/labelsys/backend/enums/TaskStatus.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.labelsys.backend.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum TaskStatus {
|
||||
PENDING,
|
||||
RUNNING,
|
||||
COMPLETED,
|
||||
FAILED;
|
||||
|
||||
public static boolean isValid(String value) {
|
||||
return Arrays.stream(values()).anyMatch(status -> status.name().equals(value));
|
||||
}
|
||||
}
|
||||
@@ -27,11 +27,17 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
@Component
|
||||
public class AuthInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final Set<String> OPEN_PATHS = Set.of("/label/api/auth/companies", "/label/api/auth/login",
|
||||
"/label/swagger-ui.html", "/label/v3/api-docs", "/label/v3/api-docs/swagger-config");
|
||||
private static final Set<String> OPEN_PATHS = Set.of(
|
||||
"/api/auth/companies", "/label/api/auth/companies",
|
||||
"/api/auth/login", "/label/api/auth/login",
|
||||
"/swagger-ui.html", "/label/swagger-ui.html",
|
||||
"/v3/api-docs", "/label/v3/api-docs",
|
||||
"/v3/api-docs/swagger-config", "/label/v3/api-docs/swagger-config");
|
||||
|
||||
private static final Set<String> ALLOWED_WHEN_MUST_CHANGE_PASSWORD =
|
||||
Set.of("/label/api/auth/change-password", "/label/api/auth/logout", "/label/api/auth/me");
|
||||
private static final Set<String> ALLOWED_WHEN_MUST_CHANGE_PASSWORD = Set.of(
|
||||
"/api/auth/change-password", "/label/api/auth/change-password",
|
||||
"/api/auth/logout", "/label/api/auth/logout",
|
||||
"/api/auth/me", "/label/api/auth/me");
|
||||
|
||||
private final TokenSessionRepository tokenSessionRepository;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.labelsys.backend.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.labelsys.backend.entity.AnnotationResultHistory;
|
||||
|
||||
public interface AnnotationResultHistoryMapper extends BaseMapper<AnnotationResultHistory> {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.labelsys.backend.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.labelsys.backend.entity.AnnotationResult;
|
||||
import java.time.LocalDateTime;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface AnnotationResultMapper extends BaseMapper<AnnotationResult> {
|
||||
|
||||
AnnotationResult findActiveByIdAndCompanyId(@Param("id") Long id, @Param("companyId") Long companyId);
|
||||
|
||||
int markArchived(@Param("id") Long id,
|
||||
@Param("companyId") Long companyId,
|
||||
@Param("reviewerId") Long reviewerId,
|
||||
@Param("reviewComment") String reviewComment,
|
||||
@Param("reviewedAt") LocalDateTime reviewedAt);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.labelsys.backend.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.labelsys.backend.entity.AnnotationTask;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface AnnotationTaskMapper extends BaseMapper<AnnotationTask> {
|
||||
|
||||
AnnotationTask findByIdAndCompanyId(@Param("id") Long id, @Param("companyId") Long companyId);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.labelsys.backend.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.labelsys.backend.entity.AnnotationTaskResource;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface AnnotationTaskResourceMapper extends BaseMapper<AnnotationTaskResource> {
|
||||
|
||||
List<Long> listResourceIdsByTaskId(@Param("taskId") Long taskId);
|
||||
|
||||
int deleteByTaskId(@Param("taskId") Long taskId);
|
||||
|
||||
int countByResourceId(@Param("resourceId") Long resourceId);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.labelsys.backend.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.labelsys.backend.entity.SourceResource;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface SourceResourceMapper extends BaseMapper<SourceResource> {
|
||||
|
||||
List<SourceResource> selectByCompanyIdAndIds(@Param("companyId") Long companyId, @Param("resourceIds") List<Long> resourceIds);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.labelsys.backend.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.labelsys.backend.entity.SysConfig;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface SysConfigMapper extends BaseMapper<SysConfig> {
|
||||
|
||||
SysConfig findByCompanyIdAndConfigName(@Param("companyId") Long companyId, @Param("configName") String configName);
|
||||
|
||||
SysConfig findByCompanyIdAndConfigNameAndType(@Param("companyId") Long companyId,
|
||||
@Param("configName") String configName,
|
||||
@Param("configType") String configType);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.labelsys.backend.scheduled;
|
||||
|
||||
import com.labelsys.backend.service.AnnotationResultArchiveService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class AutoArchiveAnnotationResultJob {
|
||||
|
||||
private final AnnotationResultArchiveService annotationResultArchiveService;
|
||||
|
||||
@Scheduled(fixedDelayString = "${labelsys.annotation.auto-archive-fixed-delay:300000}")
|
||||
public void autoArchiveEligibleResults() {
|
||||
int archivedCount = annotationResultArchiveService.autoArchiveEligibleResults();
|
||||
if (archivedCount > 0) {
|
||||
log.info("auto archived annotation results, count={}", archivedCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.labelsys.backend.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.labelsys.backend.common.ResultCode;
|
||||
import com.labelsys.backend.common.exception.BusinessException;
|
||||
import com.labelsys.backend.context.LoginUser;
|
||||
import com.labelsys.backend.dto.request.MergeReviewResultRequest;
|
||||
import com.labelsys.backend.dto.response.MergeReviewResultResponse;
|
||||
import com.labelsys.backend.entity.AnnotationResult;
|
||||
import com.labelsys.backend.entity.AnnotationResultHistory;
|
||||
import com.labelsys.backend.enums.QaContentStorageMode;
|
||||
import com.labelsys.backend.enums.UserPosition;
|
||||
import com.labelsys.backend.mapper.AnnotationResultHistoryMapper;
|
||||
import com.labelsys.backend.mapper.AnnotationResultMapper;
|
||||
import com.labelsys.backend.util.IdGenerator;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AnnotationResultArchiveService {
|
||||
|
||||
private static final String MANUAL_ARCHIVE_REASON = "MANUAL_REVIEW";
|
||||
|
||||
private final AnnotationResultMapper annotationResultMapper;
|
||||
private final AnnotationResultHistoryMapper annotationResultHistoryMapper;
|
||||
@Value("${labelsys.annotation.auto-archive-timeout:PT2H}")
|
||||
private Duration autoArchiveTimeout;
|
||||
|
||||
@Transactional
|
||||
public MergeReviewResultResponse mergeReview(LoginUser currentUser, Long resultId, MergeReviewResultRequest request) {
|
||||
assertReviewer(currentUser);
|
||||
AnnotationResult result = annotationResultMapper.findActiveByIdAndCompanyId(resultId, currentUser.companyId());
|
||||
if (result == null) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "运行态结果不存在");
|
||||
}
|
||||
|
||||
LocalDateTime archivedAt = LocalDateTime.now();
|
||||
AnnotationResultHistory history = AnnotationResultHistory.builder()
|
||||
.id(IdGenerator.nextId())
|
||||
.companyId(result.getCompanyId())
|
||||
.creatorId(result.getCreatorId())
|
||||
.creatorRole(result.getCreatorRole())
|
||||
.sourceResultId(result.getId())
|
||||
.taskId(result.getTaskId())
|
||||
.resourceId(result.getResourceId())
|
||||
.qaContentJson(request.qaContentJson())
|
||||
.qaContentStorageMode(resolveStorageMode(result))
|
||||
.qaContentFilePath(result.getQaContentFilePath())
|
||||
.archiveReason(MANUAL_ARCHIVE_REASON)
|
||||
.archivedBy(currentUser.userId())
|
||||
.archivedAt(archivedAt)
|
||||
.build();
|
||||
annotationResultHistoryMapper.insert(history);
|
||||
|
||||
int updated = annotationResultMapper.markArchived(
|
||||
result.getId(),
|
||||
currentUser.companyId(),
|
||||
currentUser.userId(),
|
||||
request.reviewComment(),
|
||||
archivedAt);
|
||||
if (updated == 0) {
|
||||
throw new BusinessException(ResultCode.CONFLICT, "结果已被其他操作处理");
|
||||
}
|
||||
|
||||
log.info("merged review result, companyId={}, reviewerId={}, resultId={}, historyId={}",
|
||||
currentUser.companyId(), currentUser.userId(), resultId, history.getId());
|
||||
return new MergeReviewResultResponse(resultId, history.getId(), MANUAL_ARCHIVE_REASON, archivedAt);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public int autoArchiveEligibleResults() {
|
||||
LocalDateTime cutoff = LocalDateTime.now().minus(autoArchiveTimeout);
|
||||
List<AnnotationResult> results = annotationResultMapper.selectList(new LambdaQueryWrapper<AnnotationResult>()
|
||||
.eq(AnnotationResult::getIsDeleted, false)
|
||||
.eq(AnnotationResult::getRequiresManualReview, false)
|
||||
.lt(AnnotationResult::getCreatedAt, cutoff));
|
||||
int archivedCount = 0;
|
||||
for (AnnotationResult result : results) {
|
||||
if (archiveRuntimeResult(result, null, "AUTO_ARCHIVE", null) != null) {
|
||||
archivedCount++;
|
||||
}
|
||||
}
|
||||
return archivedCount;
|
||||
}
|
||||
|
||||
private void assertReviewer(LoginUser currentUser) {
|
||||
if (currentUser.position() != UserPosition.REVIEWER && currentUser.position() != UserPosition.ADMIN) {
|
||||
throw new BusinessException(ResultCode.FORBIDDEN, "当前用户没有审核权限");
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveStorageMode(AnnotationResult result) {
|
||||
if (QaContentStorageMode.isValid(result.getQaContentStorageMode())) {
|
||||
return result.getQaContentStorageMode();
|
||||
}
|
||||
return QaContentStorageMode.INLINE.name();
|
||||
}
|
||||
|
||||
private MergeReviewResultResponse archiveRuntimeResult(AnnotationResult result,
|
||||
Long reviewerId,
|
||||
String archiveReason,
|
||||
String reviewComment) {
|
||||
LocalDateTime archivedAt = LocalDateTime.now();
|
||||
AnnotationResultHistory history = AnnotationResultHistory.builder()
|
||||
.id(IdGenerator.nextId())
|
||||
.companyId(result.getCompanyId())
|
||||
.creatorId(result.getCreatorId())
|
||||
.creatorRole(result.getCreatorRole())
|
||||
.sourceResultId(result.getId())
|
||||
.taskId(result.getTaskId())
|
||||
.resourceId(result.getResourceId())
|
||||
.qaContentJson(result.getQaContentJson())
|
||||
.qaContentStorageMode(resolveStorageMode(result))
|
||||
.qaContentFilePath(result.getQaContentFilePath())
|
||||
.archiveReason(archiveReason)
|
||||
.archivedBy(reviewerId)
|
||||
.archivedAt(archivedAt)
|
||||
.build();
|
||||
annotationResultHistoryMapper.insert(history);
|
||||
|
||||
int updated = annotationResultMapper.markArchived(
|
||||
result.getId(),
|
||||
result.getCompanyId(),
|
||||
reviewerId,
|
||||
reviewComment,
|
||||
archivedAt);
|
||||
if (updated == 0) {
|
||||
return null;
|
||||
}
|
||||
return new MergeReviewResultResponse(result.getId(), history.getId(), archiveReason, archivedAt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.labelsys.backend.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.labelsys.backend.common.ResultCode;
|
||||
import com.labelsys.backend.common.exception.BusinessException;
|
||||
import com.labelsys.backend.context.LoginUser;
|
||||
import com.labelsys.backend.dto.common.PageResult;
|
||||
import com.labelsys.backend.dto.request.AnnotationResultPageQuery;
|
||||
import com.labelsys.backend.dto.response.AnnotationResultCompareResponse;
|
||||
import com.labelsys.backend.dto.response.AnnotationResultResponse;
|
||||
import com.labelsys.backend.entity.AnnotationResult;
|
||||
import com.labelsys.backend.entity.SourceResource;
|
||||
import com.labelsys.backend.enums.RuntimeResultStatus;
|
||||
import com.labelsys.backend.mapper.AnnotationResultMapper;
|
||||
import com.labelsys.backend.mapper.SourceResourceMapper;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AnnotationResultService {
|
||||
|
||||
private final AnnotationResultMapper annotationResultMapper;
|
||||
private final SourceResourceMapper sourceResourceMapper;
|
||||
|
||||
public PageResult<AnnotationResultResponse> pageResults(LoginUser currentUser, AnnotationResultPageQuery query) {
|
||||
LambdaQueryWrapper<AnnotationResult> wrapper = new LambdaQueryWrapper<AnnotationResult>()
|
||||
.eq(AnnotationResult::getCompanyId, currentUser.companyId())
|
||||
.eq(query.taskId() != null, AnnotationResult::getTaskId, query.taskId())
|
||||
.eq(query.resourceId() != null, AnnotationResult::getResourceId, query.resourceId())
|
||||
.eq(query.requiresManualReview() != null, AnnotationResult::getRequiresManualReview, query.requiresManualReview())
|
||||
.orderByDesc(AnnotationResult::getCreatedAt);
|
||||
List<AnnotationResultResponse> records = annotationResultMapper.selectList(wrapper).stream()
|
||||
.map(this::toResponse)
|
||||
.filter(response -> query.runtimeStatus() == null || query.runtimeStatus().equals(response.runtimeStatus()))
|
||||
.sorted(Comparator.comparing(AnnotationResultResponse::createdAt, Comparator.nullsLast(Comparator.naturalOrder())).reversed())
|
||||
.toList();
|
||||
return paginate(records, query.pageNo(), query.pageSize());
|
||||
}
|
||||
|
||||
public AnnotationResultResponse getResult(LoginUser currentUser, Long resultId) {
|
||||
AnnotationResult result = annotationResultMapper.selectById(resultId);
|
||||
if (result == null || !currentUser.companyId().equals(result.getCompanyId())) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "结果不存在");
|
||||
}
|
||||
return toResponse(result);
|
||||
}
|
||||
|
||||
public AnnotationResultCompareResponse compareResult(LoginUser currentUser, Long resultId) {
|
||||
AnnotationResult result = annotationResultMapper.selectById(resultId);
|
||||
if (result == null || !currentUser.companyId().equals(result.getCompanyId())) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "结果不存在");
|
||||
}
|
||||
SourceResource resource = sourceResourceMapper.selectById(result.getResourceId());
|
||||
return new AnnotationResultCompareResponse(
|
||||
result.getId(),
|
||||
result.getTaskId(),
|
||||
result.getResourceId(),
|
||||
result.getQaContentJson(),
|
||||
result.getDiffSummary(),
|
||||
result.getQaContentStorageMode(),
|
||||
result.getQaContentFilePath(),
|
||||
resource == null ? null : resource.getFilePath());
|
||||
}
|
||||
|
||||
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 String deriveStatus(AnnotationResult result) {
|
||||
if (Boolean.TRUE.equals(result.getIsDeleted())) {
|
||||
return RuntimeResultStatus.ARCHIVED.name();
|
||||
}
|
||||
if (Boolean.TRUE.equals(result.getRequiresManualReview())) {
|
||||
return RuntimeResultStatus.MANUAL_REVIEW_PENDING.name();
|
||||
}
|
||||
return RuntimeResultStatus.AUTO_ARCHIVE_PENDING.name();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
package com.labelsys.backend.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.labelsys.backend.common.ResultCode;
|
||||
import com.labelsys.backend.common.exception.BusinessException;
|
||||
import com.labelsys.backend.context.LoginUser;
|
||||
import com.labelsys.backend.dto.common.PageResult;
|
||||
import com.labelsys.backend.dto.request.AnnotationTaskPageQuery;
|
||||
import com.labelsys.backend.dto.request.CreateAnnotationTaskRequest;
|
||||
import com.labelsys.backend.dto.request.UpdateAnnotationTaskRequest;
|
||||
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.AnnotationTaskResource;
|
||||
import com.labelsys.backend.entity.SourceResource;
|
||||
import com.labelsys.backend.enums.SourceStatus;
|
||||
import com.labelsys.backend.enums.TaskStatus;
|
||||
import com.labelsys.backend.mapper.AnnotationTaskMapper;
|
||||
import com.labelsys.backend.mapper.AnnotationTaskResourceMapper;
|
||||
import com.labelsys.backend.mapper.SourceResourceMapper;
|
||||
import com.labelsys.backend.util.IdGenerator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AnnotationTaskService {
|
||||
|
||||
private final AnnotationTaskMapper annotationTaskMapper;
|
||||
private final AnnotationTaskResourceMapper annotationTaskResourceMapper;
|
||||
private final SourceResourceMapper sourceResourceMapper;
|
||||
private final SysConfigService sysConfigService;
|
||||
private final DataPermissionService dataPermissionService;
|
||||
|
||||
@Transactional
|
||||
public AnnotationTaskResponse createTask(LoginUser currentUser, CreateAnnotationTaskRequest request) {
|
||||
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())
|
||||
.creatorId(currentUser.userId())
|
||||
.creatorRole(currentUser.role())
|
||||
.taskName(request.taskName())
|
||||
.industryType(defaultIndustryType(request.industryType()))
|
||||
.taskType(defaultTaskType(request.taskType()))
|
||||
.extractModelConfigId(extractModel.configId())
|
||||
.extractModelName(extractModel.modelName())
|
||||
.extractModelUrl(extractModel.modelUrl())
|
||||
.extractModelApiKey(extractModel.apiKey())
|
||||
.verifyModelConfigId(verifyModel.configId())
|
||||
.verifyModelName(verifyModel.modelName())
|
||||
.verifyModelUrl(verifyModel.modelUrl())
|
||||
.verifyModelApiKey(verifyModel.apiKey())
|
||||
.extractPromptConfigId(extractPrompt.configId())
|
||||
.extractPrompt(extractPrompt.promptText())
|
||||
.verifyPromptConfigId(verifyPrompt.configId())
|
||||
.verifyPrompt(verifyPrompt.promptText())
|
||||
.taskStatus(TaskStatus.PENDING.name())
|
||||
.isDeleted(false)
|
||||
.build();
|
||||
annotationTaskMapper.insert(task);
|
||||
saveTaskBindings(task.getId(), currentUser.companyId(), resources);
|
||||
log.info("created annotation task, companyId={}, userId={}, taskId={}, resourceCount={}",
|
||||
currentUser.companyId(), currentUser.userId(), task.getId(), resources.size());
|
||||
return buildTaskResponse(task, resourceIds(resources), extractModel, verifyModel, extractPrompt, verifyPrompt);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public AnnotationTaskResponse updateTask(LoginUser currentUser, Long taskId, UpdateAnnotationTaskRequest request) {
|
||||
AnnotationTask task = annotationTaskMapper.findByIdAndCompanyId(taskId, currentUser.companyId());
|
||||
if (task == null) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "任务不存在");
|
||||
}
|
||||
assertTaskPermission(currentUser, task);
|
||||
|
||||
List<Long> currentResourceIds = normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(taskId));
|
||||
List<Long> targetResourceIds = normalizeIds(request.resourceIds());
|
||||
boolean resourcesChanged = !currentResourceIds.equals(targetResourceIds);
|
||||
if (TaskStatus.RUNNING.name().equals(task.getTaskStatus()) && resourcesChanged) {
|
||||
throw new BusinessException(ResultCode.CONFLICT, "运行中的任务不允许修改资源");
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
task.setIndustryType(defaultIndustryType(request.industryType()));
|
||||
task.setTaskType(defaultTaskType(request.taskType()));
|
||||
task.setExtractModelConfigId(extractModel.configId());
|
||||
task.setExtractModelName(extractModel.modelName());
|
||||
task.setExtractModelUrl(extractModel.modelUrl());
|
||||
task.setExtractModelApiKey(extractModel.apiKey());
|
||||
task.setVerifyModelConfigId(verifyModel.configId());
|
||||
task.setVerifyModelName(verifyModel.modelName());
|
||||
task.setVerifyModelUrl(verifyModel.modelUrl());
|
||||
task.setVerifyModelApiKey(verifyModel.apiKey());
|
||||
task.setExtractPromptConfigId(extractPrompt.configId());
|
||||
task.setExtractPrompt(extractPrompt.promptText());
|
||||
task.setVerifyPromptConfigId(verifyPrompt.configId());
|
||||
task.setVerifyPrompt(verifyPrompt.promptText());
|
||||
annotationTaskMapper.updateById(task);
|
||||
|
||||
if (resourcesChanged) {
|
||||
annotationTaskResourceMapper.deleteByTaskId(taskId);
|
||||
saveTaskBindings(taskId, currentUser.companyId(), resources);
|
||||
}
|
||||
log.info("updated annotation task, companyId={}, userId={}, taskId={}, resourcesChanged={}",
|
||||
currentUser.companyId(), currentUser.userId(), taskId, resourcesChanged);
|
||||
return buildTaskResponse(task, resourceIds(resources), extractModel, verifyModel, extractPrompt, verifyPrompt);
|
||||
}
|
||||
|
||||
public AnnotationTaskResponse getTask(LoginUser currentUser, Long taskId) {
|
||||
AnnotationTask task = annotationTaskMapper.findByIdAndCompanyId(taskId, currentUser.companyId());
|
||||
if (task == null) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "任务不存在");
|
||||
}
|
||||
assertTaskPermission(currentUser, task);
|
||||
return buildTaskResponse(task, normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(taskId)));
|
||||
}
|
||||
|
||||
public PageResult<AnnotationTaskResponse> pageTasks(LoginUser currentUser, AnnotationTaskPageQuery query) {
|
||||
LambdaQueryWrapper<AnnotationTask> wrapper = new LambdaQueryWrapper<AnnotationTask>()
|
||||
.eq(AnnotationTask::getCompanyId, currentUser.companyId())
|
||||
.eq(StringUtils.hasText(query.taskType()), AnnotationTask::getTaskType, query.taskType())
|
||||
.eq(StringUtils.hasText(query.taskStatus()), AnnotationTask::getTaskStatus, query.taskStatus())
|
||||
.eq(query.isDeleted() != null, AnnotationTask::getIsDeleted, query.isDeleted())
|
||||
.like(StringUtils.hasText(query.keyword()), AnnotationTask::getTaskName, query.keyword())
|
||||
.orderByDesc(AnnotationTask::getCreatedAt);
|
||||
List<AnnotationTaskResponse> records = annotationTaskMapper.selectList(wrapper).stream()
|
||||
.filter(task -> dataPermissionService.canAccessCreator(currentUser, task.getCreatorId(), task.getCreatorRole()))
|
||||
.filter(task -> query.resourceId() == null || annotationTaskResourceMapper.listResourceIdsByTaskId(task.getId()).contains(query.resourceId()))
|
||||
.sorted(Comparator.comparing(AnnotationTask::getCreatedAt, Comparator.nullsLast(Comparator.naturalOrder())).reversed())
|
||||
.map(task -> buildTaskResponse(task, normalizeIds(annotationTaskResourceMapper.listResourceIdsByTaskId(task.getId()))))
|
||||
.toList();
|
||||
return paginate(records, query.pageNo(), query.pageSize());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteTask(LoginUser currentUser, Long taskId) {
|
||||
AnnotationTask task = annotationTaskMapper.findByIdAndCompanyId(taskId, currentUser.companyId());
|
||||
if (task == null) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "任务不存在");
|
||||
}
|
||||
assertTaskPermission(currentUser, task);
|
||||
if (TaskStatus.RUNNING.name().equals(task.getTaskStatus())) {
|
||||
throw new BusinessException(ResultCode.CONFLICT, "运行中的任务不允许删除");
|
||||
}
|
||||
task.setIsDeleted(true);
|
||||
annotationTaskMapper.updateById(task);
|
||||
log.info("deleted annotation task logically, companyId={}, userId={}, taskId={}",
|
||||
currentUser.companyId(), currentUser.userId(), taskId);
|
||||
}
|
||||
|
||||
private List<SourceResource> loadAndValidateResources(LoginUser currentUser, List<Long> resourceIds) {
|
||||
if (resourceIds == null || resourceIds.isEmpty()) {
|
||||
throw new BusinessException(ResultCode.BAD_REQUEST, "任务资源不能为空");
|
||||
}
|
||||
List<Long> normalizedIds = normalizeIds(resourceIds);
|
||||
List<SourceResource> resources = sourceResourceMapper.selectByCompanyIdAndIds(currentUser.companyId(), normalizedIds);
|
||||
if (resources.size() != normalizedIds.size()) {
|
||||
throw new BusinessException(ResultCode.BAD_REQUEST, "存在无效资源");
|
||||
}
|
||||
for (SourceResource resource : resources) {
|
||||
if (!dataPermissionService.canAccessCreator(currentUser, resource.getCreatorId(), resource.getCreatorRole())) {
|
||||
throw new BusinessException(ResultCode.FORBIDDEN, "无权访问资源");
|
||||
}
|
||||
if (!SourceStatus.READY.name().equals(resource.getSourceStatus())) {
|
||||
throw new BusinessException(ResultCode.BAD_REQUEST, "仅允许选择已就绪资源");
|
||||
}
|
||||
}
|
||||
resources.sort(Comparator.comparing(SourceResource::getId));
|
||||
return resources;
|
||||
}
|
||||
|
||||
private void saveTaskBindings(Long taskId, Long companyId, List<SourceResource> resources) {
|
||||
for (SourceResource resource : resources) {
|
||||
annotationTaskResourceMapper.insert(AnnotationTaskResource.builder()
|
||||
.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) {
|
||||
return new AnnotationTaskResponse(
|
||||
task.getId(),
|
||||
task.getTaskName(),
|
||||
task.getIndustryType(),
|
||||
task.getTaskType(),
|
||||
task.getTaskStatus(),
|
||||
resourceIds,
|
||||
new TaskModelConfigResponse(task.getExtractModelConfigId(), null, task.getExtractModelName(),
|
||||
task.getExtractModelUrl(), maskSecret(task.getExtractModelApiKey())),
|
||||
new TaskModelConfigResponse(task.getVerifyModelConfigId(), null, task.getVerifyModelName(),
|
||||
task.getVerifyModelUrl(), maskSecret(task.getVerifyModelApiKey())),
|
||||
new TaskPromptConfigResponse(task.getExtractPromptConfigId(), null, task.getExtractPrompt()),
|
||||
new TaskPromptConfigResponse(task.getVerifyPromptConfigId(), null, task.getVerifyPrompt()),
|
||||
task.getCreatedAt(),
|
||||
task.getUpdatedAt());
|
||||
}
|
||||
|
||||
private List<Long> resourceIds(List<SourceResource> resources) {
|
||||
return resources.stream().map(SourceResource::getId).sorted().toList();
|
||||
}
|
||||
|
||||
private List<Long> normalizeIds(List<Long> resourceIds) {
|
||||
Set<Long> uniqueIds = new HashSet<>(resourceIds);
|
||||
List<Long> sortedIds = new ArrayList<>(uniqueIds);
|
||||
sortedIds.sort(Long::compareTo);
|
||||
return sortedIds;
|
||||
}
|
||||
|
||||
private void assertTaskPermission(LoginUser currentUser, AnnotationTask task) {
|
||||
if (!dataPermissionService.canAccessCreator(currentUser, task.getCreatorId(), task.getCreatorRole())) {
|
||||
throw new BusinessException(ResultCode.FORBIDDEN, "无权操作任务");
|
||||
}
|
||||
}
|
||||
|
||||
private String defaultIndustryType(String industryType) {
|
||||
return StringUtils.hasText(industryType) ? industryType : "transport";
|
||||
}
|
||||
|
||||
private String defaultTaskType(String taskType) {
|
||||
return StringUtils.hasText(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 <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);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,21 @@
|
||||
package com.labelsys.backend.service;
|
||||
|
||||
import com.labelsys.backend.context.LoginUser;
|
||||
import com.labelsys.backend.entity.BizDataRecord;
|
||||
import com.labelsys.backend.enums.UserRole;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DataPermissionService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public boolean canAccessCreator(LoginUser currentUser, Long creatorId, UserRole creatorRole) {
|
||||
return switch (currentUser.role()) {
|
||||
case EMPLOYEE -> currentUser.userId().equals(creatorId);
|
||||
@@ -65,4 +71,25 @@ public class DataPermissionService {
|
||||
public boolean shouldFilterByUserId(LoginUser currentUser) {
|
||||
return currentUser.role() == UserRole.EMPLOYEE;
|
||||
}
|
||||
|
||||
public List<BizDataRecord> listVisibleRecords(LoginUser currentUser) {
|
||||
List<BizDataRecord> allRecords = jdbcTemplate.query("""
|
||||
select id, company_id, creator_id, creator_role, record_name, created_at, updated_at
|
||||
from biz_data_record
|
||||
where company_id = ?
|
||||
order by id
|
||||
""",
|
||||
(rs, rowNum) -> BizDataRecord.builder()
|
||||
.id(rs.getLong("id"))
|
||||
.companyId(rs.getLong("company_id"))
|
||||
.creatorId(rs.getLong("creator_id"))
|
||||
.creatorRole(UserRole.valueOf(rs.getString("creator_role")))
|
||||
.recordName(rs.getString("record_name"))
|
||||
.createdAt(rs.getTimestamp("created_at") == null ? null : rs.getTimestamp("created_at").toLocalDateTime())
|
||||
.updatedAt(rs.getTimestamp("updated_at") == null ? null : rs.getTimestamp("updated_at").toLocalDateTime())
|
||||
.build(),
|
||||
currentUser.companyId());
|
||||
|
||||
return filterByRole(currentUser, allRecords, BizDataRecord::getCreatorRole, BizDataRecord::getCreatorId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.labelsys.backend.service;
|
||||
|
||||
public interface ObjectStorageService {
|
||||
|
||||
String upload(String bucketName, String objectKey, byte[] content, String contentType);
|
||||
|
||||
void delete(String bucketName, String objectKey);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.labelsys.backend.service;
|
||||
|
||||
import com.labelsys.backend.common.ResultCode;
|
||||
import com.labelsys.backend.common.exception.BusinessException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import software.amazon.awssdk.core.sync.RequestBody;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RustfsObjectStorageService implements ObjectStorageService {
|
||||
|
||||
private final S3Client s3Client;
|
||||
|
||||
@Override
|
||||
public String upload(String bucketName, String objectKey, byte[] content, String contentType) {
|
||||
try {
|
||||
PutObjectRequest.Builder requestBuilder = PutObjectRequest.builder()
|
||||
.bucket(bucketName)
|
||||
.key(objectKey);
|
||||
if (contentType != null && !contentType.isBlank()) {
|
||||
requestBuilder.contentType(contentType);
|
||||
}
|
||||
s3Client.putObject(requestBuilder.build(), RequestBody.fromBytes(content));
|
||||
return objectKey;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException(ResultCode.ERROR, "对象存储上传失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String bucketName, String objectKey) {
|
||||
try {
|
||||
s3Client.deleteObject(DeleteObjectRequest.builder()
|
||||
.bucket(bucketName)
|
||||
.key(objectKey)
|
||||
.build());
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException(ResultCode.ERROR, "对象存储删除失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.labelsys.backend.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.labelsys.backend.common.ResultCode;
|
||||
import com.labelsys.backend.common.exception.BusinessException;
|
||||
import com.labelsys.backend.context.LoginUser;
|
||||
import com.labelsys.backend.dto.common.PageResult;
|
||||
import com.labelsys.backend.dto.request.SourceResourcePageQuery;
|
||||
import com.labelsys.backend.dto.request.SourceUploadRequest;
|
||||
import com.labelsys.backend.dto.response.SourceResourceResponse;
|
||||
import com.labelsys.backend.dto.response.SourceUploadResponse;
|
||||
import com.labelsys.backend.entity.SourceResource;
|
||||
import com.labelsys.backend.entity.SysUser;
|
||||
import com.labelsys.backend.enums.ResourceType;
|
||||
import com.labelsys.backend.enums.SourceStatus;
|
||||
import com.labelsys.backend.mapper.AnnotationTaskResourceMapper;
|
||||
import com.labelsys.backend.mapper.SourceResourceMapper;
|
||||
import com.labelsys.backend.mapper.SysUserMapper;
|
||||
import com.labelsys.backend.util.IdGenerator;
|
||||
import com.labelsys.backend.util.ObjectStoragePathBuilder;
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SourceResourceService {
|
||||
|
||||
private final SourceResourceMapper sourceResourceMapper;
|
||||
private final AnnotationTaskResourceMapper annotationTaskResourceMapper;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final DataPermissionService dataPermissionService;
|
||||
private final ObjectStorageService objectStorageService;
|
||||
private final com.labelsys.backend.config.ObjectStorageProperties objectStorageProperties;
|
||||
|
||||
@Transactional
|
||||
public SourceUploadResponse upload(LoginUser currentUser, SourceUploadRequest request) {
|
||||
MultipartFile file = request.getFile();
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new BusinessException(ResultCode.BAD_REQUEST, "上传文件不能为空");
|
||||
}
|
||||
if (!ResourceType.isValid(request.getResourceType())) {
|
||||
throw new BusinessException(ResultCode.BAD_REQUEST, "资源类型非法");
|
||||
}
|
||||
long resourceId = IdGenerator.nextId();
|
||||
String extension = resolveExtension(file.getOriginalFilename(), request.getResourceType());
|
||||
String objectKey = ObjectStoragePathBuilder.sourceObjectKey(
|
||||
currentUser.companyId(), request.getResourceType(), resourceId, extension);
|
||||
try {
|
||||
objectStorageService.upload(
|
||||
objectStorageProperties.getSourceBucket(),
|
||||
objectKey,
|
||||
file.getBytes(),
|
||||
file.getContentType());
|
||||
} catch (IOException ex) {
|
||||
throw new BusinessException(ResultCode.BAD_REQUEST, "读取上传文件失败");
|
||||
}
|
||||
|
||||
SourceResource resource = SourceResource.builder()
|
||||
.id(resourceId)
|
||||
.companyId(currentUser.companyId())
|
||||
.creatorId(currentUser.userId())
|
||||
.creatorRole(currentUser.role())
|
||||
.resourceName(StringUtils.hasText(request.getResourceName()) ? request.getResourceName() : file.getOriginalFilename())
|
||||
.resourceType(request.getResourceType())
|
||||
.bucketName(objectStorageProperties.getSourceBucket())
|
||||
.filePath(objectKey)
|
||||
.fileSize(file.getSize())
|
||||
.sourceStatus(SourceStatus.READY.name())
|
||||
.storageProvider("rustfs")
|
||||
.remark(request.getRemark())
|
||||
.build();
|
||||
sourceResourceMapper.insert(resource);
|
||||
log.info("uploaded source resource, companyId={}, userId={}, resourceId={}",
|
||||
currentUser.companyId(), currentUser.userId(), resourceId);
|
||||
return new SourceUploadResponse(
|
||||
resource.getId(),
|
||||
resource.getResourceName(),
|
||||
resource.getResourceType(),
|
||||
resource.getBucketName(),
|
||||
resource.getFilePath(),
|
||||
resource.getFileSize(),
|
||||
resource.getSourceStatus(),
|
||||
resource.getCreatedAt());
|
||||
}
|
||||
|
||||
public PageResult<SourceResourceResponse> pageResources(LoginUser currentUser, SourceResourcePageQuery query) {
|
||||
LambdaQueryWrapper<SourceResource> wrapper = new LambdaQueryWrapper<SourceResource>()
|
||||
.eq(SourceResource::getCompanyId, currentUser.companyId())
|
||||
.eq(StringUtils.hasText(query.resourceType()), SourceResource::getResourceType, query.resourceType())
|
||||
.eq(StringUtils.hasText(query.sourceStatus()), SourceResource::getSourceStatus, query.sourceStatus())
|
||||
.like(StringUtils.hasText(query.keyword()), SourceResource::getResourceName, query.keyword())
|
||||
.orderByDesc(SourceResource::getCreatedAt);
|
||||
List<SourceResourceResponse> records = sourceResourceMapper.selectList(wrapper).stream()
|
||||
.filter(resource -> dataPermissionService.canAccessCreator(currentUser, resource.getCreatorId(), resource.getCreatorRole()))
|
||||
.sorted(Comparator.comparing(SourceResource::getCreatedAt, Comparator.nullsLast(Comparator.naturalOrder())).reversed())
|
||||
.map(this::toResponse)
|
||||
.toList();
|
||||
return paginate(records, query.pageNo(), query.pageSize());
|
||||
}
|
||||
|
||||
public SourceResourceResponse getResource(LoginUser currentUser, Long resourceId) {
|
||||
SourceResource resource = sourceResourceMapper.selectById(resourceId);
|
||||
if (resource == null || !currentUser.companyId().equals(resource.getCompanyId())) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "资源不存在");
|
||||
}
|
||||
if (!dataPermissionService.canAccessCreator(currentUser, resource.getCreatorId(), resource.getCreatorRole())) {
|
||||
throw new BusinessException(ResultCode.FORBIDDEN, "无权访问资源");
|
||||
}
|
||||
return toResponse(resource);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteResource(LoginUser currentUser, Long resourceId) {
|
||||
SourceResource resource = sourceResourceMapper.selectById(resourceId);
|
||||
if (resource == null || !currentUser.companyId().equals(resource.getCompanyId())) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "资源不存在");
|
||||
}
|
||||
if (!dataPermissionService.canAccessCreator(currentUser, resource.getCreatorId(), resource.getCreatorRole())) {
|
||||
throw new BusinessException(ResultCode.FORBIDDEN, "无权删除资源");
|
||||
}
|
||||
int bindCount = annotationTaskResourceMapper.countByResourceId(resourceId);
|
||||
if (bindCount > 0) {
|
||||
resource.setSourceStatus(SourceStatus.ARCHIVED.name());
|
||||
sourceResourceMapper.updateById(resource);
|
||||
log.info("archived referenced source resource, companyId={}, userId={}, resourceId={}",
|
||||
currentUser.companyId(), currentUser.userId(), resourceId);
|
||||
return;
|
||||
}
|
||||
objectStorageService.delete(resource.getBucketName(), resource.getFilePath());
|
||||
sourceResourceMapper.deleteById(resourceId);
|
||||
log.info("deleted source resource, companyId={}, userId={}, resourceId={}",
|
||||
currentUser.companyId(), currentUser.userId(), resourceId);
|
||||
}
|
||||
|
||||
private SourceResourceResponse toResponse(SourceResource resource) {
|
||||
SysUser creator = sysUserMapper.selectById(resource.getCreatorId());
|
||||
return new SourceResourceResponse(
|
||||
resource.getId(),
|
||||
resource.getResourceName(),
|
||||
resource.getResourceType(),
|
||||
resource.getBucketName(),
|
||||
resource.getFilePath(),
|
||||
resource.getFileSize(),
|
||||
resource.getSourceStatus(),
|
||||
resource.getStorageProvider(),
|
||||
resource.getRemark(),
|
||||
creator == null ? null : creator.getRealName(),
|
||||
resource.getCreatedAt(),
|
||||
resource.getUpdatedAt());
|
||||
}
|
||||
|
||||
private String resolveExtension(String originalFilename, String resourceType) {
|
||||
if (StringUtils.hasText(originalFilename) && originalFilename.contains(".")) {
|
||||
return originalFilename.substring(originalFilename.lastIndexOf('.') + 1);
|
||||
}
|
||||
return switch (resourceType) {
|
||||
case "TEXT" -> "txt";
|
||||
case "IMAGE" -> "png";
|
||||
case "VIDEO" -> "mp4";
|
||||
default -> "bin";
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
258
src/main/java/com/labelsys/backend/service/SysConfigService.java
Normal file
258
src/main/java/com/labelsys/backend/service/SysConfigService.java
Normal file
@@ -0,0 +1,258 @@
|
||||
package com.labelsys.backend.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.labelsys.backend.common.ResultCode;
|
||||
import com.labelsys.backend.common.exception.BusinessException;
|
||||
import com.labelsys.backend.context.LoginUser;
|
||||
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.SysConfigPageQuery;
|
||||
import com.labelsys.backend.dto.request.TaskModelConfigRequest;
|
||||
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.enums.ConfigType;
|
||||
import com.labelsys.backend.mapper.SysConfigMapper;
|
||||
import com.labelsys.backend.util.IdGenerator;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SysConfigService {
|
||||
|
||||
private final SysConfigMapper sysConfigMapper;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Transactional
|
||||
public SysConfig saveConfig(LoginUser currentUser, SaveSysConfigRequest request) {
|
||||
validateConfigType(request.configType());
|
||||
SysConfig existing = sysConfigMapper.findByCompanyIdAndConfigName(currentUser.companyId(), request.configName());
|
||||
if (existing != null) {
|
||||
throw new BusinessException(ResultCode.CONFLICT, "配置名称已存在");
|
||||
}
|
||||
SysConfig config = SysConfig.builder()
|
||||
.id(IdGenerator.nextId())
|
||||
.companyId(currentUser.companyId())
|
||||
.configType(request.configType())
|
||||
.configName(request.configName())
|
||||
.configValue(request.configValue())
|
||||
.status(request.status())
|
||||
.creatorId(currentUser.userId())
|
||||
.build();
|
||||
sysConfigMapper.insert(config);
|
||||
log.info("saved sys config, companyId={}, userId={}, configName={}, configType={}",
|
||||
currentUser.companyId(), currentUser.userId(), request.configName(), request.configType());
|
||||
return config;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public SysConfig updateConfig(LoginUser currentUser, Long configId, SaveSysConfigRequest request) {
|
||||
validateConfigType(request.configType());
|
||||
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, "配置名称已存在");
|
||||
}
|
||||
existing.setConfigType(request.configType());
|
||||
existing.setConfigName(request.configName());
|
||||
existing.setConfigValue(request.configValue());
|
||||
existing.setStatus(request.status());
|
||||
sysConfigMapper.updateById(existing);
|
||||
log.info("updated sys config, companyId={}, userId={}, configId={}",
|
||||
currentUser.companyId(), currentUser.userId(), configId);
|
||||
return existing;
|
||||
}
|
||||
|
||||
public SysConfigResponse getConfig(LoginUser currentUser, Long configId) {
|
||||
return toResponse(getConfigEntity(currentUser, configId));
|
||||
}
|
||||
|
||||
public PageResult<SysConfigResponse> pageConfigs(LoginUser currentUser, SysConfigPageQuery query) {
|
||||
LambdaQueryWrapper<SysConfig> wrapper = new LambdaQueryWrapper<SysConfig>()
|
||||
.eq(SysConfig::getCompanyId, currentUser.companyId())
|
||||
.eq(StringUtils.hasText(query.configType()), SysConfig::getConfigType, query.configType())
|
||||
.eq(StringUtils.hasText(query.status()), SysConfig::getStatus, query.status())
|
||||
.like(StringUtils.hasText(query.configName()), SysConfig::getConfigName, query.configName())
|
||||
.orderByDesc(SysConfig::getCreatedAt);
|
||||
List<SysConfigResponse> records = sysConfigMapper.selectList(wrapper).stream()
|
||||
.sorted(Comparator.comparing(SysConfig::getCreatedAt, Comparator.nullsLast(Comparator.naturalOrder())).reversed())
|
||||
.map(this::toResponse)
|
||||
.toList();
|
||||
return paginate(records, query.pageNo(), query.pageSize());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ResolvedModelConfig resolveModelConfig(LoginUser currentUser, TaskModelConfigRequest request) {
|
||||
if (request == null || !StringUtils.hasText(request.mode())) {
|
||||
throw new BusinessException(ResultCode.BAD_REQUEST, "模型配置不能为空");
|
||||
}
|
||||
if ("SELECT".equalsIgnoreCase(request.mode())) {
|
||||
return resolveSelectedModel(currentUser, request.selectedConfigName());
|
||||
}
|
||||
if ("MANUAL".equalsIgnoreCase(request.mode())) {
|
||||
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) {
|
||||
return new SysConfigResponse(
|
||||
config.getId(),
|
||||
config.getConfigType(),
|
||||
config.getConfigName(),
|
||||
config.getConfigValue(),
|
||||
config.getStatus(),
|
||||
config.getCreatorId(),
|
||||
config.getCreatedAt(),
|
||||
config.getUpdatedAt());
|
||||
}
|
||||
|
||||
private ResolvedModelConfig resolveSelectedModel(LoginUser currentUser, String configName) {
|
||||
if (!StringUtils.hasText(configName)) {
|
||||
throw new BusinessException(ResultCode.BAD_REQUEST, "模型配置名称不能为空");
|
||||
}
|
||||
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) {
|
||||
SysConfig config = sysConfigMapper.selectById(configId);
|
||||
if (config == null || !currentUser.companyId().equals(config.getCompanyId())) {
|
||||
throw new BusinessException(ResultCode.NOT_FOUND, "配置不存在");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private void validateConfigType(String configType) {
|
||||
if (!ConfigType.isValid(configType)) {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.labelsys.backend.util;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public final class ObjectStoragePathBuilder {
|
||||
|
||||
private static final DateTimeFormatter YEAR_MONTH = DateTimeFormatter.ofPattern("yyyyMM");
|
||||
|
||||
private ObjectStoragePathBuilder() {
|
||||
}
|
||||
|
||||
public static String sourceObjectKey(Long companyId, String resourceType, Long resourceId, String extension) {
|
||||
String category = resourceType.toLowerCase();
|
||||
return "source/%d/%s/%s/%d/original.%s".formatted(
|
||||
companyId,
|
||||
category,
|
||||
YEAR_MONTH.format(LocalDateTime.now()),
|
||||
resourceId,
|
||||
extension.toLowerCase());
|
||||
}
|
||||
|
||||
public static String resultQaObjectKey(Long companyId, Long taskId, Long resultId) {
|
||||
return "result/%d/%s/%d/%d/qa.json".formatted(
|
||||
companyId,
|
||||
YEAR_MONTH.format(LocalDateTime.now()),
|
||||
taskId,
|
||||
resultId);
|
||||
}
|
||||
|
||||
public static String resultDiffObjectKey(Long companyId, Long taskId, Long resultId) {
|
||||
return "result/%d/%s/%d/%d/diff.json".formatted(
|
||||
companyId,
|
||||
YEAR_MONTH.format(LocalDateTime.now()),
|
||||
taskId,
|
||||
resultId);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,17 @@ labelsys:
|
||||
session:
|
||||
ttl: PT2H
|
||||
store-type: redis
|
||||
annotation:
|
||||
auto-archive-timeout: PT2H
|
||||
object-storage:
|
||||
endpoint: ${OBJECT_STORAGE_ENDPOINT:http://127.0.0.1:9000}
|
||||
region: ${OBJECT_STORAGE_REGION:us-east-1}
|
||||
access-key: ${OBJECT_STORAGE_ACCESS_KEY:demo-access-key}
|
||||
secret-key: ${OBJECT_STORAGE_SECRET_KEY:demo-secret-key}
|
||||
path-style-access: ${OBJECT_STORAGE_PATH_STYLE:true}
|
||||
source-bucket: ${OBJECT_STORAGE_SOURCE_BUCKET:source-data}
|
||||
artifact-bucket: ${OBJECT_STORAGE_ARTIFACT_BUCKET:annotation-artifacts}
|
||||
export-bucket: ${OBJECT_STORAGE_EXPORT_BUCKET:finetune-export}
|
||||
|
||||
logging:
|
||||
level:
|
||||
|
||||
50
src/main/resources/mapper/AnnotationResultMapper.xml
Normal file
50
src/main/resources/mapper/AnnotationResultMapper.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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.AnnotationResultMapper">
|
||||
<resultMap id="AnnotationResultResultMap" type="com.labelsys.backend.entity.AnnotationResult">
|
||||
<id column="id" property="id"/>
|
||||
<result column="company_id" property="companyId"/>
|
||||
<result column="creator_id" property="creatorId"/>
|
||||
<result column="creator_role" property="creatorRole"/>
|
||||
<result column="task_id" property="taskId"/>
|
||||
<result column="resource_id" property="resourceId"/>
|
||||
<result column="qa_content_json" property="qaContentJson"/>
|
||||
<result column="qa_content_storage_mode" property="qaContentStorageMode"/>
|
||||
<result column="qa_content_file_path" property="qaContentFilePath"/>
|
||||
<result column="diff_summary" property="diffSummary"/>
|
||||
<result column="requires_manual_review" property="requiresManualReview"/>
|
||||
<result column="is_deleted" property="isDeleted"/>
|
||||
<result column="reviewer_id" property="reviewerId"/>
|
||||
<result column="review_comment" property="reviewComment"/>
|
||||
<result column="reviewed_at" property="reviewedAt"/>
|
||||
<result column="created_at" property="createdAt"/>
|
||||
<result column="updated_at" property="updatedAt"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="AnnotationResultColumns">
|
||||
id, company_id, creator_id, creator_role, task_id, resource_id, qa_content_json,
|
||||
qa_content_storage_mode, qa_content_file_path, diff_summary, requires_manual_review,
|
||||
is_deleted, reviewer_id, review_comment, reviewed_at, created_at, updated_at
|
||||
</sql>
|
||||
|
||||
<select id="findActiveByIdAndCompanyId" resultMap="AnnotationResultResultMap">
|
||||
select <include refid="AnnotationResultColumns"/>
|
||||
from annotation_result
|
||||
where id = #{id}
|
||||
and company_id = #{companyId}
|
||||
and is_deleted = false
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<update id="markArchived">
|
||||
update annotation_result
|
||||
set is_deleted = true,
|
||||
reviewer_id = #{reviewerId},
|
||||
review_comment = #{reviewComment},
|
||||
reviewed_at = #{reviewedAt},
|
||||
updated_at = #{reviewedAt}
|
||||
where id = #{id}
|
||||
and company_id = #{companyId}
|
||||
and is_deleted = false
|
||||
</update>
|
||||
</mapper>
|
||||
49
src/main/resources/mapper/AnnotationTaskMapper.xml
Normal file
49
src/main/resources/mapper/AnnotationTaskMapper.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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.AnnotationTaskMapper">
|
||||
<resultMap id="AnnotationTaskResultMap" type="com.labelsys.backend.entity.AnnotationTask">
|
||||
<id column="id" property="id"/>
|
||||
<result column="company_id" property="companyId"/>
|
||||
<result column="creator_id" property="creatorId"/>
|
||||
<result column="creator_role" property="creatorRole"/>
|
||||
<result column="task_name" property="taskName"/>
|
||||
<result column="industry_type" property="industryType"/>
|
||||
<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="is_deleted" property="isDeleted"/>
|
||||
<result column="started_at" property="startedAt"/>
|
||||
<result column="finished_at" property="finishedAt"/>
|
||||
<result column="error_message" property="errorMessage"/>
|
||||
<result column="created_at" property="createdAt"/>
|
||||
<result column="updated_at" property="updatedAt"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="AnnotationTaskColumns">
|
||||
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
|
||||
</sql>
|
||||
|
||||
<select id="findByIdAndCompanyId" resultMap="AnnotationTaskResultMap">
|
||||
select <include refid="AnnotationTaskColumns"/>
|
||||
from annotation_task
|
||||
where id = #{id}
|
||||
and company_id = #{companyId}
|
||||
and is_deleted = false
|
||||
limit 1
|
||||
</select>
|
||||
</mapper>
|
||||
28
src/main/resources/mapper/AnnotationTaskResourceMapper.xml
Normal file
28
src/main/resources/mapper/AnnotationTaskResourceMapper.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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.AnnotationTaskResourceMapper">
|
||||
<resultMap id="AnnotationTaskResourceResultMap" type="com.labelsys.backend.entity.AnnotationTaskResource">
|
||||
<id column="id" property="id"/>
|
||||
<result column="company_id" property="companyId"/>
|
||||
<result column="task_id" property="taskId"/>
|
||||
<result column="resource_id" property="resourceId"/>
|
||||
<result column="created_at" property="createdAt"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="listResourceIdsByTaskId" resultType="java.lang.Long">
|
||||
select resource_id
|
||||
from annotation_task_resource
|
||||
where task_id = #{taskId}
|
||||
order by resource_id
|
||||
</select>
|
||||
|
||||
<delete id="deleteByTaskId">
|
||||
delete from annotation_task_resource where task_id = #{taskId}
|
||||
</delete>
|
||||
|
||||
<select id="countByResourceId" resultType="int">
|
||||
select count(1)
|
||||
from annotation_task_resource
|
||||
where resource_id = #{resourceId}
|
||||
</select>
|
||||
</mapper>
|
||||
35
src/main/resources/mapper/SourceResourceMapper.xml
Normal file
35
src/main/resources/mapper/SourceResourceMapper.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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.SourceResourceMapper">
|
||||
<resultMap id="SourceResourceResultMap" type="com.labelsys.backend.entity.SourceResource">
|
||||
<id column="id" property="id"/>
|
||||
<result column="company_id" property="companyId"/>
|
||||
<result column="creator_id" property="creatorId"/>
|
||||
<result column="creator_role" property="creatorRole"/>
|
||||
<result column="resource_name" property="resourceName"/>
|
||||
<result column="resource_type" property="resourceType"/>
|
||||
<result column="bucket_name" property="bucketName"/>
|
||||
<result column="file_path" property="filePath"/>
|
||||
<result column="file_size" property="fileSize"/>
|
||||
<result column="source_status" property="sourceStatus"/>
|
||||
<result column="storage_provider" property="storageProvider"/>
|
||||
<result column="remark" property="remark"/>
|
||||
<result column="created_at" property="createdAt"/>
|
||||
<result column="updated_at" property="updatedAt"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="SourceResourceColumns">
|
||||
id, company_id, creator_id, creator_role, resource_name, resource_type, bucket_name, file_path,
|
||||
file_size, source_status, storage_provider, remark, created_at, updated_at
|
||||
</sql>
|
||||
|
||||
<select id="selectByCompanyIdAndIds" resultMap="SourceResourceResultMap">
|
||||
select <include refid="SourceResourceColumns"/>
|
||||
from source_resource
|
||||
where company_id = #{companyId}
|
||||
and id in
|
||||
<foreach collection="resourceIds" item="resourceId" open="(" separator="," close=")">
|
||||
#{resourceId}
|
||||
</foreach>
|
||||
</select>
|
||||
</mapper>
|
||||
36
src/main/resources/mapper/SysConfigMapper.xml
Normal file
36
src/main/resources/mapper/SysConfigMapper.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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.SysConfigMapper">
|
||||
<resultMap id="SysConfigResultMap" type="com.labelsys.backend.entity.SysConfig">
|
||||
<id column="id" property="id"/>
|
||||
<result column="company_id" property="companyId"/>
|
||||
<result column="config_type" property="configType"/>
|
||||
<result column="config_name" property="configName"/>
|
||||
<result column="config_value" property="configValue"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="creator_id" property="creatorId"/>
|
||||
<result column="created_at" property="createdAt"/>
|
||||
<result column="updated_at" property="updatedAt"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="SysConfigColumns">
|
||||
id, company_id, config_type, config_name, config_value, status, creator_id, created_at, updated_at
|
||||
</sql>
|
||||
|
||||
<select id="findByCompanyIdAndConfigName" resultMap="SysConfigResultMap">
|
||||
select <include refid="SysConfigColumns"/>
|
||||
from sys_config
|
||||
where company_id = #{companyId}
|
||||
and config_name = #{configName}
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<select id="findByCompanyIdAndConfigNameAndType" resultMap="SysConfigResultMap">
|
||||
select <include refid="SysConfigColumns"/>
|
||||
from sys_config
|
||||
where company_id = #{companyId}
|
||||
and config_name = #{configName}
|
||||
and config_type = #{configType}
|
||||
limit 1
|
||||
</select>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user