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
-
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.
-
Quantity Control:
package_amountspecifies how many packages from the series the user receives. For example:package_amount = 1: Single subscription periodpackage_amount = 3: Three subscription periods (stacked in package queue)package_amount = 12: Annual subscription with 12 monthly packages
-
Access Control Layers:
- Basic visibility:
visible_todetermines which user group can see the production - Private productions: When
is_private = true, only users withlimit_to_extra_groupin theiruser_extra_groupscan access it - On-sale status:
on_salecontrols whether the production is currently purchasable
- Basic visibility:
-
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
ModeratororSuperAdminrole - All fields are mandatory except the private/extra_group can be zero if not private
Workflow:
- Validate package series exists and has master package
- Create production record with UUID
- Production defaults to
on_sale = true - 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
ModeratororSuperAdminrole
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:
- At Purchase Time: The system looks up the master package of the referenced package series
- Version Snapshot: The specific package version (master at that moment) is recorded in the order
- Queue Insertion: Package queue items reference the specific package ID, not the series
- 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
- Price Adjustments: Update production price separately from package content
- Content Updates: Create new package versions to change service parameters
- Catalog Refresh: Existing productions automatically deliver new master versions
- Rollback Strategy: Keep old package versions available; delete and recreate production if needed
- 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:
OrderPaidEventpublished to RabbitMQ exchangeshopwith routing keyorder_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:
InQueue→Active - Only ONE package per user can be
Activeat a time - Remaining packages wait in queue (FIFO order)
Queue States:
InQueue: Waiting to be activatedActive: Currently providing service to userConsumed: Expired due to time or traffic limitCancelled: 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_groupsarray (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:
Active→Consumed - Next package:
InQueue→Active - 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:
- User purchases Production → receives Packages
- Package activation → defines available_group
- Node Client filtering → matches available_groups
- 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
OrderPaidEventand 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
PaidtoDeliveredafter 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:
-
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
-
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
-
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
-
Production Naming: Use clear, descriptive titles that indicate:
- Service tier (Premium, Standard, Budget)
- Duration/quantity (Monthly, Quarterly, Annual)
- Special features (High-speed, Unlimited, etc.)
-
Price Management:
- Keep production prices stable
- Use coupons for temporary discounts
- Create new productions for permanent price changes
-
Access Control:
- Use
visible_tofor tier-based catalog (free users, paid users, VIP) - Use
is_private+limit_to_extra_groupfor special promotions or corporate accounts - Set
on_sale = falseto temporarily hide productions without deletion
- Use
-
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
-
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
-
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:
- Version Control of Packages - Package versioning system
- Package Queue - Package lifecycle management
- Node Client - Access control and node configuration
- Order System - Complete order lifecycle
- Ordering Flow - Purchase workflow diagrams