fix(annotation): fix use_new_version to support files without annotation

Problem:
use_new_version returned 404 annotation not found for files without
annotation, preventing users from switching to new versions.

Solution:
1. Query latest file by logical_path
2. Update LabelingProjectFile to point to latest version
3. If annotation exists: clear it and update file_id
4. If no annotation: just update project file snapshot
5. Return new file_id in response
This commit is contained in:
2026-02-06 15:22:57 +08:00
parent 1f6c821cbc
commit cd5f5ef6da

View File

@@ -1723,16 +1723,19 @@ class AnnotationEditorService:
""" """
使用文件新版本并清空标注 使用文件新版本并清空标注
如果文件有多个版本(通过 logical_path 关联),将标注切换到最新版本。
如果存在标注记录,会清空标注内容。
Args: Args:
project_id: 标注项目ID project_id: 标注项目ID
file_id: 文件ID file_id: 文件ID(当前关联的文件ID)
Returns: Returns:
操作结果 操作结果
""" """
project = await self._get_project_or_404(project_id) project = await self._get_project_or_404(project_id)
# 获取文件信息 # 获取当前文件信息
file_result = await self.db.execute( file_result = await self.db.execute(
select(DatasetFiles) select(DatasetFiles)
.join(LabelingProjectFile, LabelingProjectFile.file_id == DatasetFiles.id) .join(LabelingProjectFile, LabelingProjectFile.file_id == DatasetFiles.id)
@@ -1748,7 +1751,46 @@ class AnnotationEditorService:
status_code=404, detail=f"文件不存在或不属于该项目: {file_id}" status_code=404, detail=f"文件不存在或不属于该项目: {file_id}"
) )
# 获取标注信息 # 获取同一逻辑路径下的最新版本文件
logical_path = file_record.logical_path
latest_file_result = await self.db.execute(
select(DatasetFiles)
.where(
DatasetFiles.dataset_id == project.dataset_id,
DatasetFiles.logical_path == logical_path,
DatasetFiles.status == "ACTIVE",
)
.order_by(DatasetFiles.version.desc())
.limit(1)
)
latest_file = latest_file_result.scalar_one_or_none()
if not latest_file:
raise HTTPException(
status_code=404, detail=f"找不到该文件的最新版本: {logical_path}"
)
# 如果当前文件已经是最新版本,无需更新
if latest_file.id == file_id:
return {
"fileId": file_id,
"previousFileVersion": file_record.version,
"currentFileVersion": latest_file.version,
"message": "当前文件已是最新版本,无需切换",
}
# 更新 LabelingProjectFile 指向最新版本
project_file_result = await self.db.execute(
select(LabelingProjectFile).where(
LabelingProjectFile.project_id == project.id,
LabelingProjectFile.file_id == file_id,
)
)
project_file = project_file_result.scalar_one_or_none()
if project_file:
project_file.file_id = str(latest_file.id)
# 获取标注信息(基于原 file_id)
annotation_result = await self.db.execute( annotation_result = await self.db.execute(
select(AnnotationResult) select(AnnotationResult)
.where( .where(
@@ -1759,64 +1801,66 @@ class AnnotationEditorService:
) )
annotation = annotation_result.scalar_one_or_none() annotation = annotation_result.scalar_one_or_none()
current_file_version = file_record.version
if not annotation:
raise HTTPException(status_code=404, detail=f"标注不存在: {file_id}")
previous_file_version = annotation.file_version
if annotation.file_version is not None:
if current_file_version <= annotation.file_version:
raise HTTPException(
status_code=400,
detail=f"文件版本({current_file_version})未更新或低于标注版本({annotation.file_version}",
)
# 清空标注并更新版本号
now = datetime.utcnow() now = datetime.utcnow()
cleared_payload: Dict[str, Any] = {}
if isinstance(annotation.annotation, dict) and self._is_segmented_annotation(
annotation.annotation
):
segments = self._extract_segment_annotations(annotation.annotation)
cleared_segments: Dict[str, Dict[str, Any]] = {}
for segment_id, segment_data in segments.items():
if not isinstance(segment_data, dict):
continue
normalized = dict(segment_data)
normalized[SEGMENT_RESULT_KEY] = []
cleared_segments[str(segment_id)] = normalized
total_segments = self._resolve_segment_total(annotation.annotation) if annotation:
if total_segments is None: # 存在标注记录:清空标注并更新文件版本
total_segments = len(cleared_segments) previous_file_version = annotation.file_version
cleared_payload = { cleared_payload: Dict[str, Any] = {}
SEGMENTED_KEY: True, if isinstance(annotation.annotation, dict) and self._is_segmented_annotation(
"version": annotation.annotation.get("version", 1), annotation.annotation
SEGMENTS_KEY: cleared_segments, ):
SEGMENT_TOTAL_KEY: total_segments, segments = self._extract_segment_annotations(annotation.annotation)
cleared_segments: Dict[str, Dict[str, Any]] = {}
for segment_id, segment_data in segments.items():
if not isinstance(segment_data, dict):
continue
normalized = dict(segment_data)
normalized[SEGMENT_RESULT_KEY] = []
cleared_segments[str(segment_id)] = normalized
total_segments = self._resolve_segment_total(annotation.annotation)
if total_segments is None:
total_segments = len(cleared_segments)
cleared_payload = {
SEGMENTED_KEY: True,
"version": annotation.annotation.get("version", 1),
SEGMENTS_KEY: cleared_segments,
SEGMENT_TOTAL_KEY: total_segments,
}
# 更新标注记录指向新文件
annotation.file_id = str(latest_file.id)
annotation.annotation = cleared_payload
annotation.annotation_status = ANNOTATION_STATUS_NO_ANNOTATION
annotation.file_version = latest_file.version
annotation.updated_at = now
await self.db.commit()
await self.db.refresh(annotation)
await self._sync_annotation_to_knowledge(
project,
latest_file,
cleared_payload,
annotation.updated_at or now,
)
return {
"fileId": str(latest_file.id),
"previousFileVersion": previous_file_version,
"currentFileVersion": latest_file.version,
"message": "已切换到新版本并清空标注",
} }
else:
# 不存在标注记录:只需更新项目文件快照
await self.db.commit()
annotation.annotation = cleared_payload return {
annotation.annotation_status = ANNOTATION_STATUS_NO_ANNOTATION "fileId": str(latest_file.id),
annotation.file_version = current_file_version "previousFileVersion": file_record.version,
annotation.updated_at = now "currentFileVersion": latest_file.version,
"message": "已切换到新版本",
await self.db.commit() }
await self.db.refresh(annotation)
await self._sync_annotation_to_knowledge(
project,
file_record,
cleared_payload,
annotation.updated_at or now,
)
return {
"fileId": file_id,
"previousFileVersion": previous_file_version,
"currentFileVersion": current_file_version,
"message": "已使用新版本并清空标注",
}