Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Production

Production (also referred to as “product” in the codebase) is the user-facing catalog item that customers can purchase in the Helium shop system. It represents a complete service offering that, when purchased, grants users access to telecom packages.

Core Concept

A Production defines:

  • What users see: Display information (title, description, price)
  • What users receive: Reference to a package series and the quantity of packages
  • Who can access it: Access control through user groups and extra permission groups
pub struct ServiceProduction {
    pub id: Uuid,                      // Unique identifier
    pub title: String,                 // Display name
    pub description: String,           // Marketing description
    pub price: Decimal,                // Purchase price
    pub package_series: Uuid,          // References a package series
    pub package_amount: i32,           // Number of packages to deliver
    pub visible_to: i32,               // User group visibility
    pub is_private: bool,              // Private production flag
    pub limit_to_extra_group: i32,     // Extra group requirement if private
    pub on_sale: bool,                 // Whether currently available for purchase
}

Key Characteristics

  1. Package Series Linkage: Production always references a package series, not individual packages. This ensures version control flexibility - the actual package delivered is the master package of the series at purchase time.

  2. Quantity Control: package_amount specifies how many packages from the series the user receives. For example:

    • package_amount = 1: Single subscription period
    • package_amount = 3: Three subscription periods (stacked in package queue)
    • package_amount = 12: Annual subscription with 12 monthly packages
  3. Access Control Layers:

    • Basic visibility: visible_to determines which user group can see the production
    • Private productions: When is_private = true, only users with limit_to_extra_group in their user_extra_groups can access it
    • On-sale status: on_sale controls whether the production is currently purchasable
  4. Price Stability: The production price is independent of package contents. Even if the underlying master package changes (through version control), the production price remains stable unless explicitly updated.

Production Views

The system provides two different views of productions for different audiences:

User View (ProductionUserView)

pub struct ProductionUserView {
    pub id: Uuid,
    pub title: String,
    pub description: String,
    pub price: Decimal,
    pub package_amount: i32,
    pub traffic_limit: i64,           // From master package
    pub max_client_number: i32,       // From master package
    pub expire_duration: PgInterval,  // From master package
}

Purpose: Shows end users what they’re buying with key package details (traffic, connections, duration) pulled from the current master package.

Admin View (ProductionAdminView)

pub struct ProductionAdminView {
    pub id: Uuid,
    pub title: String,
    pub description: String,
    pub price: Decimal,
    pub package_series: Uuid,
    pub package_amount: i32,
    pub visible_to: i32,
    pub is_private: bool,
    pub limit_to_extra_group: i32,
    pub package_id: i64,              // Master package ID
    pub package_version: i32,         // Master package version
    pub package_available_group: i32, // Access control from package
    // ... additional package details
}

Purpose: Provides administrators with complete production metadata including internal IDs, version information, and access control settings.

Admin Management of Production

Administrators manage productions through the ManageService in the shop module. All operations require appropriate AdminRole permissions and are logged in the audit system.

Available Operations

1. Create Production

Operation: CreateProduction

pub struct CreateProduction {
    pub title: String,
    pub description: String,
    pub price: Decimal,
    pub package_series: Uuid,          // Must reference existing series
    pub package_amount: i32,           // Quantity to deliver
    pub visible_to: i32,               // User group visibility
    pub is_private: bool,              // Private production flag
    pub limit_to_extra_group: i32,     // Extra group requirement
}

Requirements:

  • Package series must exist in the telecom module
  • Package series must have a master package (is_master = true)
  • Admin must have Moderator or SuperAdmin role
  • All fields are mandatory except the private/extra_group can be zero if not private

Workflow:

  1. Validate package series exists and has master package
  2. Create production record with UUID
  3. Production defaults to on_sale = true
  4. Logged in admin audit system

2. Delete Production

Operation: DeleteProduction

pub struct DeleteProduction {
    pub id: Uuid,  // Production ID to delete
}

Requirements:

  • Production must exist
  • Admin must have Moderator or SuperAdmin role

