Skip to content

v1.2 — 21/04/2026

Thay đổiSectionẢnh hưởng
Khóa mặc định implementation cho role seed, raw source seed v1, confidence rubric và hybrid refreshC2, C3, C5, C6, C8, C11BE, FE, TL, QA
Đồng bộ report.group_id, bổ sung state transition rules, Hasura permission metadata và trace tới test casesC5, C6, C8, C12BE, FE, TL, QA
Khởi tạo dev spec cho LTV Phase 1Toàn fileBE, FE, TL

Dev Spec — LTV Phase 1

Ref: PRD v1.2 | Date: 21/04/2026

Dev spec này chốt contract triển khai cho lớp canonical LTV, mapping version, backfill orchestration và report/query layer.

Nên đọc theo thứ tự C3 Rules, C4 Data Model, C5 Query Contract, C6 State Transition Rules, rồi đến C12 Traceability.


C1) Scope & Architecture Overview

text
┌──────────────┐    GraphQL / Action     ┌─────────────────────┐
│ diva-admin   │ ──────────────────────→ │ Hasura + Go service │
│ report FE    │                         │ / scheduler layer   │
└──────┬───────┘                         └─────────┬───────────┘
       │                                           │
       │                                           │
       │                              ┌────────────▼────────────┐
       │                              │ PostgreSQL (ecommerce)  │
       │                              │ customer_revenue        │
       │                              │ customer_revenue_stats  │
       │                              │ ltv_* new tables        │
       │                              └─────────────────────────┘

       └──── Export / report queries đọc từ lớp snapshot + aggregate mới

Deliverable kỹ thuật phase 1

  • Reuse customer_revenue / customer_revenue_stats làm nền doanh thu customer-level.
  • Tạo lớp snapshot canonical cho:
    • LTV chuẩn
    • Nguồn gốc LTV chuẩn
    • Kênh nguồn chi tiết
    • mapping_version
  • Tạo report queries cho:
    • summary cards
    • aggregate tables
    • customer detail table
    • data quality panel
  • Tạo admin flows cho:
    • quản lý mapping version/rules
    • chạy backfill
    • manual override source snapshot

Không thuộc scope phase 1

  • Multi-touch attribution
  • Forecast / predictive LTV
  • Rebuild toàn bộ customer_revenue
  • Dashboard chiến lược 6 bài toán đầy đủ

C2) Impact Map

AreaExisting assetAction
Revenue event storecustomer_revenueReuse
Revenue aggregatecustomer_revenue_statsExtend / đọc nền
Raw customer sourceecommerce_user.customer_sourceReuse as raw only
Raw source catalog seedmaster_data(type = customer_source)Freeze toàn bộ active catalog vào mapping version v1
Report groupcustomer_cycle_report_groupExtend FE route/tab + report registry
Report role seedcustomer_cycle_report / customer_cycle_overview_report đang seed bod, hr_leader, it_leaderReuse cùng role set cho customer_cycle_ltv_report ở phase 1
Mapping / snapshotChưa cóBuild mới
Backfill orchestrationChưa có flow riêngBuild mới

C3) Rules / Formulas (Implementation Delta)

FORMULA-001: Base LTV

  • Ref: PRD A10 FORMULA-001
  • Implementation note: không tính lại từ order total; ưu tiên đọc từ canonical revenue events ở customer_revenue.
  • Revenue events được cộng: invoice_payment, invoice_payment_wallet nếu là actual revenue hợp lệ.
  • Reverse events được trừ: refund_wallet, refund_cash và các adjustment đảo chiều hợp lệ khác nếu đã ghi vào customer_revenue.
  • Source-of-truth cho payment eligibility: join từ customer_revenue.reference_id sang invoice.id cho các action type invoice_paymentinvoice_payment_wallet, sau đó đọc invoice.payment_method_id.
  • Rule loại trừ ví khuyến mãi: chỉ cộng event wallet khi invoice.payment_method_id = 'wallet'; nếu invoice.payment_method_id = 'wallet_promotion' thì loại khỏi canonical LTV.
  • Rule an toàn: nếu payment event không join được về invoice hoặc không xác định được payment method hợp lệ thì không tự đoán; event đó phải bị loại khỏi canonical aggregate cho đến khi có evidence rõ.

