188 lines
5.9 KiB
Python
188 lines
5.9 KiB
Python
import uuid
|
|
from datetime import datetime, timedelta
|
|
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models.citation_record import CitationRecord
|
|
from app.models.query import Query
|
|
from app.models.subscription import Subscription
|
|
from app.models.user import User
|
|
from app.services.subscription import PLANS
|
|
|
|
|
|
async def get_system_stats(db: AsyncSession) -> dict:
|
|
total_users_result = await db.execute(select(func.count()).select_from(User))
|
|
total_users = total_users_result.scalar_one()
|
|
|
|
total_queries_result = await db.execute(select(func.count()).select_from(Query))
|
|
total_queries = total_queries_result.scalar_one()
|
|
|
|
total_citations_result = await db.execute(
|
|
select(func.count()).select_from(CitationRecord)
|
|
)
|
|
total_citations = total_citations_result.scalar_one()
|
|
|
|
cited_result = await db.execute(
|
|
select(func.count()).select_from(CitationRecord).where(CitationRecord.cited.is_(True))
|
|
)
|
|
cited_count = cited_result.scalar_one()
|
|
|
|
citation_rate = round(cited_count / total_citations * 100, 2) if total_citations > 0 else 0.0
|
|
|
|
today = datetime.utcnow().date()
|
|
today_start = datetime(today.year, today.month, today.day)
|
|
today_active_result = await db.execute(
|
|
select(func.count(func.distinct(Query.user_id))).where(Query.last_queried_at >= today_start)
|
|
)
|
|
today_active_users = today_active_result.scalar_one()
|
|
|
|
return {
|
|
"total_users": total_users,
|
|
"total_queries": total_queries,
|
|
"total_citations": total_citations,
|
|
"citation_rate": citation_rate,
|
|
"today_active_users": today_active_users,
|
|
}
|
|
|
|
|
|
async def get_users(
|
|
db: AsyncSession, skip: int = 0, limit: int = 20, search: str | None = None
|
|
) -> dict:
|
|
base_stmt = select(User)
|
|
count_stmt = select(func.count()).select_from(User)
|
|
|
|
if search:
|
|
like_pattern = f"%{search}%"
|
|
base_stmt = base_stmt.where(
|
|
(User.email.ilike(like_pattern)) | (User.name.ilike(like_pattern))
|
|
)
|
|
count_stmt = count_stmt.where(
|
|
(User.email.ilike(like_pattern)) | (User.name.ilike(like_pattern))
|
|
)
|
|
|
|
base_stmt = base_stmt.order_by(User.created_at.desc()).offset(skip).limit(limit)
|
|
|
|
result = await db.execute(base_stmt)
|
|
users = result.scalars().all()
|
|
|
|
count_result = await db.execute(count_stmt)
|
|
total = count_result.scalar_one()
|
|
|
|
items = []
|
|
for user in users:
|
|
query_count_result = await db.execute(
|
|
select(func.count()).select_from(Query).where(Query.user_id == user.id)
|
|
)
|
|
query_count = query_count_result.scalar_one()
|
|
items.append(
|
|
{
|
|
"id": user.id,
|
|
"email": user.email,
|
|
"name": user.name,
|
|
"plan": user.plan,
|
|
"is_active": user.is_active,
|
|
"is_admin": user.is_admin,
|
|
"email_verified": user.email_verified,
|
|
"query_count": query_count,
|
|
"created_at": user.created_at,
|
|
}
|
|
)
|
|
|
|
return {"items": items, "total": total}
|
|
|
|
|
|
async def get_user_detail(db: AsyncSession, user_id: uuid.UUID) -> dict | None:
|
|
user_result = await db.execute(select(User).where(User.id == user_id))
|
|
user = user_result.scalar_one_or_none()
|
|
if user is None:
|
|
return None
|
|
|
|
queries_result = await db.execute(
|
|
select(Query).where(Query.user_id == user_id).order_by(Query.created_at.desc())
|
|
)
|
|
queries = queries_result.scalars().all()
|
|
|
|
citations_result = await db.execute(
|
|
select(CitationRecord)
|
|
.join(Query, CitationRecord.query_id == Query.id)
|
|
.where(Query.user_id == user_id)
|
|
.order_by(CitationRecord.queried_at.desc())
|
|
.limit(10)
|
|
)
|
|
citations = citations_result.scalars().all()
|
|
|
|
return {
|
|
"user": {
|
|
"id": user.id,
|
|
"email": user.email,
|
|
"name": user.name,
|
|
"plan": user.plan,
|
|
"is_active": user.is_active,
|
|
"is_admin": user.is_admin,
|
|
"email_verified": user.email_verified,
|
|
"max_queries": user.max_queries,
|
|
"created_at": user.created_at,
|
|
"updated_at": user.updated_at,
|
|
},
|
|
"queries": [
|
|
{
|
|
"id": q.id,
|
|
"keyword": q.keyword,
|
|
"target_brand": q.target_brand,
|
|
"status": q.status,
|
|
"frequency": q.frequency,
|
|
"created_at": q.created_at,
|
|
}
|
|
for q in queries
|
|
],
|
|
"recent_citations": [
|
|
{
|
|
"id": c.id,
|
|
"platform": c.platform,
|
|
"cited": c.cited,
|
|
"citation_position": c.citation_position,
|
|
"queried_at": c.queried_at,
|
|
}
|
|
for c in citations
|
|
],
|
|
}
|
|
|
|
|
|
async def toggle_user_active(db: AsyncSession, user_id: uuid.UUID) -> dict | None:
|
|
user_result = await db.execute(select(User).where(User.id == user_id))
|
|
user = user_result.scalar_one_or_none()
|
|
if user is None:
|
|
return None
|
|
|
|
user.is_active = not user.is_active
|
|
await db.commit()
|
|
|
|
return {
|
|
"id": user.id,
|
|
"is_active": user.is_active,
|
|
"message": "用户已启用" if user.is_active else "用户已禁用",
|
|
}
|
|
|
|
|
|
async def update_user_plan(db: AsyncSession, user_id: uuid.UUID, plan: str) -> dict | None:
|
|
plan_data = PLANS.get(plan)
|
|
if plan_data is None:
|
|
raise ValueError(f"Invalid plan: {plan}")
|
|
|
|
user_result = await db.execute(select(User).where(User.id == user_id))
|
|
user = user_result.scalar_one_or_none()
|
|
if user is None:
|
|
return None
|
|
|
|
user.plan = plan
|
|
user.max_queries = plan_data["max_queries"]
|
|
await db.commit()
|
|
|
|
return {
|
|
"id": user.id,
|
|
"plan": user.plan,
|
|
"max_queries": user.max_queries,
|
|
"message": f"用户套餐已更新为{plan_data['name']}",
|
|
}
|