diff --git a/api/app/services/elevation_file_record_service.py b/api/app/services/elevation_file_record_service.py index 7c79aba..9924412 100644 --- a/api/app/services/elevation_file_record_service.py +++ b/api/app/services/elevation_file_record_service.py @@ -32,7 +32,6 @@ from .elevation_service import ( RASTER_FILE_FORMATS, TERRAIN_SUPPORTED_DATASET_FORMATS, _analyze_dataset_content, - _build_dataset_or_400, _build_raster_preview, _decode_csv_bytes, _default_terrain_status_for_format, diff --git a/api/tests/test_elevation_file_record_service_imports.py b/api/tests/test_elevation_file_record_service_imports.py new file mode 100644 index 0000000..c412f84 --- /dev/null +++ b/api/tests/test_elevation_file_record_service_imports.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +import ast +import unittest +from pathlib import Path + + +class ElevationFileRecordServiceImportTests(unittest.TestCase): + def test_elevation_service_imports_reference_defined_symbols(self) -> None: + service_path = Path("api/app/services/elevation_file_record_service.py") + elevation_service_path = Path("api/app/services/elevation_service.py") + + service_tree = ast.parse(service_path.read_text(encoding="utf-8")) + elevation_service_tree = ast.parse(elevation_service_path.read_text(encoding="utf-8")) + + imported_names = { + alias.name + for node in service_tree.body + if isinstance(node, ast.ImportFrom) and node.module == "elevation_service" + for alias in node.names + } + defined_names = { + node.name + for node in elevation_service_tree.body + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)) + } + defined_names.update( + target.id + for node in elevation_service_tree.body + if isinstance(node, ast.Assign) + for target in node.targets + if isinstance(target, ast.Name) + ) + defined_names.update( + node.target.id + for node in elevation_service_tree.body + if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name) + ) + defined_names.update( + alias.asname or alias.name.split(".")[0] + for node in elevation_service_tree.body + if isinstance(node, ast.Import) + for alias in node.names + ) + defined_names.update( + alias.asname or alias.name + for node in elevation_service_tree.body + if isinstance(node, ast.ImportFrom) + for alias in node.names + ) + + self.assertLessEqual(imported_names, defined_names) + + +if __name__ == "__main__": + unittest.main() diff --git a/memory/2026-06-20.md b/memory/2026-06-20.md index c6342fa..2168495 100644 --- a/memory/2026-06-20.md +++ b/memory/2026-06-20.md @@ -208,6 +208,24 @@ - 风险与关注点: - 改动仅移除 Worker 监控移动卡片容器的额外顶部间距,不影响接口、权限、表格、抽屉或数据筛选逻辑。 +# Work Log - 高程文件记录服务导入修复(FL-213) + +- 背景: + - API 容器启动时报 `elevation_file_record_service` 无法从 `elevation_service` 导入 `_build_dataset_or_400`,导致容器反复重启。 + +- 本次处理: + - 移除 `api/app/services/elevation_file_record_service.py` 中未使用且不存在的 `_build_dataset_or_400` 导入。 + - 新增 `api/tests/test_elevation_file_record_service_imports.py`,用 AST 检查 `elevation_file_record_service` 从 `elevation_service` 导入的符号均在目标模块作用域存在,防止同类启动期 ImportError 回归。 + +- 验证: + - 基线:`PYTHONPATH=api python3 -c 'from app.services import elevation_file_record_service'` 因本地未安装 `sqlalchemy` 提前失败;随后使用 `uv` 准备完整 API 依赖时耗时过长,未作为有效基线。 + - 修改后:`python3 -m unittest api.tests.test_elevation_file_record_service_imports` 通过。 + - 修改后:`python3 -m py_compile api/app/services/elevation_file_record_service.py api/app/services/elevation_service.py api/tests/test_elevation_file_record_service_imports.py` 通过。 + - 修改后:`git diff --check` 通过。 + +- 风险与关注点: + - 改动仅删除未使用导入并补充静态回归测试,不改变高程文件记录接口、任务派发、分析或地形瓦片生成逻辑。 + # Work Log - 任务监控页面一致性优化(FL-156) - 背景: