Skip to content

Thu thập & Chuẩn hoá thông tin cá nhân khách hàng

Version: 1.1 Date: 25/03/2026 Author: PO/BA Type: New Feature Complexity: M Module: Customer App (Flutter) + Admin Web (Settings) + Backend (Hasura)


Changelog

VersionDateAuthorThay đổi
1.025/03/2026PO/BAInitial
1.127/03/2026PO/BADEC-009: Occupation dropdown lấy từ default_master_data thay vì hardcode. Bỏ CRUD occupation inline trong tab Consent → reference trang Master Data

Hướng dẫn đọc (RACI)

AudienceĐọc sectionsTrách nhiệm
PO/BAPRD: A0 (overview) → Z → A5 (FR)Approve requirements, UX flows
Tech LeadPRD: A0 → Z. Dev Spec: C1-C4Approve architecture
FE Dev (Mobile)UI Spec: B2-B6. Dev Spec: C5-C6Implement Flutter screens
FE Dev (Admin)UI Spec: B2 (SCR-03). Dev Spec: C6Implement admin tab
BE DevDev Spec: C1-C12Migration + Hasura metadata
QAPRD: A5. QA Plan: D1-D5Write + execute test cases

Executive Summary (TL;DR)

Thu thập thông tin cá nhân (ngày sinh, nghề nghiệp, tỉnh/thành) của khách hàng qua popup đề xuất cập nhật trên app Flutter, kèm consent screen đơn giản để ghi nhận đồng ý xử lý dữ liệu. Admin quản lý nội dung từ web, thống kê % cập nhật để BOD theo dõi KPI 40-50%.

Milestones

MilestoneTargetOwnerĐiều kiện
BE: Migration + Hasura metadata + seed dataT+1 ngàyBE Dev
Mobile: Consent screen + Profile update popupT+4 ngàyMobile DevSau BE deploy
FE Web: Admin tab Consent + thống kêT+3 ngàyFE DevSau BE deploy
QA: Test all platformsT+7 ngàyQASau Mobile + FE deploy
Go-LiveT+8 ngàyTech LeadQA pass

Trạng thái Sign-off

DomainNgườiStatus
BusinessPOPending
TechTech LeadPending
QAQA LeadPending

Pending Decisions

Không có Pending Decision — tất cả đã locked trong Decision Log.

Backlog Phase 2 (Out-of-scope)

#Tính năngLý do defer
1Enforce consent (filter notification, ẩn ảnh điều trị)Sếp quyết định chỉ thu thập, không enforce
2Incentive (tặng điểm/voucher khi cập nhật)Sếp quyết định không có
3Settings > Quyền riêng tư trên app kháchChưa có nhu cầu
4Multi-page consent (như 30Shine)Quá phức tạp cho scope hiện tại
5Admin/POS consentChỉ áp dụng cho customer app
6Consent log/history chi tiếtDùng audit fields cơ bản
7GPS auto-detect tỉnh/thànhKhông cần thêm plugin, phức tạp permission

Z) Decision Log

