116 lines
4.5 KiB
Python
116 lines
4.5 KiB
Python
"""市場情報排程掛載 preview。
|
||
|
||
只描述未來 scheduler job 的節奏、gate 與備援;不註冊 job、不啟動 crawler、
|
||
不寫 DB、不連外。
|
||
"""
|
||
|
||
|
||
SCHEDULER_JOBS = (
|
||
{
|
||
"key": "campaign_discovery_daily",
|
||
"label": "跨平台活動入口探索",
|
||
"cadence": "daily 09:20 Asia/Taipei",
|
||
"platforms": ["momo", "pchome", "coupang", "shopee"],
|
||
"entrypoint": "services.market_intel.jobs.discover_campaigns",
|
||
"max_runtime_minutes": 20,
|
||
"requires_external_network": True,
|
||
"requires_database_write": False,
|
||
"write_target": "market_crawler_runs preview only",
|
||
},
|
||
{
|
||
"key": "campaign_product_probe",
|
||
"label": "已核准活動頁商品探測",
|
||
"cadence": "manual-first, then every 4 hours for active campaigns",
|
||
"platforms": ["momo", "pchome", "coupang", "shopee"],
|
||
"entrypoint": "services.market_intel.jobs.probe_campaign_products",
|
||
"max_runtime_minutes": 30,
|
||
"requires_external_network": True,
|
||
"requires_database_write": True,
|
||
"write_target": "market_campaign_products / market_product_price_history",
|
||
},
|
||
{
|
||
"key": "product_match_review_seed",
|
||
"label": "商品比對審核候選生成",
|
||
"cadence": "daily 11:10 Asia/Taipei after discovery",
|
||
"platforms": ["momo", "pchome", "coupang", "shopee"],
|
||
"entrypoint": "services.market_intel.jobs.seed_product_matches",
|
||
"max_runtime_minutes": 15,
|
||
"requires_external_network": False,
|
||
"requires_database_write": True,
|
||
"write_target": "market_product_matches",
|
||
},
|
||
)
|
||
|
||
|
||
def build_scheduler_attach_plan(
|
||
*,
|
||
runtime_status,
|
||
mcp_fetch_gate,
|
||
schema_smoke,
|
||
):
|
||
"""建立排程掛載計畫;所有輸出都是 preview,不會建立 scheduler job。"""
|
||
schema_passed = bool(
|
||
schema_smoke.get("passed")
|
||
or (schema_smoke.get("schema_smoke") or {}).get("passed")
|
||
)
|
||
gate_checks = {
|
||
"market_intel_enabled": bool(runtime_status.enabled),
|
||
"market_intel_crawler_enabled": bool(runtime_status.crawler_enabled),
|
||
"market_intel_write_enabled": bool(runtime_status.write_enabled),
|
||
"database_write_allowed": bool(runtime_status.database_write_allowed),
|
||
"schema_smoke_passed": schema_passed,
|
||
"mcp_fetch_gate_open": bool(mcp_fetch_gate.get("manual_fetch_gate_open")),
|
||
"scheduler_currently_detached": not bool(runtime_status.scheduler_attached),
|
||
"manual_operator_approval": False,
|
||
}
|
||
blocked_reasons = [
|
||
key for key, passed in gate_checks.items()
|
||
if not passed
|
||
]
|
||
|
||
return {
|
||
"mode": "scheduler_attach_plan_preview",
|
||
"ready_to_attach_scheduler": False,
|
||
"scheduler_attached": False,
|
||
"scheduler_registration_executed": False,
|
||
"crawler_job_started": False,
|
||
"external_network_executed": False,
|
||
"database_session_created": False,
|
||
"database_write_executed": False,
|
||
"database_commit_executed": False,
|
||
"writes_executed": False,
|
||
"would_write_database": False,
|
||
"job_count": len(SCHEDULER_JOBS),
|
||
"jobs": list(SCHEDULER_JOBS),
|
||
"gate_checks": gate_checks,
|
||
"blocked_reasons": blocked_reasons,
|
||
"attach_sequence": [
|
||
"先完成 market_* migration 與 platform seed 寫入驗證",
|
||
"MCP deploy preflight、activation runbook、fetch gate 必須全過",
|
||
"先以 manual discovery fetch 產生小樣本,確認 parser diagnostics 正常",
|
||
"只掛 campaign_discovery_daily,觀察 24 小時後才評估 product probe",
|
||
"任何異常立即關閉 MARKET_INTEL_CRAWLER_ENABLED,不影響既有 MOMO 排程",
|
||
],
|
||
"fallback_plan": [
|
||
{
|
||
"key": "feature_flag_pause",
|
||
"label": "關閉 MARKET_INTEL_CRAWLER_ENABLED 即可阻止新排程執行",
|
||
},
|
||
{
|
||
"key": "detach_market_jobs_only",
|
||
"label": "只移除 market_intel job,不動既有 scheduler 與 momo-db",
|
||
},
|
||
{
|
||
"key": "manual_fetch_only_mode",
|
||
"label": "回到人工 fetch gate 模式,保留 UI/API preview",
|
||
},
|
||
],
|
||
"safe_boundaries": [
|
||
"do_not_attach_scheduler_from_api",
|
||
"do_not_run_external_crawler_from_preview",
|
||
"do_not_write_market_tables_before_migration",
|
||
"do_not_touch_existing_momo_scheduler_jobs",
|
||
"do_not_touch_momo_db_lifecycle",
|
||
],
|
||
}
|