Parcourir la source

feat: 重构为音频时长计费系统

核心变更:按音频时长(分钟)计价,用户直接知道'这段音频要花多少钱'

计费公式:
- 音频时长 = 字数 ÷ 150(语速)
- 生成费用 = 音频时长 × 单价(元/分钟)

新套餐定价:
- 免费版: ¥0,30分钟/月
- 入门版: ¥9.9,100分钟/月
- 专业版: ¥49,500分钟/月 ⭐推荐
- 旗舰版: ¥199,2000分钟/月

新增:
- docs/音频时长计费系统设计.md
- AUDIO_BILLING_CONFIG 配置
- calculateAudioDuration() 计算音频时长
- calculateAudioCost() 计算生成费用
- getUserAudioBalance() 获取音频余额
- checkAudioQuota() 检查配额
- consumeAudioMinutes() 扣除时长
- getAudioEstimate() 获取预估

数据库变更:
- User: usedAudioMinutes, subscriptionResetDate
- SubscriptionPlan: monthlyMinutes
MyFramework User il y a 4 mois
Parent
commit
f3189bff89

+ 56 - 41
agent-progress.txt

@@ -165,49 +165,64 @@ feature_list_optimize.json 所有30个功能已完成
 
 
 提交: 功能1开发完成
 提交: 功能1开发完成
 
 
-=== 2026-04-12 TTS成本分析更新 ===
-
-【TTS服务商价格对比(用户提供)】
-1. 小米 MiMo-TTS: ¥0(限时免费) ★★★★☆
-2. 百度智能云-基础版: ¥30/万字符(200万/月免费) ★★★★☆
-3. 百度智能云-精品版: ¥150/万字符 ★★★★★
-4. 阿里云 Qwen3-Flash: ¥40/万字符 ★★★★☆
-5. 阿里云 Qwen3-Plus: ¥120/万字符 ★★★★★
-6. 科大讯飞-基础版: ¥30/万字符(10万/月永久)★★★☆☆
-7. 科大讯飞-高清版: ¥100/万字符 ★★★★☆
-8. 火山引擎-标准版: ¥500/万字符(2万/应用)★★★★★
-9. 腾讯云-通用精品: ¥300/万字符 ★★★☆☆
-
-【综合成本计算】
-- AI生成成本: ¥2-4/千token
-- TTS合成成本: ¥0-50/千字符
-- 综合成本: ¥3.5-55/千字符
-
-【新定价方案】
-1. 免费版: ¥0/月,5000 Token
-   - 成本: ¥17.5,平台补贴
-   - TTS: 科大讯飞免费额度
-
-2. 入门版: ¥9.9/月,10000 Token
-   - 成本: ¥35,亏损¥25.1
-   - TTS: 百度基础版¥3/千字符
-
-3. 专业版: ¥49/月,30000 Token ⭐推荐
-   - 成本: ¥105,亏损¥56
-   - TTS: 百度精品版¥15/千字符
-
-4. 旗舰版: ¥199/月,100000 Token
-   - 成本: ¥350,建议涨价到¥399-499
-   - TTS: 火山引擎¥50/千字符
+=== 2026-04-12 音频时长计费系统重构 ===
+
+【核心变更:按音频时长计价】
+
+用户付费是为了得到音频文件,应该按音频时长计费:
+- 用户问:这段内容生成音频要多少钱?
+- 系统答:预计3分钟音频,按 ¥1/分钟 = ¥3
+
+【计费公式】
+```
+生成费用 = 音频时长(分钟)× 单价(元/分钟)
+
+估算:字数 ÷ 150(语速)= 音频时长(分钟)
+单价:
+  - 标准音质: ¥0.5/分钟
+  - 高清音质: ¥1.0/分钟
+  - 无损音质: ¥2.0/分钟
+```
+
+【新定价方案(按音频时长)】
+1. 免费版: ¥0/月
+   - 配额: 30分钟/月
+   - 单次: 2分钟
+   - 成本: ¥10.2(平台补贴)
+
+2. 入门版: ¥9.9/月
+   - 配额: 100分钟/月
+   - 单次: 5分钟
+   - 成本: ¥100(亏损¥90.1)
+
+3. 专业版: ¥49/月 ⭐推荐
+   - 配额: 500分钟/月
+   - 单次: 20分钟
+   - 成本: ¥500(亏损¥451)
+
+4. 旗舰版: ¥199/月
+   - 配额: 2000分钟/月
+   - 单次: 60分钟
+   - 成本: ¥680(建议涨价到¥699+)
 
 
 【新增文档】
 【新增文档】
-✅ docs/TTS成本分析报告.md
+✅ docs/音频时长计费系统设计.md
 
 
 【更新文件】
 【更新文件】
+✅ server/prisma/schema.prisma
+   - User模型: 添加 usedAudioMinutes, subscriptionResetDate
+   - SubscriptionPlan: 添加 monthlyMinutes 字段
+
 ✅ server/src/modules/subscription/subscription.service.ts
 ✅ server/src/modules/subscription/subscription.service.ts
-   - 添加 TTS_COST_CONFIG 配置
-   - 更新 DEFAULT_PLANS 新定价
-✅ feature_list_subscription.json
-   - 更新 pricing_plan.tiers
-   - 添加 tts_cost_basis
-   - 添加 cost_analysis
+   - AUDIO_BILLING_CONFIG: 音频时长计费配置
+   - calculateAudioDuration(): 计算音频时长
+   - calculateAudioCost(): 计算生成费用
+   - getUserAudioBalance(): 获取用户音频余额
+   - checkAudioQuota(): 检查音频配额
+   - consumeAudioMinutes(): 扣除音频时长
+   - getAudioEstimate(): 获取生成预估
+
+【待完成】
+- [ ] npx prisma db push 更新数据库
+- [ ] 更新前端预估成本UI
+- [ ] 集成到TTS生成流程

+ 434 - 0
docs/音频时长计费系统设计.md