IDCategoryQuyết địnhLý doNgàyStatus
DEC-001BusinessĐề xuất cập nhật thông tin là trọng tâm, consent là phụMục đích chính là thu thập data thống kê (birthday + occupation + province), không phải compliance. 2 hướng: (A) consent-first → phức tạp, low conversion; (B) data-collection-first → đơn giản, đạt KPI. Chọn B25/03/2026Locked
DEC-002UXConsent đơn giản single-page, không multi-page2 hướng: (A) multi-page từng bước (30Shine style) → UX chuyên nghiệp hơn nhưng dài, drop-off cao; (B) single-page checkbox → nhanh, ít friction. Sếp chọn B vì consent chỉ là phụ25/03/2026Locked
DEC-003BusinessThu thập 3 fields: ngày sinh + nghề nghiệp + tỉnh/thành2 hướng: (A) chỉ birthday → thiếu data; (B) birthday + occupation + province → đủ phân khúc tuổi + lifestyle + vùng miền. Chọn B vì 3 fields vừa đủ, không quá nhiều gây bỏ qua25/03/2026Locked
DEC-004TechnicalBỏ GPS auto — khách tự chọn tỉnh/thành từ dropdown2 hướng: (A) GPS auto-detect → cần thêm plugin, xin GPS permission, battery drain; (B) dropdown từ danh sách có sẵn → không cần plugin, data tỉnh/thành đã có trong geo DB. Chọn B25/03/2026Locked
DEC-005UXHỏi lại nếu bỏ qua — sau mỗi 4 lần mở app, tối đa 3 lần2 hướng: (A) hỏi 1 lần duy nhất → KPI thấp (~20%); (B) hỏi lại có kiểm soát → KPI 40-50%. Chọn B với cap 3 lần để không spam25/03/2026Locked
DEC-006BusinessThống kê % cập nhật trên tab Consent admin2 hướng: (A) dashboard/report riêng → effort lớn; (B) tab trong Settings > Ứng dụng KH → tận dụng UI có sẵn, aggregate query đơn giản. Chọn B25/03/2026Locked
DEC-007BusinessKhông có incentive (không tặng điểm/voucher)2 hướng: (A) tặng điểm/voucher khi cập nhật → tăng conversion nhưng thêm cost + logic; (B) không incentive → đơn giản. Sếp quyết định B25/03/2026Locked
DEC-008TechnicalNội dung consent + popup quản lý từ server (app_setting)2 hướng: (A) hardcode trong app → đơn giản nhưng cần deploy mỗi lần sửa; (B) server-driven JSON từ app_setting → admin sửa không cần deploy app. Chọn B25/03/2026Locked
DEC-009TechnicalDanh sách nghề nghiệp lấy từ default_master_data (type='occupation'), không hardcode trong app_setting2 hướng: (A) hardcode 4 options trong profile_update_info.fields[].options → đơn giản nhưng mỗi lần thêm/sửa phải update seed; (B) lấy từ default_master_data đã có sẵn 11 items + FE Settings CRUD → tái sử dụng data có sẵn, admin tự quản lý tại Cài đặt > Dữ liệu tham chiếu > Nghề nghiệp. Chọn B27/03/2026Locked

A) PRD

A0) Feature Overview

Section này là "bức tranh toàn cảnh" — đọc 2 phút nắm hết feature. Chi tiết xem A1-A9.

Ý tưởng cốt lõi

App khách hàng Diva hiện thiếu data nghề nghiệp và tỉnh/thành, nhiều khách bỏ trống ngày sinh khi đăng ký. Feature này thêm popup đề xuất cập nhật thông tin (birthday + occupation + province) sau login, kèm consent screen đơn giản ghi nhận đồng ý xử lý dữ liệu. Mục tiêu: 40-50% khách cập nhật để BOD có data phân khúc thống kê.

Kiến trúc tổng thể

┌─────────────┐     ┌──────────────┐     ┌──────────────────┐
│ Flutter App  │────→│ Hasura GQL   │────→│ PostgreSQL       │
│ (Customer)   │     │ (Controller) │     │ (default DB)     │
├─────────────┤     ├──────────────┤     ├──────────────────┤
│ ConsentScreen│     │ customer_    │     │ customer_consent │
│ ProfilePopup │     │ consent CRUD │     │ account          │
│ Auth Flow    │     │ app_setting  │     │ account_address  │
└─────────────┘     │ account CRUD │     │ app_setting      │
                    └──────────────┘     └──────────────────┘
┌─────────────┐           │
│ Admin Web   │───────────┘
│ (Settings)  │
├─────────────┤
│ Consent Tab │
│ Config Form │
│ Stats Panel │
└─────────────┘

Mapping sang tài liệu chi tiết:

