Vendor Persona Flows
1. Vendor Onboarding
sequenceDiagram
participant Admin as Platform Admin
participant API as REST API
participant KC as Keycloak
participant DB as PostgreSQL
participant Email as EmailService
Admin->>API: POST /api/v1/admin/vendors {name, type, email, ...}
API->>DB: Create Vendor aggregate (FlowerShop or FreelanceFlorist)
API->>KC: KeycloakAdminClient.CreateUserAsync(email, role=VendorAdmin)
KC-->>API: userId
API->>DB: Create User {role=VendorAdmin, vendorId}
API->>Email: Send welcome email with verification link
API-->>Admin: 201 {vendorId}
Note over Admin,Email: Vendor receives email, verifies account
Admin->>API: POST /api/v1/admin/vendors/{id}/assign-technician
API->>DB: Assign technician to install vases
2. Bouquet Lifecycle
sequenceDiagram
participant VP as Vendor Portal
participant API as REST API
participant DB as PostgreSQL
participant QR as QrCodeGeneratorService
VP->>API: POST /api/v1/bouquets {name, price, vaseId, photoUrl}
API->>DB: Create Bouquet (status=Available)
API->>QR: GenerateQrCode(bouquetId)
QR-->>API: qrCodeData
API->>DB: Update Bouquet.QrShortCode
API-->>VP: 201 {bouquetId, qrCode}
VP->>API: PUT /api/v1/bouquets/{id}/price {price}
API->>DB: Update Bouquet.Price (only when status=Available)
API-->>VP: 200 OK
VP->>API: DELETE /api/v1/bouquets/{id}
API->>DB: Bouquet status → Expired (soft delete)
API-->>VP: 204 No Content
3. Bouquet Status State Machine
stateDiagram-v2
[*] --> Available : POST /bouquets (created)
Available --> Reserved : Order created (bouquet reserved 15min)
Reserved --> Available : Reservation expired / order cancelled
Reserved --> Sold : Payment confirmed (OrderPaidEvent)
Available --> Expired : DELETE /bouquets or freshness=0
Sold --> [*]
Expired --> [*]
4. Order Fulfillment
sequenceDiagram
participant VP as Vendor Portal
participant API as REST API
participant DB as PostgreSQL
participant Bus as InMemoryEventBus
participant Hub as BouquetHub (SignalR)
VP->>API: GET /api/v1/vendor/orders?status=Confirmed
API->>DB: Orders for vendorId where status=Confirmed
API-->>VP: 200 [{orderId, customerName, lines, deliveryAddress}]
VP->>API: PUT /api/v1/vendor/orders/{id}/status {status: Preparing}
API->>DB: Order status → Preparing
API->>Bus: Publish OrderStatusChangedEvent
Bus->>Hub: SendAsync("OrderStatusChanged", orderId, status)
API-->>VP: 200 OK
VP->>API: PUT /api/v1/vendor/orders/{id}/status {status: InDelivery}
API->>DB: Order status → InDelivery
API->>Bus: Publish OrderStatusChangedEvent
Bus->>Hub: SendAsync("OrderStatusChanged", orderId, status)
API-->>VP: 200 OK
note over DB,Hub: Error: vase offline → bouquet still sold, fulfillment continues