From 4065d993e230c63ee3bc89cce60c07fd711861a9 Mon Sep 17 00:00:00 2001 From: wh Date: Fri, 8 May 2026 22:38:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E7=95=8C=E9=9D=A2=E9=9C=80?= =?UTF-8?q?=E6=B1=82=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AnnotationResultController.java | 3 +- .../dto/request/SourceResourcePageQuery.java | 1 - .../AnnotationResultDetailResponse.java | 63 +++++++++++++++++++ .../dto/response/SourceResourceResponse.java | 1 - .../dto/response/SourceUploadResponse.java | 3 +- .../backend/entity/SourceResource.java | 1 - .../labelsys/backend/enums/SourceStatus.java | 17 ----- .../service/AnnotationResultService.java | 53 +++++++++++++++- .../service/AnnotationTaskService.java | 4 -- .../service/SourceResourceService.java | 21 +------ .../resources/mapper/SourceResourceMapper.xml | 3 +- src/main/resources/sql/data.sql | 14 ++--- src/main/resources/sql/schema.sql | 3 - 13 files changed, 128 insertions(+), 59 deletions(-) create mode 100644 src/main/java/com/labelsys/backend/dto/response/AnnotationResultDetailResponse.java delete mode 100644 src/main/java/com/labelsys/backend/enums/SourceStatus.java diff --git a/src/main/java/com/labelsys/backend/controller/AnnotationResultController.java b/src/main/java/com/labelsys/backend/controller/AnnotationResultController.java index 21edc21..1c773ed 100644 --- a/src/main/java/com/labelsys/backend/controller/AnnotationResultController.java +++ b/src/main/java/com/labelsys/backend/controller/AnnotationResultController.java @@ -6,6 +6,7 @@ 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.AnnotationResultDetailResponse; import com.labelsys.backend.dto.response.AnnotationResultResponse; import com.labelsys.backend.enums.UserPosition; import com.labelsys.backend.service.AnnotationResultService; @@ -39,7 +40,7 @@ public class AnnotationResultController { @Operation(summary = "查询标注结果详情") @GetMapping("/{id}") - public ResponseEntity getResult( + public ResponseEntity getResult( @Parameter(description = "结果ID", example = "191000000000000401") @PathVariable Long id) { return ResponseEntity.ok(annotationResultService.getResult(UserContext.requireUser(), id)); diff --git a/src/main/java/com/labelsys/backend/dto/request/SourceResourcePageQuery.java b/src/main/java/com/labelsys/backend/dto/request/SourceResourcePageQuery.java index ea59399..4aec8da 100644 --- a/src/main/java/com/labelsys/backend/dto/request/SourceResourcePageQuery.java +++ b/src/main/java/com/labelsys/backend/dto/request/SourceResourcePageQuery.java @@ -6,7 +6,6 @@ import io.swagger.v3.oas.annotations.media.Schema; public record SourceResourcePageQuery( @Schema(description = "关键字", example = "运输") String keyword, @Schema(description = "资源类型", example = "TEXT") String resourceType, - @Schema(description = "资源状态", example = "READY") String sourceStatus, @Schema(description = "页码(可选,与pageSize同时提供时启用分页)", example = "1") Integer pageNo, @Schema(description = "每页数量(可选,与pageNo同时提供时启用分页)", example = "10") Integer pageSize ) { diff --git a/src/main/java/com/labelsys/backend/dto/response/AnnotationResultDetailResponse.java b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultDetailResponse.java new file mode 100644 index 0000000..ff0c62a --- /dev/null +++ b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultDetailResponse.java @@ -0,0 +1,63 @@ +package com.labelsys.backend.dto.response; + +import com.labelsys.backend.enums.AnnotationResultStatus; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "标注结果详情响应(包含文件内容)") +public record AnnotationResultDetailResponse( + @Schema(description = "结果ID", example = "191000000000000401") Long id, + @Schema(description = "任务ID", example = "191000000000000301") Long taskId, + @Schema(description = "任务名称", example = "产品说明书标注") String taskName, + @Schema(description = "资源ID", example = "191000000000000101") Long resourceId, + @Schema(description = "资源名称", example = "产品A说明书.pdf") String resourceName, + @Schema(description = "标注结果状态", example = "MANUAL_REVIEW_PENDING") AnnotationResultStatus runtimeStatus, + @Schema(description = "是否需要人工审核", example = "true") Boolean requiresManualReview, + @Schema(description = "是否已删除", example = "false") Boolean isDeleted, + + // 文件路径 + @Schema(description = "问答内容文件路径", example = "annotation-results/2/qa/801.json") String qaContentFilePath, + @Schema(description = "差异摘要文件路径", example = "annotation-results/2/diff/801.json") String diffSummaryFilePath, + + // 文件内容 + @Schema(description = "问答内容") QaContentDto qaContent, + @Schema(description = "差异摘要(需要审核时有值)") DiffContentDto diffSummary, + + @Schema(description = "审核备注", example = "需统一时间字段口径。") String reviewComment, + @Schema(description = "审核时间", example = "2026-04-27T11:00:00") LocalDateTime reviewedAt, + @Schema(description = "创建时间", example = "2026-04-27T10:40:00") LocalDateTime createdAt +) { + @Schema(description = "问答内容结构") + public record QaContentDto( + @Schema(description = "问答记录列表") List records + ) { + } + + @Schema(description = "问答记录") + public record QaRecordDto( + @Schema(description = "记录ID", example = "q1") String id, + @Schema(description = "问题", example = "产品重量是多少?") String question, + @Schema(description = "答案", example = "5kg") String answer, + @Schema(description = "是否需要审核", example = "false") Boolean requiresReview + ) { + } + + @Schema(description = "差异摘要结构") + public record DiffContentDto( + @Schema(description = "差异记录列表") List records + ) { + } + + @Schema(description = "差异记录") + public record DiffRecordDto( + @Schema(description = "关联问答ID", example = "q2") String qaId, + @Schema(description = "问题", example = "保修期多久?") String question, + @Schema(description = "抽取答案", example = "2年") String extractAnswer, + @Schema(description = "验证答案", example = "3年") String verifyAnswer, + @Schema(description = "差异原因", example = "抽取与验证结果不一致") String diffReason, + @Schema(description = "合并后答案") String mergedAnswer + ) { + } +} \ No newline at end of file diff --git a/src/main/java/com/labelsys/backend/dto/response/SourceResourceResponse.java b/src/main/java/com/labelsys/backend/dto/response/SourceResourceResponse.java index 3925590..11e528f 100644 --- a/src/main/java/com/labelsys/backend/dto/response/SourceResourceResponse.java +++ b/src/main/java/com/labelsys/backend/dto/response/SourceResourceResponse.java @@ -11,7 +11,6 @@ public record SourceResourceResponse( @Schema(description = "桶名称", example = "annotation-source") String bucketName, @Schema(description = "文件路径", example = "company/191000000000000001/text/191000000000000101.txt") String filePath, @Schema(description = "文件大小", example = "20480") Long fileSize, - @Schema(description = "资源状态", example = "READY") String sourceStatus, @Schema(description = "存储提供方", example = "rustfs") String storageProvider, @Schema(description = "是否有BBOX标注,不显示", example = "false") Boolean hasBbox, @Schema(description = "备注", example = "第一批导入样本") String remark, diff --git a/src/main/java/com/labelsys/backend/dto/response/SourceUploadResponse.java b/src/main/java/com/labelsys/backend/dto/response/SourceUploadResponse.java index 6145246..b17c5ca 100644 --- a/src/main/java/com/labelsys/backend/dto/response/SourceUploadResponse.java +++ b/src/main/java/com/labelsys/backend/dto/response/SourceUploadResponse.java @@ -11,7 +11,6 @@ public record SourceUploadResponse( @Schema(description = "桶名称", example = "annotation-source") String bucketName, @Schema(description = "文件路径", example = "company/191000000000000001/text/191000000000000101.txt") String filePath, @Schema(description = "文件大小", example = "20480") Long fileSize, - @Schema(description = "资源状态", example = "READY") String sourceStatus, @Schema(description = "创建时间", example = "2026-04-27T10:00:00") LocalDateTime createdAt ) { -} +} \ No newline at end of file diff --git a/src/main/java/com/labelsys/backend/entity/SourceResource.java b/src/main/java/com/labelsys/backend/entity/SourceResource.java index 08e82c2..3ff3b9e 100644 --- a/src/main/java/com/labelsys/backend/entity/SourceResource.java +++ b/src/main/java/com/labelsys/backend/entity/SourceResource.java @@ -26,7 +26,6 @@ public class SourceResource { private String bucketName; private String filePath; private Long fileSize; - private String sourceStatus; private String storageProvider; private Boolean hasBbox; private String remark; diff --git a/src/main/java/com/labelsys/backend/enums/SourceStatus.java b/src/main/java/com/labelsys/backend/enums/SourceStatus.java deleted file mode 100644 index 4be5ad0..0000000 --- a/src/main/java/com/labelsys/backend/enums/SourceStatus.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.labelsys.backend.enums; - -import java.util.Arrays; - -import io.swagger.v3.oas.annotations.media.Schema; - -@Schema(description = "资源状态,暂不使用,枚举值:UPLOADED:已上传、PROCESSING:处理中、READY:已完成、ARCHIVED:已归档") -public enum SourceStatus { - UPLOADED, - PROCESSING, - READY, - ARCHIVED; - - public static boolean isValid(String value) { - return Arrays.stream(values()).anyMatch(status -> status.name().equals(value)); - } -} diff --git a/src/main/java/com/labelsys/backend/service/AnnotationResultService.java b/src/main/java/com/labelsys/backend/service/AnnotationResultService.java index 59a45f4..5408086 100644 --- a/src/main/java/com/labelsys/backend/service/AnnotationResultService.java +++ b/src/main/java/com/labelsys/backend/service/AnnotationResultService.java @@ -11,6 +11,7 @@ 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.AnnotationResultDetailResponse; import com.labelsys.backend.dto.response.AnnotationResultResponse; import com.labelsys.backend.entity.AnnotationResult; import com.labelsys.backend.entity.AnnotationResultHistory; @@ -80,7 +81,7 @@ public class AnnotationResultService { } } - public AnnotationResultResponse getResult(LoginUser currentUser, Long resultId) { + public AnnotationResultDetailResponse getResult(LoginUser currentUser, Long resultId) { try { AnnotationResult result = annotationResultMapper.findActiveByIdAndCompanyId(resultId, currentUser.companyId()); @@ -91,7 +92,13 @@ public class AnnotationResultService { throw new BusinessException(ResultCode.NOT_FOUND, "结果不存在"); } //assertResultPermission(currentUser, result); - return toResponse(result); + + // 加载文件内容 + QaContent qaContent = loadQaContent(result); + DiffContent diffContent = Boolean.TRUE.equals(result.getRequiresManualReview()) ? + loadDiffSummary(result) : null; + + return toDetailResponse(result, qaContent, diffContent); } catch (BusinessException e) { throw e; } catch (Exception e) { @@ -101,6 +108,48 @@ public class AnnotationResultService { } } + private AnnotationResultDetailResponse toDetailResponse(AnnotationResult result, + QaContent qaContent, DiffContent diffContent) { + + // 转换 QA 内容(仅保留 records) + AnnotationResultDetailResponse.QaContentDto qaContentDto = new AnnotationResultDetailResponse.QaContentDto( + qaContent.records().stream() + .map(r -> new AnnotationResultDetailResponse.QaRecordDto( + r.id(), r.question(), r.answer(), r.requiresReview())) + .toList() + ); + + // 转换差异内容(仅保留 records) + AnnotationResultDetailResponse.DiffContentDto diffContentDto = null; + if (diffContent != null) { + diffContentDto = new AnnotationResultDetailResponse.DiffContentDto( + diffContent.records().stream() + .map(r -> new AnnotationResultDetailResponse.DiffRecordDto( + r.qaId(), r.question(), r.extractAnswer(), + r.verifyAnswer(), r.diffReason(), r.mergedAnswer())) + .toList() + ); + } + + return new AnnotationResultDetailResponse( + result.getId(), + result.getTaskId(), + result.getTaskName(), + result.getResourceId(), + result.getResourceName(), + deriveStatus(result), + result.getRequiresManualReview(), + result.getIsDeleted(), + result.getQaContentFilePath(), + result.getDiffSummaryFilePath(), + qaContentDto, + diffContentDto, + result.getReviewComment(), + result.getReviewedAt(), + result.getCreatedAt() + ); + } + public AnnotationResultCompareResponse compareResult(LoginUser currentUser, Long resultId) { try { AnnotationResult result = annotationResultMapper.findActiveByIdAndCompanyId(resultId, diff --git a/src/main/java/com/labelsys/backend/service/AnnotationTaskService.java b/src/main/java/com/labelsys/backend/service/AnnotationTaskService.java index 956aa12..0f4d8d0 100644 --- a/src/main/java/com/labelsys/backend/service/AnnotationTaskService.java +++ b/src/main/java/com/labelsys/backend/service/AnnotationTaskService.java @@ -14,7 +14,6 @@ import com.labelsys.backend.entity.AnnotationTask; import com.labelsys.backend.entity.AnnotationTaskResource; import com.labelsys.backend.entity.SourceResource; import com.labelsys.backend.enums.IndustryType; -import com.labelsys.backend.enums.SourceStatus; import com.labelsys.backend.enums.TaskStatus; import com.labelsys.backend.enums.TaskType; import com.labelsys.backend.mapper.AnnotationTaskMapper; @@ -219,9 +218,6 @@ public class AnnotationTaskService { 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; diff --git a/src/main/java/com/labelsys/backend/service/SourceResourceService.java b/src/main/java/com/labelsys/backend/service/SourceResourceService.java index 7d52d16..fe24e0a 100644 --- a/src/main/java/com/labelsys/backend/service/SourceResourceService.java +++ b/src/main/java/com/labelsys/backend/service/SourceResourceService.java @@ -20,7 +20,7 @@ import com.labelsys.backend.entity.ImageBboxAnnotation; 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.ImageBboxAnnotationMapper; import com.labelsys.backend.mapper.SourceResourceMapper; @@ -91,14 +91,12 @@ public class SourceResourceService { 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(); + .filePath(objectKey).fileSize(file.getSize()).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()); } catch (BusinessException e) { throw e; @@ -118,8 +116,6 @@ public class SourceResourceService { new LambdaQueryWrapper().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()); @@ -195,14 +191,6 @@ public class SourceResourceService { 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(), @@ -231,9 +219,6 @@ public class SourceResourceService { resourceId, currentUser.companyId(), currentUser.userId()); throw new BusinessException(ResultCode.NOT_FOUND, "资源不存在"); } - if (!"READY".equals(resource.getSourceStatus())) { - throw new BusinessException(ResultCode.BAD_REQUEST, "资源未就绪"); - } return objectStorageService.download(resource.getBucketName(), resource.getFilePath()); } catch (BusinessException e) { throw e; @@ -376,7 +361,7 @@ public class SourceResourceService { 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.getBucketName(), resource.getFilePath(), resource.getFileSize(), resource.getStorageProvider(), resource.getHasBbox(), resource.getRemark(), creator == null ? null : creator.getRealName(), resource.getCreatedAt(), resource.getUpdatedAt()); } diff --git a/src/main/resources/mapper/SourceResourceMapper.xml b/src/main/resources/mapper/SourceResourceMapper.xml index c496633..ceba1ac 100644 --- a/src/main/resources/mapper/SourceResourceMapper.xml +++ b/src/main/resources/mapper/SourceResourceMapper.xml @@ -11,7 +11,6 @@ - @@ -19,7 +18,7 @@ id, company_id, creator_id, creator_role, resource_name, - resource_type, bucket_name, file_path, file_size, source_status, storage_provider, remark, + resource_type, bucket_name, file_path, file_size, storage_provider, remark, created_at, updated_at