KhốiPRD (FR)UI Spec (SCR)Dev Spec (Section)
Consent Screen (Flutter)FR-001SCR-01C4, C5
Profile Update Popup (Flutter)FR-002, FR-003SCR-02C4, C5
Auth Flow IntegrationFR-004C6
App Open TrackingFR-005C5
Admin Consent TabFR-006, FR-007SCR-03C5, C6

Quyết định chính (tóm tắt theo nhóm)

NhómQuyết địnhRef
Chiến lượcData collection là trọng tâm, consent là phụ; không incentiveDEC-001, DEC-007
UXSingle-page consent; retry popup tối đa 3 lần, mỗi 4 lần mở appDEC-002, DEC-005
Data3 fields: birthday + occupation + province (dropdown, không GPS)DEC-003, DEC-004
TechnicalServer-driven content từ app_setting; thống kê trên tab adminDEC-006, DEC-008

Bảng FR tóm tắt

FRMô tảPriorityPhase
FR-001Consent Screen — fullscreen, dynamic content, checkbox itemsMust1
FR-002Popup đề xuất cập nhật TT — birthday + occupation + provinceMust1
FR-003Logic retry popup — hỏi lại có kiểm soátMust1
FR-004Tích hợp auth flow — check consent + update_info sau loginMust1
FR-005Track app_open_count — đếm số lần mở appMust1
FR-006Admin: Config consent + profile updateMust1
FR-007Admin: Thống kê % cập nhậtMust1

Data Model tóm tắt

TableLoạiMục đích
customer_consentMỚILưu consent data + tracking skip/complete/app_open
accountCÓ SẴNCột birthday, occupation đã có
account_addressCÓ SẴNCột province_code, province_name đã có
app_settingSEEDThêm consent_config + profile_update_info

Impact lên code hiện tại

File/AreaThay đổiRisk
splash_bloc.dart (Flutter)Thêm load consent_config từ app_settingMed — core flow
Auth flow navigation (Flutter)Thêm consent + update_info check sau login/signupMed — core flow
ServerToAppSetting (Dart model)Extend thêm consent_config, profile_update_infoLow
Admin settings pageThêm tab "Consent"Low

Timeline

PhaseNội dungTarget
Phase 1BE: Migration + Hasura + seedT+1 ngày
Phase 2Mobile: Consent + Popup + Auth integrationT+4 ngày
Phase 3FE Web: Admin tab + thống kêT+3 ngày
Phase 4QA + Go-liveT+8 ngày

A1) Blueprint

FieldValue
FeatureThu thập & Chuẩn hoá thông tin cá nhân khách hàng
TypeNew Feature
PlatformCustomer App (Flutter mobile) + Admin Web (diva-admin)
Module ảnh hưởngFlutter: authentication, splash, dashboard. Admin: settings. BE: Hasura controller (default DB)

A2) Context

As-Is

  • App khách hàng Flutter đăng ký bằng SĐT → OTP → Signup form (tên, giới tính, ngày sinh, email, chi nhánh)
  • Nhiều khách bỏ trống ngày sinh, email khi đăng ký
  • Không có dữ liệu nghề nghiệp (occupation column tồn tại nhưng chưa collect)
  • Không có tỉnh/thành khách hàng (chỉ có cho branch/staff)
  • Không có cơ chế consent xử lý dữ liệu, không có audit trail
  • Terms/Privacy hiện chỉ là link webview ở trang Tài khoản
  • Flow sau login: Dashboard → Event popup (nếu có) → Push notification prompt (OS)

To-Be

  • Sau login/signup → Consent Screen (1 lần, ghi nhận đồng ý) → Popup đề xuất cập nhật (birthday + occupation + province) → Dashboard
  • Khách bỏ qua popup → hỏi lại sau 4 lần mở app, tối đa 3 lần
  • Admin quản lý nội dung consent + popup từ web, không cần deploy app
  • Thống kê % khách đã cập nhật hiển thị trên admin