FORMULA-002: First Paid Date

  • Ref: PRD A10 FORMULA-002
  • Implementation note: lấy min của payment event hợp lệ theo invoice.paid_at.
  • Do not use: order.created_at, order.paid_at.

FORMULA-003: Average LTV by Source Group

  • Ref: PRD A10 FORMULA-003
  • Implementation note: aggregate trên snapshot customer-level đã canonicalized, không aggregate trực tiếp từ raw source hiện tại.

FORMULA-004: Cohort Filter Semantics

  • Ref: PRD A10 FORMULA-004
  • Implementation note: filter thời gian chỉ lọc tập customer theo first_paid_at; ltv_amount vẫn là all-time.

ATTR-001: Canonical Source Assignment

  • Ref: PRD FR-002, FR-004
  • Rule: 1 customer = 1 normalized source group = 1 detailed source channel.
  • Source of truth ưu tiên:
    1. manual override hợp lệ
    2. historical evidence khó sửa / record có timestamp gần first_paid_at
    3. raw source hiện tại nếu pass safe-to-infer
    4. fallback unknown
  • Output fields: source_group, source_channel, determination_method, confidence_level, mapping_version_id

ATTR-002: Branch / Service Semantics

  • Ref: PRD DEC-013, DEC-014
  • Rule: first_paid_branch_idinitial_service_group được derive từ giao dịch hợp lệ đầu tiên.
  • Not allowed: phân bổ multi-branch hoặc multi-service lifecycle trong phase 1.

ATTR-003: Confidence Rubric

  • Ref: PRD PD-005, DEC-020
  • Rule:
    • high: có manual_override hợp lệ hoặc có direct evidence gắn gần first_paid_at đủ để map dứt khoát
    • medium: chỉ có đúng 1 raw source hiện hành hợp lệ và không có tín hiệu mâu thuẫn
    • unknown: raw source trống, nhiều raw source, hoặc evidence mâu thuẫn / không đủ chắc
  • Do not use: tự suy diễn high chỉ vì raw source hiện tại có giá trị.
  • Expected output: confidence_level phải deterministic cho cùng 1 tập evidence.

C4) Data Model (SQL)

Existing tables / views

Table / viewPurpose
customer_revenueRevenue events theo customer
customer_revenue_statsAggregate revenue hiện có theo customer
ecommerce_userRaw source hiện tại, profile customer

New tables đề xuất

1. ltv_customer_snapshot

ColumnTypeNotes
customer_idtextAlign với customer_revenue.customer_id
ltv_amountbigintBase LTV canonical
first_paid_attimestamptzDerived
last_paid_attimestamptzDerived
first_paid_branch_iduuid nullableBranch semantic
initial_service_grouptext nullableService semantic
snapshot_attimestamptzLast refresh time
audit fieldsstandardcreated_at, updated_at, ...

2. ltv_source_mapping_version

ColumnTypeNotes
iduuidPK
codetextHuman-readable version code
statustextdraft, published, archived
effective_fromtimestamptzStart effect
effective_totimestamptz nullableEnd effect
notestext nullableNotes
audit fieldsstandard

3. ltv_source_mapping_rule

ColumnTypeNotes
iduuidPK
mapping_version_iduuidFK -> version
raw_source_idtextMaster data ID / raw identifier
raw_source_name_snapshottextSnapshot label
normalized_source_grouptextCanonical group
detailed_source_channeltextDetailed channel
priorityintTie-break nếu cần
statustextactive/inactive
audit fieldsstandard

4. ltv_customer_source_snapshot

ColumnTypeNotes
customer_idtextPK / unique
source_grouptextCanonical group
source_channeltextDetailed channel
mapping_version_iduuid nullableVersion used
determination_methodtextauto, manual_override, unknown
confidence_leveltexthigh, medium, unknown
evidence_payloadjsonbEvidence used to determine
determined_attimestamptzSnapshot time
audit fieldsstandard

5. ltv_customer_source_audit

