# Kiến Trúc Promotion Service

## Tổng Quan

Promotion Service quản lý chiến dịch khuyến mãi, Voucher và Gift Card cho GoodGo Platform.

```
┌─────────────────────────────────────────────────────────────┐
│                    Promotion Service                         │
├─────────────────────────────────────────────────────────────┤
│  API Layer (Controllers, CQRS)                               │
├─────────────────────────────────────────────────────────────┤
│  Domain Layer (Campaign, Voucher Aggregates)                │
├─────────────────────────────────────────────────────────────┤
│  Infrastructure Layer (EF Core, Repositories, Events)       │
├─────────────────────────────────────────────────────────────┤
│  PostgreSQL Database                                         │
└─────────────────────────────────────────────────────────────┘
```

## Decoupling Concept

```mermaid
graph TB
    subgraph "Voucher Creation"
        BA["Backing Asset<br/>(Tài sản đảm bảo)"] --> V["Voucher"]
        AM["Acquisition Method<br/>(Phương thức sở hữu)"] --> V
    end
    
    subgraph "Backing Assets"
        BA1["CURRENCY (VND, USD)"]
        BA2["POINT (PPoint)"]
    end
    
    subgraph "Acquisition Methods"
        AM1["FREE"]
        AM2["EXCHANGE_POINTS"]
        AM3["PURCHASE"]
    end
```

## Các Pattern Kiến Trúc

### Domain-Driven Design (DDD)

- **Aggregates**: Campaign, Voucher (nested in Campaign)
- **Entities**: Voucher, Redemption
- **Value Objects**: AssetType, AcquisitionType
- **Domain Events**: CampaignCreated, VoucherClaimed, VoucherRedeemed

### CQRS Pattern

```
Commands (Ghi)                    Queries (Đọc)
     │                                 │
     ▼                                 ▼
CreateCampaignCommand           GetCampaignQuery
ActivateCampaignCommand         GetCampaignsQuery
ClaimVoucherCommand             GetVoucherQuery
ExchangeVoucherCommand          GetUserVouchersQuery
RedeemVoucherCommand            ValidateVoucherQuery
CancelCampaignCommand           GetCampaignStatisticsQuery
```

## Domain Model

### Campaign Aggregate

```mermaid
classDiagram
    class Campaign {
        +Guid Id
        +Guid MerchantId
        +string Name
        +AssetType BackingAssetType
        +string BackingAssetCode
        +decimal FaceValue
        +AcquisitionType AcquisitionType
        +decimal AcquisitionPrice
        +Guid EscrowHoldId
        +int TotalVouchers
        +int IssuedVouchers
        +CampaignStatus Status
        +List~Voucher~ Vouchers
        +Activate()
        +Pause()
        +Cancel()
        +IssueVoucher(userId)
    }
    
    class Voucher {
        +Guid Id
        +string Code
        +Guid? OwnerId
        +VoucherStatus Status
        +decimal RemainingValue
        +DateTime? ClaimedAt
        +DateTime? RedeemedAt
        +Claim(userId)
        +Redeem(amount)
        +Expire()
    }
    
    class Redemption {
        +Guid Id
        +Guid VoucherId
        +Guid? OrderId
        +decimal AmountUsed
        +decimal AmountRefunded
        +DateTime RedeemedAt
    }
    
    Campaign "1" --> "*" Voucher
    Voucher "1" --> "*" Redemption
```

### Enums

```csharp
public enum AssetType 
{ 
    Currency = 1,  // VND, USD
    Point = 2      // PPoint
}

public enum AcquisitionType 
{ 
    Free = 1,           // Tặng miễn phí
    ExchangePoints = 2, // Đổi điểm
    Purchase = 3        // Mua bằng tiền
}

public enum CampaignStatus 
{ 
    Draft = 1, 
    Active = 2, 
    Paused = 3, 
    Completed = 4, 
    Cancelled = 5 
}

public enum VoucherStatus 
{ 
    Available = 1, 
    Claimed = 2, 
    PartiallyRedeemed = 3, 
    FullyRedeemed = 4, 
    Expired = 5 
}
```

## Database Schema

### Các Bảng

| Bảng | Mô Tả |
|------|-------|
| `campaigns` | Chiến dịch của Merchant |
| `vouchers` | Mã voucher/gift card |
| `redemptions` | Lịch sử sử dụng |

### Schema Chi Tiết

```sql
CREATE TABLE campaigns (
    id UUID PRIMARY KEY,
    merchant_id UUID NOT NULL,
    name VARCHAR(255) NOT NULL,
    backing_asset_type INT NOT NULL,
    backing_asset_code VARCHAR(10) NOT NULL,
    face_value DECIMAL(18,2) NOT NULL,
    acquisition_type INT NOT NULL,
    acquisition_price DECIMAL(18,2) DEFAULT 0,
    escrow_hold_id UUID,
    total_vouchers INT NOT NULL,
    issued_vouchers INT DEFAULT 0,
    start_date TIMESTAMP NOT NULL,
    end_date TIMESTAMP NOT NULL,
    status INT NOT NULL,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE vouchers (
    id UUID PRIMARY KEY,
    campaign_id UUID NOT NULL REFERENCES campaigns(id),
    code VARCHAR(20) UNIQUE NOT NULL,
    owner_id UUID,
    status INT NOT NULL,
    remaining_value DECIMAL(18,2) NOT NULL,
    claimed_at TIMESTAMP,
    redeemed_at TIMESTAMP,
    expires_at TIMESTAMP,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE redemptions (
    id UUID PRIMARY KEY,
    voucher_id UUID NOT NULL REFERENCES vouchers(id),
    order_id UUID,
    amount_used DECIMAL(18,2) NOT NULL,
    amount_refunded DECIMAL(18,2) DEFAULT 0,
    redeemed_at TIMESTAMP DEFAULT NOW()
);
```