A3) Goals & Success Metrics

GoalMetricTarget
Thu thập thông tin cá nhân% khách cập nhật ít nhất 1 field40-50% trong 3 tháng
Ghi nhận consent% khách đã consent70%+ (vì consent screen bắt buộc hiện)
Phân khúc tuổi% khách có ngày sinh40-50%
Phân khúc nghề nghiệp% khách có occupation40-50%
Phân khúc vùng miền% khách có tỉnh/thành40-50%

A4) Personas

PersonaVai tròJTBDFrequency
Khách hàng (app)Customer (role user)Đồng ý consent + cập nhật thông tin cá nhân để nhận trải nghiệm tốt hơn1-3 lần (consent 1 lần, popup tối đa 3 lần)
Admin/BODAdmin (role admin)Quản lý nội dung consent, theo dõi % cập nhật qua thống kêHàng tuần

A5) Functional Requirements

Priority: Must | SCR: SCR-01

Flow:

Auth success → Load consent_config (app_setting)
  → Query customer_consent
  → Không có record HOẶC version < current → Show Consent Screen
  → version = current → Skip

AC:

  • [ ] Hiển thị fullscreen, không có nút back/close — khách phải bấm "Đồng ý & Tiếp tục"
  • [ ] Render nội dung động từ consent_config trong app_setting: title, body, checkbox items
  • [ ] Mỗi checkbox item hiển thị label + description, mặc định bật (checked)
  • [ ] Khách có thể bỏ checkbox — vẫn cho vào app, lưu lựa chọn
  • [ ] Link "Chính sách bảo mật" mở webview với URL từ app_setting.privacy_policy
  • [ ] Link "Điều khoản sử dụng" mở webview với URL từ app_setting.term_condition
  • [ ] Bấm "Đồng ý & Tiếp tục" → upsert customer_consent với consent_data (JSON chứa trạng thái từng checkbox) + consent_version + accepted_at
  • [ ] Nếu consent_config chưa tồn tại trong app_setting → skip, vào Dashboard bình thường
  • [ ] Kill app giữa consent screen → login sau hiện lại (vì chưa có record)

FR-002: Popup đề xuất cập nhật thông tin (Ref: DEC-001, DEC-003, DEC-004, DEC-005)

Priority: Must | SCR: SCR-02

Flow:

Sau Consent (hoặc skip consent) → Check account fields:
  → birthday != null AND occupation != null AND province_code != null → Skip
  → update_info_completed = true → Skip
  → update_info_skip_count >= max_skip (3) → Skip
  → Chưa đủ lần mở app → Skip (xem FR-003)
  → Else → Show Popup

AC:

  • [ ] Hiển thị dạng popup (dialog), có nút X (close) và nút "Bỏ qua" — không bắt buộc
  • [ ] Render nội dung động từ profile_update_info trong app_setting: title, body, fields config
  • [ ] Chỉ hiển thị fields mà khách chưa có data (birthday null → hiện, đã có → ẩn)
  • [ ] Field ngày sinh: date picker, format dd-mm-yyyy
  • [ ] Field nghề nghiệp: dropdown lấy danh sách từ cấu hình hệ thống (default_master_data type='occupation', chỉ items có disabled = false). Quản lý tại Cài đặt > Dữ liệu tham chiếu > Nghề nghiệp (Ref: DEC-009)
  • [ ] Field tỉnh/thành phố: dropdown dùng danh sách province có sẵn trong geo DB (đã load sẵn tại splash)
  • [ ] Mỗi field có hint text giải thích benefit (VD: "Cập nhật để nhận voucher sinh nhật từ Diva")
  • [ ] Bấm "Cập nhật" → mutation update account (birthday, occupation) + upsert account_address (province_code, province_name) + set update_info_completed = true
  • [ ] Khách chỉ cập nhật 1-2 fields → lưu fields đó, lần sau chỉ hỏi fields còn thiếu
  • [ ] Nếu tất cả 3 fields đã có data → không hiện popup