ColumnTypeNotes
iduuidPK
customer_idtextcustomer
old_source_grouptextbefore
new_source_grouptextafter
old_source_channeltextbefore
new_source_channeltextafter
reasontextrequired
changed_bytext/uuidactor
changed_attimestamptzaudit time

6. ltv_backfill_job

ColumnTypeNotes
iduuidPK
scope_typetextfull, customer_ids, cohort_range
mapping_version_iduuidversion applied
statustextqueued, running, done, failed
started_attimestamptz
finished_attimestamptz nullable
summary_payloadjsonbtotals, unknown count, error count

Indexes đề xuất

  • ltv_customer_snapshot(customer_id)
  • ltv_customer_snapshot(first_paid_at)
  • ltv_customer_snapshot(first_paid_branch_id)
  • ltv_customer_snapshot(initial_service_group)
  • ltv_customer_source_snapshot(source_group)
  • ltv_customer_source_snapshot(source_channel)
  • ltv_customer_source_snapshot(determination_method, confidence_level)
  • ltv_source_mapping_rule(mapping_version_id, raw_source_id)
  • unique active mapping guard theo mapping_version_id + raw_source_id

C5) API / Hasura / Query Contract

Queries

NamePurposeOutput
search_report_ltv_overviewCards + aggregate tabssummary + grouped rows
search_report_ltv_customersBảng khách hàngpaginated rows
search_report_ltv_data_qualityPanel data qualityunknown stats, unmapped raw source, last jobs
search_ltv_source_mapping_versionsDrawer configversion list
search_ltv_source_mapping_rulesDrawer configrules by version
search_ltv_source_mapping_previewPreview runtime trong SCR-02mapped/unmapped count + sample mapped rows + effective window

Actions / mutations

NamePurpose
upsert_ltv_source_mapping_versionTạo / sửa version
upsert_ltv_source_mapping_rulesSeed / update rules
publish_ltv_source_mapping_versionPublish version
run_ltv_backfillTạo backfill job
override_customer_ltv_sourceManual override một customer

FE route / registry

  • Extend CustomerCycleReport.tsx:
    • thêm tab key customer_cycle_ltv
    • render component mới CustomerCycleLtvReport
  • Extend report metadata:
    • report id mới đề xuất: customer_cycle_ltv_report
    • backend report.group_id = customer_cycle_report_group để cùng nhóm seed report hiện có
    • backend report_role seed mặc định phase 1: bod, hr_leader, it_leader
    • FE route / placement vẫn nằm tại /r/reports/customer_cycle_report_group
    • FE report tree / permission lookup phải map report mới vào nhóm customer_cycle_report_group để card/menu và tab discoverability hoạt động đúng
  • Export contract:
    • reuse pattern export của report module hiện có
    • export dùng đúng committed filter state của SCR-01
    • cột export theo cùng thứ tự business của customer table / aggregate tab đang được chọn

C6) Scheduler / Backfill Strategy

Job types

JobTriggerPurpose
Initial full backfillManual bởi Admin/ITTạo snapshot cho khách cũ
Incremental refreshEvent-driven từ payment/refund/override signal hợp lệRefresh customer vừa phát sinh thay đổi mới
Scheduled reconcileScheduler định kỳSoát drift, retry case miss event, không rewrite lịch sử nếu không rerun explicit
Mapping publish follow-upManual chọn rerunChỉ rerun scope hợp lệ khi business chủ động

Refresh strategy default

  • Runtime mặc định phase 1 là hybrid:
    • event-driven để enqueue incremental refresh khi có payment/refund mới hoặc manual override
    • cron reconcile để bắt case miss event / retry an toàn
    • manual backfill cho initial load, rerun theo scope, và thay đổi mapping version đã publish
  • Không dùng scheduler định kỳ như nguồn refresh duy nhất cho case payment/refund mới.

Algorithm mức cao

  1. Xác định tập customer cần xử lý.
  2. Tính lại ltv_customer_snapshot từ revenue events canonical.
  3. Tìm first_paid_at, first_paid_branch_id, initial_service_group.
  4. Xác định source evidence theo thứ tự ưu tiên.
  5. Map qua mapping_version đang áp dụng.
  6. Upsert ltv_customer_source_snapshot.
  7. Ghi ltv_backfill_job.summary_payload.

Safety rules

  • Publish mapping version mới không tự động rewrite snapshot lịch sử.
  • Rerun backfill phải explicit theo action của Admin/IT.
  • Unknown là output hợp lệ.
  • Scheduled reconcile chỉ sửa snapshot đang active khi evidence nguồn thay đổi hợp lệ; không remap snapshot lịch sử sang mapping version mới nếu chưa rerun explicit.

State Transition Rules

LIFECYCLE-001: Mapping version

from_stateeventguardto_stateside effectserror / notes
nonecreate_draftactor có quyền Admin/ITdraftTạo row mới ở ltv_source_mapping_version, có thể clone rule từ version published gần nhấtKhông đụng snapshot lịch sử
draftsave_draftversion code hợp lệdraftPersist rules + preview dataValidation fail giữ nguyên draft
draftpublisheffective_from, không còn rule bắt buộc bị thiếu, actor có quyềnpublishedLock row, mở hiệu lực cho khách mới / snapshot mớiKhông auto tạo backfill job
publishedclone_to_draftactor có quyềndraft (record mới)Copy rules sang version mới để chỉnh tiếpSource row published vẫn giữ nguyên
publishedarchiveKhông vi phạm guard active version của tenant/workflowarchivedChỉ đổi trạng thái version nguồnKhông rewrite snapshot đã có

LIFECYCLE-002: Backfill job

from_stateeventguardto_stateside effectserror / notes
nonerun_backfillactor Admin/IT, chọn scope + mapping versionqueuedTạo ltv_backfill_job mớiMỗi lần rerun tạo row job mới
queuedworker_claimedworker lock thành côngrunningGhi started_atNếu claim fail thì vẫn queued
runningcompletedSnapshot + summary ghi thành côngdoneUpsert ltv_customer_snapshot, ltv_customer_source_snapshot, ghi summary_payload, finished_atReport chỉ đọc số mới sau khi state = done
runningfailedCó runtime / validation errorfailedGhi lỗi + finished_atGiữ snapshot hợp lệ gần nhất, không rollback về dữ liệu rỗng
failedrerunactor Admin/IT xác nhậnqueued (job mới)Tạo job mới cùng/sửa scopeKhông mutate row failed

C7) Migration Plan

StepMigration / taskOwner
1Tạo bảng ltv_source_mapping_version, ltv_source_mapping_ruleBE
2Tạo bảng ltv_customer_snapshot, ltv_customer_source_snapshot, ltv_customer_source_audit, ltv_backfill_jobBE
3Seed taxonomy nhóm nguồn chuẩn mặc địnhBE + PO
4Seed mapping version v1 từ toàn bộ active master_data(type = customer_source) với raw_source_id + raw_source_name_snapshotBE + Admin/IT
5Tạo query/functions/materialized layer cho overview/customers/data-qualityBE
6Expose Hasura metadata / actions + hook incremental/event reconcileBE
7FE thêm tab report + mapping drawer + override dialogFE
8Chạy initial backfill trên stagingBE / Admin/IT
9QA sanity + business UATQA / PO

C8) Security / RBAC

CapabilityRule
View reportTheo report_role của customer_cycle_report_group / report con; seed mặc định phase 1 dùng bod, hr_leader, it_leader
ExportCùng quyền xem report
Mapping configChỉ Admin/IT
Publish mapping versionChỉ Admin/IT
Run backfillChỉ Admin/IT
Manual overrideChỉ Admin/IT, bắt buộc reason

Hasura permission metadata

ArtifactRolePermission shape
search_report_ltv_overview, search_report_ltv_customers, search_report_ltv_data_qualityrole có report_role hợp lệ của customer_cycle_report_group / report conexecute / select theo filter backend đã canonicalize
search_ltv_source_mapping_versions, search_ltv_source_mapping_rules, search_ltv_source_mapping_previewAdmin/ITselect / action execute only
ltv_source_mapping_version, ltv_source_mapping_ruleservice role / Admin/ITinsert/update chỉ qua action layer; không expose edit trực tiếp cho role report thường
ltv_customer_snapshot, ltv_customer_source_snapshotservice role, query layerFE report chỉ đọc qua query contract, không update trực tiếp
publish_ltv_source_mapping_version, run_ltv_backfill, override_customer_ltv_sourceAdmin/ITaction execute only, bắt buộc audit actor

