""" 订阅计划相关 API 路由 """ from fastapi import APIRouter, Depends from pydantic import BaseModel from typing import List, Optional from datetime import datetime from sqlalchemy.ext.asyncio import AsyncSession from middleware.auth import get_current_user from database.models import User from database import get_async_db router = APIRouter(prefix="/api/plans", tags=["plans"]) class PlanResponse(BaseModel): """订阅计划响应""" id: str name: str display_name: str price: float currency: str features: List[str] max_conversations: Optional[int] = None # None表示无限制 max_chapters: Optional[int] = None max_words: Optional[int] = None is_popular: bool = False class CurrentPlanResponse(BaseModel): """当前订阅计划响应""" plan_id: str plan_name: str subscription_type: str expires_at: Optional[str] = None # 过期时间,None表示永久 features: List[str] usage: dict # 使用情况统计 # 预定义的订阅计划 # 免费版:50 轮对话 + 1 个章节整理 # Pro:88 元,2000 轮对话,无章节限制 # Pro+:288 元,10000 轮对话,无章节限制 AVAILABLE_PLANS = [ PlanResponse( id="free", name="free", display_name="免费体验版", price=0.0, currency="CNY", features=[ "50 轮对话", "1 个章节整理(所有对话整理到一个章节)", "体验回忆录生成流程" ], max_conversations=50, max_chapters=1, max_words=None, is_popular=False ), PlanResponse( id="pro", name="pro", display_name="Pro 版", price=88.0, currency="CNY", features=[ "2000 轮对话", "无章节限制", "完整回忆录生成" ], max_conversations=2000, max_chapters=None, max_words=None, is_popular=True ), PlanResponse( id="pro_plus", name="pro_plus", display_name="Pro+ 版", price=288.0, currency="CNY", features=[ "10000 轮对话", "无章节限制", "完整回忆录生成", "长期创作无忧" ], max_conversations=10000, max_chapters=None, max_words=None, is_popular=False ) ] def get_plan_by_type(subscription_type: str) -> Optional[PlanResponse]: """根据订阅类型获取计划信息。旧字段 premium 按 pro 展示。""" if subscription_type == "premium": subscription_type = "pro" for plan in AVAILABLE_PLANS: if plan.id == subscription_type: return plan return AVAILABLE_PLANS[0] # 默认返回免费版 @router.get("", response_model=List[PlanResponse]) async def get_plans(): """ 获取所有可用的订阅计划 """ return AVAILABLE_PLANS @router.get("/current", response_model=CurrentPlanResponse) async def get_current_plan( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_async_db) ): """ 获取当前用户的订阅计划信息 """ plan = get_plan_by_type(current_user.subscription_type) # 计算使用情况(对话轮数 = Segment 数量) from routers.quota import get_segment_count, get_chapter_count segment_count = await get_segment_count(current_user.id, db) chapter_count = await get_chapter_count(current_user.id, db) usage = { "conversations": segment_count, # 已用对话轮数 "chapters": chapter_count, "max_conversations": plan.max_conversations, "max_chapters": plan.max_chapters, } expires_at = None if current_user.subscription_expires_at: expires_at = current_user.subscription_expires_at.isoformat() return CurrentPlanResponse( plan_id=plan.id, plan_name=plan.display_name, subscription_type=current_user.subscription_type, expires_at=expires_at, features=plan.features, usage=usage )