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:
@@ -13,6 +13,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.api.auth import get_current_user
|
||||
from app.api.deps import get_current_workspace_id
|
||||
from app.models.user import User
|
||||
from app.models.knowledge_entry import KnowledgeEntry
|
||||
from app.services.bottleneck_detector import bottleneck_detector
|
||||
@@ -53,19 +54,17 @@ def get_knowledge_entries(
|
||||
limit: int = Query(50, ge=1, le=200, description="返回条数"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
workspace_id: str = Depends(get_current_workspace_id),
|
||||
):
|
||||
"""获取知识条目列表,按创建时间倒序。"""
|
||||
"""获取当前工作区知识条目列表,按创建时间倒序。"""
|
||||
since = datetime.now() - timedelta(days=days)
|
||||
entries = (
|
||||
db.query(KnowledgeEntry)
|
||||
.filter(
|
||||
KnowledgeEntry.created_at >= since,
|
||||
KnowledgeEntry.is_active == True,
|
||||
)
|
||||
.order_by(KnowledgeEntry.created_at.desc())
|
||||
.limit(limit)
|
||||
.all()
|
||||
q = db.query(KnowledgeEntry).filter(
|
||||
KnowledgeEntry.created_at >= since,
|
||||
KnowledgeEntry.is_active == True,
|
||||
)
|
||||
if workspace_id:
|
||||
q = q.filter(KnowledgeEntry.workspace_id == workspace_id)
|
||||
entries = q.order_by(KnowledgeEntry.created_at.desc()).limit(limit).all()
|
||||
return [e.to_dict() for e in entries]
|
||||
|
||||
|
||||
@@ -74,24 +73,24 @@ def get_knowledge_trend(
|
||||
days: int = Query(7, ge=1, le=365, description="统计天数"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
workspace_id: str = Depends(get_current_workspace_id),
|
||||
):
|
||||
"""知识条目增长趋势 — 按天统计新增数量。"""
|
||||
"""知识条目增长趋势(当前工作区)— 按天统计新增数量。"""
|
||||
since = datetime.now() - timedelta(days=days)
|
||||
|
||||
# GROUP BY date(created_at)
|
||||
rows = (
|
||||
db.query(
|
||||
func.date(KnowledgeEntry.created_at).label("date"),
|
||||
func.count(KnowledgeEntry.id).label("count"),
|
||||
)
|
||||
.filter(
|
||||
KnowledgeEntry.created_at >= since,
|
||||
KnowledgeEntry.is_active == True,
|
||||
)
|
||||
.group_by(func.date(KnowledgeEntry.created_at))
|
||||
.order_by(func.date(KnowledgeEntry.created_at).asc())
|
||||
.all()
|
||||
q = db.query(
|
||||
func.date(KnowledgeEntry.created_at).label("date"),
|
||||
func.count(KnowledgeEntry.id).label("count"),
|
||||
).filter(
|
||||
KnowledgeEntry.created_at >= since,
|
||||
KnowledgeEntry.is_active == True,
|
||||
)
|
||||
if workspace_id:
|
||||
q = q.filter(KnowledgeEntry.workspace_id == workspace_id)
|
||||
|
||||
rows = q.group_by(func.date(KnowledgeEntry.created_at)).order_by(
|
||||
func.date(KnowledgeEntry.created_at).asc()
|
||||
).all()
|
||||
|
||||
# Fill in missing dates with 0 count
|
||||
trend = []
|
||||
|
||||
Reference in New Issue
Block a user