PRD: Thiết lập & vòng đời khuyến mãi
| Module | Chiến dịch (EXT-03) | PRD ID | PRD-PRM-001 |
| Status | Shipped | Owner | Campaign squad |
| Date | 2026-03-23 | Version | v1.0 |
| Packages | @nx/pricing · @nx/core · apps/client | URD | PRM |
TL;DR
Cho phép merchant thiết lập một khuyến mãi như một khái niệm pricing hạng nhất - phương thức giảm giá (phần trăm hoặc cố định), quy tắc điều kiện/nguồn/mục tiêu, khung thời gian hiệu lực và giới hạn sử dụng - trong một thao tác duy nhất, rồi đưa nó qua vòng đời rõ ràng
DRAFT → ACTIVATED → DEACTIVATED/EXPIRED/ARCHIVED. Khuyến mãi có phạm vi theo từng merchant, nên mỗi gian hàng tự sở hữu và kiểm soát ưu đãi của mình từ bản nháp đến khi ngừng.
1. Bối cảnh & Vấn đề
Merchant muốn thúc đẩy doanh số bằng các khuyến mãi giảm giá có mục tiêu, nhưng package pricing mới chỉ mang các primitive dựa trên fare (Fare, FareRule). Chưa có entity khuyến mãi, chưa có phương thức giảm giá, và chưa có vòng đời để đưa một khuyến mãi từ bản nháp thành ưu đãi đang chạy - nên merchant không có cách nào để tạo một giảm giá có cấu trúc, định nghĩa nó áp cho ai/cái gì, giới hạn số lần dùng, hay lên lịch khi nào nó có hiệu lực.
Increment này giới thiệu khuyến mãi như một khái niệm pricing hạng nhất. Primitive quy tắc được tổng quát hóa để cùng một điều kiện điều kiện/nguồn/mục tiêu được chia sẻ bởi cả fare và khuyến mãi, và cặp Promotion + PromotionMethod cho merchant đầy đủ CRUD, một khung thời gian hiệu lực, một giới hạn sử dụng, và một vòng đời theo trạng thái. Khuyến mãi có phạm vi theo từng merchant nên mỗi gian hàng sở hữu danh mục ưu đãi của riêng mình.
2. Mục tiêu & Ngoài phạm vi
Mục tiêu
- Cặp
Promotion+PromotionMethodlà entity pricing hạng nhất, được tạo cùng phương thức và quy tắc trong một aggregate operation duy nhất. - Một phương thức giảm giá trên khuyến mãi:
FIXED/PERCENTAGE, với một target type và một chiến lược allocation. - Một primitive
Ruletái sử dụng cho quy tắc điều kiện, nguồn và mục tiêu - chia sẻ với fare. - Một vòng đời theo trạng thái mặc định
DRAFT, bao gồmDRAFT → ACTIVATED → DEACTIVATED / EXPIRED / ARCHIVED. - Một khung thời gian hiệu lực (time-base fields) và một giới hạn sử dụng cho mỗi khuyến mãi.
- Loại khuyến mãi
STANDARDvàBUY_GET. - Phạm vi theo từng merchant (
merchantIdtrênPromotion).
Ngoài phạm vi
- Tự áp / coupon thủ công khi thanh toán - thuộc Áp giảm giá (
APP, P2, URD-APP-001…003). - Tích/đổi điểm thân thiết - thuộc Khách hàng thân thiết.
- Gửi tin nhắn khuyến mãi - thuộc Marketing.
- Tính thuế - thuộc engine thuế của Pricing.
3. Success Metrics
| Chỉ số | Mục tiêu / tín hiệu |
|---|---|
| Tính một thao tác duy nhất | 100% lần tạo khuyến mãi lưu khuyến mãi + phương thức + quy tắc cùng nhau, hoặc không lưu gì khi lỗi |
| Toàn vẹn vòng đời | Mỗi khuyến mãi nằm ở đúng một trạng thái hợp lệ; chỉ chấp nhận các chuyển trạng thái hợp lệ |
| Cô lập tenancy | Một merchant chỉ thấy và thay đổi khuyến mãi của chính mình (phạm vi theo merchantId) |
| Độ phủ thiết lập | Merchant có thể cấu hình ưu đãi phần trăm và cố định cùng quy tắc điều kiện/mục tiêu mà không cần kỹ sư hỗ trợ |
4. Personas & Use Cases
| Persona | Mục tiêu trong tính năng này |
|---|---|
| Owner | Tạo, kích hoạt và lưu trữ bất kỳ khuyến mãi nào của merchant |
| Manager | Soạn và quản lý khuyến mãi - phương thức, quy tắc, hiệu lực, giới hạn sử dụng |
Kịch bản cốt lõi: tạo một khuyến mãi cùng phương thức và quy tắc trong một bước → đặt khung thời gian hiệu lực và giới hạn sử dụng → kích hoạt → sau đó hủy kích hoạt, để hết hạn, hoặc lưu trữ.
5. User Stories
- Là một manager, tôi muốn tạo một khuyến mãi cùng phương thức giảm giá và quy tắc trong một bước, để ưu đãi được định nghĩa đầy đủ trong một thao tác duy nhất.
- Là một manager, tôi muốn chọn phương thức
FIXEDhoặcPERCENTAGEvới một target type và allocation, để giảm giá hoạt động đúng như chiến dịch cần. - Là một manager, tôi muốn định nghĩa quy tắc điều kiện, nguồn và mục tiêu, để khuyến mãi chỉ đủ điều kiện cho đúng khách hàng và sản phẩm.
- Là một manager, tôi muốn đặt một khung thời gian hiệu lực và một giới hạn sử dụng, để ưu đãi chạy trong một khoảng cố định và không bị dùng quá mức.
- Là một owner, tôi muốn đưa một khuyến mãi qua vòng đời của nó (kích hoạt, hủy kích hoạt, hết hạn, lưu trữ), để tôi kiểm soát khi nào một ưu đãi đang chạy.
- Là một owner, tôi muốn khuyến mãi có phạm vi theo merchant của mình, để ưu đãi của tôi không bao giờ rò rỉ qua các gian hàng khác.
6. Functional Requirements
| # | Yêu cầu | URD ref |
|---|---|---|
| FR-1 | Tạo một khuyến mãi cùng phương thức và quy tắc trong một aggregate operation (tất cả hoặc không gì khi lỗi) | URD-PRM-001 |
| FR-2 | Vòng đời DRAFT → ACTIVATED / DEACTIVATED / EXPIRED / ARCHIVED, mặc định DRAFT (IGNIS Statuses) | URD-PRM-002 |
| FR-3 | Phương thức giảm giá FIXED / PERCENTAGE, với target type và chiến lược allocation | URD-PRM-001 |
| FR-4 | Định nghĩa quy tắc điều kiện, nguồn và mục tiêu qua primitive Rule dùng chung | URD-PRM-003 |
| FR-5 | Loại khuyến mãi STANDARD và BUY_GET | URD-PRM-001 |
| FR-6 | Các trường khung thời gian hiệu lực (time-base) trên khuyến mãi | URD-PRM-002 |
| FR-7 | Thực thi usageLimit cho mỗi khuyến mãi | URD-PRM-004 |
| FR-8 | Phạm vi theo từng merchant (merchantId trên Promotion, index (merchantId, status)) | URD-PRM-001 |
Toàn văn yêu cầu và tiêu chí chấp nhận nằm trong Campaign URD. PRD này tham chiếu chúng thay vì lặp lại.
7. Non-Functional Requirements
| Khía cạnh | Yêu cầu |
|---|---|
| Toàn vẹn dữ liệu | Khuyến mãi, phương thức và quy tắc được tạo cùng nhau theo transaction - lỗi một phần không để lại khuyến mãi ghi dở |
| Tính hợp lệ vòng đời | status mặc định DRAFT; các chuyển trạng thái ánh xạ trực tiếp tới IGNIS built-in Statuses (không enum tự chế) |
| Tenancy & authz | Mọi thao tác có phạm vi theo từng merchant; được kiểm soát bởi permission pricing/campaign |
| Tái sử dụng | Primitive Rule được chia sẻ giữa fare và khuyến mãi - một model quy tắc, hai bên dùng |
| Cascade | Xóa một khuyến mãi sẽ cascade tới phương thức và quy tắc của nó |
| i18n | Nhãn/trạng thái hiển thị cho người dùng là song ngữ ({ en, vi }) |
8. UX & Flows
Màn hình chính (trong apps/client): màn hình danh sách khuyến mãi, cùng tạo/sửa khuyến mãi cho phương thức, quy tắc, khung thời gian hiệu lực và giới hạn sử dụng.
9. Data & Domain
| Entity | Vai trò |
|---|---|
Promotion | Tài liệu khuyến mãi - loại, status, khung thời gian hiệu lực, usageLimit, merchantId |
PromotionMethod | Phương thức giảm giá - FIXED/PERCENTAGE, target type, chiến lược allocation |
Rule | Primitive điều kiện điều kiện/nguồn/mục tiêu dùng chung (tái sử dụng bởi fare và khuyến mãi) |
Chỉ ở mức khái niệm - schema và bất biến đầy đủ nằm trong pricing domain model và tài liệu promotion.
10. Dependencies & Assumptions
Phụ thuộc vào
- Primitive
Rule(@nx/pricing/@nx/core) - tổng quát hóa từFareRuleđể khuyến mãi và fare dùng chung một model quy tắc. - Sản phẩm (Sản phẩm) - quy tắc mục tiêu tham chiếu sản phẩm và danh mục.
- Khách hàng (Khách hàng) - quy tắc điều kiện tham chiếu phân khúc khách hàng.
Giả định
- Một khuyến mãi có phương thức hợp lệ trước khi có thể kích hoạt (ràng buộc C-01).
- Mỗi merchant sở hữu khuyến mãi của riêng mình;
merchantIdluôn hiện diện.
11. Risks & Open Questions
| Rủi ro / câu hỏi | Giảm thiểu / trạng thái |
|---|---|
| Ghi khuyến mãi/phương thức/quy tắc có thể lệch khi lỗi một phần | Được tạo theo transaction như một aggregate - tất cả hoặc không gì |
Đổi tên FareRule thành Rule có thể phá vỡ các bên dùng fare | Primitive dùng chung đã được kiểm chứng trên cả fare và khuyến mãi |
| Khuyến mãi được cấu hình nhưng chưa áp khi thanh toán | Chấp nhận - áp giảm giá là tính năng APP riêng (P2); service tính toán đã tắt ở đây |
| Kích hoạt một khuyến mãi mà không có phương thức | Được bảo vệ bởi ràng buộc C-01 (cần phương thức trước khi kích hoạt) |
12. Release Plan & Launch Criteria
| Khía cạnh | Kế hoạch |
|---|---|
| Phase | P1 (nền tảng) - xem URD feature catalog |
| Rollout | Tất cả merchant; không feature flag |
| Migration | Entity mới (Promotion, PromotionMethod); đổi tên FareRule → Rule; thêm merchantId vào Promotion |
| Tiêu chí ra mắt | Aggregate create lưu khuyến mãi + phương thức + quy tắc; chuyển trạng thái vòng đời được kiểm chứng; cô lập theo từng merchant được kiểm chứng; danh sách phía client render được |
| Giám sát | Số khuyến mãi mỗi merchant, tỷ lệ lỗi tạo, lỗi chuyển trạng thái vòng đời |
13. FAQ
Phương thức và quy tắc của một khuyến mãi có được tạo riêng không? Không - khuyến mãi, phương thức và quy tắc được tạo cùng nhau trong một aggregate operation; khi lỗi không lưu gì.
Những phương thức giảm giá nào được hỗ trợ? FIXED và PERCENTAGE, mỗi loại có một target type và một chiến lược allocation.
Vòng đời là gì? Một khuyến mãi bắt đầu ở DRAFT và đi qua ACTIVATED → DEACTIVATED / EXPIRED / ARCHIVED, ánh xạ tới IGNIS built-in Statuses.
Kích hoạt một khuyến mãi có áp giảm giá khi thanh toán không? Chưa - thiết lập và vòng đời đã hoạt động, nhưng tự áp/coupon khi thanh toán là tính năng Áp giảm giá riêng (APP, P2). Service tính toán đã tắt ở đây.
Một khuyến mãi có thể chia sẻ qua nhiều merchant không? Không - khuyến mãi có phạm vi theo từng merchant qua merchantId; một merchant chỉ thấy và thay đổi khuyến mãi của mình.
References
- URD: Chiến dịch - Thiết lập & Vòng đời khuyến mãi
- PRD liên quan: Áp giảm giá (
APP, kế hoạch) - Module: Chiến dịch - tổng quan + traceability
- Developer: @nx/pricing · Promotions · domain model