diff --git a/api/app/services/atp_asset_service.py b/api/app/services/atp_asset_service.py index 0a0b5c5..c10d51a 100644 --- a/api/app/services/atp_asset_service.py +++ b/api/app/services/atp_asset_service.py @@ -888,6 +888,17 @@ def delete_asset(db: Session, asset_id: str) -> bool: item = get_asset_by_id(db, asset_id) if not item: return False + + # Delete physical files for all releases before deleting database records + for release in item.releases: + try: + mount = _resolve_mount(db, release.storage_mount_code) + driver = _build_driver_or_400(mount) + driver.delete_path(release.storage_root_path, is_dir=True, recursive=True) + except Exception: + # Log error but continue deletion - don't let file deletion failure block database cleanup + pass + db.delete(item) db.commit() _publish_change("asset.deleted", {"action": "deleted", "asset_id": asset_id}) diff --git a/api/tests/test_atp_asset_service.py b/api/tests/test_atp_asset_service.py index ae7a465..db54c28 100644 --- a/api/tests/test_atp_asset_service.py +++ b/api/tests/test_atp_asset_service.py @@ -330,3 +330,51 @@ def test_run_release_dry_run_materializes_directory(tmp_path, monkeypatch) -> No assert result.output_manifest_json["file_count"] >= 2 finally: session.close() + + +def test_delete_asset_removes_storage_files(tmp_path) -> None: + testing_session = _build_sessionmaker() + session: Session = testing_session() + try: + _seed_vfs_mount(session, root_dir=tmp_path / "vfs") + asset = atp_asset_service.create_asset( + session, + AtpAssetCreateRequest( + code="ATP-ASSET-DELETE-TEST", + name="删除测试模型", + voltage_level="500", + tower_type="danhuita", + scene_type="raoji3", + ), + actor_user_id="tester", + ) + assert asset is not None + + # Create a release with files + release = atp_asset_service.create_release_from_archive( + session, + asset_id=asset.id, + release_tag="v1", + archive_filename="release.zip", + archive_content=_build_zip({ + "work.atp": b"ATP INPUT", + "README.txt": b"documentation", + }), + actor_user_id="tester", + ) + assert release.storage_root_path == "/atp-library/500/danhuita/r1" + + # Verify files exist before deletion + storage_path = tmp_path / "vfs" / "atp-library" / "500" / "danhuita" / "r1" + assert storage_path.exists() + assert (storage_path / "work.atp").exists() + assert (storage_path / "README.txt").exists() + + # Delete the asset + deleted = atp_asset_service.delete_asset(session, asset.id) + assert deleted is True + + # Verify files are deleted + assert not storage_path.exists() + finally: + session.close()