Important Notes:

  • Soft delete behavior: Production is removed from catalog but existing orders referencing it remain valid
  • No cascade deletion: Deleting a production does NOT delete its associated package series
  • Impact: Users can no longer purchase this production, but already-purchased orders are unaffected

3. List Productions

Operation: ListProductions

// No input parameters - returns all productions

Returns: Vec<ProductionAdminView> with complete production details including master package information

Use Cases:

  • Catalog management dashboards
  • Production inventory audits
  • Package version tracking
  • Access control verification

Reference to Version Control of Packages

Productions are tightly integrated with the Version Control of Packages system. Understanding this relationship is crucial for managing service offerings.

How Productions Use Package Versions

When a user purchases a production:

  1. At Purchase Time: The system looks up the master package of the referenced package series
  2. Version Snapshot: The specific package version (master at that moment) is recorded in the order
  3. Queue Insertion: Package queue items reference the specific package ID, not the series
  4. Version Isolation: If the master package changes later, existing purchases are unaffected

Example: Package Version Evolution

Timeline:

Day 1: Create Production "Monthly Premium"
  └─> References package_series: abc-123
  └─> Master package: version 1 (100GB traffic, $10)

Day 15: User A purchases "Monthly Premium"
  └─> Receives: package version 1 (100GB)

Day 30: Marketing updates package series
  └─> New master package: version 2 (200GB traffic, same $10 price)
  └─> Old version 1: is_master = false (preserved for existing users)

Day 45: User B purchases "Monthly Premium"
  └─> Receives: package version 2 (200GB)

Day 60: Both users' services:
  └─> User A: Still has 100GB (version 1) - not affected by update
  └─> User B: Has 200GB (version 2) - received new version

Version Control Best Practices for Productions

  1. Price Adjustments: Update production price separately from package content
  2. Content Updates: Create new package versions to change service parameters
  3. Catalog Refresh: Existing productions automatically deliver new master versions
  4. Rollback Strategy: Keep old package versions available; delete and recreate production if needed
  5. Testing: Verify master package is correct before major version changes

See Version Control of Packages for detailed information about:

  • Creating new package versions
  • Promoting packages to master
  • Version change vs. non-version change edits
  • Admin package management operations

Relationship Flow: Production → Package → Package Queue → Order → Node Client

Understanding how these components interconnect is essential for system comprehension and troubleshooting.

Component Relationships

┌──────────────┐      references      ┌──────────────┐      has master      ┌──────────────┐
│              │─────────────────────>│   Package    │─────────────────────>│   Package    │
│  Production  │                       │    Series    │                       │   (Master)   │
│              │                       │              │                       │              │
└──────────────┘                       └──────────────┘                       └──────────────┘
       │                                                                              │
       │                                                                              │ defines
       │ purchase creates                                                             │ service
       │                                                                              │ parameters
       ▼                                                                              ▼
┌──────────────┐      delivers        ┌──────────────┐      references      ┌──────────────┐
│              │─────────────────────>│   Package    │─────────────────────>│   Package    │
│    Order     │                       │ Queue Item   │                       │              │
│              │                       │              │                       │              │
└──────────────┘                       └──────────────┘                       └──────────────┘
       │                                      │                                       │
       │                                      │                                       │ controls
       │                                      │ activates                             │ access
       │                                      │ for user                              │
       │                                      ▼                                       ▼
┌──────────────┐                       ┌──────────────┐      filtered by     ┌──────────────┐
│   Payment    │                       │ Active       │─────────────────────>│ Node Client  │
│   Status     │                       │ Package      │                       │ Access       │
│              │                       │              │                       │              │
└──────────────┘                       └──────────────┘                       └──────────────┘

Detailed Relationship Flow

1. Production → Order

When: User initiates purchase
Process:

// User browses available productions
ListVisibleProductions { user_group, extra_groups }
  └─> Returns productions matching access control

// User creates order
CreateOrder { user_id, production_id, coupon_id }
  └─> Creates UserOrder with status: Unpaid
  └─> Records production reference
  └─> Applies coupon discount if provided
  └─> Emits OrderCreatedEvent

