Skip to content

PRD: Người đăng ký bản tin

ModuleKhách hàng (CORE-09)PRD IDPRD-SUB-001
StatusShippedOwnerCustomer squad
Date2026-04-03Versionv1.0
Packages@nx/outreach · @nx/core · apps/boURDSUB

TL;DR

Cho phép khách truy cập trang công khai đăng ký bản tin bằng email, kèm topics và locale, rồi hủy đăng ký chỉ với một cú nhấp qua một token duy nhất - và cung cấp cho back-office một góc nhìn thống kê có xác thực, duy nhất về độ tăng trưởng của danh sách (tổng số, mới-trong-tháng, đếm theo trạng thái). Kết quả: một danh sách gửi thư sạch, email duy nhất toàn cục với đăng ký/đăng ký lại idempotent và một bảng theo dõi tình trạng, thay cho một form đăng ký mờ mịt không có khả năng quan sát cho admin.

1. Context & Problem

Trang marketing công khai Overture thu thập đăng ký bản tin, nhưng back-office (apps/bo) không có cách nào để đọc danh sách hay xem nó đang tăng trưởng ra sao. Khách truy cập có thể đăng ký, nhưng admin không thể trả lời những câu hỏi cơ bản - có bao nhiêu người đăng ký, bao nhiêu người tham gia trong tháng này, bao nhiêu người đã rời đi. Việc đăng ký cũng cần hành xử an toàn khi lặp lại: cùng một email gửi hai lần không được tạo bản trùng, và một địa chỉ đã rời đi trước đó phải có thể tham gia lại mà không cần dọn dẹp thủ công. Thiếu sự đảm bảo email duy nhất, một hành động hủy đăng ký một-lần-nhấp, và một lần đọc thống kê có xác thực, danh sách trở nên mờ mịt và dễ lỗi - một rào cản cho tầng tương-tác-marketing mà Khách hàng hướng tới.

Tính năng này xây dựng vòng đời người đăng ký (đăng ký, hủy đăng ký, đăng ký lại) trên service @nx/outreach và phơi bày một aggregate thống kê chỉ-đọc cho back-office.

2. Goals & Non-Goals

Goals

  • Đăng ký bằng email kèm topics và locale tùy chọn, giữ email duy nhất toàn cục trong số những người đăng ký.
  • Đăng ký idempotent: một email đang hoạt động trả về người đăng ký hiện có; một email đã bị vô hiệu hóa sẽ được kích hoạt lại.
  • Hủy đăng ký một-lần-nhấp qua một liên kết token duy nhất, không yêu cầu xác thực.
  • Một endpoint thống kê có xác thực cho back-office - tổng số, mới-trong-tháng, và đếm theo nhóm trạng thái activated/deactivated.

Non-Goals

  • Engine chiến dịch Email / SMS - Dự kiến (xem URD §7).
  • Phân khúc và nhắm mục tiêu khách hàng.
  • Phân tích giá trị vòng đời của người đăng ký.
  • Gửi email / vận chuyển SMTP (thuộc phạm vi ngoài module này).

3. Success Metrics

MetricTarget / signal
Tính toàn vẹn của danh sáchKhông có email trùng trong số người đăng ký; mỗi người đăng ký có một token hủy đăng ký duy nhất
Tính idempotent của đăng kýĐăng ký lặp cùng một email không tạo row mới; row đã vô hiệu hóa được kích hoạt lại sạch sẽ
Độ tin cậy của hủy đăng kýLiên kết token một-lần-nhấp vô hiệu hóa ngay ở lần gọi đầu; token không hợp lệ trả về 404
Khả năng quan sát của adminGóc nhìn thống kê back-office trả về tổng số, mới-trong-tháng, và đếm theo trạng thái khi yêu cầu

4. Personas & Use Cases

PersonaMục tiêu trong tính năng này
Site VisitorTham gia bản tin bằng email; rời đi chỉ với một cú nhấp
MarketingXem kích thước danh sách, tăng trưởng theo tháng, và số lượng rời đi trong back-office
OwnerXác nhận danh sách gửi thư khỏe mạnh và đang tăng trưởng

Core scenarios: khách truy cập đăng ký bằng email (topics + locale) → một email lặp lại hoặc đã từng rời đi được xử lý idempotent → khách truy cập hủy đăng ký qua một liên kết token → marketing mở back-office và đọc thống kê người đăng ký.

5. User Stories

  • Là một site visitor, tôi muốn đăng ký bằng email, topics, và locale của mình, để tôi nhận được bản tin.
  • Là một site visitor, tôi muốn việc đăng ký hai lần là an toàn, để tôi không bao giờ tạo đăng ký trùng hay lỗi.
  • Là một khách đã hủy đăng ký trước đó, tôi muốn đăng ký lại và được kích hoạt lại, để tôi có thể tham gia lại mà không cần trợ giúp.
  • Là một site visitor, tôi muốn hủy đăng ký chỉ với một cú nhấp qua một liên kết token, để việc rời đi không tốn công.
  • marketing, tôi muốn một góc nhìn thống kê có xác thực về danh sách, để tôi có thể theo dõi tăng trưởng và số lượng rời đi.

6. Functional Requirements