@@ -0,0 +1,434 @@
+# 音频时长计费系统设计
+
+> 更新时间:2026-04-12
+
+## 一、计费核心原则
+
+### 🎯 计价单位:音频时长(分钟)
+
+用户付费是为了得到**音频文件**,所以应该按**音频时长**计费:
+
+```
+用户问:这个章节生成音频要多少钱?
+系统答:预计3分钟音频,按 ¥0.5/分钟 = ¥1.5
+```
+
+### 📊 成本拆解
+
+```
+总成本 = AI文本生成成本 + TTS合成成本
+
+1. AI生成成本:
+   - DeepSeek V3.2: ¥2.0/千token
+   - 约 ¥2.0/千字(1字≈1token)
+
+2. TTS合成成本(按音频时长):
+   - 基础版: ¥30/万字符 → 约¥0.05/分钟(150字/分钟语速)
+   - 精品版: ¥150/万字符 → 约¥0.25/分钟
+   - 火山引擎: ¥500/万字符 → 约¥0.83/分钟
+
+3. 综合成本(DeepSeek + 百度精品):
+   - AI生成: ¥2/千字
+   - TTS合成: ¥0.25/分钟 × 150字/分钟 = ¥0.25/千字
+   - 总成本: ¥2.25/千字 ≈ ¥0.34/分钟
+```
+
+---
+
+## 二、套餐设计(按音频时长配额)
+
+### 💰 定价方案
+
+| 套餐 | 月费 | 音频时长 | 单价折算 | 适用场景 |
+|------|------|---------|---------|---------|
+| **免费版** | ¥0 | 30分钟/月 | ¥0/分钟 | 体验引流 |
+| **入门版** | ¥9.9 | 100分钟/月 | ¥0.099/分钟 | 轻度使用 |
+| **专业版** | ¥49 | 500分钟/月 | ¥0.098/分钟 | 内容创作者 |
+| **旗舰版** | ¥199 | 2000分钟/月 | ¥0.10/分钟 | 重度/企业 |
+
+### 📐 套餐详情
+
+#### 免费版(¥0/月)
+```
+配额:30分钟音频/月
+成本:¥10.2(平台补贴)
+
+包含:
+✓ 每天3次生成
+✓ 每次最多2分钟音频(约300字)
+✓ 基础音色5种
+✓ 标准音质
+✓ 科大讯飞TTS(免费额度)
+
+计算:30分钟 × ¥0.34/分钟 = ¥10.2(平台承担)
+```
+
+#### 入门版(¥9.9/月)⭐ 转化版
+```
+配额:100分钟音频/月
+成本:¥34(AI¥20 + TTS¥14)
+
+包含:
+✓ 每天10次生成
+✓ 每次最多5分钟音频(约750字)
+✓ 全部音色
+✓ 高清音质
+✓ 百度基础版TTS
+
+计算:100分钟 × ¥0.34/分钟 = ¥34
+利润:¥9.9 - ¥34 = -¥24.1(亏损获客)
+```
+
+#### 专业版(¥49/月)⭐ 推荐
+```
+配额:500分钟音频/月
+成本:¥170(AI¥100 + TTS¥70)
+
+包含:
+✓ 无限制生成
+✓ 每次最多20分钟音频(约3000字)
+✓ 全部音色+定制音色
+✓ 无损音质
+✓ 百度精品版TTS
+✓ VIP优先队列
+
+计算:500分钟 × ¥0.34/分钟 = ¥170
+利润:¥49 - ¥170 = -¥121(大量补贴促转化)
+```
+
+#### 旗舰版(¥199/月)
+```
+配额:2000分钟音频/月
+成本:¥680(AI¥400 + TTS¥280)
+
+包含:
+✓ 无限制生成
+✓ 每次最多60分钟音频
+✓ 全部功能
+✓ 无损音质
+✓ 火山引擎豆包TTS
+✓ 专属技术支持
+✓ 批量处理
+✓ 团队管理
+
+建议定价:¥699-999/月(高端定位)
+```
+
+---
+
+## 三、按次计费(适合临时用户)
+
+### 💵 单次购买
+
+| 时长 | 基础音质 | 高清音质 | 无损音质 |
+|------|---------|---------|---------|
+| 1分钟 | ¥0.5 | ¥1.0 | ¥2.0 |
+| 5分钟 | ¥2.5 | ¥5.0 | ¥10.0 |
+| 10分钟 | ¥5.0 | ¥10.0 | ¥20.0 |
+| 30分钟 | ¥15.0 | ¥30.0 | ¥60.0 |
+| 60分钟 | ¥30.0 | ¥60.0 | ¥120.0 |
+
+### 📦 音频包购买
+
+| 包名 | 时长 | 价格 | 单价 | 折扣 |
+|------|------|------|------|------|
+| 体验包 | 10分钟 | ¥8 | ¥0.8/分钟 | - |
+| 标准包 | 50分钟 | ¥35 | ¥0.7/分钟 | 12% |
+| 大量包 | 200分钟 | ¥120 | ¥0.6/分钟 | 25% |
+| 企业包 | 1000分钟 | ¥500 | ¥0.5/分钟 | 37% |
+
+---
+
+## 四、实时成本预估
+
+### 📊 生成前预估
+
+```typescript
+// 前端显示预估成本
+async function estimateAudioCost(textLength: number, quality: string) {
+  // 估算音频时长(按150字/分钟语速)
+  const audioMinutes = Math.ceil(textLength / 150);
+  
+  // 根据音质计算单价
+  const pricePerMinute = {
+    standard: 0.5,   // ¥0.5/分钟
+    high: 1.0,      // ¥1.0/分钟
+    lossless: 2.0   // ¥2.0/分钟
+  }[quality] || 1.0;
+  
+  const totalPrice = audioMinutes * pricePerMinute;
+  
+  return {
+    textLength,
+    audioMinutes,
+    pricePerMinute,
+    totalPrice,
+    displayText: `预计${audioMinutes}分钟音频,约¥${totalPrice.toFixed(1)}`
+  };
+}
+```
+
+### 📋 预估示例
+
+| 文本字数 | 估算音频 | 标准音质 | 高清音质 | 无损音质 |
+|---------|---------|---------|---------|---------|
+| 300字 | 2分钟 | ¥1.0 | ¥2.0 | ¥4.0 |
+| 750字 | 5分钟 | ¥2.5 | ¥5.0 | ¥10.0 |
+| 1500字 | 10分钟 | ¥5.0 | ¥10.0 | ¥20.0 |
+| 3000字 | 20分钟 | ¥10.0 | ¥20.0 | ¥40.0 |
+
+---
+
+## 五、余额扣费逻辑
+
+### 🔧 实现方案
+
+```typescript
+// server/src/modules/audio/audio.service.ts
+
+export const AUDIO_BILLING_CONFIG = {
+  // 音频时长单价(按音质)
+  pricePerMinute: {
+    standard: 0.5,    // ¥0.5/分钟(基础版TTS)
+    high: 1.0,       // ¥1.0/分钟(精品版TTS)
+    lossless: 2.0    // ¥2.0/分钟(火山引擎)
+  },
+  
+  // 语速配置(字/分钟)
+  speakingRate: 150,
+  
+  // 每分钟Token消耗
+  tokensPerMinute: 200  // AI处理消耗
+};
+
+// 计算音频生成费用
+export function calculateAudioCost(
+  textLength: number,
+  quality: string = 'high'
+): {
+  audioMinutes: number;
+  pricePerMinute: number;
+  totalPrice: number;
+  tokensUsed: number;
+} {
+  const audioMinutes = Math.ceil(textLength / AUDIO_BILLING_CONFIG.speakingRate);
+  const pricePerMinute = AUDIO_BILLING_CONFIG.pricePerMinute[quality] || 1.0;
+  const totalPrice = audioMinutes * pricePerMinute;
+  const tokensUsed = audioMinutes * AUDIO_BILLING_CONFIG.tokensPerMinute;
+  
+  return {
+    audioMinutes,
+    pricePerMinute,
+    totalPrice: Math.round(totalPrice * 100) / 100,
+    tokensUsed
+  };
+}
+
+// 扣费流程
+export async function generateAudioWithBilling(
+  userId: number,
+  text: string,
+  quality: string = 'high'
+) {
+  // 1. 计算费用
+  const cost = calculateAudioCost(text.length, quality);
+  
+  // 2. 检查用户余额
+  const balance = await getUserAudioBalance(userId);
+  const remainingMinutes = balance.remainingMinutes;
+  
+  if (remainingMinutes < cost.audioMinutes) {
+    throw new Error(`音频时长不足,需要${cost.audioMinutes}分钟,剩余${remainingMinutes}分钟`);
+  }
+  
+  // 3. 扣除时长
+  await prisma.audioBalance.update({
+    where: { userId },
+    data: { usedMinutes: { increment: cost.audioMinutes } }
+  });
+  
+  // 4. 记录使用
+  await prisma.audioUsage.create({
+    data: {
+      userId,
+      type: 'audio_generation',
+      minutesUsed: cost.audioMinutes,
+      textLength: text.length,
+      quality,
+      cost: cost.totalPrice
+    }
+  });
+  
+  // 5. 生成音频
+  const audioUrl = await callTTSAPI(text, quality);
+  
+  return {
+    audioUrl,
+    audioMinutes: cost.audioMinutes,
+    cost: cost.totalPrice,
+    remainingMinutes: remainingMinutes - cost.audioMinutes
+  };
+}
+```
+
+---
+
+## 六、数据库模型
+
+### 📋 Prisma Schema
+
+```prisma
+// 用户音频余额
+model AudioBalance {
+  id              Int      @id @default(autoincrement())
+  userId          Int      @unique
+  totalMinutes    Int      @default(30)    // 本月总时长
+  usedMinutes     Int      @default(0)     // 已使用时长
+  resetDate       DateTime @default(now())  // 重置日期
+  createdAt       DateTime @default(now())
+  updatedAt       DateTime @updatedAt
+  
+  user            User     @relation(fields: [userId], references: [id])
+  
+  @@map("audio_balance")
+}
+
+// 音频使用记录
+model AudioUsage {
+  id            Int      @id @default(autoincrement())
+  userId        Int
+  type          String   // 'generation' | 'download' | 'package_purchase'
+  minutesUsed   Int      // 本次使用分钟数
+  textLength    Int?     // 对应文本字数
+  quality       String   // 'standard' | 'high' | 'lossless'
+  cost          Float    // 实际费用
+  audioUrl      String?  // 生成的音频URL
+  createdAt     DateTime @default(now())
+  
+  user          User     @relation(fields: [userId], references: [id])
+  
+  @@map("audio_usage")
+}
+
+// 音频包产品
+model AudioPackage {
+  id          Int      @id @default(autoincrement())
+  name        String   // '体验包' | '标准包' | '大量包'
+  minutes     Int      // 时长(分钟)
+  price       Float    // 价格
+  discount    Float    @default(1.0)  // 折扣
+  isActive    Boolean  @default(true)
+  sortOrder   Int      @default(0)
+  createdAt   DateTime @default(now())
+  
+  @@map("audio_package")
+}
+
+// 音频包购买记录
+model AudioPackagePurchase {
+  id          Int      @id @default(autoincrement())
+  userId      Int
+  packageId   Int
+  minutes     Int      // 购买时长
+  price       Float    // 实付金额
+  status      String   @default("active")  // 'active' | 'expired' | 'used_up'
+  expireDate  DateTime // 过期时间(购买后1年)
+  createdAt   DateTime @default(now())
+  
+  package     AudioPackage @relation(fields: [packageId], references: [id])
+  user        User         @relation(fields: [userId], references: [id])
+  
+  @@map("audio_package_purchase")
+}
+```
+
+---
+
+## 七、套餐对比表
+
+| 功能 | 免费版 | 入门版 | 专业版 | 旗舰版 |
+|------|--------|--------|--------|--------|
+| **月费** | ¥0 | ¥9.9 | ¥49 | ¥199 |
+| **音频时长** | 30分钟 | 100分钟 | 500分钟 | 2000分钟 |
+| **单次最大** | 2分钟 | 5分钟 | 20分钟 | 60分钟 |
+| **每日次数** | 3次 | 10次 | 不限 | 不限 |
+| **音质** | 标准 | 高清 | 无损 | 无损 |
+| **音色数量** | 5种 | 全部 | 全部+定制 | 全部+定制 |
+| **TTS方案** | 科大讯飞 | 百度基础 | 百度精品 | 火山引擎 |
+| **VIP队列** | ❌ | ❌ | ✅ | ✅ |
+| **API访问** | ❌ | ❌ | ❌ | ✅ |
+| **技术支持** | ❌ | ❌ | ❌ | ✅ |
+| **批量处理** | ❌ | ❌ | ❌ | ✅ |
+| **团队管理** | ❌ | ❌ | ❌ | ✅ |
+
+---
+
+## 八、用户体验设计
+
+### 📱 生成前预估显示
+
+```
+┌─────────────────────────────────┐
+│  📝 文本内容                    │
+│  一键世遵处重与处重世尊无上正等 │
+│  正觉。欲令众生皆得解脱...      │
+│                                 │
+│  ⏱️ 预计生成                    │
+│  ┌─────────────────────────┐   │
+│  │   📢 5分钟  🔊 高清音质  │   │
+│  │   💰 约 ¥5.0             │   │
+│  └─────────────────────────┘   │
+│                                 │
+│  [      生成音频      ]         │
+└─────────────────────────────────┘
+```
+
+### 📊 个人中心显示
+
+```
+┌─────────────────────────────────┐
+│  🎧 我的音频配额                │
+│                                 │
+│  本月已用:45分钟 / 500分钟     │
+│  ████████░░░░░░░░░░  9%        │
+│                                 │
+│  剩余:455分钟(约75小时)       │
+│                                 │
+│  [续费套餐]  [购买音频包]        │
+└─────────────────────────────────┘
+```
+
+---
+
+## 九、定价总结
+
+### 🎯 核心公式
+
+```
+生成费用 = 音频时长(分钟)× 单价(元/分钟)
+
+单分钟成本 ≈ ¥0.34(DeepSeek + 基础TTS)
+建议售价 ≈ ¥0.5-1.0/分钟(150-300%利润率)
+```
+
+### 📋 最终定价
+
+| 套餐 | 月费 | 时长配额 | 折合单价 | 成本 | 利润 |
+|------|------|---------|---------|------|------|
+| 免费版 | ¥0 | 30分钟 | ¥0 | ¥10.2 | -¥10.2 |
+| 入门版 | ¥9.9 | 100分钟 | ¥0.099 | ¥34 | -¥24.1 |
+| 专业版 | ¥49 | 500分钟 | ¥0.098 | ¥170 | -¥121 |
+| 旗舰版 | ¥199 | 2000分钟 | ¥0.10 | ¥680 | -¥481 |
+
+> ⚠️ 注意:当前定价为获客转化策略,前期亏损运营,待用户规模扩大后可逐步提价。
+
+---
+
+## 十、下一步行动
+
+- [ ] 更新 Prisma Schema 添加音频时长相关表
+- [ ] 重写音频生成服务(按时长扣费)
+- [ ] 更新前端预估成本UI
+- [ ] 更新个人中心配额显示
+- [ ] 添加音频包购买功能
+- [ ] 测试验证完整流程

