Điểm Khách hàng
1. Tổng quan
| Thuộc tính | Giá trị |
|---|---|
| ID | FEAT-SALE-POINTS |
| Trạng thái | Stable (P1 — chỉ award; redemption ở P3) |
| Owner | sale-team |
| Phụ thuộc | Customer (số dư điểm), SaleOrder (nguồn), Configuration (tỷ lệ quy đổi per-merchant) |
Khi một sale order hoàn tất thanh toán, sale cộng điểm loyalty cho khách hàng được liên kết. Lượng điểm = order.total × conversionRate (per-merchant, đọc từ Configuration). Idempotent theo saleOrderId để chịu được redelivery Kafka và retry webhook.
2. Mô hình thực thể
Chi tiết trường — xem Mô hình miền §3.8 và §3.9.
3. Vận hành
CustomerPointService (customer-point.service.ts — 148 dòng).
| Phương thức | Chữ ký | Mục đích |
|---|---|---|
awardPointsForOrder | { saleOrderId, customerId, merchantId, total, transaction? } | Cộng điểm; idempotent |
Kiểm tra Idempotency
PointTransactionRepository.existsBySaleOrderId là khóa dedup.
4. REST Endpoints
| Verb | Path | Auth | Permission | Handler |
|---|---|---|---|---|
| 6× CRUD | /point-transactions | JWT/BASIC | PointTransaction.<crud> | merchant-scoped (thực tế chỉ đọc — ghi xảy ra nội bộ khi thanh toán) |
OpenAPI live:
/v1/api/sale/doc/openapi.json.
5. Sự kiện
Inbound (gọi nội bộ): kích hoạt từ SaleOrderPaymentWebhookService._handleOrderPaymentSuccess khi order chuyển sang COMPLETED (không phải khi PARTIAL).
Outbound: không trực tiếp. Cập nhật order qua WebSocket (ORDER_PAYMENT_UPDATED) mang theo Customer.pointBalance đã cập nhật một cách gián tiếp qua chi tiết order.
6. Cấu hình
| Nguồn | Trường | Mặc định |
|---|---|---|
Bảng Configuration per merchant | loyalty.conversionRate (đường dẫn jsonb) | 0 (không cộng điểm) |
| Giả định tiền tệ | order currency × conversionRate = points | tự định nghĩa per-merchant |
7. Trường hợp biên
| Trường hợp | Hành vi |
|---|---|
customerId null trên order | Bỏ qua — điểm cần khách hàng |
conversionRate là 0 hoặc chưa đặt | Bỏ qua — không có điểm để cộng |
Order vào COMPLETED rồi CANCELLED (refund) | Flow hoàn tiền chèn PointTransaction âm (P3 — TBD) |
Order vào PARTIAL trước, sau đó COMPLETED | Award chỉ kích hoạt khi COMPLETED (idempotent — một lần award duy nhất per order) |