fix(growth): stabilize comparison coverage cohort

This commit is contained in:
ogt
2026-07-15 10:35:20 +08:00
parent c15c800e5d
commit 4bc5be6d2f
4 changed files with 61 additions and 7 deletions

View File

@@ -496,7 +496,7 @@ def build_coverage_snapshot(payload: dict[str, Any]) -> dict[str, Any]:
contract = stats.get("comparison_metric_contract") or {}
partition = contract.get("state_partition") or {}
return {
"scope": contract.get("scope") or f"top_{len(opportunities)}_revenue_opportunities",
"scope": contract.get("scope") or f"top_{len(opportunities)}_revenue_products",
"comparison_ready_count": int(
partition.get("comparison_ready") or stats.get("mapped_count") or 0
),

View File

@@ -4,6 +4,7 @@
from __future__ import annotations
import hashlib
import json
import logging
from datetime import date, datetime, time
@@ -202,7 +203,7 @@ def _fetch_sales_rows(conn, limit: int) -> tuple[list[dict[str, Any]], str | Non
SELECT *
FROM sales
WHERE sales_7d > 0 OR sales_prev_7d > 0
ORDER BY {order_metric} DESC, qty_7d DESC
ORDER BY {order_metric} DESC, qty_7d DESC, pchome_product_id ASC
LIMIT :limit
"""), {"limit": candidate_limit}).mappings().all()
@@ -1178,6 +1179,9 @@ def build_pchome_growth_opportunities(engine, limit: int = 20) -> dict[str, Any]
"opportunities": [],
"message": str(exc),
}
# Coverage must use a stable revenue-ranked cohort. Mapping status may
# reorder work inside the cohort, but must never remove a success from it.
sales_rows = sales_rows[:limit]
sales_ids = [str(row.get("pchome_product_id") or "") for row in sales_rows]
external_map, market_offer_map = _fetch_external_offer_maps(
conn,
@@ -1281,13 +1285,24 @@ def build_pchome_growth_opportunities(engine, limit: int = 20) -> dict[str, Any]
if runtime_external_source_count < registered_external_source_count:
blocked_reason_codes.append("platform_runtime_coverage_gap")
scope_ids = [str(row.get("pchome_product_id") or "") for row in sales_rows]
scope_fingerprint = hashlib.sha256(
json.dumps(scope_ids, ensure_ascii=True, separators=(",", ":")).encode("utf-8")
).hexdigest()[:24]
comparison_metric_contract = {
"version": "pchome_comparison_coverage_v2",
"scope": f"top_{len(opportunities)}_revenue_opportunities",
"version": "pchome_comparison_coverage_v3",
"scope": f"top_{len(opportunities)}_revenue_products",
"scope_membership": {
"selection_stage": "before_action_priority_scoring",
"ranking": "max_current_or_previous_7d_revenue_desc_then_qty_desc_then_product_id_asc",
"member_count": len(scope_ids),
"fingerprint_sha256": scope_fingerprint,
"mapping_status_can_change_membership": False,
},
"numerator": mapped_count,
"denominator": len(opportunities),
"rate": mapping_rate,
"meaning": "通過同款與價格品質門檻具正式外部價格證據的高優先商品",
"meaning": "營收排名母體中,通過同款與價格品質門檻具正式外部價格證據的商品",
"state_partition": {
"comparison_ready": mapped_count,
"candidate_validation": candidate_validation_count,
@@ -1314,7 +1329,7 @@ def build_pchome_growth_opportunities(engine, limit: int = 20) -> dict[str, Any]
"numerator": round(comparison_ready_sales_7d, 2),
"denominator": round(total_opportunity_sales_7d, 2),
"rate": comparison_revenue_coverage_rate,
"meaning": "高優先商品近 7 天業績中,已有正式同商品外部價格證據的占比",
"meaning": "營收排名母體近 7 天業績中,已有正式同商品外部價格證據的占比",
"state_partition": {
"comparison_ready": round(comparison_ready_sales_7d, 2),
"candidate_validation": round(candidate_validation_sales_7d, 2),