Audit requirements

  • Mọi publish mapping version phải có audit actor + timestamp.
  • Mọi override customer source phải ghi vào ltv_customer_source_audit.
  • Backfill jobs phải lưu summary kết quả.

C9) NFR

AreaRequirement
TimezoneTất cả semantics date theo Asia/Ho_Chi_Minh
PerformanceQuery list/aggregate phải chịu được cohort filter phổ biến mà không full scan không kiểm soát
ConsistencyReport không đọc trực tiếp ecommerce_user.customer_source để hiển thị canonical source
RecoverabilityCó thể rerun backfill theo scope
ExportExport phản ánh đúng filter + semantics hiện tại

C10) Observability

SignalDetail
Job logsbackfill_job_id, totals, unknown_count, error_count
Query metricslatency cho overview/customers/data-quality
Audit logsmapping publish, override submit
Data quality metricsunknown_ratio, manual_override_ratio, unmapped_raw_source_count

C11) Implementation Tasks

BE

  1. Tạo schema/migrations cho ltv_* tables.
  2. Tạo canonical calculator cho ltv_customer_snapshot.
  3. Tạo source attribution resolver + confidence model.
  4. Tạo mapping version CRUD + publish flow.
  5. Seed mapping version v1 từ full active master_data(type = customer_source) và snapshot tên source tại thời điểm cutover.
  6. Tạo preview query cho drawer mapping (mapped/unmapped count, sample output).
  7. Tạo backfill job orchestration + state handling queued/running/done/failed.
  8. Tạo event-driven incremental refresh cho payment/refund/override.
  9. Tạo scheduled reconcile job để quét drift / miss-event an toàn.
  10. Expose Hasura metadata/actions/queries.

FE

  1. Extend CustomerCycleReport.tsx với tab LTV Phase 1.
  2. Build CustomerCycleLtvReport page.
  3. Build aggregate tabs + customer table + data quality panel.
  4. Build mapping drawer cho Admin/IT, gồm draft/published/archived state handling + preview block.
  5. Build override dialog + audit display tối thiểu.
  6. Export theo dataset hiện tại.
  7. Deeplink từ publish success về SCR-01 để chạy backfill explicit.

QA / UAT

  1. Seed mapping version đầu tiên.
  2. Verify unknown / override / rerun cases.
  3. Verify semantics time/branch/service trên UI labels.

C12) Traceability

FRUIBE / DBQA TCNotes
FR-001SCR-01ltv_customer_snapshot + calculatorTC-001-01TC-001-06Base LTV
FR-002SCR-01, SCR-03ltv_customer_source_snapshotTC-002-01TC-002-03, TC-008-04Source snapshot
FR-003SCR-02ltv_source_mapping_version, ltv_source_mapping_rule, search_ltv_source_mapping_previewTC-003-01TC-003-06Versioned mapping + lifecycle
FR-004SCR-01ltv_backfill_job + resolverTC-004-01TC-004-09Backfill + confidence + explicit rerun + hybrid refresh
FR-005SCR-01 customer tablesearch_report_ltv_customersTC-005-01TC-005-05Customer list
FR-006SCR-01 aggregate tabssearch_report_ltv_overviewTC-006-01TC-006-04Management summary
FR-007SCR-01 data qualitysearch_report_ltv_data_qualityTC-007-01TC-007-03Data quality
FR-008SCR-03override_customer_ltv_source + audit tableTC-008-01TC-008-05Manual override + confidence escalation
FR-009SCR-01 wording + tooltipQuery semantics by first paid / initial serviceTC-009-01TC-009-04Semantic lock
FR-010Out-of-scope guardNo multi-touch / forecastTC-010-01TC-010-02Scope guard