From 6765cb2a92f570f7b624c78894bc7ecc83ef24fa Mon Sep 17 00:00:00 2001 From: Harmon Date: Mon, 15 Jun 2026 16:17:51 +0800 Subject: [PATCH] fix: register GF3 assets with searchable metadata --- .../app/services/gf3_standardize_service.py | 31 ++++++++++++++++--- frontend/src/DataMonitorPanel.jsx | 31 +++++++++++-------- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/backend/app/services/gf3_standardize_service.py b/backend/app/services/gf3_standardize_service.py index 9e8aa5c..2e1dd09 100644 --- a/backend/app/services/gf3_standardize_service.py +++ b/backend/app/services/gf3_standardize_service.py @@ -46,6 +46,19 @@ def _db_now() -> datetime: return datetime.now(timezone.utc).replace(tzinfo=None, microsecond=0) +def _date_to_naive_utc(value: Any) -> datetime | None: + text = str(value or "").strip() + if not text: + return None + digits = "".join(ch for ch in text if ch.isdigit()) + if len(digits) != 8: + return None + try: + return datetime.strptime(digits, "%Y%m%d") + except ValueError: + return None + + def _path_kind(path: str) -> str: text = str(path or "").strip() if text.startswith("\\\\"): @@ -534,6 +547,8 @@ async def _upsert_source_product_asset( return None standard_dir = Path(standard_dir_text) metadata = scene_manifest.get("metadata") or {} + imaging_date = str(metadata.get("imaging_date") or "").strip() or None + acquisition_start = _date_to_naive_utc(imaging_date) scene_name = scene_manifest.get("scene_name") or standard_dir.name now = _db_now() root = await _find_managed_root_for_path(db, standard_dir_text) @@ -565,9 +580,9 @@ async def _upsert_source_product_asset( "absolute_orbit": metadata.get("absolute_orbit") or metadata.get("orbit_circle"), "relative_orbit": metadata.get("relative_orbit"), "orbit_direction": metadata.get("orbit_direction"), - "acquisition_start_time_utc": None, + "acquisition_start_time_utc": acquisition_start, "acquisition_stop_time_utc": None, - "imaging_date": metadata.get("imaging_date"), + "imaging_date": imaging_date, "root_ref_id": root.id if root else None, "root_path": root.path if root else str(standard_dir.parent), "file_path": standard_dir_text, @@ -625,6 +640,8 @@ async def _upsert_radar_data( center_lon, center_lat = _scene_center_from_polygon(polygon) geom = _geom_from_polygon(polygon) metadata = scene_manifest.get("metadata") or {} + imaging_date = str(metadata.get("imaging_date") or "").strip() or None + acquisition_start = _date_to_naive_utc(imaging_date) radar_metadata = _metadata_for_radar(scene_manifest, standard_manifest) scene_name = scene_manifest.get("scene_name") or Path(str(scene_manifest.get("native_dir") or "")).name unique_id = f"gf3_sarscape:{scene_name}" @@ -634,7 +651,7 @@ async def _upsert_radar_data( "unique_id": unique_id, "satellite": "GF3", "satellite_family": "GF3", - "imaging_date": metadata.get("imaging_date"), + "imaging_date": imaging_date, "imaging_mode": metadata.get("imaging_mode"), "polarization": ",".join( pol @@ -644,8 +661,14 @@ async def _upsert_radar_data( or metadata.get("polarization"), "scene_center_lon": metadata.get("scene_center_lon") if metadata.get("scene_center_lon") is not None else center_lon, "scene_center_lat": metadata.get("scene_center_lat") if metadata.get("scene_center_lat") is not None else center_lat, + "acquisition_time_utc": acquisition_start.isoformat() if acquisition_start else None, "product_level": "L2", - "product_unique_id": metadata.get("product_unique_id"), + "product_unique_id": metadata.get("product_unique_id") or scene_name, + "source_product_token": scene_name, + "acquisition_start_time_utc": acquisition_start, + "acquisition_stop_time_utc": None, + "absolute_orbit": metadata.get("absolute_orbit") or metadata.get("orbit_circle"), + "relative_orbit": metadata.get("relative_orbit"), "source_format": SOURCE_ASSET_FORMAT, "source_product_ref_id": source_product_ref_id, "image_data_format": "GEOTIFF", diff --git a/frontend/src/DataMonitorPanel.jsx b/frontend/src/DataMonitorPanel.jsx index 769ff1e..426ca0e 100644 --- a/frontend/src/DataMonitorPanel.jsx +++ b/frontend/src/DataMonitorPanel.jsx @@ -796,23 +796,28 @@ const DataMonitorPanel = ({ apiEndpoint, onTaskStart, readOnly = false, enabled return; } setGf3ScanLoading(true); - setGf3Message('GF3 扫描启动中...'); + setGf3Message('GF3 资产扫描启动中...'); try { - const res = await fetch(`${apiEndpoint}/monitor/run-now?target=gf3`, { - method: 'POST', - credentials: 'include', + const inventoryStatus = await getAssetInventoryStatus(); + const gf3SourcePathSet = new Set(config.gf3_archive_source_dirs.map(normalizeComparePath)); + const rootIds = (inventoryStatus?.states || []) + .filter((item) => item?.inventory_type === 'source_product' && gf3SourcePathSet.has(normalizeComparePath(item?.root_path))) + .map((item) => item.root_ref_id) + .filter((value, index, array) => value && array.indexOf(value) === index); + const data = await scanAssetInventory({ + inventory_types: ['source_product'], + root_ids: rootIds, + bind_orbits: false, }); - const data = await parseJsonSafe(res, {}); - if (res.ok) { - setGf3Message(data.message || 'GF3 扫描任务已启动'); - if (onTaskStart) { - onTaskStart(data.task_id, '已触发 GF3 手动扫描...'); - } - } else { - setGf3Message(`失败:${data.detail || '未知错误'}`); + setGf3Message(data.message || 'GF3 资产扫描任务已启动'); + if (onTaskStart) { + onTaskStart(data.task_id, '已触发 GF3 资产扫描...', { + nonBlocking: true, + taskType: 'SCAN_ASSET_INVENTORY', + }); } } catch (err) { - setGf3Message(`失败:${err.message || '未知错误'}`); + setGf3Message(`失败:${err?.response?.data?.detail || err.message || '未知错误'}`); } finally { setGf3ScanLoading(false); }