MSO Cloud · Documentation

Shopper Segments — cách tính (commerce-performance report)

Source: docs/product/shopper-segments.md Updated 2026-09-21
On this page

Câu hỏi khách hay hỏi: "Số Shopper Segments ở Báo cáo hiệu suất thương mại — tính từ đâu, mỗi sàn riêng hay gộp?"

Hai segment xuất hiện#

Nhãn (router) Nhãn hiển thị Định nghĩa
new Khách mới Shopper có đơn hàng đầu tiên (đời) rơi vào khoảng ?from..?to của báo cáo
current Khách quay lại Shopper có đơn trong khoảng ?from..?to nhưng đơn đầu tiên đời họ là trước khoảng đó

KPI = số shopper riêng biệt, không phải số đơn. Một shopper có 5 đơn trong kỳ chỉ tính 1.

SQL (rút gọn — apps/web/src/server/routers/reports.ts ~155)#

WITH shopper_firsts AS (
  SELECT shopper_id, MIN(placed_at)::date AS first_day
  FROM orders
  WHERE org_id = $orgId AND shopper_id IS NOT NULL
    AND platform = ANY($platforms)  -- nếu FilterBar chọn sàn
    AND status = ANY($statuses)
  GROUP BY shopper_id
),
shopper_window AS (
  SELECT DISTINCT shopper_id
  FROM orders
  WHERE org_id = $orgId AND shopper_id IS NOT NULL
    AND placed_at BETWEEN $from AND $to
    AND platform = ANY($platforms)
    AND status = ANY($statuses)
)
SELECT
  CASE WHEN sf.first_day BETWEEN $fromDate AND $toDate THEN 'new' ELSE 'current' END AS segment,
  COUNT(*)::int AS shoppers
FROM shopper_window sw
JOIN shopper_firsts sf USING (shopper_id)
GROUP BY 1;

Mỗi sàn xử lý thế nào#

  • FilterBar chọn 1 sàn (vd "Shopee") → kết quả phản ánh chỉ Shopee shoppers (đơn của họ ở Shopee). Một người mua cả TikTok và Shopee chỉ tính ở Shopee bucket.
  • FilterBar chọn nhiều sàn / để trống → gộp tất cả sàn được chọn. KPI là số shopper riêng biệt cross-platform: một người mua ở Shopee + Lazada chỉ tính 1.
  • first_day quyết định "new" vs "current" — tính trên toàn lịch sử org (không reset theo cửa sổ thời gian). Tức là một khách lần đầu mua ở TikTok tháng 1 và quay lại Shopee tháng 5, khi xem báo cáo Shopee tháng 5 họ là current (vì first_day là tháng 1 trên TikTok).
  • Hệ thống không đếm "khách mới của Shopee" tách biệt với "khách mới của Lazada" trong query này — đó là cross-platform identity. Nếu cần per-platform first-touch attribution, phải đổi MIN(placed_at) trong shopper_firsts thành MIN(placed_at) WHERE platform=... — chưa có yêu cầu nên chưa làm.

shopper_id từ đâu ra#

CSV upload thường mask danh tính khách: TikTok cho "Buyer Username" (vd nga_267), Shopee cho "Người Mua", Lazada cho customerName + shippingPhone. Các giá trị này không phải PII gốc nhưng stable per shopper trên cùng sàn.

Worker promoter (apps/worker/src/promoters/orders.ts) hash chúng:

const identityKey = `${orgId}|${platform}|${primary}|${secondary}`;
const shopperIdHash = createHash('sha256').update(identityKey).digest('hex').slice(0, 64);

→ ghi vào orders.shopper_id (foreign key tới shoppers table). Cùng buyer-username Shopee → cùng shopper_id. Khác sàn → khác hash → xem là 2 shopper khác nhau ở mức backend, NHƯNG shopper_firsts ở SQL trên dùng MIN(placed_at) qua tất cả sàn, nên nếu cùng shopper_id lặp lại trên nhiều sàn (vd masked PII identical giữa nguồn) thì gộp.

Thực tế: mỗi sàn = identity space riêng biệt vì hash bao gồm platform. Cross-platform de-dup chỉ xảy ra khi worker derive ra cùng hash, hiện tại không có. Nếu khách hỏi "có biết X mua cả Shopee + Lazada không?" — trả lời: không, vì giới hạn mask của CSV (không có anchor cross-platform như email/phone hash chung).

Độ phủ dữ liệu#

  • Đơn có shopper_id mới vào segment KPI. Đơn anonymous (nguồn không chứa identity hint) vẫn đếm vào GMV/orders nhưng không vào segment KPI; tỷ lệ phủ tùy chất lượng nguồn của từng org.

Edge cases khách hay hỏi#

Hỏi Trả lời
"Vì sao tổng new + current < số Đơn hàng?" Bucket là shopper, không phải đơn. 1 shopper có 5 đơn = 1 ở segment, 5 ở orders.
"Anonymous orders đi đâu?" Không vào segment KPI (vì không có shopper_id). Vào tổng GMV/Orders bình thường.
"Quay lại bao lâu thì là current?" Bất kỳ. Miễn first_day < ?from. Không có lookback cap. Nếu cần, đổi SQL thành first_day BETWEEN ?from - INTERVAL 'X days' AND ?from
"Mỗi sàn new/current riêng có được không?" Hiện chưa, vì identity hash gồm platform. Cross-platform MIN(first_day) chỉ kích hoạt nếu hash trùng — hiếm.

Code references#

  • Query: apps/web/src/server/routers/reports.ts:155
  • Identity derivation: apps/worker/src/promoters/orders.ts:383
  • FE display: apps/web/src/app/(dashboard)/reports/commerce-performance/_components/commerce-performance-report.tsx:129