Skip to content

Architecture

1. Bối cảnh hệ thống (C4 L1)

2. Góc nhìn Container (C4 L2)

3. Góc nhìn Component (C4 L3) — Phân lớp nội bộ

LớpTrách nhiệm
RoutesBề mặt HTTP, khai báo trong RestPaths (src/common/constants.ts)
ControllersAuth + cổng permission + ánh xạ DTO
WorkerKafka → quyết định hành động phát hành theo issuanceMode / taxMethod; resync TaxInfo
Queue serviceEnqueue (hash phân vùng) + xử lý job; chính sách retry/DLQ
Action servicesDựng payload adapter, gọi nhà cung cấp, ghi audit + WS
Connection componentsĐăng ký client nhà cung cấp với iiapi / t-van lúc boot
RepositoriesTruy vấn Drizzle (thuộc @nx/core), soft-delete

4. Mục lục máy trạng thái

EntityTrạng tháiSơ đồ
Invoice.issuanceStatusPENDING, PROCESSING, SUCCESS, FAILED, CANCELLED→ nhảy tới
InvoiceRequest.claimStatePENDING, CLAIMED, EXPIRED→ nhảy tới

Phát hành hoá đơn

TừSự kiệnĐếnGuard
(không)createPendingInvoicePENDINGmột hoá đơn ORIGIN chưa xoá mỗi nguồn
PENDINGworker dequeuePROCESSINGđã resolve config nhà cung cấp active
PROCESSINGnhà cung cấp 2xxSUCCESSresponse mang số hoá đơn
PROCESSINGtạm thời (5xx / 429 / mạng)PENDINGretryCount ≤ maxRetryCount → re-enqueue với backoff
PROCESSINGvĩnh viễn 4xx (≠429) hoặc hết retryFAILEDmetadata.permanent = true khi 4xx
SUCCESShuỷCANCELLEDInvoiceCancellationService

Nguồn: Invoice.constants.ts (IssuanceStatuses), InvoiceIssuanceQueueService._handleIssuanceFailure. Retry mặc định: maxRetryCount = 3, retryDelayMinutes = [5, 15, 60] (từ InvoiceProviderConfig.retryMetadata).

Khai báo người mua

TừSự kiệnĐếnGuard
(không)payment success, mode BUYER_SELF_SERVICEPENDINGtạo token claim + deadline; job expiry delay đến deadline
PENDINGngười mua gửiCLAIMEDenqueue issuance
PENDINGjob expiryEXPIREDáp dụng defaultBuyerInfo, enqueue issuance
CLAIMEDjob expiryEXPIREDissuance đã enqueue; no-op

5. Kịch bản runtime

5.1 Payment success → phát hành bất đồng bộ

BướcChi tiết
kiểm traorder.status === COMPLETED, có saleChannelId, chưa có hoá đơn active (chưa huỷ)
resolvemapping theo principal SALE_CHANNEL; guard khớp merchant-id
phân vùnggetPartitionByKey(orderId) (Java hashCode mod 3) — cùng đơn luôn cùng phân vùng
chế độAUTO/SCHEDULED để PENDING; REAL_TIME enqueue ngay; BUYER_SELF_SERVICE mở cửa sổ claim

5.2 Merchant CDC → TaxInfo có thẩm quyền

BướcChi tiết
phát hiện thay đổidiff so với TaxInfo đã lưu, KHÔNG phải before-image của Debezium (đúng bất kể REPLICA IDENTITY)
ghi có thẩm quyềnTaxInfo (principalType=Merchant) là nguồn sự thật; FE đọc quan hệ merchant.taxInfo, không phải metadata.tax
onboardingđánh dấu bước onboarding TAX_INFO hoàn tất (idempotent)

5.3 Retry phát hành / DLQ

6. Mối quan tâm xuyên suốt

Mối quan tâmService xử lý thế nào
AuthNJWT (ES256, JWKS từ identity); VerifierApplication
AuthZController có cổng permission + AuthorizationService ở mức merchant
i18njsonb / label map { en, vi } (vd INVOICE_TYPE_DISPLAY_NAMES)
LoggingKey-value có cấu trúc (key: %s)
IdempotencyIssuance jobId = orderId; index unique-một-phần (sourceType, sourceId) cho ORIGIN; một InvoiceIssuance mỗi hoá đơn (lần thử mới nhất thắng)
Mã hoá credentialAES-256-GCM; khoá 32 byte (APP_ENV_INVOICE_CREDENTIALS_KEY)
Tin cậy webhookHai secret HMAC (merchant→platform, iiapi→platform)
Soft-deleteSoftDeletableRepository (deletedAt); index unique-một-phần loại trừ hàng đã xoá
IDsSnowflake qua IdGenerator (worker id ⚠️ chưa đặt)

7. Trang liên quan

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