Explorar o código

fix: 移除单次限制,只保留月度总配额限制

- 移除 maxMinutesPerGeneration 单次限制配置
- 用户可一次生成任意长度内容
- 只限制月度总配额
- 超额部分按零售价计费
MyFramework User hai 4 meses
pai
achega
c1589c11cf
Modificáronse 1 ficheiros con 20 adicións e 76 borrados
  1. 20 76
      server/src/modules/subscription/subscription.service.ts

+ 20 - 76
server/src/modules/subscription/subscription.service.ts

@@ -163,30 +163,24 @@ export const DEFAULT_PLANS = [
     priceYearly: 0,
     description: '适合轻度体验',
     features: JSON.stringify([
-      '每天3次生成',
       '每月10分钟音频',
-      '每次最多2分钟',
       '基础音色5种',
       '标准音质'
     ]),
     isRecommended: false,
     sortOrder: 0,
     dailyGenerations: 3,
-    perGenerationLimit: 300,       // ~2分钟音频
+    perGenerationLimit: 10000,
     monthlyTokens: 1500,
-    monthlyMinutes: 10,            // 音频时长配额
+    monthlyMinutes: 10,
     yearlyTokens: null,
     voiceOptions: 5,
     audioQuality: 'standard',
     apiAccess: false,
     batchProcessing: false,
     teamManagement: false,
-    overageEnabled: false,        // 不支持超出配额
-    overagePrice: 0,
-    pricing: {
-      monthlyPricePerMinute: 5.0,  // ¥5/分钟(包月)
-      overagePricePerMinute: null  // 不支持按量
-    }
+    overageEnabled: false,
+    overagePrice: 0
   },
   {
     name: '入门版',
@@ -195,9 +189,7 @@ export const DEFAULT_PLANS = [
     priceYearly: 190,
     description: '适合轻度使用',
     features: JSON.stringify([
-      '每天10次生成',
       '每月30分钟音频',
-      '每次最多5分钟',
       '全部音色',
       '高清音质',
       '超出¥8/分钟'
@@ -205,7 +197,7 @@ export const DEFAULT_PLANS = [
     isRecommended: false,
     sortOrder: 1,
     dailyGenerations: 10,
-    perGenerationLimit: 750,       // ~5分钟音频
+    perGenerationLimit: 10000,
     monthlyTokens: 4500,
     monthlyMinutes: 30,
     yearlyTokens: null,
@@ -215,11 +207,7 @@ export const DEFAULT_PLANS = [
     batchProcessing: false,
     teamManagement: false,
     overageEnabled: true,
-    overagePrice: 8.0,            // 超出¥8/分钟(零售价)
-    pricing: {
-      monthlyPricePerMinute: 5.0,  // ¥5/分钟(批发)
-      overagePricePerMinute: 8.0   // ¥8/分钟(零售)
-    }
+    overagePrice: 8.0
   },
   {
     name: '专业版',
@@ -228,9 +216,7 @@ export const DEFAULT_PLANS = [
     priceYearly: 590,
     description: '适合内容创作者 ⭐推荐',
     features: JSON.stringify([
-      '无限制生成',
       '每月100分钟音频',
-      '每次最多10分钟',
       '全部音色+定制音色',
       '高清音质',
       '超出¥7/分钟'
@@ -238,7 +224,7 @@ export const DEFAULT_PLANS = [
     isRecommended: true,
     sortOrder: 2,
     dailyGenerations: -1,
-    perGenerationLimit: 1500,      // ~10分钟音频
+    perGenerationLimit: 10000,
     monthlyTokens: 15000,
     monthlyMinutes: 100,
     yearlyTokens: null,
@@ -248,11 +234,7 @@ export const DEFAULT_PLANS = [
     batchProcessing: false,
     teamManagement: false,
     overageEnabled: true,
-    overagePrice: 7.0,            // 超出¥7/分钟
-    pricing: {
-      monthlyPricePerMinute: 5.0,
-      overagePricePerMinute: 7.0
-    }
+    overagePrice: 7.0
   },
   {
     name: '旗舰版',
@@ -261,9 +243,7 @@ export const DEFAULT_PLANS = [
     priceYearly: 1990,
     description: '适合重度使用',
     features: JSON.stringify([
-      '无限制生成',
       '每月400分钟音频',
-      '每次最多20分钟',
       '全部音色+定制音色',
       '高清音质',
       'VIP优先队列',
@@ -272,7 +252,7 @@ export const DEFAULT_PLANS = [
     isRecommended: false,
     sortOrder: 3,
     dailyGenerations: -1,
-    perGenerationLimit: 3000,      // ~20分钟音频
+    perGenerationLimit: 10000,
     monthlyTokens: 60000,
     monthlyMinutes: 400,
     yearlyTokens: null,
@@ -282,11 +262,7 @@ export const DEFAULT_PLANS = [
     batchProcessing: false,
     teamManagement: false,
     overageEnabled: true,
-    overagePrice: 6.0,            // 超出¥6/分钟
-    pricing: {
-      monthlyPricePerMinute: 5.0,
-      overagePricePerMinute: 6.0
-    }
+    overagePrice: 6.0
   },
   {
     name: '企业版',
@@ -295,11 +271,8 @@ export const DEFAULT_PLANS = [
     priceYearly: 5990,
     description: '适合企业用户',
     features: JSON.stringify([
-      '无限制生成',
       '每月1500分钟音频',
-      '每次最多60分钟',
       '全部功能',
-      '高清音质',
       '专属技术支持',
       '批量处理',
       '团队管理',
@@ -308,7 +281,7 @@ export const DEFAULT_PLANS = [
     isRecommended: false,
     sortOrder: 4,
     dailyGenerations: -1,
-    perGenerationLimit: 9000,      // ~60分钟音频
+    perGenerationLimit: 10000,
     monthlyTokens: 225000,
     monthlyMinutes: 1500,
     yearlyTokens: null,
@@ -318,11 +291,7 @@ export const DEFAULT_PLANS = [
     batchProcessing: true,
     teamManagement: true,
     overageEnabled: true,
-    overagePrice: 5.0,            // 超出¥5/分钟(最低零售价)
-    pricing: {
-      monthlyPricePerMinute: 5.0,
-      overagePricePerMinute: 5.0
-    }
+    overagePrice: 5.0
   }
 ];
 
@@ -553,14 +522,8 @@ export const AUDIO_BILLING_CONFIG = {
   
   // 定价策略:包月=批发价,按量=零售价
   pricing: {
-    // 包月(批发价)
-    monthly: {
-      basePricePerMinute: 5.0,  // ¥5/分钟(30%利润)
-    },
-    // 按量(零售价)
-    payAsYouGo: {
-      pricePerMinute: 8.0,     // ¥8/分钟(100%利润)
-    }
+    monthly: 5.0,     // 包月批发价:¥5/分钟
+    overage: 8.0      // 超额零售价:¥8/分钟
   },
   
   // 语速配置(字/分钟)
@@ -575,22 +538,13 @@ export const AUDIO_BILLING_CONFIG = {
     4: 1500    // 企业版:1500分钟/月
   },
   
-  // 各等级超出配额单价(元/分钟)
+  // 各等级超出配额单价(元/分钟),null=不支持超出
   overagePrice: {
     0: null,       // 免费版:不支持超出
     1: 8.0,        // 入门版:¥8/分钟
     2: 7.0,        // 专业版:¥7/分钟
     3: 6.0,        // 旗舰版:¥6/分钟
-    4: 5.0         // 企业版:¥5/分钟
-  },
-  
-  // 各等级单次生成最大时长(分钟)
-  maxMinutesPerGeneration: {
-    0: 2,     // 免费版:2分钟/次
-    1: 5,     // 入门版:5分钟/次
-    2: 10,    // 专业版:10分钟/次
-    3: 20,    // 旗舰版:20分钟/次
-    4: 60     // 企业版:60分钟/次
+    4: 5.0         // 企业版:¥5/分钟(最低价)
   }
 };
 
@@ -624,7 +578,7 @@ export function calculateAudioCost(
   const overageMinutes = Math.max(0, audioMinutes - inQuotaMinutes);
   
   // 配额内价格(包月批发价)
-  const inQuotaPrice = inQuotaMinutes * AUDIO_BILLING_CONFIG.pricing.monthly.basePricePerMinute;
+  const inQuotaPrice = inQuotaMinutes * AUDIO_BILLING_CONFIG.pricing.monthly;
   
   // 超额价格(按量零售价)
   const overagePrice = overageMinutes > 0 && overagePricePerMinute !== null
@@ -695,29 +649,19 @@ export async function getUserAudioBalance(userId: number) {
 }
 
 // 检查音频生成配额
+// 检查音频生成配额(只检查月度总配额,无单次限制)
 export async function checkAudioQuota(
   userId: number,
   textLength: number
 ) {
   const audioMinutes = calculateAudioDuration(textLength);
   const balance = await getUserAudioBalance(userId);
-  const maxMinutes = AUDIO_BILLING_CONFIG.maxMinutesPerGeneration[balance.memberLevel] || 2;
-  
-  // 检查单次限制
-  if (audioMinutes > maxMinutes) {
-    return {
-      allowed: false,
-      reason: `单次生成不能超过${maxMinutes}分钟`,
-      audioMinutes,
-      maxMinutes
-    };
-  }
   
-  // 检查月度配额
+  // 免费版不支持超出配额
   if (balance.remainingMinutes < audioMinutes && !balance.overageEnabled) {
     return {
       allowed: false,
-      reason: `音频时长不足,本月配额已用完`,
+      reason: `音频时长不足,本月配额已用完(${balance.totalMinutes}分钟)`,
       audioMinutes,
       remainingMinutes: balance.remainingMinutes,
       totalMinutes: balance.totalMinutes,