Data Flow:

  • Production ID stored in order.production
  • Production price becomes order.total_amount (after coupon)
  • Order status starts as Unpaid

2. Order → Payment → Package Queue

When: User completes payment
Process:

// Payment via ePay gateway
PayOrderWithEpay { callback }
  └─> Validates payment signature
  └─> Updates order: status = Paid, paid_at = NOW()
  └─> Emits OrderPaidEvent

// OR payment via account balance
PayOrderWithBalance { user_id, order_id }
  └─> Deducts user balance
  └─> Updates order: status = Paid, paid_at = NOW()
  └─> Emits OrderPaidEvent

Data Flow:

  • OrderPaidEvent published to RabbitMQ exchange shop with routing key order_paid
  • Market module consumes this event for affiliate rewards
  • Note: Current codebase shows OrderPaidEvent is published but the actual delivery hook that creates package queue items is not yet visible in the telecom module hooks. This delivery mechanism needs to be implemented or is handled through a separate service/cron job.

Expected Delivery Flow (to be implemented):

// Expected hook (not found in current codebase)
consume OrderPaidEvent:
  1. Query order details (production_id, user_id)
  2. Query production (package_series, package_amount)
  3. Find master package in package_series
  4. Create package queue items:
     CreateQueueItems {
         user_id: order.user,
         package_id: master_package.id,  // Specific version
         by_order: Some(order.id),
         amount: production.package_amount
     }
  5. Trigger package queue push event
  6. Update order: status = Delivered

3. Package Queue → Active Package

When: Package queue items are created
Process:

// Package queue push event triggers activation
PackageQueuePushEvent emitted
  └─> TelecomPackageQueueHook consumes event
  └─> process_package_queue_push(transaction, user_id)
      └─> Check if user has active package
      └─> If no active package:
          - Find oldest queued package (ORDER BY created_at)
          - Activate: status = Active, activated_at = NOW()
          - Emit PackageActivateEvent

Data Flow:

  • Package queue item status transitions: InQueueActive
  • Only ONE package per user can be Active at a time
  • Remaining packages wait in queue (FIFO order)

Queue States:

  • InQueue: Waiting to be activated
  • Active: Currently providing service to user
  • Consumed: Expired due to time or traffic limit
  • Cancelled: Refunded or cancelled by admin

4. Active Package → Node Client Access

When: User requests node list or subscription link
Process:

// User lists available nodes
ListMyNodes { user_id }
  └─> FindActiveAvailableGroup { user_id }
      └─> Query active package
      └─> Extract package.available_group
  └─> ListUserNodeClients { group }
      └─> SELECT * FROM node_client
          WHERE available_groups @> [group]
      └─> Returns accessible node clients

Access Control Logic:

-- Node client access check
SELECT * FROM "telecom"."node_client"
WHERE available_groups && ARRAY[user_active_package.available_group]

Data Flow:

  • Active package defines user’s available_group (e.g., group 1 = Premium)
  • Node clients have available_groups array (e.g., [1, 2] = Premium and Standard users)
  • User can access node client if their package group is in node’s group array
  • No active package = empty node list (user cannot connect)

5. Package Expiration → Queue Advancement

When: Package expires (time or traffic limit)
Process:

// Time-based expiration (cron job)
FindExpirePackageByTimeBefore { time: NOW() }
  └─> For each expired package:
      └─> Emit PackageExpiringEvent { reason: Time }

// Usage-based expiration (billing)
RecordPackageUsage { user_id, upload, download }
  └─> If usage >= traffic_limit + adjust_quota:
      └─> Update status: Consumed
      └─> Emit PackageExpiringEvent { reason: Usage }

// Queue advancement (hook)
PackageExpiringEvent consumed
  └─> TelecomPackageQueueHook processes:
      └─> Find next queued package
      └─> If found: Activate next package
      └─> If none: Emit AllPackageExpiredEvent

Data Flow:

  • Current package: ActiveConsumed
  • Next package: InQueueActive
  • User’s node access switches to new package’s available_group
  • If no packages remain: User loses node access

Complete Purchase-to-Access Example

Scenario: User purchases “Monthly Premium” production

Step 1: Purchase
  User: "Buy Monthly Premium ($30, 3 months)"
  └─> Production {
        title: "Monthly Premium",
        price: $30,
        package_series: series-uuid-123,
        package_amount: 3
      }
  └─> Order created (Unpaid)

Step 2: Payment
  User: "Pay with AliPay"
  └─> Payment gateway callback
  └─> Order status: Unpaid → Paid
  └─> OrderPaidEvent emitted

Step 3: Delivery (Expected Implementation)
  OrderPaidEvent consumed
  └─> Find master package of series-uuid-123
      └─> Package {
            id: 12345,
            version: 2,
            available_group: 1 (Premium),
            traffic_limit: 100GB,
            expire_duration: 30 days
          }
  └─> Create 3 package queue items:
      └─> Item 1: status = Active (immediately activated)
      └─> Item 2: status = InQueue
      └─> Item 3: status = InQueue
  └─> Order status: Paid → Delivered

Step 4: Service Access
  User: "Show my nodes"
  └─> Query active package: Item 1 (package 12345)
  └─> Extract available_group: 1
  └─> Query node clients: WHERE available_groups @> [1]
  └─> Returns: Premium tier nodes
  User: "Generate subscription link"
  └─> Generates config for premium nodes
  └─> User can now connect

Step 5: Package Lifecycle (Day 30)
  System: "Package 1 expired (30 days)"
  └─> Item 1: Active → Consumed
  └─> Item 2: InQueue → Active (auto-activated)
  └─> User continues service with Item 2
  └─> Still has Item 3 waiting in queue

Step 6: All Packages Consumed (Day 90)
  System: "Package 3 expired"
  └─> Item 3: Active → Consumed
  └─> No more items in queue
  └─> AllPackageExpiredEvent emitted
  └─> User loses access to nodes (need to repurchase)

Node Client Relationship

Node clients use the active package’s available_group for access control. This creates a seamless flow from purchase to service access:

Access Control Chain:

  1. User purchases Production → receives Packages
  2. Package activation → defines available_group
  3. Node Client filtering → matches available_groups
  4. User connection → accesses filtered nodes

Example Group Mapping:

Package Groups:
- Group 1: Premium ($30/month, 200GB, premium nodes)
- Group 2: Standard ($15/month, 100GB, standard nodes)
- Group 3: Budget ($8/month, 50GB, budget nodes)

Node Client Configuration:
- "🇺🇸 US Premium": available_groups = [1]
- "🇺🇸 US Standard": available_groups = [1, 2]
- "🇸🇬 SG Budget": available_groups = [1, 2, 3]

User with Group 1 (Premium) package:
  ✅ Can access: US Premium, US Standard, SG Budget

User with Group 2 (Standard) package:
  ❌ Cannot access: US Premium
  ✅ Can access: US Standard, SG Budget

User with Group 3 (Budget) package:
  ❌ Cannot access: US Premium, US Standard
  ✅ Can access: SG Budget only

See Node Client for detailed information about node access control and configuration.

Implementation Notes for Developers

Current Implementation Status

Implemented:

  • ✅ Production CRUD operations (Create, Delete, List)
  • ✅ Production access control (user groups, private productions)
  • ✅ Order creation with production reference
  • ✅ Payment processing with OrderPaidEvent emission
  • ✅ Package queue system with activation logic
  • ✅ Node client access control based on active package

Needs Implementation/Verification:

  • ⚠️ Order delivery hook: The mechanism that consumes OrderPaidEvent and creates package queue items is not visible in the current codebase. This critical component needs to be implemented or located.
  • ⚠️ Order status update: Automatic transition from Paid to Delivered after package delivery
  • ⚠️ Error handling: What happens if package delivery fails after payment?
  • ⚠️ Refund workflow: How to handle package queue items when orders are refunded?

Expected Delivery Hook Implementation

Location: Should be in either:

  • modules/shop/src/hooks/delivery.rs (new file)
  • modules/telecom/src/hooks/order.rs (new file)
  • Integration service in server/src/

Pseudocode:

// File: modules/shop/src/hooks/delivery.rs (suggested)
pub struct ShopDeliveryHook {
    pub telecom_db: DatabaseProcessor,  // Access to telecom DB
    pub shop_db: DatabaseProcessor,     // Access to shop DB
    pub mq: AmqpPool,
}

impl AmqpMessageProcessor<OrderPaidEvent> for ShopDeliveryHook {
    const QUEUE: &'static str = "helium_shop_order_delivery";
}

impl Processor<OrderPaidEvent, Result<(), Error>> for ShopDeliveryHook {
    async fn process(&self, event: OrderPaidEvent) -> Result<(), Error> {
        // 1. Fetch order and production details
        let order = self.shop_db.process(FindOrderByIdOnly {
            order_id: event.order_id
        }).await?.ok_or(Error::NotFound)?;

        let production = self.shop_db.process(FindProductionById {
            id: order.production
        }).await?.ok_or(Error::NotFound)?;

        // 2. Find master package of the series
        let package = self.telecom_db.process(FindMasterPackageBySeries {
            series: production.package_series
        }).await?.ok_or(Error::NotFound)?;

        // 3. Create package queue items
        let items = self.telecom_db.process(CreateQueueItems {
            user_id: event.user_id,
            package_id: package.id,
            by_order: Some(event.order_id),
            amount: production.package_amount,
        }).await?;

        // 4. Emit package queue push event
        PackageQueuePushEvent {
            item_ids: items.iter().map(|i| i.id).collect(),
            user_id: event.user_id,
            package_id: package.id,
            pushed_at: OffsetDateTime::now_utc().unix_timestamp() as u64,
        }.send(&self.mq).await?;

        // 5. Update order status to delivered
        self.shop_db.process(UpdateOrderDelivered {
            order_id: event.order_id
        }).await?;

        Ok(())
    }
}

Testing Checklist

When implementing or verifying the delivery mechanism:

  1. Happy Path:

    • User purchases production → order created
    • User pays order → OrderPaidEvent emitted
    • Delivery hook triggered → package queue items created
    • First package auto-activated → user can access nodes
    • Order status updated to Delivered
  2. Edge Cases:

    • Package series has no master package → error handling
    • Production deleted after order created but before payment
    • Duplicate payment callbacks (idempotency)
    • User already has active package → new packages queue correctly
  3. Error Recovery:

    • Delivery fails → order remains Paid (manual intervention needed)
    • Partial delivery → some items created, transaction rollback
    • Event replay → idempotent delivery (check by_order reference)

Best Practices

  1. Production Naming: Use clear, descriptive titles that indicate:

    • Service tier (Premium, Standard, Budget)
    • Duration/quantity (Monthly, Quarterly, Annual)
    • Special features (High-speed, Unlimited, etc.)
  2. Price Management:

    • Keep production prices stable
    • Use coupons for temporary discounts
    • Create new productions for permanent price changes
  3. Access Control:

    • Use visible_to for tier-based catalog (free users, paid users, VIP)
    • Use is_private + limit_to_extra_group for special promotions or corporate accounts
    • Set on_sale = false to temporarily hide productions without deletion
  4. Package Amount Strategy:

    • package_amount = 1: Pay-per-period (most common)
    • package_amount = 3: Quarterly bundles (slight discount)
    • package_amount = 12: Annual subscriptions (significant discount)
    • Higher amounts = fewer transactions, better user retention
  5. Version Control Integration:

    • Review current master package before creating production
    • Coordinate with telecom team when updating master packages
    • Document package version changes that affect existing productions
    • Consider creating new production for major service upgrades
  6. Audit Trail:

    • All production operations are logged via admin audit system
    • Track which admin created/deleted productions
    • Monitor order counts per production for popularity metrics
    • Review production-to-package-version history for customer support

See Also: