Skip to content

PRD: Thiết lập & vòng đời khuyến mãi

ModuleChiến dịch (EXT-03)PRD IDPRD-PRM-001
StatusShippedOwnerCampaign squad
Date2026-03-23Versionv1.0
Packages@nx/pricing · @nx/core · apps/clientURDPRM

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 + PromotionMethod là 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 Rule tá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ồm DRAFT → 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 STANDARDBUY_GET.
  • Phạm vi theo từng merchant (merchantId trên Promotion).

Ngoài phạm vi

3. Success Metrics

Chỉ sốMục tiêu / tín hiệu
Tính một thao tác duy nhất100% 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 đờiMỗ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 tenancyMộ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ậpMerchant 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

PersonaMục tiêu trong tính năng này
OwnerTạo, kích hoạt và lưu trữ bất kỳ khuyến mãi nào của merchant
ManagerSoạ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 FIXED hoặc PERCENTAGE vớ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ầuURD ref
FR-1Tạ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-2Vòng đời DRAFT → ACTIVATED / DEACTIVATED / EXPIRED / ARCHIVED, mặc định DRAFT (IGNIS Statuses)URD-PRM-002
FR-3Phương thức giảm giá FIXED / PERCENTAGE, với target type và chiến lược allocationURD-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 chungURD-PRM-003
FR-5Loại khuyến mãi STANDARDBUY_GETURD-PRM-001
FR-6Các trường khung thời gian hiệu lực (time-base) trên khuyến mãiURD-PRM-002
FR-7Thực thi usageLimit cho mỗi khuyến mãiURD-PRM-004
FR-8Phạ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ạnhYêu cầu
Toàn vẹn dữ liệuKhuyế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 đờistatus 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 & authzMọ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ụngPrimitive Rule được chia sẻ giữa fare và khuyến mãi - một model quy tắc, hai bên dùng
CascadeXóa một khuyến mãi sẽ cascade tới phương thức và quy tắc của nó
i18nNhã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

EntityVai trò
PromotionTài liệu khuyến mãi - loại, status, khung thời gian hiệu lực, usageLimit, merchantId
PromotionMethodPhương thức giảm giá - FIXED/PERCENTAGE, target type, chiến lược allocation
RulePrimitive đ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 modeltà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; merchantId luôn hiện diện.

11. Risks & Open Questions

Rủi ro / câu hỏiGiả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 farePrimitive 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ánChấ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ạnhKế hoạch
PhaseP1 (nền tảng) - xem URD feature catalog
RolloutTất cả merchant; không feature flag
MigrationEntity mới (Promotion, PromotionMethod); đổi tên FareRuleRule; thêm merchantId vào Promotion
Tiêu chí ra mắtAggregate 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átSố 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ợ? FIXEDPERCENTAGE, 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

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