Files
aiagent/backend/app/models/billing.py
renjianbo 876789fac1 feat: multi-tenant workspace isolation, RBAC, sidebar nav, billing, and Android enhancements
- Backend: workspace_id isolation for 14 model tables + safe migration/backfill
- Backend: RBAC system with 4 roles and 23 permissions, seeded on startup
- Backend: workspace admin endpoints (list/manage all workspaces)
- Backend: admin user management API (CRUD, reset password)
- Backend: billing API with subscription plans, usage tracking, rate limiting
- Backend: fix system_logs.py UNION query and wrong column references
- Backend: WebSocket JWT auth and workspace enforcement
- Frontend: sidebar navigation replacing top dropdown menu
- Frontend: user management page (Users.vue) for admins
- Frontend: enhanced Workspaces.vue with admin table view
- Frontend: workspace RBAC computed properties in user store
- Android: agent marketplace, billing/subscription UI, onboarding wizard
- Android: phone login, analytics tracker, crash handler, network diagnostics
- Android: splash screen, encrypted token storage, app update enhancements
- Docs: multi-tenant RBAC guide with 8 sections and role-permission matrix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-04 01:00:22 +08:00

94 lines
5.1 KiB
Python

"""
付费体系模型:套餐、订单、订阅、用量记录
"""
from sqlalchemy import Boolean, Column, String, Integer, Float, DateTime, Date, Text, JSON, ForeignKey, func, Index
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
import uuid
class BillingPlan(Base):
"""套餐定义表"""
__tablename__ = "billing_plans"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="套餐ID")
name = Column(String(100), nullable=False, comment="套餐名称")
tier = Column(String(20), nullable=False, comment="套餐等级: free/pro/enterprise")
price_monthly = Column(Float, nullable=False, default=0, comment="月付价格(CNY)")
price_yearly = Column(Float, nullable=False, default=0, comment="年付价格(CNY)")
daily_quota = Column(Integer, nullable=False, default=0, comment="每日对话次数限制, 0=无限")
agent_limit = Column(Integer, nullable=False, default=0, comment="智能体数量限制, 0=无限")
knowledge_limit = Column(Integer, nullable=False, default=0, comment="知识条目限制, 0=无限")
file_upload_mb = Column(Integer, nullable=False, default=5, comment="文件上传大小限制(MB)")
models = Column(JSON, nullable=True, comment="可用模型列表")
features = Column(JSON, nullable=True, comment="权益特性列表")
is_active = Column(Boolean, default=True, comment="是否启用")
sort_order = Column(Integer, default=0, comment="排序")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
def __repr__(self):
return f"<BillingPlan(id={self.id}, tier={self.tier}, name={self.name})>"
class BillingOrder(Base):
"""订单记录表"""
__tablename__ = "billing_orders"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="订单ID")
user_id = Column(CHAR(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True, comment="用户ID")
plan_id = Column(CHAR(36), ForeignKey("billing_plans.id", ondelete="SET NULL"), nullable=True, comment="套餐ID")
amount = Column(Float, nullable=False, comment="支付金额(CNY)")
period = Column(String(10), nullable=False, default="monthly", comment="周期: monthly/yearly")
status = Column(String(20), nullable=False, default="pending", comment="状态: pending/paid/cancelled/expired")
payment_method = Column(String(20), nullable=True, comment="支付方式: mock/wechat/alipay")
payment_ref = Column(String(255), nullable=True, comment="第三方支付流水号")
paid_at = Column(DateTime, nullable=True, comment="支付时间")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
user = relationship("User", backref="orders")
def __repr__(self):
return f"<BillingOrder(id={self.id}, user_id={self.user_id}, amount={self.amount}, status={self.status})>"
class UserSubscription(Base):
"""用户订阅状态表"""
__tablename__ = "user_subscriptions"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="订阅ID")
user_id = Column(CHAR(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, unique=True, comment="用户ID")
plan_id = Column(CHAR(36), ForeignKey("billing_plans.id", ondelete="SET NULL"), nullable=True, comment="当前套餐ID")
tier = Column(String(20), nullable=False, default="free", comment="当前等级")
status = Column(String(20), nullable=False, default="active", comment="状态: active/cancelled/expired")
started_at = Column(DateTime, nullable=True, comment="订阅开始时间")
expires_at = Column(DateTime, nullable=True, comment="订阅到期时间")
auto_renew = Column(Boolean, default=False, comment="是否自动续费")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
user = relationship("User", backref="subscription")
plan = relationship("BillingPlan")
def __repr__(self):
return f"<UserSubscription(id={self.id}, user_id={self.user_id}, tier={self.tier}, status={self.status})>"
class UsageRecord(Base):
"""每日用量记录表"""
__tablename__ = "usage_records"
__table_args__ = (
Index("ix_usage_user_date", "user_id", "date", unique=True),
)
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="记录ID")
user_id = Column(CHAR(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True, comment="用户ID")
date = Column(Date, nullable=False, comment="日期")
count = Column(Integer, nullable=False, default=0, comment="当日对话次数")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
def __repr__(self):
return f"<UsageRecord(user_id={self.user_id}, date={self.date}, count={self.count})>"