-- Petstore Pilot Metrics v2(MySQL 5.7+/8.x,只读) -- 默认时间窗为最近 30 天,但默认门店集合为 0(不返回业务数据)。 -- 执行人必须把 @pilot_store_ids 显式改为 3~5 个试点门店内部 ID,例如 '101,205,309'。 -- 输出仅含 store_id、计数、比率和时效,不含客户/员工 PII。 SET @pilot_to = DATE_ADD(CURRENT_DATE, INTERVAL 1 DAY); SET @pilot_from = DATE_SUB(@pilot_to, INTERVAL 30 DAY); SET @pilot_store_ids = '0'; -- 1. 核心服务与报告漏斗。[from, to),各事实按稳定 aggregate_id 去重。 -- 比率只认同窗且时间顺序正确的 cohort,避免跨窗口事件把比率推到 100% 以上。 SELECT e.store_id, COUNT(DISTINCT CASE WHEN e.event_type = 'appointment_created' THEN e.aggregate_id END) AS appointment_created_count, COUNT(DISTINCT CASE WHEN e.event_type = 'service_started' THEN e.aggregate_id END) AS service_started_count, COUNT(DISTINCT CASE WHEN e.event_type = 'service_completed' THEN e.aggregate_id END) AS complete_service_count, COUNT(DISTINCT CASE WHEN e.event_type = 'report_submitted' THEN e.aggregate_id END) AS report_submitted_count, COUNT(DISTINCT CASE WHEN e.event_type = 'report_sent' AND EXISTS ( SELECT 1 FROM t_business_event submitted WHERE submitted.store_id = e.store_id AND submitted.event_type = 'report_submitted' AND submitted.aggregate_type = 'report' AND submitted.aggregate_id = e.aggregate_id AND submitted.occurred_at >= @pilot_from AND submitted.occurred_at <= e.occurred_at ) THEN e.aggregate_id END) AS report_sent_count, COUNT(DISTINCT CASE WHEN e.event_type IN ('report_opened', 'report_reopened') AND EXISTS ( SELECT 1 FROM t_business_event sent JOIN t_business_event submitted ON submitted.store_id = sent.store_id AND submitted.event_type = 'report_submitted' AND submitted.aggregate_type = 'report' AND submitted.aggregate_id = sent.aggregate_id AND submitted.occurred_at >= @pilot_from AND submitted.occurred_at <= sent.occurred_at WHERE sent.store_id = e.store_id AND sent.event_type = 'report_sent' AND sent.aggregate_type = 'report' AND sent.aggregate_id = e.aggregate_id AND sent.occurred_at >= @pilot_from AND sent.occurred_at <= e.occurred_at ) THEN e.aggregate_id END) AS report_opened_count, COUNT(DISTINCT CASE WHEN e.event_type = 'lead_submitted' THEN e.aggregate_id END) AS lead_submitted_count, COUNT(DISTINCT CASE WHEN e.event_type = 'rebook_created' THEN e.aggregate_id END) AS rebook_created_count, ROUND( 100 * COUNT(DISTINCT CASE WHEN e.event_type = 'service_completed' AND EXISTS ( SELECT 1 FROM t_business_event started WHERE started.store_id = e.store_id AND started.event_type = 'service_started' AND started.aggregate_type = 'appointment' AND started.aggregate_id = e.aggregate_id AND started.occurred_at >= @pilot_from AND started.occurred_at <= e.occurred_at ) THEN e.aggregate_id END) / NULLIF(COUNT(DISTINCT CASE WHEN e.event_type = 'service_started' THEN e.aggregate_id END), 0), 1 ) AS service_completion_rate_pct, ROUND( 100 * COUNT(DISTINCT CASE WHEN e.event_type = 'report_sent' AND EXISTS ( SELECT 1 FROM t_business_event submitted WHERE submitted.store_id = e.store_id AND submitted.event_type = 'report_submitted' AND submitted.aggregate_type = 'report' AND submitted.aggregate_id = e.aggregate_id AND submitted.occurred_at >= @pilot_from AND submitted.occurred_at <= e.occurred_at ) THEN e.aggregate_id END) / NULLIF(COUNT(DISTINCT CASE WHEN e.event_type = 'report_submitted' THEN e.aggregate_id END), 0), 1 ) AS report_sent_rate_pct, ROUND( 100 * COUNT(DISTINCT CASE WHEN e.event_type IN ('report_opened', 'report_reopened') AND EXISTS ( SELECT 1 FROM t_business_event sent JOIN t_business_event submitted ON submitted.store_id = sent.store_id AND submitted.event_type = 'report_submitted' AND submitted.aggregate_type = 'report' AND submitted.aggregate_id = sent.aggregate_id AND submitted.occurred_at >= @pilot_from AND submitted.occurred_at <= sent.occurred_at WHERE sent.store_id = e.store_id AND sent.event_type = 'report_sent' AND sent.aggregate_type = 'report' AND sent.aggregate_id = e.aggregate_id AND sent.occurred_at >= @pilot_from AND sent.occurred_at <= e.occurred_at ) THEN e.aggregate_id END) / NULLIF(COUNT(DISTINCT CASE WHEN e.event_type = 'report_sent' AND EXISTS ( SELECT 1 FROM t_business_event submitted WHERE submitted.store_id = e.store_id AND submitted.event_type = 'report_submitted' AND submitted.aggregate_type = 'report' AND submitted.aggregate_id = e.aggregate_id AND submitted.occurred_at >= @pilot_from AND submitted.occurred_at <= e.occurred_at ) THEN e.aggregate_id END), 0), 1 ) AS report_open_rate_pct, ROUND( 100 * COUNT(DISTINCT CASE WHEN e.event_type = 'lead_submitted' AND lead_fact.report_id IS NOT NULL AND EXISTS ( SELECT 1 FROM t_business_event opened JOIN t_business_event sent ON sent.store_id = opened.store_id AND sent.event_type = 'report_sent' AND sent.aggregate_type = 'report' AND sent.aggregate_id = opened.aggregate_id AND sent.occurred_at >= @pilot_from AND sent.occurred_at <= opened.occurred_at JOIN t_business_event submitted ON submitted.store_id = sent.store_id AND submitted.event_type = 'report_submitted' AND submitted.aggregate_type = 'report' AND submitted.aggregate_id = sent.aggregate_id AND submitted.occurred_at >= @pilot_from AND submitted.occurred_at <= sent.occurred_at WHERE opened.store_id = e.store_id AND opened.event_type IN ('report_opened', 'report_reopened') AND opened.aggregate_type = 'report' AND opened.aggregate_id = lead_fact.report_id AND opened.occurred_at >= @pilot_from AND opened.occurred_at <= e.occurred_at ) THEN lead_fact.report_id END) / NULLIF(COUNT(DISTINCT CASE WHEN e.event_type IN ('report_opened', 'report_reopened') AND EXISTS ( SELECT 1 FROM t_business_event sent JOIN t_business_event submitted ON submitted.store_id = sent.store_id AND submitted.event_type = 'report_submitted' AND submitted.aggregate_type = 'report' AND submitted.aggregate_id = sent.aggregate_id AND submitted.occurred_at >= @pilot_from AND submitted.occurred_at <= sent.occurred_at WHERE sent.store_id = e.store_id AND sent.event_type = 'report_sent' AND sent.aggregate_type = 'report' AND sent.aggregate_id = e.aggregate_id AND sent.occurred_at >= @pilot_from AND sent.occurred_at <= e.occurred_at ) THEN e.aggregate_id END), 0), 1 ) AS opened_to_lead_rate_pct, ROUND( 100 * COUNT(DISTINCT CASE WHEN e.event_type = 'rebook_created' AND rebook.source_lead_id IS NOT NULL AND EXISTS ( SELECT 1 FROM t_business_event submitted_lead WHERE submitted_lead.store_id = e.store_id AND submitted_lead.event_type = 'lead_submitted' AND submitted_lead.aggregate_type = 'report_lead' AND submitted_lead.aggregate_id = rebook.source_lead_id AND submitted_lead.occurred_at >= @pilot_from AND submitted_lead.occurred_at <= e.occurred_at ) THEN rebook.source_lead_id END) / NULLIF(COUNT(DISTINCT CASE WHEN e.event_type = 'lead_submitted' THEN e.aggregate_id END), 0), 1 ) AS lead_to_rebook_rate_pct FROM t_business_event e LEFT JOIN t_report_lead lead_fact ON e.event_type = 'lead_submitted' AND lead_fact.id = e.aggregate_id AND lead_fact.store_id = e.store_id LEFT JOIN t_follow_up_task rebook ON e.event_type = 'rebook_created' AND rebook.rebooked_appointment_id = e.aggregate_id AND rebook.store_id = e.store_id WHERE e.occurred_at >= @pilot_from AND e.occurred_at < @pilot_to AND FIND_IN_SET(CAST(e.store_id AS CHAR), @pilot_store_ids) > 0 GROUP BY e.store_id ORDER BY e.store_id; -- 2. 报告发送时效:只纳入已经完整观察 24 小时的提交 cohort。 SELECT submitted.store_id, COUNT(*) AS matured_submitted_report_count, SUM(CASE WHEN sent.occurred_at IS NOT NULL AND sent.occurred_at >= submitted.occurred_at AND sent.occurred_at <= DATE_ADD(submitted.occurred_at, INTERVAL 24 HOUR) THEN 1 ELSE 0 END) AS sent_within_24h_count, ROUND( 100 * SUM(CASE WHEN sent.occurred_at IS NOT NULL AND sent.occurred_at >= submitted.occurred_at AND sent.occurred_at <= DATE_ADD(submitted.occurred_at, INTERVAL 24 HOUR) THEN 1 ELSE 0 END) / NULLIF(COUNT(*), 0), 1 ) AS sent_within_24h_rate_pct FROM t_business_event submitted LEFT JOIN t_business_event sent ON sent.store_id = submitted.store_id AND sent.event_type = 'report_sent' AND sent.aggregate_type = 'report' AND sent.aggregate_id = submitted.aggregate_id WHERE submitted.event_type = 'report_submitted' AND submitted.aggregate_type = 'report' AND submitted.occurred_at >= @pilot_from AND submitted.occurred_at < DATE_SUB(@pilot_to, INTERVAL 24 HOUR) AND FIND_IN_SET(CAST(submitted.store_id AS CHAR), @pilot_store_ids) > 0 GROUP BY submitted.store_id ORDER BY submitted.store_id; -- 3. 回访执行质量:以任务 due_date 落在窗口内为队列口径;退订不计员工完成。 SELECT t.store_id, COUNT(*) AS due_follow_up_count, SUM(CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END) AS staff_completed_count, SUM(CASE WHEN t.status = 'canceled' AND t.outcome = 'unsubscribed' THEN 1 ELSE 0 END) AS unsubscribed_count, SUM(CASE WHEN t.status IN ('pending', 'in_progress') THEN 1 ELSE 0 END) AS still_open_count, SUM(CASE WHEN t.status IN ('pending', 'in_progress') AND t.due_date < CURRENT_DATE THEN 1 ELSE 0 END) AS overdue_open_count, SUM(CASE WHEN t.status = 'completed' AND t.closed_at < DATE_ADD(t.due_date, INTERVAL 3 DAY) THEN 1 ELSE 0 END) AS completed_by_due_plus_2d_count, ROUND( 100 * SUM(CASE WHEN t.status = 'completed' AND t.closed_at < DATE_ADD(t.due_date, INTERVAL 3 DAY) THEN 1 ELSE 0 END) / NULLIF(SUM(CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END), 0), 1 ) AS on_time_completion_rate_pct, ROUND(AVG(t.attempt_count), 2) AS average_attempt_count, SUM(CASE WHEN t.outcome = 'rebooked' THEN 1 ELSE 0 END) AS rebooked_task_count, SUM(CASE WHEN t.outcome = 'not_interested' THEN 1 ELSE 0 END) AS not_interested_count, SUM(CASE WHEN t.outcome = 'invalid_contact' THEN 1 ELSE 0 END) AS invalid_contact_count FROM t_follow_up_task t WHERE t.due_date >= DATE(@pilot_from) AND t.due_date < DATE(@pilot_to) AND FIND_IN_SET(CAST(t.store_id AS CHAR), @pilot_store_ids) > 0 GROUP BY t.store_id ORDER BY t.store_id; -- 4. 约 200 次完整服务样本闸门:严格使用试点窗口,按店 + 最后一行试点店合计。 SELECT e.store_id, COUNT(DISTINCT e.aggregate_id) AS complete_service_count, MIN(e.occurred_at) AS first_complete_service_at, MAX(e.occurred_at) AS latest_complete_service_at FROM t_business_event e WHERE e.event_type = 'service_completed' AND e.aggregate_type = 'appointment' AND e.occurred_at >= @pilot_from AND e.occurred_at < @pilot_to AND FIND_IN_SET(CAST(e.store_id AS CHAR), @pilot_store_ids) > 0 GROUP BY e.store_id WITH ROLLUP; -- 5. 指标可靠性反例:四项均须为 0;完整发布还必须通过 31 项 production preflight。 SELECT COUNT(*) AS completed_services_without_submitted_report_event_count FROM t_business_event completed LEFT JOIN t_report r ON r.appointment_id = completed.aggregate_id AND r.store_id = completed.store_id AND r.deleted = 0 LEFT JOIN t_business_event submitted ON submitted.store_id = completed.store_id AND submitted.event_type = 'report_submitted' AND submitted.aggregate_type = 'report' AND submitted.aggregate_id = r.id WHERE completed.event_type = 'service_completed' AND completed.aggregate_type = 'appointment' AND (r.id IS NULL OR submitted.id IS NULL) AND completed.occurred_at >= @pilot_from AND completed.occurred_at < @pilot_to AND FIND_IN_SET(CAST(completed.store_id AS CHAR), @pilot_store_ids) > 0; SELECT COUNT(*) AS invalid_rebook_attribution_count FROM t_follow_up_task t LEFT JOIN t_appointment a ON a.id = t.rebooked_appointment_id AND a.store_id = t.store_id AND a.deleted = 0 LEFT JOIN t_business_event e ON e.idempotency_key = CONCAT('rebook_created:', t.rebooked_appointment_id) AND e.event_type = 'rebook_created' AND e.store_id = t.store_id AND e.aggregate_type = 'appointment' AND e.aggregate_id = t.rebooked_appointment_id WHERE t.outcome = 'rebooked' AND (a.id IS NULL OR e.id IS NULL) AND t.closed_at >= @pilot_from AND t.closed_at < @pilot_to AND FIND_IN_SET(CAST(t.store_id AS CHAR), @pilot_store_ids) > 0; SELECT COUNT(*) AS duplicate_open_follow_up_task_count FROM ( SELECT t.store_id, t.source_lead_id FROM t_follow_up_task t WHERE t.status IN ('pending', 'in_progress') AND FIND_IN_SET(CAST(t.store_id AS CHAR), @pilot_store_ids) > 0 GROUP BY t.store_id, t.source_lead_id HAVING COUNT(*) > 1 ) duplicated; SELECT SUM(anomaly_count) AS invalid_metric_event_order_count FROM ( SELECT COUNT(*) AS anomaly_count FROM t_business_event sent JOIN t_business_event submitted ON submitted.store_id = sent.store_id AND submitted.event_type = 'report_submitted' AND submitted.aggregate_type = 'report' AND submitted.aggregate_id = sent.aggregate_id WHERE sent.event_type = 'report_sent' AND sent.aggregate_type = 'report' AND sent.occurred_at < submitted.occurred_at AND sent.occurred_at >= @pilot_from AND sent.occurred_at < @pilot_to AND FIND_IN_SET(CAST(sent.store_id AS CHAR), @pilot_store_ids) > 0 UNION ALL SELECT COUNT(*) AS anomaly_count FROM t_business_event rebooked JOIN t_follow_up_task task ON task.store_id = rebooked.store_id AND task.rebooked_appointment_id = rebooked.aggregate_id JOIN t_business_event submitted_lead ON submitted_lead.store_id = rebooked.store_id AND submitted_lead.event_type = 'lead_submitted' AND submitted_lead.aggregate_type = 'report_lead' AND submitted_lead.aggregate_id = task.source_lead_id WHERE rebooked.event_type = 'rebook_created' AND rebooked.aggregate_type = 'appointment' AND rebooked.occurred_at < submitted_lead.occurred_at AND rebooked.occurred_at >= @pilot_from AND rebooked.occurred_at < @pilot_to AND FIND_IN_SET(CAST(rebooked.store_id AS CHAR), @pilot_store_ids) > 0 ) anomalies;