+ 52 - 49
feature_list_subscription.json

@@ -8,15 +8,22 @@
     "auth_enabled": false
     "auth_enabled": false
   },
   },
   "design_overview": {
   "design_overview": {
-    "problem": "当前系统只有简单的包月/包年会员,没有token计费系统,无法精细控制资源消耗",
-    "solution": "设计多层级套餐体系,结合包月订阅和token配额,实现精细化资源管理"
+    "problem": "当前按Token计费,用户不理解",
+    "solution": "改为按音频时长计费,用户直接知道'这段音频要花多少钱'"
+  },
+  "billing_model": {
+    "unit": "音频时长(分钟)",
+    "formula": "生成费用 = 音频时长 × 单价",
+    "speaking_rate": 150,
+    "speaking_unit": "字/分钟"
   },
   },
   "pricing_plan": {
   "pricing_plan": {
     "last_updated": "2026-04-12",
     "last_updated": "2026-04-12",
-    "tts_cost_basis": {
-      "ai_model_cost": "¥2-4/千token",
-      "tts_provider_cost": "¥0-50/千字符",
-      "total_cost_range": "¥3.5-55/千字符"
+    "billing_unit": "audio_minutes",
+    "price_per_minute": {
+      "standard": 0.5,
+      "high": 1.0,
+      "lossless": 2.0
     },
     },
     "tiers": [
     "tiers": [
       {
       {
@@ -28,22 +35,22 @@
         "description": "适合轻度体验",
         "description": "适合轻度体验",
         "features": [
         "features": [
           "每天3次生成",
           "每天3次生成",
-          "每次最多2000字",
+          "每月30分钟音频",
+          "每次最多2分钟",
           "基础音色5种",
           "基础音色5种",
-          "标准音质",
-          "科大讯飞免费额度(10万/月永久)"
+          "标准音质"
         ],
         ],
         "limits": {
         "limits": {
           "daily_generations": 3,
           "daily_generations": 3,
-          "per_generation_limit": 2000,
-          "monthly_tokens": 5000,
+          "per_generation_limit": 300,
+          "monthly_minutes": 30,
+          "monthly_tokens": 3000,
           "voice_options": 5,
           "voice_options": 5,
           "audio_quality": "standard"
           "audio_quality": "standard"
         },
         },
         "cost_analysis": {
         "cost_analysis": {
-          "token_cost": 17.5,
-          "tts_provider": "科大讯飞-基础版",
-          "profit": -17.5,
+          "minutes_cost": 10.2,
+          "profit": -10.2,
           "strategy": "平台补贴引流"
           "strategy": "平台补贴引流"
         }
         }
       },
       },
@@ -56,23 +63,23 @@
         "description": "适合日常使用",
         "description": "适合日常使用",
         "recommended": false,
         "recommended": false,
         "features": [
         "features": [
-          "每月10000 Token",
-          "每次最多5000字",
+          "每天10次生成",
+          "每月100分钟音频",
+          "每次最多5分钟",
           "全部音色",
           "全部音色",
-          "高清音质",
-          "百度基础版TTS(¥3/千字符)"
+          "高清音质"
         ],
         ],
         "limits": {
         "limits": {
           "daily_generations": 10,
           "daily_generations": 10,
-          "per_generation_limit": 5000,
-          "monthly_tokens": 10000,
+          "per_generation_limit": 750,
+          "monthly_minutes": 100,
+          "monthly_tokens": 15000,
           "voice_options": -1,
           "voice_options": -1,
           "audio_quality": "high"
           "audio_quality": "high"
         },
         },
         "cost_analysis": {
         "cost_analysis": {
-          "token_cost": 35,
-          "tts_provider": "百度智能云-基础版",
-          "profit": -25.1,
+          "minutes_cost": 100,
+          "profit": -90.1,
           "strategy": "亏损获客转化"
           "strategy": "亏损获客转化"
         }
         }
       },
       },
@@ -82,29 +89,29 @@
         "price": 49,
         "price": 49,
         "price_monthly": 49,
         "price_monthly": 49,
         "price_yearly": 490,
         "price_yearly": 490,
-        "description": "适合内容创作者",
+        "description": "适合内容创作者 ⭐推荐",
         "recommended": true,
         "recommended": true,
         "features": [
         "features": [
-          "每月30000 Token",
-          "每次最多20000字",
+          "无限制生成",
+          "每月500分钟音频",
+          "每次最多20分钟",
           "全部音色+定制音色",
           "全部音色+定制音色",
-          "高清音质",
-          "百度精品版TTS(¥15/千字符)",
+          "无损音质",
           "VIP优先队列"
           "VIP优先队列"
         ],
         ],
         "limits": {
         "limits": {
           "daily_generations": -1,
           "daily_generations": -1,
-          "per_generation_limit": 20000,
-          "monthly_tokens": 30000,
+          "per_generation_limit": 3000,
+          "monthly_minutes": 500,
+          "monthly_tokens": 75000,
           "voice_options": -1,
           "voice_options": -1,
           "audio_quality": "lossless",
           "audio_quality": "lossless",
           "api_access": false
           "api_access": false
         },
         },
         "cost_analysis": {
         "cost_analysis": {
-          "token_cost": 105,
-          "tts_provider": "百度智能云-精品版",
-          "profit": -56,
-          "strategy": "核心转化产品"
+          "minutes_cost": 500,
+          "profit": -451,
+          "strategy": "亏损转化,盈利靠续费"
         }
         }
       },
       },
       {
       {
@@ -116,19 +123,20 @@
         "description": "适合企业用户",
         "description": "适合企业用户",
         "recommended": false,
         "recommended": false,
         "features": [
         "features": [
-          "每月100000 Token",
-          "每次最多100000字",
+          "无限制生成",
+          "每月2000分钟音频",
+          "每次最多60分钟",
           "全部功能",
           "全部功能",
           "无损音质",
           "无损音质",
-          "火山引擎豆包TTS(¥50/千字符)",
           "专属技术支持",
           "专属技术支持",
           "批量处理",
           "批量处理",
           "团队管理"
           "团队管理"
         ],
         ],
         "limits": {
         "limits": {
           "daily_generations": -1,
           "daily_generations": -1,
-          "per_generation_limit": 100000,
-          "monthly_tokens": 100000,
+          "per_generation_limit": 9000,
+          "monthly_minutes": 2000,
+          "monthly_tokens": 300000,
           "voice_options": -1,
           "voice_options": -1,
           "audio_quality": "lossless",
           "audio_quality": "lossless",
           "api_access": true,
           "api_access": true,
@@ -136,18 +144,13 @@
           "team_management": true
           "team_management": true
         },
         },
         "cost_analysis": {
         "cost_analysis": {
-          "token_cost": 350,
-          "tts_provider": "火山引擎豆包",
-          "profit": -151,
-          "suggestion": "建议定价¥399-499/月",
-          "strategy": "高端产品需涨价"
+          "minutes_cost": 680,
+          "profit": -481,
+          "suggestion": "建议定价¥699-999/月",
+          "strategy": "高端定位"
         }
         }
       }
       }
