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>
This commit is contained in:
112
backend/scripts/seed_billing_plans.py
Normal file
112
backend/scripts/seed_billing_plans.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
种子数据:初始化 3 个默认付费套餐
|
||||
用法: cd backend && ./venv/Scripts/python.exe scripts/seed_billing_plans.py
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from app.core.database import SessionLocal, init_db
|
||||
from app.models.billing import BillingPlan
|
||||
import uuid
|
||||
|
||||
PLANS = [
|
||||
{
|
||||
"id": "a0000000-0000-0000-0000-000000000001",
|
||||
"name": "免费版",
|
||||
"tier": "free",
|
||||
"price_monthly": 0,
|
||||
"price_yearly": 0,
|
||||
"daily_quota": 50,
|
||||
"agent_limit": 3,
|
||||
"knowledge_limit": 10,
|
||||
"file_upload_mb": 5,
|
||||
"models": ["deepseek-v4-pro"],
|
||||
"features": [
|
||||
"每日 50 次对话",
|
||||
"3 个自建智能体",
|
||||
"基础模型 DeepSeek-V3",
|
||||
"10 条全局知识",
|
||||
"5 MB 文件上传",
|
||||
"社区支持",
|
||||
],
|
||||
"is_active": True,
|
||||
"sort_order": 0,
|
||||
},
|
||||
{
|
||||
"id": "a0000000-0000-0000-0000-000000000002",
|
||||
"name": "专业版",
|
||||
"tier": "pro",
|
||||
"price_monthly": 29,
|
||||
"price_yearly": 198,
|
||||
"daily_quota": 500,
|
||||
"agent_limit": 20,
|
||||
"knowledge_limit": 500,
|
||||
"file_upload_mb": 50,
|
||||
"models": ["deepseek-v4-pro", "gpt-4o", "claude-sonnet-4-6", "gemini-2.5-pro"],
|
||||
"features": [
|
||||
"每日 500 次对话",
|
||||
"20 个自建智能体",
|
||||
"全部高级模型",
|
||||
"500 条全局知识",
|
||||
"50 MB 文件上传",
|
||||
"优先响应队列",
|
||||
"对话历史云端同步",
|
||||
"对话导出 Markdown/PDF",
|
||||
"专属客服通道",
|
||||
],
|
||||
"is_active": True,
|
||||
"sort_order": 1,
|
||||
},
|
||||
{
|
||||
"id": "a0000000-0000-0000-0000-000000000003",
|
||||
"name": "企业版",
|
||||
"tier": "enterprise",
|
||||
"price_monthly": 99,
|
||||
"price_yearly": 899,
|
||||
"daily_quota": 0,
|
||||
"agent_limit": 0,
|
||||
"knowledge_limit": 0,
|
||||
"file_upload_mb": 200,
|
||||
"models": ["deepseek-v4-pro", "gpt-4o", "claude-sonnet-4-6", "gemini-2.5-pro"],
|
||||
"features": [
|
||||
"无限对话次数",
|
||||
"无限智能体",
|
||||
"团队协作(10 人)",
|
||||
"全部知识库功能",
|
||||
"200 MB 文件上传",
|
||||
"最高优先响应",
|
||||
"API 调用额度 ¥50/月",
|
||||
"管理员面板",
|
||||
"专属成功经理 + 电话支持",
|
||||
],
|
||||
"is_active": True,
|
||||
"sort_order": 2,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def seed():
|
||||
init_db()
|
||||
db = SessionLocal()
|
||||
try:
|
||||
for plan_data in PLANS:
|
||||
existing = db.query(BillingPlan).filter(BillingPlan.id == plan_data["id"]).first()
|
||||
if existing:
|
||||
print(f"跳过已存在: {plan_data['name']}")
|
||||
continue
|
||||
plan = BillingPlan(**plan_data)
|
||||
db.add(plan)
|
||||
print(f"创建套餐: {plan_data['name']} (tier={plan_data['tier']})")
|
||||
db.commit()
|
||||
print("种子数据初始化完成")
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
print(f"错误: {e}")
|
||||
raise
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
seed()
|
||||
Reference in New Issue
Block a user