根据界面需求优化
This commit is contained in:
@@ -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<AnnotationResultResponse> getResult(
|
||||
public ResponseEntity<AnnotationResultDetailResponse> getResult(
|
||||
@Parameter(description = "结果ID", example = "191000000000000401")
|
||||
@PathVariable Long id) {
|
||||
return ResponseEntity.ok(annotationResultService.getResult(UserContext.requireUser(), id));
|
||||
|
||||
@@ -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
|
||||
) {
|
||||
|
||||
@@ -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<QaRecordDto> 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<DiffRecordDto> 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
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<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());
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<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" />
|
||||
@@ -19,7 +18,7 @@
|
||||
</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,
|
||||
resource_type, bucket_name, file_path, file_size, storage_provider, remark,
|
||||
created_at, updated_at </sql>
|
||||
|
||||
<select id="selectByCompanyIdAndIds" resultMap="SourceResourceResultMap"> select <include
|
||||
|
||||
@@ -57,12 +57,12 @@ ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO source_resource (
|
||||
id, company_id, creator_id, creator_role, resource_name, resource_type,
|
||||
bucket_name, file_path, file_size, source_status, storage_provider, has_bbox, remark
|
||||
bucket_name, file_path, file_size, storage_provider, has_bbox, remark
|
||||
) VALUES
|
||||
(601, 2, 3, 'EMPLOYEE', '设备巡检规范.txt', 'TEXT',
|
||||
'source-data', 'text/202604/601.txt', 20480, 'READY', 'rustfs', NULL, '文本资源示例'),
|
||||
'source-data', 'text/202604/601.txt', 20480, 'rustfs', NULL, '文本资源示例'),
|
||||
(602, 2, 3, 'EMPLOYEE', '控制柜照片.jpg', 'IMAGE',
|
||||
'source-data', 'image/202604/602.jpg', 532480, 'READY', 'rustfs', TRUE, '图片资源示例')
|
||||
'source-data', 'image/202604/602.jpg', 532480, 'rustfs', TRUE, '图片资源示例')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO annotation_task (
|
||||
@@ -89,12 +89,12 @@ INSERT INTO annotation_result (
|
||||
requires_manual_review, is_deleted, reviewer_id, review_comment, reviewed_at
|
||||
) VALUES
|
||||
(801, 2, 3, 'EMPLOYEE', 701, '多资源问答抽取任务', 601, '设备巡检规范.txt',
|
||||
'annotation-results/qa/801.json',
|
||||
'annotation-results/diff/801.json',
|
||||
'annotation-artifacts/qa/qa1.json',
|
||||
'annotation-artifacts/diff/diff1.json',
|
||||
TRUE, FALSE, NULL, NULL, NULL),
|
||||
(802, 2, 3, 'EMPLOYEE', 702, '图片问答抽取任务', 602, '控制柜照片.jpg',
|
||||
'annotation-results/qa/802.json',
|
||||
NULL,
|
||||
'annotation-artifacts/qa/qa2.json',
|
||||
'annotation-artifacts/diff/diff2.json',
|
||||
FALSE, FALSE, 5, '结果可通过。', CURRENT_TIMESTAMP)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
|
||||
@@ -133,7 +133,6 @@ CREATE TABLE IF NOT EXISTS source_resource
|
||||
bucket_name VARCHAR(128) NOT NULL,
|
||||
file_path VARCHAR(512) NOT NULL,
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
source_status VARCHAR(32) NOT NULL DEFAULT 'UPLOADED',
|
||||
storage_provider VARCHAR(64) NOT NULL DEFAULT 'rustfs',
|
||||
has_bbox BOOLEAN,
|
||||
remark VARCHAR(255),
|
||||
@@ -153,7 +152,6 @@ COMMENT ON COLUMN source_resource.resource_type IS '资源类型,默认 TEXT
|
||||
COMMENT ON COLUMN source_resource.bucket_name IS '对象存储桶名称。';
|
||||
COMMENT ON COLUMN source_resource.file_path IS '文件存储路径,表示对象在存储系统中的实际路径。';
|
||||
COMMENT ON COLUMN source_resource.file_size IS '文件大小,单位字节,默认 0。';
|
||||
COMMENT ON COLUMN source_resource.source_status IS '资源状态,默认 UPLOADED,可选 PROCESSING、READY、ARCHIVED。';
|
||||
COMMENT ON COLUMN source_resource.storage_provider IS '存储提供方,默认 rustfs。';
|
||||
COMMENT ON COLUMN source_resource.has_bbox IS '是否有BBOX标注。NULL表示非图片资源或未标注;TRUE表示已标注BBOX;FALSE表示已删除BBOX标注。';
|
||||
COMMENT ON COLUMN source_resource.remark IS '备注说明。';
|
||||
@@ -418,7 +416,6 @@ CREATE INDEX IF NOT EXISTS idx_sys_user_position ON sys_user (company_id, positi
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_menu_company_sort ON sys_menu (company_id, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_sys_config_company_type ON sys_config (company_id, config_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_resource_company_type ON source_resource (company_id, resource_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_resource_company_status ON source_resource (company_id, source_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_resource_creator ON source_resource (company_id, creator_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_resource_has_bbox ON source_resource (company_id, has_bbox);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_resource_has_bbox ON source_resource (company_id, has_bbox);
|
||||
|
||||
Reference in New Issue
Block a user