feat: Admin定价管理界面 + 定价DB配置化 (P2)
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { InjectModel } from '@nestjs/mongoose'
|
||||
import { Model } from 'mongoose'
|
||||
import { SiteConfig, SiteConfigDocument } from './site-config.schema'
|
||||
|
||||
interface PricingConfig {
|
||||
interview: { pricePerSession: number; creditsPerPurchase: number }
|
||||
resumeOptimize: { freeLimit: number; pricePerOptimize: number; creditsPerPurchase: number }
|
||||
resumeDownload: { pricePerDownload: number; creditsPerPurchase: number }
|
||||
plans: {
|
||||
growth: { price: number; durationDays: number; credits: { interview: number; resumeOptimize: number; resumeDownload: number }; features: string[] }
|
||||
sprint: { price: number; durationDays: number; credits: { interview: number; resumeOptimize: number; resumeDownload: number }; features: string[] }
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_PRICING: PricingConfig = {
|
||||
interview: { pricePerSession: 500, creditsPerPurchase: 1 },
|
||||
resumeOptimize: { freeLimit: 3, pricePerOptimize: 300, creditsPerPurchase: 1 },
|
||||
resumeDownload: { pricePerDownload: 200, creditsPerPurchase: 1 },
|
||||
plans: {
|
||||
growth: { price: 1990, durationDays: 30, credits: { interview: 999, resumeOptimize: 20, resumeDownload: 10 }, features: [] },
|
||||
sprint: { price: 4990, durationDays: 30, credits: { interview: 999, resumeOptimize: 50, resumeDownload: 30 }, features: [] },
|
||||
},
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PricingService {
|
||||
private cache: PricingConfig | null = null
|
||||
private cacheTime = 0
|
||||
|
||||
constructor(
|
||||
@InjectModel(SiteConfig.name) private configModel: Model<SiteConfigDocument>,
|
||||
) {}
|
||||
|
||||
async getConfig(): Promise<PricingConfig> {
|
||||
// Cache for 60s
|
||||
if (this.cache && Date.now() - this.cacheTime < 60000) {
|
||||
return this.cache
|
||||
}
|
||||
try {
|
||||
const doc = await this.configModel.findOne({ key: 'pricing' }).exec()
|
||||
if (doc?.value) {
|
||||
this.cache = this.mergeDefaults(doc.value)
|
||||
this.cacheTime = Date.now()
|
||||
return this.cache
|
||||
}
|
||||
} catch {}
|
||||
return DEFAULT_PRICING
|
||||
}
|
||||
|
||||
invalidateCache() {
|
||||
this.cache = null
|
||||
}
|
||||
|
||||
private mergeDefaults(value: any): PricingConfig {
|
||||
return {
|
||||
interview: { ...DEFAULT_PRICING.interview, ...value?.interview },
|
||||
resumeOptimize: { ...DEFAULT_PRICING.resumeOptimize, ...value?.resumeOptimize },
|
||||
resumeDownload: { ...DEFAULT_PRICING.resumeDownload, ...value?.resumeDownload },
|
||||
plans: {
|
||||
growth: { ...DEFAULT_PRICING.plans.growth, ...value?.plans?.growth, credits: { ...DEFAULT_PRICING.plans.growth.credits, ...value?.plans?.growth?.credits } },
|
||||
sprint: { ...DEFAULT_PRICING.plans.sprint, ...value?.plans?.sprint, credits: { ...DEFAULT_PRICING.plans.sprint.credits, ...value?.plans?.sprint?.credits } },
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user