#RequirementURD ref
FR-1Đăng ký bằng email (POST /subscribe, không xác thực) với locale (vi/en) tùy chọn và topics[] tùy chọn; trả về id của người đăng kýURD-SUB-001
FR-2Email duy nhất toàn cục trong số người đăng ký; token hủy đăng ký duy nhất toàn cụcURD-SUB-002
FR-3Đăng ký là idempotent - email đang hoạt động trả về người đăng ký hiện có; email đã vô hiệu hóa được kích hoạt lại (subscribedAt đặt lại, unsubscribedAt xóa)URD-SUB-004
FR-4Hủy đăng ký bằng token (GET /unsubscribe?token=…, không xác thực) vô hiệu hóa người đăng ký; token không hợp lệ trả về 404URD-SUB-003
FR-5Endpoint thống kê (GET /statistics, xác thực JWT hoặc Basic) trả về total, monthlyNew, và byStatus { activated, deactivated }URD-SUB-005
FR-6Thống kê là một aggregate SQL duy nhất trên các row chưa xóa: COUNT(*) FILTER (…) cho mới-trong-tháng (subscribedAt >= startOfMonth) và đếm theo trạng tháiURD-SUB-005

Toàn văn requirement và tiêu chí chấp nhận nằm trong URD Khách hàng. PRD này tham chiếu chúng thay vì lặp lại.

7. Non-Functional Requirements

AreaRequirement
Tính toàn vẹn dữ liệuEmail và token hủy đăng ký duy nhất toàn cục; đăng ký không bao giờ tạo bản trùng; mọi row dùng soft-delete
Tính idempotentĐăng ký lặp lại là an toàn (trả về hiện có hoặc kích hoạt lại); các chuyển trạng thái là tất định
Tenancy & authzĐăng ký/hủy đăng ký công khai không yêu cầu xác thực; thống kê yêu cầu strategy JWT hoặc BASIC; permission khai báo qua crudPermissions(Subscriber.AUTHORIZATION_SUBJECT, …)
Hiệu năng / quy môThống kê tính bằng một aggregate SQL duy nhất (COUNT(*) FILTER), không đếm từng row
i18nLocale ghi nhận theo từng người đăng ký (vi/en); nhãn hiển thị cho người dùng là song ngữ ({ en, vi })

8. UX & Flows

Màn hình chính: form đăng ký bản tin công khai trên trang Overture, và màn hình danh sách người đăng ký + góc nhìn thống kê trong back-office apps/bo.

9. Data & Domain

EntityRole
SubscriberMục trong danh sách gửi thư - email (duy nhất), status (activated/deactivated), locale, topics[], subscribedAt, unsubscribedAt, unsubscribeToken duy nhất
Aggregate thống kêMột projection chỉ-đọc trên Subscriber: đếm total, monthlyNew, và byStatus

Chỉ ở mức khái niệm - schema và bất biến đầy đủ nằm trong Outreach domain model.

10. Dependencies & Assumptions

Phụ thuộc vào

  • @nx/outreach - chứa service, repository, và route của người đăng ký.
  • @nx/core - cung cấp model Subscriber, schema, và crudPermissions.
  • apps/bo - bề mặt back-office đọc endpoint thống kê.
  • Trang công khai Overture - nguồn các đăng ký bản tin.

Giả định

  • Trang công khai có thể gọi các route đăng ký/hủy đăng ký không xác thực.
  • Người dùng back-office xác thực qua JWT hoặc BASIC để đọc thống kê.

11. Risks & Open Questions

Risk / câu hỏiGiảm thiểu / trạng thái
Đăng ký trùng do gửi lặp lạiĐăng ký là idempotent; email duy nhất toàn cục
Đoán / phát lại token hủy đăng kýToken duy nhất toàn cục; token không hợp lệ trả về 404; hủy đăng ký idempotent trên row đã vô hiệu hóa
Chưa có engine chiến dịch để hành động trên danh sáchNgoài phạm vi; ghi nhận là Dự kiến trong URD
Chi phí thống kê khi danh sách lớn dầnAggregate SQL duy nhất; xem lại caching nếu back-office poll nhiều

12. Release Plan & Launch Criteria

AspectPlan
PhaseP2 - feature Khách hàng SUB trong danh mục feature URD
RolloutMọi deployment; không có feature flag
MigrationKhông (entity mới; không backfill dữ liệu)
Launch criteriaĐăng ký/hủy đăng ký/đăng ký lại được kiểm chứng idempotent từ đầu đến cuối; endpoint thống kê trả về đúng tổng số, mới-trong-tháng, và đếm theo trạng thái; màn hình danh sách back-office render được dữ liệu
MonitoringTăng trưởng người đăng ký (mới-trong-tháng), tỷ lệ rời đi (số deactivated), tỷ lệ lỗi hủy đăng ký

13. FAQ

Điều gì xảy ra nếu cùng một email đăng ký hai lần? Đăng ký là idempotent - một email đang hoạt động trả về người đăng ký hiện có, và một email đã vô hiệu hóa trước đó được kích hoạt lại. Không có row trùng nào được tạo.

Hủy đăng ký có cần đăng nhập không? Không - hủy đăng ký là một liên kết công khai một-lần-nhấp mang một token duy nhất. Ngược lại, thống kê yêu cầu xác thực JWT hoặc Basic.

Endpoint thống kê trả về gì? total người đăng ký, monthlyNew (tham gia kể từ đầu tháng hiện tại), và đếm byStatus chia thành activateddeactivated.

Tính năng này có gửi email không? Không - nó quản lý danh sách người đăng ký và thống kê của nó. Engine chiến dịch/gửi là Dự kiến và nằm ngoài module này.

Người đăng ký đã xóa có được đếm không? Không - thống kê chỉ aggregate trên các row chưa xóa (nhận biết soft-delete).

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.