FR-003: Logic retry popup đề xuất cập nhật (Ref: DEC-005)

Priority: Must | SCR:

AC:

  • [ ] Khách bấm "Bỏ qua" hoặc X → tăng update_info_skip_count (+1) trong customer_consent
  • [ ] Popup chỉ hiện lại khi app_open_count >= update_info_skip_count * reshow_after_opens (mặc định reshow_after_opens = 4)
  • [ ] Tối đa hỏi max_skip lần (mặc định 3). Khi update_info_skip_count >= max_skip → không hỏi nữa
  • [ ] Giá trị max_skipreshow_after_opens config từ app_setting, admin có thể sửa
  • [ ] VD: skip lần 1 → hỏi lại sau 4 lần mở app. Skip lần 2 → hỏi lại sau 8 lần. Skip lần 3 → không hỏi nữa

FR-004: Tích hợp auth flow (Ref: DEC-001, DEC-002)

Priority: Must | SCR:

Flow (sau auth success):

Auth success
  → Load consent_config từ app_setting
  → Query customer_consent + account fields

  Bước 1 — Consent:
  → Không có record                    → Show ConsentScreen
  → version < consent_config.version   → Show ConsentScreen
  → version = current                  → Skip

  Bước 2 — Đề xuất cập nhật:
  → birthday + occupation + province_code đều có → Skip
  → update_info_completed = true                 → Skip
  → update_info_skip_count >= max_skip           → Skip
  → app_open_count < skip_count * reshow_after_opens → Skip
  → Else                                         → Show ProfileUpdatePopup

  → Navigate to Dashboard

AC:

  • [ ] Khách mới (signup) → Consent Screen → Popup → Dashboard
  • [ ] Khách cũ chưa consent → Consent Screen (1 lần) → Popup → Dashboard
  • [ ] Khách đã consent + chưa cập nhật đủ → Popup (nếu đủ điều kiện retry) → Dashboard
  • [ ] Khách đã consent + đã cập nhật đủ → Dashboard trực tiếp
  • [ ] Flow consent/popup không block các flow hiện tại (event popup, push prompt vẫn chạy sau Dashboard)

FR-005: Track app_open_count (Ref: DEC-005)

Priority: Must | SCR:

AC:

  • [ ] Mỗi lần mở app (auth success) → increment app_open_count (+1) trong customer_consent qua Hasura _inc mutation
  • [ ] Chỉ increment khi đã có record customer_consent (đã consent ít nhất 1 lần)
  • [ ] Giá trị dùng để tính điều kiện retry popup (FR-003)

Priority: Must | SCR: SCR-03

AC:

  • [ ] Thêm tab "Consent" trong Cài đặt > Ứng dụng khách hàng
  • [ ] Section Consent: form sửa consent_config trong app_setting — input title, textarea body, CRUD danh sách checkbox items (key, label, description, default)
  • [ ] Nút "Lưu" → update JSON giữ nguyên version (khách không bị hỏi lại)
  • [ ] Nút "Lưu & Tăng version" → update JSON + version++ + hiện dialog xác nhận cảnh báo "Tất cả khách hàng sẽ phải đồng ý lại consent khi mở app"
  • [ ] Section Đề xuất cập nhật: toggle bật/tắt popup, input max_skip, input reshow_after_opens. Danh sách nghề nghiệp quản lý tại Cài đặt > Dữ liệu tham chiếu > Nghề nghiệp (default_master_data type='occupation') — không CRUD inline trong tab Consent (Ref: DEC-009)
  • [ ] Bấm "Lưu cài đặt" → update profile_update_info trong app_setting

FR-007: Admin — Thống kê % cập nhật (Ref: DEC-006)

Priority: Must | SCR: SCR-03

