feat: billing system — service plans, wallet, cost calculation
- Backend: billing module with ServicePlan, PricingRule, Wallet, WalletTransaction entities - Admin can CRUD service plans with hourly/monthly/yearly billing cycles - Each plan has flexible pricing rules (base_fee, cpu, memory, storage, db addon) - Wallet system: auto-created per user, charge, deduct, refund, transaction history - Cost calculation endpoint: cross-cycle conversion (hourly*720=monthly, monthly*12=yearly) - Frontend: admin billing management page (/dashboard/admin/billing) - Frontend: user wallet page with balance, quick-charge, transaction history - Deploy page: cost breakdown shown in Review step (hourly/monthly/yearly) - Navigation: Wallet link for users, Billing Plans link for admins
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import { IsString, IsEnum, IsOptional, IsBoolean, IsNumber, IsArray, ValidateNested, Min } from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { BillingCycle, PricingResourceType } from '../../common/enums';
|
||||
|
||||
export class CreatePricingRuleDto {
|
||||
@ApiProperty({ enum: PricingResourceType })
|
||||
@IsEnum(PricingResourceType)
|
||||
resourceType: PricingResourceType;
|
||||
|
||||
@ApiProperty({ example: 5000, description: 'Unit price in Toman' })
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
unitPrice: number;
|
||||
|
||||
@ApiPropertyOptional({ example: 'CPU per core per hour' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class CreateServicePlanDto {
|
||||
@ApiProperty({ example: 'Node.js Basic' })
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@ApiPropertyOptional({ example: 'Basic plan for Node.js applications' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
|
||||
@ApiProperty({ enum: BillingCycle })
|
||||
@IsEnum(BillingCycle)
|
||||
billingCycle: BillingCycle;
|
||||
|
||||
@ApiProperty({ type: [CreatePricingRuleDto] })
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => CreatePricingRuleDto)
|
||||
pricingRules: CreatePricingRuleDto[];
|
||||
}
|
||||
|
||||
export class UpdateServicePlanDto {
|
||||
@ApiPropertyOptional()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
|
||||
@ApiPropertyOptional({ enum: BillingCycle })
|
||||
@IsOptional()
|
||||
@IsEnum(BillingCycle)
|
||||
billingCycle?: BillingCycle;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
|
||||
@ApiPropertyOptional({ type: [CreatePricingRuleDto] })
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => CreatePricingRuleDto)
|
||||
pricingRules?: CreatePricingRuleDto[];
|
||||
}
|
||||
|
||||
export class ChargeWalletDto {
|
||||
@ApiProperty({ example: 50000, description: 'Amount to charge in Toman' })
|
||||
@IsNumber()
|
||||
@Min(1000)
|
||||
amount: number;
|
||||
|
||||
@ApiPropertyOptional({ example: 'Manual top-up' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class CalculateCostDto {
|
||||
@ApiProperty({ example: 'nodejs' })
|
||||
@IsString()
|
||||
runtime: string;
|
||||
|
||||
@ApiProperty({ example: 'postgresql' })
|
||||
@IsString()
|
||||
databaseType: string;
|
||||
|
||||
@ApiProperty({ example: '500m', description: 'CPU limit' })
|
||||
@IsString()
|
||||
cpuLimit: string;
|
||||
|
||||
@ApiProperty({ example: '512Mi', description: 'Memory limit' })
|
||||
@IsString()
|
||||
memoryLimit: string;
|
||||
|
||||
@ApiProperty({ example: 1, description: 'Number of replicas' })
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
replicas: number;
|
||||
|
||||
@ApiProperty({ example: '1Gi', description: 'Database storage size' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
dbStorageSize?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user