fix: persist product archive cover and provenance

This commit is contained in:
2026-09-12 13:28:11 +08:00
parent 1c51a9bc2b
commit 87a3d44359
6 changed files with 262 additions and 36 deletions
+25 -27
View File
@@ -68,12 +68,9 @@ class ProductArchiveService:
resolved.append((source, db_path))
return resolved
def _rollback_metadata(self, version_id: str) -> None:
# ProductArchiveStore deliberately keeps its public API small. These rows
# all belong to the just-created version and can be removed safely here.
self.store._execute("DELETE FROM product_wordcloud_archives WHERE version_id = ?", (version_id,))
self.store._execute("DELETE FROM product_images WHERE version_id = ?", (version_id,))
self.store._execute("DELETE FROM product_versions WHERE version_id = ?", (version_id,))
@staticmethod
def _move_staging(staging_dir: Path, final_dir: Path) -> None:
staging_dir.replace(final_dir)
def archive_version(
self,
@@ -97,14 +94,13 @@ class ProductArchiveService:
product_dir = self.archive_root / product_id
staging_dir = product_dir / f".{uuid.uuid4().hex}.staging"
version: ProductVersionRecord | None = None
final_dir: Path | None = None
try:
staging_dir.mkdir(parents=True, exist_ok=False)
preview_path = staging_dir / "design-preview.png"
preview_path.write_bytes(preview_bytes)
snapshots: list[tuple[WordcloudSource, Path]] = []
snapshots: list[tuple[WordcloudSource, Path, str]] = []
for index, (source, source_db) in enumerate(source_dbs, start=1):
snapshot_path = (
staging_dir
@@ -112,37 +108,39 @@ class ProductArchiveService:
/ f"{index:02d}-{source.source_job_id}"
/ "word_locations.sqlite"
)
copy_word_locations_snapshot(source_db, snapshot_path)
snapshots.append((source, snapshot_path))
checksum = copy_word_locations_snapshot(source_db, snapshot_path)
snapshots.append((source, snapshot_path, checksum))
version = self.store.create_version(
version_id = f"ver_{uuid.uuid4().hex}"
target_dir = product_dir / version_id
self._move_staging(staging_dir, target_dir)
final_dir = target_dir
preview = final_dir / "design-preview.png"
archive_rows = [
{
"archive_path": str(final_dir / snapshot_path.relative_to(staging_dir)),
"source_job_id": source.source_job_id,
"source_asset_id": source.asset_id,
"db_checksum": checksum,
}
for source, snapshot_path, checksum in snapshots
]
version, _, _ = self.store.write_archive_version(
product_id,
version_id=version_id,
metadata={
"document": document,
"wordcloud_count": len(snapshots),
"wordcloud_sources": [
{"asset_id": source.asset_id, "source_job_id": source.source_job_id}
for source, _ in snapshots
for source, _, _ in snapshots
],
},
preview_path=str(preview),
archives=archive_rows,
now=now,
)
target_dir = product_dir / version.version_id
staging_dir.replace(target_dir)
final_dir = target_dir
final_preview = final_dir / "design-preview.png"
self.store.add_image(
product_id, version.version_id, image_path=str(final_preview), image_type="design_preview", now=now
)
for _, snapshot_path in snapshots:
final_snapshot = final_dir / snapshot_path.relative_to(staging_dir)
self.store.add_wordcloud_archive(
product_id, version.version_id, archive_path=str(final_snapshot), now=now
)
return version
except Exception:
if version is not None:
self._rollback_metadata(version.version_id)
shutil.rmtree(final_dir or staging_dir, ignore_errors=True)
raise