AC:

  • [ ] Hiển thị trên đầu tab Consent:
    • Tổng khách dùng app (count account)
    • Đã consent (count customer_consent) + %
    • Đã cập nhật ngày sinh (count account WHERE birthday IS NOT NULL) + %
    • Đã cập nhật nghề nghiệp (count account WHERE occupation IS NOT NULL) + %
    • Có tỉnh/thành phố (count account_address WHERE province_code IS NOT NULL AND primary_contact = true) + %
  • [ ] % tính = count / tổng khách dùng app * 100, hiển thị 1 decimal
  • [ ] Data realtime (query mỗi lần mở tab, không cache)

A6) Assumptions

IDAssumptionOwner xác nhận
ASM-001Cột occupation trong bảng account đã tồn tại và Hasura user role có quyền updateBE (verified qua codebase discovery)
ASM-002Bảng account_address đã có province_code, province_name và Hasura customer role có quyền insert/updateBE (verified)
ASM-003Danh sách tỉnh/thành phố đã có sẵn trong geo DB và Flutter app đã load tại splash (getProvincesData())Mobile (verified)
ASM-004app_setting table dùng JSON field app_settings để lưu config, admin đã có UI sửa app_settingFE Web (verified)
ASM-005Consent screen là fullscreen bắt buộc nhưng không enforce — khách bỏ checkbox vẫn vào appPO

A7) Risks

IDRiskImpactProbabilityMitigation
RSK-001Khách bỏ qua popup, KPI < 40%Trung bìnhTrung bìnhRetry logic (3 lần), hint text giải thích benefit
RSK-002Consent screen gây friction, tăng churnThấpThấpSingle-page đơn giản, checkbox mặc định bật
RSK-003Admin thay đổi consent version vô ý → tất cả khách phải consent lạiCaoThấpDialog xác nhận cảnh báo khi "Lưu & Tăng version"
RSK-004Splash load thêm consent_config tăng startup timeThấpThấpconsent_config nhỏ (< 1KB JSON), load song song với config hiện tại

A8) Metrics (Post-launch)

MetricCách đoTargetKhi nào đo
% khách đã consentcount(customer_consent) / count(account)70%+Sau 1 tuần
% khách cập nhật birthdaycount(birthday NOT NULL) / count(account)40-50%Sau 1 tháng
% khách cập nhật occupationcount(occupation NOT NULL) / count(account)40-50%Sau 1 tháng
% khách có provincecount(province_code NOT NULL) / count(account)40-50%Sau 1 tháng
Tỉ lệ bỏ qua popupcount(skip >= max_skip) / count(customer_consent)< 30%Sau 1 tháng
Consent screen drop-offSố khách kill app tại consent / tổng hiện consent< 5%Sau 1 tuần

A9) Glossary

Thuật ngữ (VI)Thuật ngữ (EN)Định nghĩaPhân biệt với
ConsentConsentKhách đồng ý cho Diva xử lý dữ liệu cá nhân. Ghi nhận 1 lần, hỏi lại khi version tăng≠ Đề xuất cập nhật (update_info)
Đề xuất cập nhậtProfile Update PromptPopup mời khách bổ sung thông tin (birthday, occupation, province). Có thể bỏ qua, hỏi lại tối đa 3 lần≠ Consent (consent là đồng ý xử lý data)
consent_versionConsent VersionSố version của nội dung consent. Tăng khi admin bấm "Lưu & Tăng version" → khách phải consent lại
update_info_skip_countSkip CountSố lần khách bỏ qua popup đề xuất cập nhật. Cap tại max_skip (3)
app_open_countApp Open CountSố lần khách mở app (auth success). Dùng tính điều kiện retry popup
reshow_after_opensReshow IntervalSố lần mở app giữa 2 lần hiện popup. Mặc định 4

RACI

DeliverablePOTLUI/UXFE Dev (Mobile)FE Dev (Admin)BE DevQA
PRD (file này)ACIIIII
UI SpecCIRCCII
Dev SpecIAICCRI
QA Test PlanCIIIIIR