### Indexes Chính

- `IX_Campaigns_MerchantId` - Campaigns theo merchant
- `IX_Vouchers_CampaignId` - Vouchers theo campaign
- `IX_Vouchers_Code` - Tra cứu theo mã
- `IX_Vouchers_OwnerId` - Vouchers theo user

## Tích Hợp Giữa Các Service

### Kiến Trúc Tổng Quan

```mermaid
graph TB
    APP["Mobile/Web App"]
    PS["Promotion Service"]
    WS["Wallet Service"]
    MS["Merchant Service"]
    MQ["RabbitMQ"]
    
    APP --> PS
    PS --> WS
    PS --> MS
    PS --> MQ
    WS --> MQ
    
    style PS fill:#90EE90
```

### Tích Hợp Wallet Service (Escrow)

```

Promotion Service ──────► Wallet Service
        │                      │
        │   Hold/Execute/      │
        │   Release            │
        ▼                      ▼
    Campaign              Escrow Balance
    Created               Locked/Released
```

**API Calls:**

| Action | Wallet API | Mô Tả |
|--------|------------|-------|
| Tạo Campaign | `POST /holds` | Lock tiền/điểm vào escrow |
| Sử dụng Voucher | `POST /holds/{id}/execute` | Trừ tiền từ escrow |
| Hoàn dư | `POST /holds/{id}/release` | Trả lại tiền dư |
| Hủy Campaign | `POST /holds/{id}/release` | Hoàn toàn bộ escrow |

## Luồng Xử Lý

### 1. Tạo Campaign

```
1. Merchant → POST /api/v1/campaigns
2. Validate thông tin campaign
3. Gọi Wallet Service: Hold(amount, merchantWallet)
4. Lưu Campaign với escrowHoldId
5. Sinh voucher codes
6. Response → Campaign created
```

### 2. User Lấy Voucher (Free)

```
1. User → POST /api/v1/vouchers/claim
2. Validate campaign đang active
3. Kiểm tra user chưa có voucher này
4. Cập nhật voucher.ownerId = userId
5. Response → Voucher code
```

### 3. User Sử Dụng Voucher

```
1. Checkout → POST /api/v1/vouchers/redeem
2. Validate voucher còn hiệu lực
3. Tính toán: amountUsed = min(faceValue, orderAmount)
4. Tính toán: surplus = faceValue - amountUsed
5. Gọi Wallet: Execute(holdId, amountUsed)
6. Nếu surplus > 0: Gọi Wallet: Release(holdId, surplus)
7. Cập nhật voucher.status = Redeemed
8. Lưu redemption history
9. Response → Redemption confirmed
```

## Integration Events

### Outgoing Events

```csharp
// Khi campaign được tạo
public record CampaignCreatedIntegrationEvent(
    Guid CampaignId,
    Guid MerchantId,
    string AssetCode,
    decimal TotalEscrow);

// Khi voucher được sử dụng
public record VoucherRedeemedIntegrationEvent(
    Guid VoucherId,
    Guid CampaignId,
    Guid UserId,
    Guid? OrderId,
    decimal AmountUsed,
    decimal AmountRefunded);
```

### Incoming Events

```csharp
// Từ Wallet Service
public record EscrowHeldIntegrationEvent(
    Guid HoldId,
    Guid WalletId,
    Guid ReferenceId,
    decimal Amount);

public record EscrowExecutedIntegrationEvent(
    Guid HoldId,
    decimal ExecutedAmount,
    string ExecutionRef);
```

## Bảo Mật

### Xác Thực
- JWT Bearer token từ IAM Service
- Merchant context từ Merchant Service

### Phân Quyền
- Merchant chỉ quản lý campaigns của mình
- User chỉ xem/sử dụng vouchers của mình
- Admin có full access

### Validation
- FluentValidation cho tất cả commands
- Rate limiting cho claim/redeem APIs

## Triển Khai

### Docker Compose

```yaml
promotion-service:
  build:
    context: ../..
    dockerfile: services/promotion-service-net/Dockerfile
  environment:
    - DATABASE_URL=${PROMOTION_DATABASE_URL}
    - WALLET_SERVICE_URL=http://wallet-service:5000
    - RABBITMQ_URL=${RABBITMQ_URL}
    - JWT_AUTHORITY=${IAM_SERVICE_URL}
  labels:
    - traefik.http.routers.promotion.rule=PathPrefix(`/api/v1/campaigns`) || PathPrefix(`/api/v1/vouchers`)
```

### Health Checks

| Endpoint | Kiểm Tra |
|----------|----------|
| `/health/live` | Service đang chạy |
| `/health/ready` | Database + RabbitMQ + Wallet Service |
| `/health` | Trạng thái đầy đủ |

## Giám Sát

### Metrics
- Số lượng campaigns active
- Vouchers issued/redeemed per campaign
- Tỷ lệ redemption
- Thời gian response

### Logging
- Serilog structured logging
- Correlation IDs cho tracing
- Audit log cho financial transactions
