diff --git a/pom.xml b/pom.xml
index ab3f177..ad3696d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -111,8 +111,8 @@
maven-compiler-plugin
3.11.0
- ${java.version}
- ${java.version}
+ 21
+ 21
org.projectlombok
@@ -120,6 +120,7 @@
1.18.30
+ --enable-preview
diff --git a/src/main/java/com/labelsys/backend/controller/AnnotationResultArchiveController.java b/src/main/java/com/labelsys/backend/controller/AnnotationResultArchiveController.java
index 1fbcb85..232dbc8 100644
--- a/src/main/java/com/labelsys/backend/controller/AnnotationResultArchiveController.java
+++ b/src/main/java/com/labelsys/backend/controller/AnnotationResultArchiveController.java
@@ -3,8 +3,8 @@ package com.labelsys.backend.controller;
import com.labelsys.backend.context.UserContext;
import com.labelsys.backend.dto.common.PageResult;
import com.labelsys.backend.dto.request.AnnotationResultHistoryPageQuery;
+import com.labelsys.backend.dto.response.AnnotationResultHistoryDetailResponse;
import com.labelsys.backend.dto.response.AnnotationResultHistoryResponse;
-import com.labelsys.backend.dto.response.FileContentResponse;
import com.labelsys.backend.service.AnnotationResultArchiveService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -34,17 +34,18 @@ public class AnnotationResultArchiveController {
@Operation(summary = "查询归档历史详情")
@GetMapping("/{id}")
- public ResponseEntity getHistory(
+ public ResponseEntity getHistory(
@Parameter(description = "历史记录ID", example = "901")
@PathVariable Long id) {
return ResponseEntity.ok(annotationResultArchiveService.getHistory(UserContext.requireUser(), id));
}
+}
- @Operation(summary = "加载归档文件内容")
- @GetMapping("/{id}/content")
- public ResponseEntity loadFileContent(
- @Parameter(description = "历史记录ID", example = "901")
- @PathVariable Long id) {
- return ResponseEntity.ok(annotationResultArchiveService.loadFileContent(UserContext.requireUser(), id));
- }
-}
\ No newline at end of file
+// @Operation(summary = "加载归档文件内容")
+// @GetMapping("/{id}/content")
+// public ResponseEntity loadFileContent(
+// @Parameter(description = "历史记录ID", example = "901")
+// @PathVariable Long id) {
+// return ResponseEntity.ok(annotationResultArchiveService.loadFileContent(UserContext.requireUser(), id));
+// }
+//}
\ No newline at end of file
diff --git a/src/main/java/com/labelsys/backend/dto/request/MergeReviewResultRequest.java b/src/main/java/com/labelsys/backend/dto/request/MergeReviewResultRequest.java
index e7930f0..e45c90c 100644
--- a/src/main/java/com/labelsys/backend/dto/request/MergeReviewResultRequest.java
+++ b/src/main/java/com/labelsys/backend/dto/request/MergeReviewResultRequest.java
@@ -9,7 +9,7 @@ public record MergeReviewResultRequest(
@Schema(description = "合并后的答案映射,key为qa记录ID,value为合并后的答案")
Map mergedAnswers,
- @Schema(description = "审核备注")
- String reviewComment
+ @Schema(description = "每条QA记录的审核评论映射,key为qa记录ID,value为审核评论")
+ Map reviewComments
) {
}
\ No newline at end of file
diff --git a/src/main/java/com/labelsys/backend/dto/response/AnnotationResultCompareResponse.java b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultCompareResponse.java
index b389969..9e8b100 100644
--- a/src/main/java/com/labelsys/backend/dto/response/AnnotationResultCompareResponse.java
+++ b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultCompareResponse.java
@@ -18,10 +18,25 @@ public record AnnotationResultCompareResponse(
@Schema(description = "问答记录")
public record QaRecord(
@Schema(description = "记录ID", example = "qa_001") String id,
+ @Schema(description = "批次ID", example = "50") Long batchId,
@Schema(description = "问题", example = "运输时效是多久?") String question,
@Schema(description = "答案", example = "3天") String answer,
- @Schema(description = "是否需要审核", example = "true") Boolean requiresReview
- ) {}
+ @Schema(description = "是否需要审核", example = "true") Boolean requiresReview,
+ @Schema(description = "源片段信息") SourceSegments sourceSegments,
+ @Schema(description = "问题分类") String questionCategory,
+ @Schema(description = "评分") Scores scores,
+ @Schema(description = "审核评论") String reviewComment
+ ) {
+ }
+
+ @Schema(description = "源片段信息")
+ public record SourceSegments(
+ @Schema(description = "片段内容") String segment,
+ @Schema(description = "块索引", example = "0") Integer chunkIndex,
+ @Schema(description = "块标题") String chunkTitle,
+ @Schema(description = "块内容") String chunkContent
+ ) {
+ }
@Schema(description = "差异记录")
public record DiffRecord(
@@ -30,6 +45,19 @@ public record AnnotationResultCompareResponse(
@Schema(description = "提取模型答案", example = "3天") String extractAnswer,
@Schema(description = "校验模型答案", example = "72小时") String verifyAnswer,
@Schema(description = "差异原因", example = "时间单位不一致") String diffReason,
- @Schema(description = "合并后的最终答案", example = "72小时(3天)") String mergedAnswer
- ) {}
+ @Schema(description = "合并后的最终答案", example = "72小时(3天)") String mergedAnswer,
+ @Schema(description = "问题分类") String questionCategory,
+ @Schema(description = "评分") Scores scores
+ ) {
+ }
+
+ @Schema(description = "评分结构")
+ public record Scores(
+ @Schema(description = "相似度", example = "0.7") Double similarity,
+ @Schema(description = "置信度1", example = "0.9") Double confidence1,
+ @Schema(description = "置信度2", example = "0.85") Double confidence2,
+ @Schema(description = "幻觉检测", example = "0.9") Double hallucination,
+ @Schema(description = "信任度", example = "0.64") Double trust
+ ) {
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/labelsys/backend/dto/response/AnnotationResultDetailResponse.java b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultDetailResponse.java
index ff0c62a..9fa3257 100644
--- a/src/main/java/com/labelsys/backend/dto/response/AnnotationResultDetailResponse.java
+++ b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultDetailResponse.java
@@ -25,8 +25,6 @@ public record AnnotationResultDetailResponse(
@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 = "问答内容结构")
@@ -38,9 +36,23 @@ public record AnnotationResultDetailResponse(
@Schema(description = "问答记录")
public record QaRecordDto(
@Schema(description = "记录ID", example = "q1") String id,
+ @Schema(description = "批次ID", example = "50") Long batchId,
@Schema(description = "问题", example = "产品重量是多少?") String question,
@Schema(description = "答案", example = "5kg") String answer,
- @Schema(description = "是否需要审核", example = "false") Boolean requiresReview
+ @Schema(description = "是否需要审核", example = "false") Boolean requiresReview,
+ @Schema(description = "源片段信息") SourceSegmentsDto sourceSegments,
+ @Schema(description = "问题分类") String questionCategory,
+ @Schema(description = "评分") ScoresDto scores,
+ @Schema(description = "审核评论") String reviewComment
+ ) {
+ }
+
+ @Schema(description = "源片段信息")
+ public record SourceSegmentsDto(
+ @Schema(description = "片段内容") String segment,
+ @Schema(description = "块索引", example = "0") Integer chunkIndex,
+ @Schema(description = "块标题") String chunkTitle,
+ @Schema(description = "块内容") String chunkContent
) {
}
@@ -57,7 +69,19 @@ public record AnnotationResultDetailResponse(
@Schema(description = "抽取答案", example = "2年") String extractAnswer,
@Schema(description = "验证答案", example = "3年") String verifyAnswer,
@Schema(description = "差异原因", example = "抽取与验证结果不一致") String diffReason,
- @Schema(description = "合并后答案") String mergedAnswer
+ @Schema(description = "合并后答案") String mergedAnswer,
+ @Schema(description = "问题分类") String questionCategory,
+ @Schema(description = "评分") ScoresDto scores
+ ) {
+ }
+
+ @Schema(description = "评分结构")
+ public record ScoresDto(
+ @Schema(description = "相似度", example = "0.7") Double similarity,
+ @Schema(description = "置信度1", example = "0.9") Double confidence1,
+ @Schema(description = "置信度2", example = "0.85") Double confidence2,
+ @Schema(description = "幻觉检测", example = "0.9") Double hallucination,
+ @Schema(description = "信任度", example = "0.64") Double trust
) {
}
}
\ No newline at end of file
diff --git a/src/main/java/com/labelsys/backend/dto/response/AnnotationResultHistoryDetailResponse.java b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultHistoryDetailResponse.java
new file mode 100644
index 0000000..2b2571f
--- /dev/null
+++ b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultHistoryDetailResponse.java
@@ -0,0 +1,63 @@
+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 AnnotationResultHistoryDetailResponse(
+ @Schema(description = "历史记录ID", example = "901") 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 = "annotation-results/2/qa/802.json") String qaContentFilePath,
+ @Schema(description = "问答内容") QaContentDto qaContent,
+ @Schema(description = "归档原因", example = "审核通过后归档") String archiveReason,
+ @Schema(description = "归档操作人ID", example = "5") Long archivedBy,
+ @Schema(description = "归档时间", example = "2026-05-06T10:30:00") LocalDateTime archivedAt,
+ @Schema(description = "创建时间", example = "2026-05-06T10:30:00") LocalDateTime createdAt,
+ @Schema(description = "审核人ID,自动归档时为null", example = "5") Long reviewerId,
+ @Schema(description = "审核人姓名,自动归档时为auto", example = "张三") String reviewerName
+) {
+ @Schema(description = "问答内容结构")
+ public record QaContentDto(
+ @Schema(description = "问答记录列表") List records
+ ) {
+ }
+
+ @Schema(description = "问答记录")
+ public record QaRecordDto(
+ @Schema(description = "记录ID", example = "q1") String id,
+ @Schema(description = "批次ID", example = "50") Long batchId,
+ @Schema(description = "问题", example = "产品重量是多少?") String question,
+ @Schema(description = "答案", example = "5kg") String answer,
+ @Schema(description = "是否需要审核", example = "false") Boolean requiresReview,
+ @Schema(description = "源片段信息") SourceSegmentsDto sourceSegments,
+ @Schema(description = "问题分类") String questionCategory,
+ @Schema(description = "评分") ScoresDto scores,
+ @Schema(description = "审核评论") String reviewComment
+ ) {
+ }
+
+ @Schema(description = "源片段信息")
+ public record SourceSegmentsDto(
+ @Schema(description = "片段内容") String segment,
+ @Schema(description = "块索引", example = "0") Integer chunkIndex,
+ @Schema(description = "块标题") String chunkTitle,
+ @Schema(description = "块内容") String chunkContent
+ ) {
+ }
+
+ @Schema(description = "评分结构")
+ public record ScoresDto(
+ @Schema(description = "相似度", example = "0.7") Double similarity,
+ @Schema(description = "置信度1", example = "0.9") Double confidence1,
+ @Schema(description = "置信度2", example = "0.85") Double confidence2,
+ @Schema(description = "幻觉检测", example = "0.9") Double hallucination,
+ @Schema(description = "信任度", example = "0.64") Double trust
+ ) {
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/labelsys/backend/dto/response/AnnotationResultHistoryResponse.java b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultHistoryResponse.java
index 025ec2d..38e90b7 100644
--- a/src/main/java/com/labelsys/backend/dto/response/AnnotationResultHistoryResponse.java
+++ b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultHistoryResponse.java
@@ -20,7 +20,6 @@ public record AnnotationResultHistoryResponse(
@Schema(description = "归档时间", example = "2026-05-06T10:30:00") LocalDateTime archivedAt,
@Schema(description = "创建时间", example = "2026-05-06T10:30:00") LocalDateTime createdAt,
@Schema(description = "审核人ID,自动归档时为null", example = "5") Long reviewerId,
- @Schema(description = "审核人姓名,自动归档时为auto", example = "张三") String reviewerName,
- @Schema(description = "审核意见,自动归档时为auto", example = "内容符合要求") String reviewerComment
+ @Schema(description = "审核人姓名,自动归档时为auto", example = "张三") String reviewerName
) {
}
\ No newline at end of file
diff --git a/src/main/java/com/labelsys/backend/dto/response/AnnotationResultResponse.java b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultResponse.java
index 171b692..7f045f7 100644
--- a/src/main/java/com/labelsys/backend/dto/response/AnnotationResultResponse.java
+++ b/src/main/java/com/labelsys/backend/dto/response/AnnotationResultResponse.java
@@ -17,8 +17,6 @@ public record AnnotationResultResponse(
@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 = "审核备注", example = "需统一时间字段口径。") String reviewComment,
- @Schema(description = "审核时间", example = "2026-04-27T11:00:00") LocalDateTime reviewedAt,
@Schema(description = "创建时间", example = "2026-04-27T10:40:00") LocalDateTime createdAt
) {
}
\ No newline at end of file
diff --git a/src/main/java/com/labelsys/backend/entity/AnnotationResult.java b/src/main/java/com/labelsys/backend/entity/AnnotationResult.java
index c25c8a4..884f750 100644
--- a/src/main/java/com/labelsys/backend/entity/AnnotationResult.java
+++ b/src/main/java/com/labelsys/backend/entity/AnnotationResult.java
@@ -56,12 +56,6 @@ public class AnnotationResult {
@TableField("reviewer_id")
private Long reviewerId;
- @TableField("review_comment")
- private String reviewComment;
-
- @TableField("reviewed_at")
- private LocalDateTime reviewedAt;
-
@TableField("created_at")
private LocalDateTime createdAt;
diff --git a/src/main/java/com/labelsys/backend/entity/AnnotationResultHistory.java b/src/main/java/com/labelsys/backend/entity/AnnotationResultHistory.java
index 06b3f2f..b196baf 100644
--- a/src/main/java/com/labelsys/backend/entity/AnnotationResultHistory.java
+++ b/src/main/java/com/labelsys/backend/entity/AnnotationResultHistory.java
@@ -37,5 +37,4 @@ public class AnnotationResultHistory {
// 新增审核人相关字段
private Long reviewerId;
private String reviewerName;
- private String reviewerComment;
}
\ No newline at end of file
diff --git a/src/main/java/com/labelsys/backend/mapper/AnnotationResultMapper.java b/src/main/java/com/labelsys/backend/mapper/AnnotationResultMapper.java
index bdda150..448ddd4 100644
--- a/src/main/java/com/labelsys/backend/mapper/AnnotationResultMapper.java
+++ b/src/main/java/com/labelsys/backend/mapper/AnnotationResultMapper.java
@@ -11,7 +11,5 @@ public interface AnnotationResultMapper extends BaseMapper {
int markArchived(@Param("id") Long id,
@Param("companyId") Long companyId,
- @Param("reviewerId") Long reviewerId,
- @Param("reviewComment") String reviewComment,
- @Param("reviewedAt") LocalDateTime reviewedAt);
+ @Param("reviewerId") Long reviewerId);
}
\ No newline at end of file
diff --git a/src/main/java/com/labelsys/backend/service/AnnotationResultArchiveService.java b/src/main/java/com/labelsys/backend/service/AnnotationResultArchiveService.java
index 4570421..f3ea0bd 100644
--- a/src/main/java/com/labelsys/backend/service/AnnotationResultArchiveService.java
+++ b/src/main/java/com/labelsys/backend/service/AnnotationResultArchiveService.java
@@ -8,6 +8,7 @@ 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.AnnotationResultHistoryPageQuery;
+import com.labelsys.backend.dto.response.AnnotationResultHistoryDetailResponse;
import com.labelsys.backend.dto.response.AnnotationResultHistoryResponse;
import com.labelsys.backend.dto.response.FileContentResponse;
import com.labelsys.backend.dto.response.MergeReviewResultResponse;
@@ -29,6 +30,8 @@ import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
+import static org.springframework.util.StringUtils.hasText;
+
@Slf4j
@Service
@RequiredArgsConstructor
@@ -66,6 +69,7 @@ public class AnnotationResultArchiveService {
var page = new Page(query.pageNo(), query.pageSize());
var resultPage = annotationResultHistoryMapper.selectPage(page, wrapper);
+ // 分页查询不加载 qa 内容
var records = resultPage.getRecords().stream()
.map(this::toResponse)
.toList();
@@ -79,14 +83,17 @@ public class AnnotationResultArchiveService {
}
}
- public AnnotationResultHistoryResponse getHistory(LoginUser currentUser, Long historyId) {
+ public AnnotationResultHistoryDetailResponse getHistory(LoginUser currentUser, Long historyId) {
try {
AnnotationResultHistory history = annotationResultHistoryMapper.selectById(historyId);
if (history == null || !history.getCompanyId().equals(currentUser.companyId())) {
throw new BusinessException(ResultCode.NOT_FOUND, "历史记录不存在");
}
assertHistoryPermission(currentUser, history);
- return toResponse(history);
+
+ // 详情查询加载 QA 内容
+ QaContent qaContent = loadQaContent(history.getQaContentFilePath());
+ return toDetailResponse(history, qaContent);
} catch (Exception e) {
log.error("getHistory failed, companyId={}, userId={}, historyId={}, error={}",
currentUser.companyId(), currentUser.userId(), historyId, e.getMessage(), e);
@@ -104,19 +111,66 @@ public class AnnotationResultArchiveService {
private AnnotationResultHistoryResponse toResponse(AnnotationResultHistory history) {
return new AnnotationResultHistoryResponse(
history.getId(),
- // history.getSourceResultId(),
history.getTaskId(),
- history.getTaskName(), // 新增
+ history.getTaskName(),
history.getResourceId(),
- history.getResourceName(), // 新增
+ history.getResourceName(),
history.getQaContentFilePath(),
history.getArchiveReason(),
history.getArchivedBy(),
history.getArchivedAt(),
history.getCreatedAt(),
history.getReviewerId(),
- history.getReviewerName(),
- history.getReviewerComment()
+ history.getReviewerName()
+ );
+ }
+
+ private AnnotationResultHistoryDetailResponse toDetailResponse(AnnotationResultHistory history,
+ QaContent qaContent) {
+ // 转换 QA 内容
+ AnnotationResultHistoryDetailResponse.QaContentDto qaContentDto = null;
+ if (qaContent != null && qaContent.records() != null) {
+ qaContentDto = new AnnotationResultHistoryDetailResponse.QaContentDto(
+ qaContent.records().stream()
+ .map(r -> new AnnotationResultHistoryDetailResponse.QaRecordDto(
+ r.id(),
+ r.batchId(),
+ r.question(),
+ r.answer(),
+ r.requiresReview(),
+ r.sourceSegments() != null ?
+ new AnnotationResultHistoryDetailResponse.SourceSegmentsDto(
+ r.sourceSegments().segment(),
+ r.sourceSegments().chunkIndex(),
+ r.sourceSegments().chunkTitle(),
+ r.sourceSegments().chunkContent()) :
+ null,
+ r.questionCategory(),
+ r.scores() != null ? new AnnotationResultHistoryDetailResponse.ScoresDto(
+ r.scores().similarity(),
+ r.scores().confidence1(),
+ r.scores().confidence2(),
+ r.scores().hallucination(),
+ r.scores().trust()) : null,
+ r.reviewComment()))
+ .toList()
+ );
+ }
+
+ return new AnnotationResultHistoryDetailResponse(
+ history.getId(),
+ history.getTaskId(),
+ history.getTaskName(),
+ history.getResourceId(),
+ history.getResourceName(),
+ history.getQaContentFilePath(),
+ qaContentDto,
+ history.getArchiveReason(),
+ history.getArchivedBy(),
+ history.getArchivedAt(),
+ history.getCreatedAt(),
+ history.getReviewerId(),
+ history.getReviewerName()
);
}
@@ -124,13 +178,14 @@ public class AnnotationResultArchiveService {
public int autoArchiveEligibleResults() {
try {
LocalDateTime cutoff = LocalDateTime.now().minus(autoArchiveTimeout);
- List results = annotationResultMapper.selectList(new LambdaQueryWrapper()
- .eq(AnnotationResult::getIsDeleted, false)
- .eq(AnnotationResult::getRequiresManualReview, false)
- .lt(AnnotationResult::getCreatedAt, cutoff));
+ List results = annotationResultMapper.selectList(
+ new LambdaQueryWrapper()
+ .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) {
+ if (archiveRuntimeResult(result, null, "AUTO_ARCHIVE") != null) {
archivedCount++;
}
}
@@ -153,8 +208,7 @@ public class AnnotationResultArchiveService {
*/
private MergeReviewResultResponse archiveRuntimeResult(AnnotationResult result,
Long reviewerId,
- String archiveReason,
- String reviewComment) {
+ String archiveReason) {
LocalDateTime archivedAt = LocalDateTime.now();
// 从对象存储读取 qa.json 内容
@@ -177,22 +231,64 @@ public class AnnotationResultArchiveService {
.archivedAt(archivedAt)
.reviewerId(null)
.reviewerName("auto")
- .reviewerComment("auto")
.build();
annotationResultHistoryMapper.insert(history);
int updated = annotationResultMapper.markArchived(
result.getId(),
result.getCompanyId(),
- reviewerId,
- reviewComment,
- archivedAt);
+ reviewerId);
if (updated == 0) {
return null;
}
return new MergeReviewResultResponse(result.getId(), history.getId(), archiveReason, archivedAt);
}
+ /**
+ * 加载 QA 内容
+ */
+ private QaContent loadQaContent(String filePath) {
+ try {
+ if (!hasText(filePath)) {
+ log.warn("QA content file path is empty");
+ return new QaContent(null, null, List.of(), null);
+ }
+ String bucketName = extractBucketName(filePath);
+ String objectKey = extractObjectKey(filePath);
+ byte[] content = objectStorageService.download(bucketName, objectKey);
+ String jsonContent = new String(content, StandardCharsets.UTF_8);
+ return objectMapper.readValue(jsonContent, new com.fasterxml.jackson.core.type.TypeReference() {
+ });
+ } catch (Exception e) {
+ log.warn("Failed to load QA content, returning empty content. filePath={}, error={}", filePath,
+ e.getMessage());
+ return new QaContent(null, null, List.of(), null);
+ }
+ }
+
+ // 内部类:qa.json 结构
+ private record QaContent(
+ Long taskId,
+ Long resourceId,
+ List records,
+ Metadata metadata
+ ) {
+ private record QaRecord(String id, Long batchId, String question, String answer,
+ Boolean requiresReview, SourceSegments sourceSegments,
+ String questionCategory, Scores scores, String reviewComment) {
+ }
+
+ private record SourceSegments(String segment, Integer chunkIndex, String chunkTitle, String chunkContent) {
+ }
+
+ private record Scores(Double similarity, Double confidence1, Double confidence2,
+ Double hallucination, Double trust) {
+ }
+
+ private record Metadata(String createdAt, String updatedAt) {
+ }
+ }
+
/**
* 从对象存储读取 qa.json 内容
*/
@@ -239,8 +335,9 @@ public class AnnotationResultArchiveService {
/**
* 加载归档记录的文件内容
+ *
* @param currentUser 当前用户
- * @param historyId 历史记录ID
+ * @param historyId 历史记录ID
* @return 文件内容响应
*/
public FileContentResponse loadFileContent(LoginUser currentUser, Long historyId) {
@@ -250,17 +347,17 @@ public class AnnotationResultArchiveService {
throw new BusinessException(ResultCode.NOT_FOUND, "历史记录不存在");
}
//assertHistoryPermission(currentUser, history);
-
+
String filePath = history.getQaContentFilePath();
if (filePath == null || filePath.isEmpty()) {
throw new BusinessException(ResultCode.ERROR, "文件路径为空");
}
-
+
String bucketName = extractBucketName(filePath);
String objectKey = extractObjectKey(filePath);
byte[] content = objectStorageService.download(bucketName, objectKey);
String contentStr = new String(content, StandardCharsets.UTF_8);
-
+
return new FileContentResponse(filePath, contentStr, content.length);
} catch (BusinessException e) {
throw e;
@@ -270,4 +367,5 @@ public class AnnotationResultArchiveService {
throw new BusinessException(ResultCode.ERROR, "加载文件内容失败");
}
}
+
}
\ No newline at end of file
diff --git a/src/main/java/com/labelsys/backend/service/AnnotationResultService.java b/src/main/java/com/labelsys/backend/service/AnnotationResultService.java
index e8a6eb5..c125a6d 100644
--- a/src/main/java/com/labelsys/backend/service/AnnotationResultService.java
+++ b/src/main/java/com/labelsys/backend/service/AnnotationResultService.java
@@ -116,7 +116,24 @@ public class AnnotationResultService {
AnnotationResultDetailResponse.QaContentDto qaContentDto = new AnnotationResultDetailResponse.QaContentDto(
qaContent.records().stream()
.map(r -> new AnnotationResultDetailResponse.QaRecordDto(
- r.id(), r.question(), r.answer(), r.requiresReview()))
+ r.id(),
+ r.batchId(),
+ r.question(),
+ r.answer(),
+ r.requiresReview(),
+ r.sourceSegments() != null ? new AnnotationResultDetailResponse.SourceSegmentsDto(
+ r.sourceSegments().segment(),
+ r.sourceSegments().chunkIndex(),
+ r.sourceSegments().chunkTitle(),
+ r.sourceSegments().chunkContent()) : null,
+ r.questionCategory(),
+ r.scores() != null ? new AnnotationResultDetailResponse.ScoresDto(
+ r.scores().similarity(),
+ r.scores().confidence1(),
+ r.scores().confidence2(),
+ r.scores().hallucination(),
+ r.scores().trust()) : null,
+ r.reviewComment()))
.toList()
);
@@ -127,7 +144,14 @@ public class AnnotationResultService {
diffContent.records().stream()
.map(r -> new AnnotationResultDetailResponse.DiffRecordDto(
r.qaId(), r.question(), r.extractAnswer(),
- r.verifyAnswer(), r.diffReason(), r.mergedAnswer()))
+ r.verifyAnswer(), r.diffReason(), r.mergedAnswer(),
+ r.questionCategory(),
+ r.scores() != null ? new AnnotationResultDetailResponse.ScoresDto(
+ r.scores().similarity(),
+ r.scores().confidence1(),
+ r.scores().confidence2(),
+ r.scores().hallucination(),
+ r.scores().trust()) : null))
.toList()
);
}
@@ -145,8 +169,6 @@ public class AnnotationResultService {
result.getDiffSummaryFilePath(),
qaContentDto,
diffContentDto,
- result.getReviewComment(),
- result.getReviewedAt(),
result.getCreatedAt()
);
}
@@ -173,9 +195,23 @@ public class AnnotationResultService {
List qaRecords = qaContent.records().stream()
.map(qa -> new AnnotationResultCompareResponse.QaRecord(
qa.id(),
+ qa.batchId(),
qa.question(),
qa.answer(),
- qa.requiresReview()
+ qa.requiresReview(),
+ qa.sourceSegments() != null ? new AnnotationResultCompareResponse.SourceSegments(
+ qa.sourceSegments().segment(),
+ qa.sourceSegments().chunkIndex(),
+ qa.sourceSegments().chunkTitle(),
+ qa.sourceSegments().chunkContent()) : null,
+ qa.questionCategory(),
+ qa.scores() != null ? new AnnotationResultCompareResponse.Scores(
+ qa.scores().similarity(),
+ qa.scores().confidence1(),
+ qa.scores().confidence2(),
+ qa.scores().hallucination(),
+ qa.scores().trust()) : null,
+ qa.reviewComment()
)).toList();
// 转换差异记录
@@ -187,7 +223,14 @@ public class AnnotationResultService {
diff.extractAnswer(),
diff.verifyAnswer(),
diff.diffReason(),
- diff.mergedAnswer()
+ diff.mergedAnswer(),
+ diff.questionCategory(),
+ diff.scores() != null ? new AnnotationResultCompareResponse.Scores(
+ diff.scores().similarity(),
+ diff.scores().confidence1(),
+ diff.scores().confidence2(),
+ diff.scores().hallucination(),
+ diff.scores().trust()) : null
)).toList() : List.of();
return new AnnotationResultCompareResponse(
@@ -220,16 +263,22 @@ public class AnnotationResultService {
// 读取当前 qa.json
QaContent qaContent = loadQaContent(result);
- // 更新 qa.json 的 answer 字段
+ // 更新 qa.json 的 answer 字段和 reviewComment
List updatedQaRecords = qaContent.records().stream()
.map(record -> {
String mergedAnswer = request.mergedAnswers().get(record.id());
- if (mergedAnswer != null) {
+ String reviewComment = request.reviewComments() != null ? request.reviewComments().get(record.id()) : null;
+ if (mergedAnswer != null || reviewComment != null) {
return new QaContent.QaRecord(
record.id(),
+ record.batchId(),
record.question(),
- mergedAnswer,
- false
+ mergedAnswer != null ? mergedAnswer : record.answer(),
+ false,
+ record.sourceSegments(),
+ record.questionCategory(),
+ record.scores(),
+ reviewComment != null ? reviewComment : record.reviewComment()
);
}
return record;
@@ -247,11 +296,18 @@ public class AnnotationResultService {
);
saveQaContent(result, updatedQaContent);
- // 更新数据库记录
- result.setIsDeleted(Boolean.TRUE);
- result.setReviewerId(currentUser.userId());
- result.setReviewComment(request.reviewComment());
- result.setReviewedAt(LocalDateTime.now());
+ // 更新数据库记录(使用 markArchived 保证幂等性,防止并发重复归档)
+ int updated = annotationResultMapper.markArchived(
+ result.getId(),
+ currentUser.companyId(),
+ currentUser.userId());
+
+ if (updated == 0) {
+ // 记录已被其他进程归档
+ throw new BusinessException(ResultCode.CONFLICT, "记录已被归档");
+ }
+
+ // 更新 requires_manual_review 字段
result.setRequiresManualReview(false);
annotationResultMapper.updateById(result);
@@ -281,8 +337,6 @@ public class AnnotationResultService {
result.getIsDeleted(),
result.getQaContentFilePath(),
result.getDiffSummaryFilePath(),
- result.getReviewComment(),
- result.getReviewedAt(),
result.getCreatedAt()
);
}
@@ -392,17 +446,15 @@ public class AnnotationResultService {
// 根据归档类型设置审核人信息
if (isAutoArchive) {
- // 自动归档:reviewer_id为NULL,name和comment为"auto"
+ // 自动归档:reviewer_id为NULL,name为"auto"
historyBuilder
.reviewerId(null)
- .reviewerName("auto")
- .reviewerComment("auto");
+ .reviewerName("auto");
} else {
// 人工审核后归档:使用审核人信息
historyBuilder
.reviewerId(result.getReviewerId())
- .reviewerName(currentUser.realName())
- .reviewerComment(result.getReviewComment());
+ .reviewerName(currentUser.realName());
}
annotationResultHistoryMapper.insert(historyBuilder.build());
@@ -436,7 +488,16 @@ public class AnnotationResultService {
List records,
Metadata metadata
) {
- private record QaRecord(String id, String question, String answer, Boolean requiresReview) {
+ private record QaRecord(String id, Long batchId, String question, String answer,
+ Boolean requiresReview, SourceSegments sourceSegments,
+ String questionCategory, Scores scores, String reviewComment) {
+ }
+
+ private record SourceSegments(String segment, Integer chunkIndex, String chunkTitle, String chunkContent) {
+ }
+
+ private record Scores(Double similarity, Double confidence1, Double confidence2,
+ Double hallucination, Double trust) {
}
private record Metadata(String createdAt, String updatedAt) {
@@ -451,7 +512,12 @@ public class AnnotationResultService {
Metadata metadata
) {
private record DiffRecord(String qaId, String question, String extractAnswer,
- String verifyAnswer, String diffReason, String mergedAnswer) {
+ String verifyAnswer, String diffReason, String mergedAnswer,
+ String questionCategory, Scores scores) {
+ }
+
+ private record Scores(Double similarity, Double confidence1, Double confidence2,
+ Double hallucination, Double trust) {
}
private record Metadata(String createdAt) {
diff --git a/src/main/resources/mapper/AnnotationResultMapper.xml b/src/main/resources/mapper/AnnotationResultMapper.xml
index 76dd7b9..ec18f1a 100644
--- a/src/main/resources/mapper/AnnotationResultMapper.xml
+++ b/src/main/resources/mapper/AnnotationResultMapper.xml
@@ -13,16 +13,14 @@
-
-
id, company_id, creator_id, creator_role, task_id, resource_id, qa_content_file_path,
- diff_summary_file_path, requires_manual_review, is_deleted, reviewer_id, review_comment,
- reviewed_at, created_at, updated_at
+ diff_summary_file_path, requires_manual_review, is_deleted, reviewer_id,
+ created_at, updated_at