Files
life-echo/api/routers/plans.py
iammm0 3690417fdc feat: 新增后端API路由模块
- 新增faqs.py常见问题路由
- 新增feedback.py反馈路由
- 新增orders.py订单路由
- 新增plans.py套餐路由
- 新增quota.py配额路由
- 新增user.py用户路由
- 更新main.py注册新路由
- 更新requirements.txt添加依赖
2026-01-23 14:02:36 +08:00

150 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
订阅计划相关 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 # 使用情况统计
# 预定义的订阅计划
AVAILABLE_PLANS = [
PlanResponse(
id="free",
name="free",
display_name="免费版",
price=0.0,
currency="CNY",
features=[
"基础对话功能",
"生成回忆录章节",
"最多3次对话",
"最多10个章节"
],
max_conversations=3,
max_chapters=10,
max_words=50000,
is_popular=False
),
PlanResponse(
id="premium",
name="premium",
display_name="高级版",
price=99.0,
currency="CNY",
features=[
"无限对话",
"无限章节",
"无限字数",
"优先处理",
"专属客服支持"
],
max_conversations=None,
max_chapters=None,
max_words=None,
is_popular=True
)
]
def get_plan_by_type(subscription_type: str) -> Optional[PlanResponse]:
"""根据订阅类型获取计划信息"""
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)
# 计算使用情况
from sqlalchemy import select, func
# 统计对话数量
from database.models import Conversation
stmt = select(func.count(Conversation.id)).where(
Conversation.user_id == current_user.id
)
result = await db.execute(stmt)
conversation_count = result.scalar() or 0
# 统计章节数量
from database.models import Chapter
stmt = select(func.count(Chapter.id)).where(
Chapter.user_id == current_user.id
)
result = await db.execute(stmt)
chapter_count = result.scalar() or 0
# 统计总字数
stmt = select(func.sum(func.length(Chapter.content))).where(
Chapter.user_id == current_user.id
)
result = await db.execute(stmt)
total_words = result.scalar() or 0
usage = {
"conversations": conversation_count,
"chapters": chapter_count,
"words": total_words,
"max_conversations": plan.max_conversations,
"max_chapters": plan.max_chapters,
"max_words": plan.max_words
}
return CurrentPlanResponse(
plan_id=plan.id,
plan_name=plan.display_name,
subscription_type=current_user.subscription_type,
expires_at=None, # 目前没有过期时间概念
features=plan.features,
usage=usage
)