-    ],
-    "token_pricing": {
-      "text_to_speech": 3.5,
-      "per_char": 0.0035
-    }
+    ]
   },
   },
   "features": [
   "features": [
     {
     {

+ 5 - 0
server/prisma/schema.prisma

@@ -20,6 +20,10 @@ model User {
   createdAt       DateTime  @default(now())
   createdAt       DateTime  @default(now())
   updatedAt       DateTime  @updatedAt
   updatedAt       DateTime  @updatedAt
 
 
+  // 音频时长配额(2026-04-12 新增)
+  usedAudioMinutes     Int       @default(0)   // 本月已使用音频分钟数
+  subscriptionResetDate DateTime?              // 配额重置日期
+
   orders          Order[]
   orders          Order[]
   playRecords     PlayRecord[]
   playRecords     PlayRecord[]
   preferences     UserPreference?
   preferences     UserPreference?
@@ -415,6 +419,7 @@ model SubscriptionPlan {
   dailyGenerations Int      @default(3) // 每日生成次数,-1表示无限制
   dailyGenerations Int      @default(3) // 每日生成次数,-1表示无限制
   perGenerationLimit Int     @default(2000) // 单次生成限制字数
   perGenerationLimit Int     @default(2000) // 单次生成限制字数
   monthlyTokens    Int      @default(10000) // 每月token配额,-1表示无限制
   monthlyTokens    Int      @default(10000) // 每月token配额,-1表示无限制
+  monthlyMinutes   Int      @default(30)   // 每月音频时长配额(分钟)
   yearlyTokens     Int?      // 每年token配额(旗舰版用)
   yearlyTokens     Int?      // 每年token配额(旗舰版用)
   voiceOptions     Int      @default(5) // 可用音色数,-1表示全部
   voiceOptions     Int      @default(5) // 可用音色数,-1表示全部
   audioQuality     String    @default("standard") // standard, high, lossless
   audioQuality     String    @default("standard") // standard, high, lossless

+ 273 - 51
server/src/modules/subscription/subscription.service.ts

@@ -154,7 +154,7 @@ export function calculateTokenCost(textLength: number, model: string = 'deepseek
   };
   };
 }
 }
 
 
-// 默认套餐配置(基于TTS成本,2026-04-12更新)
+// 默认套餐配置(基于音频时长,2026-04-12更新)
 export const DEFAULT_PLANS = [
 export const DEFAULT_PLANS = [
   {
   {
     name: '免费版',
     name: '免费版',
@@ -164,30 +164,28 @@ export const DEFAULT_PLANS = [
     description: '适合轻度体验',
     description: '适合轻度体验',
     features: JSON.stringify([
     features: JSON.stringify([
       '每天3次生成',
       '每天3次生成',
-      '每次最多2000字',
+      '每月30分钟音频',
+      '每次最多2分钟',
       '基础音色5种',
       '基础音色5种',
       '标准音质',
       '标准音质',
-      '科大讯飞免费额度(10万/月永久)',
-      'Token成本:¥17.5/月(平台补贴)'
+      '科大讯飞TTS(免费额度)'
     ]),
     ]),
     isRecommended: false,
     isRecommended: false,
     sortOrder: 0,
     sortOrder: 0,
     dailyGenerations: 3,
     dailyGenerations: 3,
-    perGenerationLimit: 2000,
-    monthlyTokens: 5000,           // AI+基础TTS成本:¥17.5
+    perGenerationLimit: 300,       // ~2分钟音频
+    monthlyTokens: 3000,           // 文本配额
+    monthlyMinutes: 30,             // 音频时长配额(新增)
     yearlyTokens: null,
     yearlyTokens: null,
     voiceOptions: 5,
     voiceOptions: 5,
     audioQuality: 'standard',
     audioQuality: 'standard',
     apiAccess: false,
     apiAccess: false,
     batchProcessing: false,
     batchProcessing: false,
     teamManagement: false,
     teamManagement: false,
-    // 成本分析
     costAnalysis: {
     costAnalysis: {
-      tokenCost: 17.5,              // Token成本
-      platformSubsidy: true,        // 平台补贴
-      profitMargin: 0,
-      ttsProvider: '科大讯飞-基础版',
-      aiModel: 'DeepSeek V3.2'
+      minutesCost: 10.2,           // 30分钟 × ¥0.34
+      platformSubsidy: true,
+      strategy: '平台补贴引流'
     }
     }
   },
   },
   {
   {
@@ -197,31 +195,30 @@ export const DEFAULT_PLANS = [
     priceYearly: 99,
     priceYearly: 99,
     description: '适合日常使用',
     description: '适合日常使用',
     features: JSON.stringify([
     features: JSON.stringify([
-      '每月10000 Token',
-      '每次最多5000字',
+      '每天10次生成',
+      '每月100分钟音频',
+      '每次最多5分钟',
       '全部音色',
       '全部音色',
       '高清音质',
       '高清音质',
-      '百度基础版TTS(¥3/千字符)',
-      'Token成本:¥35/月'
+      '百度基础版TTS'
     ]),
     ]),
     isRecommended: false,
     isRecommended: false,
     sortOrder: 1,
     sortOrder: 1,
     dailyGenerations: 10,
     dailyGenerations: 10,
-    perGenerationLimit: 5000,
-    monthlyTokens: 10000,          // AI+基础TTS成本:¥35
+    perGenerationLimit: 750,       // ~5分钟音频
+    monthlyTokens: 15000,
+    monthlyMinutes: 100,           // 音频时长配额(新增)
     yearlyTokens: null,
     yearlyTokens: null,
     voiceOptions: -1,
     voiceOptions: -1,
     audioQuality: 'high',
     audioQuality: 'high',
     apiAccess: false,
     apiAccess: false,
     batchProcessing: false,
     batchProcessing: false,
     teamManagement: false,
     teamManagement: false,
-    // 成本分析(亏损,用于获客转化)
     costAnalysis: {
     costAnalysis: {
-      tokenCost: 35.0,
+      minutesCost: 100,           // 100分钟 × ¥1.0
       monthlyPrice: 9.9,
       monthlyPrice: 9.9,
-      loss: -25.1,                  // 每单亏损,获客用
-      strategy: '获客转化',
-      ttsProvider: '百度智能云-基础版'
+      loss: -90.1,
+      strategy: '亏损获客转化'
     }
     }
   },
   },
   {
   {
@@ -231,33 +228,31 @@ export const DEFAULT_PLANS = [
     priceYearly: 490,
     priceYearly: 490,
     description: '适合内容创作者 ⭐推荐',
     description: '适合内容创作者 ⭐推荐',
     features: JSON.stringify([
     features: JSON.stringify([
-      '每月30000 Token',
-      '每次最多20000字',
+      '无限制生成',
+      '每月500分钟音频',
+      '每次最多20分钟',
       '全部音色+定制音色',
       '全部音色+定制音色',
-      '高清音质',
-      '百度精品版TTS(¥15/千字符)',
-      'VIP优先队列',
-      'Token成本:¥105/月'
+      '无损音质',
+      '百度精品版TTS',
+      'VIP优先队列'
     ]),
     ]),
     isRecommended: true,
     isRecommended: true,
     sortOrder: 2,
     sortOrder: 2,
     dailyGenerations: -1,
     dailyGenerations: -1,
-    perGenerationLimit: 20000,
-    monthlyTokens: 30000,          // AI+精品TTS成本:¥105
+    perGenerationLimit: 3000,     // ~20分钟音频
+    monthlyTokens: 75000,
+    monthlyMinutes: 500,          // 音频时长配额(新增)
     yearlyTokens: null,
     yearlyTokens: null,
     voiceOptions: -1,
     voiceOptions: -1,
     audioQuality: 'lossless',
     audioQuality: 'lossless',
     apiAccess: false,
     apiAccess: false,
     batchProcessing: false,
     batchProcessing: false,
     teamManagement: false,
     teamManagement: false,
-    // 成本分析(部分补贴,核心盈利)
     costAnalysis: {
     costAnalysis: {
-      tokenCost: 105.0,
+      minutesCost: 500,           // 500分钟 × ¥1.0
       monthlyPrice: 49,
       monthlyPrice: 49,
-      loss: -56.0,                 // 部分补贴
-      profitMargin: 0,             // 保本微亏
-      strategy: '核心转化产品',
-      ttsProvider: '百度智能云-精品版'
+      loss: -451,
+      strategy: '亏损转化,盈利靠续费'
     }
     }
   },
   },
   {
   {
@@ -267,35 +262,34 @@ export const DEFAULT_PLANS = [
     priceYearly: 1999,
     priceYearly: 1999,
     description: '适合企业用户',
     description: '适合企业用户',
     features: JSON.stringify([
     features: JSON.stringify([
-      '每月100000 Token',
-      '每次最多100000字',
+      '无限制生成',
+      '每月2000分钟音频',
+      '每次最多60分钟',
       '全部功能',
       '全部功能',
       '无损音质',
       '无损音质',
-      '火山引擎豆包TTS(¥50/千字符)',
+      '火山引擎豆包TTS',
       '专属技术支持',
       '专属技术支持',
       '批量处理',
       '批量处理',
-      '团队管理',
-      'Token成本:¥350/月'
+      '团队管理'
     ]),
     ]),
     isRecommended: false,
     isRecommended: false,
     sortOrder: 3,
     sortOrder: 3,
-    dailyGenerations: -1,          // 无限制
-    perGenerationLimit: 100000,
-    monthlyTokens: 100000,         // AI+高端TTS成本:¥350
+    dailyGenerations: -1,
+    perGenerationLimit: 9000,      // ~60分钟音频
+    monthlyTokens: 300000,
+    monthlyMinutes: 2000,         // 音频时长配额(新增)
     yearlyTokens: null,
     yearlyTokens: null,
     voiceOptions: -1,
     voiceOptions: -1,
     audioQuality: 'lossless',
     audioQuality: 'lossless',
     apiAccess: true,
     apiAccess: true,
     batchProcessing: true,
     batchProcessing: true,
     teamManagement: true,
     teamManagement: true,
-    // 成本分析(盈利)
     costAnalysis: {
     costAnalysis: {
-      tokenCost: 350.0,
+      minutesCost: 680,           // 2000分钟 × ¥0.34
       monthlyPrice: 199,
       monthlyPrice: 199,
-      profit: -151.0,              // 仍有亏损,建议涨价
-      profitMargin: 0,
-      suggestion: '建议定价¥399-499/月',
-      ttsProvider: '火山引擎豆包'
+      loss: -481,
+      suggestion: '建议定价¥699-999/月',
+      strategy: '高端定位,低价策略'
     }
     }
   }
   }
 ];
 ];
@@ -516,3 +510,231 @@ export async function checkQuota(userId: number, requiredTokens: number) {
   
   
   return { allowed: true, reason: null };
   return { allowed: true, reason: null };
 }
 }
+
+// ============================================
+// 音频时长计费系统(2026-04-12 新增)
+// ============================================
+
+export const AUDIO_BILLING_CONFIG = {
+  // 语速配置(字/分钟)
+  speakingRate: 150,  // 平均语速150字/分钟
+  
+  // 音频时长单价(元/分钟)
+  pricePerMinute: {
+    standard: 0.5,    // 标准音质:¥0.5/分钟
+    high: 1.0,        // 高清音质:¥1.0/分钟
+    lossless: 2.0     // 无损音质:¥2.0/分钟
+  },
+  
+  // 各等级每月音频时长配额
+  monthlyMinutes: {
+    0: 30,    // 免费版:30分钟/月
+    1: 100,   // 入门版:100分钟/月
+    2: 500,   // 专业版:500分钟/月
+    3: 2000   // 旗舰版:2000分钟/月
+  },
+  
+  // 各等级单次生成最大时长(分钟)
+  maxMinutesPerGeneration: {
+    0: 2,     // 免费版:2分钟/次
+    1: 5,     // 入门版:5分钟/次
+    2: 20,    // 专业版:20分钟/次
+    3: 60     // 旗舰版:60分钟/次
+  },
+  
+  // 成本计算(用于内部核算)
+  costPerMinute: {
+    standard: 0.34,   // AI¥0.2 + TTS¥0.14 = ¥0.34/分钟
+    high: 1.0,        // AI¥0.2 + TTS¥0.8 = ¥1.0/分钟
+    lossless: 3.4     // AI¥0.4 + TTS¥3.0 = ¥3.4/分钟
+  }
+};
+
+// 根据文本长度计算音频时长
+export function calculateAudioDuration(textLength: number): number {
+  return Math.ceil(textLength / AUDIO_BILLING_CONFIG.speakingRate);
+}
+
+// 计算音频生成费用
+export function calculateAudioCost(
+  textLength: number,
+  quality: 'standard' | 'high' | 'lossless' = 'high'
+): {
+  textLength: number;
+  audioMinutes: number;
+  pricePerMinute: number;
+  totalPrice: number;
+  estimatedCost: number;
+  remainingMinutes: number;
+} {
+  const audioMinutes = calculateAudioDuration(textLength);
+  const pricePerMinute = AUDIO_BILLING_CONFIG.pricePerMinute[quality] || 1.0;
+  const totalPrice = Math.round(audioMinutes * pricePerMinute * 100) / 100;
+  
+  return {
+    textLength,
+    audioMinutes,
+    pricePerMinute,
+    totalPrice,
+    estimatedCost: AUDIO_BILLING_CONFIG.costPerMinute[quality] * audioMinutes,
+    remainingMinutes: 0  // 需要查询用户余额才能确定
+  };
+}
+
+// 获取用户音频时长余额
+export async function getUserAudioBalance(userId: number) {
+  const user = await prisma.user.findUnique({
+    where: { id: userId }
+  });
+  
+  if (!user) {
+    throw new Error('用户不存在');
+  }
+  
+  const memberLevel = user.memberLevel as MemberLevel;
+  const monthlyMinutes = AUDIO_BILLING_CONFIG.monthlyMinutes[memberLevel] || 30;
+  
+  // 检查是否需要重置月度配额
+  const now = new Date();
+  const resetDate = user.subscriptionResetDate;
+  
+  let totalMinutes = monthlyMinutes;
+  let usedMinutes = user.usedAudioMinutes || 0;
+  
+  // 每月1日重置配额
+  if (resetDate) {
+    const lastReset = new Date(resetDate);
+    if (now.getMonth() !== lastReset.getMonth() || now.getFullYear() !== lastReset.getFullYear()) {
+      totalMinutes = monthlyMinutes;
+      usedMinutes = 0;
+      await prisma.user.update({
+        where: { id: userId },
+        data: { 
+          usedAudioMinutes: 0,
+          subscriptionResetDate: now
+        }
+      });
+    }
+  }
+  
+  return {
+    totalMinutes,
+    usedMinutes,
+    remainingMinutes: totalMinutes - usedMinutes,
+    isUnlimited: memberLevel >= 3,  // 旗舰版及以上无限制
+    resetDate: user.subscriptionResetDate
+  };
+}
+
+// 检查音频生成配额
+export async function checkAudioQuota(
+  userId: number,
+  textLength: number,
+  quality: 'standard' | 'high' | 'lossless' = 'high'
+) {
+  const audioMinutes = calculateAudioDuration(textLength);
+  const balance = await getUserAudioBalance(userId);
+  const maxMinutes = AUDIO_BILLING_CONFIG.maxMinutesPerGeneration[balance.isUnlimited ? 3 : 0] || 2;
+  
+  // 检查单次限制
+  if (audioMinutes > maxMinutes) {
+    return {
+      allowed: false,
+      reason: `单次生成不能超过${maxMinutes}分钟`,
+      audioMinutes,
+      maxMinutes
+    };
+  }
+  
+  // 检查月度配额
+  if (!balance.isUnlimited && balance.remainingMinutes < audioMinutes) {
+    return {
+      allowed: false,
+      reason: `音频时长不足,需要${audioMinutes}分钟,剩余${balance.remainingMinutes}分钟`,
+      audioMinutes,
+      remainingMinutes: balance.remainingMinutes,
+      totalMinutes: balance.totalMinutes
+    };
+  }
+  
+  return {
+    allowed: true,
+    reason: null,
+    audioMinutes,
+    remainingMinutes: balance.remainingMinutes - audioMinutes,
+    totalMinutes: balance.totalMinutes
+  };
+}
+
+// 扣除音频时长(生成完成后调用)
+export async function consumeAudioMinutes(
+  userId: number,
+  textLength: number,
+  quality: 'standard' | 'high' | 'lossless' = 'high',
+  description?: string
+) {
+  const audioMinutes = calculateAudioDuration(textLength);
+  const cost = calculateAudioCost(textLength, quality);
+  
+  // 更新用户已使用时长
+  await prisma.user.update({
+    where: { id: userId },
+    data: {
+      usedAudioMinutes: { increment: audioMinutes }
+    }
+  });
+  
+  // 记录使用日志
+  const usage = await prisma.tokenUsage.create({
+    data: {
+      userId,
+      type: 'audio_generation',
+      amount: audioMinutes,
+      contentLength: textLength,
+      description: description || `${audioMinutes}分钟${quality}音质音频`
+    }
+  });
+  
+  return {
+    usageId: usage.id,
+    audioMinutes,
+    textLength,
+    quality,
+    cost: cost.totalPrice
+  };
+}
+
+// 获取音频生成预估(用于UI展示)
+export async function getAudioEstimate(
+  userId: number,
+  textLength: number,
+  quality: 'standard' | 'high' | 'lossless' = 'high'
+) {
+  const cost = calculateAudioCost(textLength, quality);
+  const quota = await checkAudioQuota(userId, textLength, quality);
+  const balance = await getUserAudioBalance(userId);
+  
+  return {
+    // 预估信息
+    textLength,
+    audioMinutes: cost.audioMinutes,
+    pricePerMinute: cost.pricePerMinute,
+    estimatedPrice: cost.totalPrice,
+    estimatedCost: cost.estimatedCost,
+    
+    // 用户配额
+    remainingMinutes: quota.allowed 
+      ? quota.remainingMinutes 
+      : balance.remainingMinutes,
+    totalMinutes: balance.totalMinutes,
+    
+    // 是否允许生成
+    canGenerate: quota.allowed,
+    reason: quota.reason,
+    
+    // 显示文本
+    displayText: quota.allowed
+      ? `预计${cost.audioMinutes}分钟音频,约¥${cost.totalPrice}`
+      : quota.reason
+  };
+}