feat: initialize aivideo project
This commit is contained in:
50
backend/app/modules/admins/repository.py
Normal file
50
backend/app/modules/admins/repository.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.entities import (
|
||||
AdminUser,
|
||||
InviteRelation,
|
||||
RechargeOrder,
|
||||
User,
|
||||
VideoGenerationTask,
|
||||
)
|
||||
|
||||
|
||||
class AdminsRepository:
|
||||
def __init__(self, db: Session) -> None:
|
||||
self.db = db
|
||||
|
||||
def get_admin_by_username(self, username: str) -> AdminUser | None:
|
||||
return self.db.scalar(select(AdminUser).where(AdminUser.username == username))
|
||||
|
||||
def list_users(self):
|
||||
return self.db.query(User).order_by(User.id.desc())
|
||||
|
||||
def get_user(self, user_id: int) -> User | None:
|
||||
return self.db.scalar(select(User).where(User.id == user_id))
|
||||
|
||||
def count_users(self) -> int:
|
||||
return self.db.query(func.count(User.id)).scalar() or 0
|
||||
|
||||
def count_paid_orders(self) -> int:
|
||||
return (
|
||||
self.db.query(func.count(RechargeOrder.id))
|
||||
.filter(RechargeOrder.status == "paid")
|
||||
.scalar()
|
||||
or 0
|
||||
)
|
||||
|
||||
def count_tasks(self) -> int:
|
||||
return self.db.query(func.count(VideoGenerationTask.id)).scalar() or 0
|
||||
|
||||
def count_success_tasks(self) -> int:
|
||||
return (
|
||||
self.db.query(func.count(VideoGenerationTask.id))
|
||||
.filter(VideoGenerationTask.task_status == "succeeded")
|
||||
.scalar()
|
||||
or 0
|
||||
)
|
||||
|
||||
def invite_relations(self):
|
||||
return self.db.query(InviteRelation).order_by(InviteRelation.id.desc())
|
||||
|
||||
86
backend/app/modules/admins/router.py
Normal file
86
backend/app/modules/admins/router.py
Normal file
@@ -0,0 +1,86 @@
|
||||
from fastapi import APIRouter, Depends, Response
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.common.db.session import get_db
|
||||
from app.common.responses.api_response import success_response
|
||||
from app.common.security.deps import get_current_admin, require_admin_permission
|
||||
from app.modules.admins.schema import AdminLoginRequest, ManualAdjustRequest
|
||||
from app.modules.admins.service import AdminsService
|
||||
|
||||
|
||||
auth_router = APIRouter(prefix="/api/v1/admin/auth", tags=["admin-auth"])
|
||||
router = APIRouter(prefix="/api/v1/admin", tags=["admin"])
|
||||
|
||||
|
||||
@auth_router.post("/login")
|
||||
def admin_login(
|
||||
payload: AdminLoginRequest,
|
||||
response: Response,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
return {"code": 0, "message": "ok", "data": AdminsService(db).login(payload, response)}
|
||||
|
||||
|
||||
@auth_router.post("/logout")
|
||||
def admin_logout(response: Response, db: Session = Depends(get_db)):
|
||||
AdminsService(db).logout(response)
|
||||
return {"code": 0, "message": "ok", "data": {"success": True}}
|
||||
|
||||
|
||||
@auth_router.get("/me")
|
||||
def admin_me(admin=Depends(get_current_admin)):
|
||||
return success_response(AdminsService.serialize_admin(admin))
|
||||
|
||||
|
||||
@router.get("/dashboard")
|
||||
def dashboard(
|
||||
_=Depends(require_admin_permission()),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
return success_response(AdminsService(db).dashboard())
|
||||
|
||||
|
||||
@router.get("/users")
|
||||
def list_users(
|
||||
_=Depends(require_admin_permission()),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
return success_response(AdminsService(db).list_users())
|
||||
|
||||
|
||||
@router.get("/users/{user_id}")
|
||||
def get_user_detail(
|
||||
user_id: int,
|
||||
_=Depends(require_admin_permission()),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
return success_response(AdminsService(db).get_user_detail(user_id))
|
||||
|
||||
|
||||
@router.post("/users/{user_id}/wallet-adjust")
|
||||
def manual_adjust_wallet(
|
||||
user_id: int,
|
||||
payload: ManualAdjustRequest,
|
||||
_=Depends(require_admin_permission()),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
return success_response(
|
||||
AdminsService(db).manual_adjust_wallet(user_id, payload.amount_points, payload.reason)
|
||||
)
|
||||
|
||||
|
||||
@router.get("/users/{user_id}/invite-relations")
|
||||
def user_invite_relations(
|
||||
user_id: int,
|
||||
_=Depends(require_admin_permission()),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
return success_response(AdminsService(db).user_invite_relations(user_id))
|
||||
|
||||
|
||||
@router.get("/invite-relations")
|
||||
def list_invite_relations(
|
||||
_=Depends(require_admin_permission()),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
return success_response(AdminsService(db).list_invite_relations())
|
||||
12
backend/app/modules/admins/schema.py
Normal file
12
backend/app/modules/admins/schema.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class AdminLoginRequest(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class ManualAdjustRequest(BaseModel):
|
||||
amount_points: int = Field(alias="amountPoints")
|
||||
reason: str
|
||||
|
||||
134
backend/app/modules/admins/service.py
Normal file
134
backend/app/modules/admins/service.py
Normal file
@@ -0,0 +1,134 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import Response
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.common.errors.app_error import AuthenticationError, NotFoundAppError
|
||||
from app.common.security.jwt import (
|
||||
clear_auth_cookies,
|
||||
create_access_token,
|
||||
create_refresh_token,
|
||||
set_auth_cookies,
|
||||
)
|
||||
from app.common.security.password import verify_password
|
||||
from app.models.entities import InviteRelation
|
||||
from app.modules.admins.repository import AdminsRepository
|
||||
from app.modules.wallets.service import WalletService
|
||||
|
||||
|
||||
class AdminsService:
|
||||
def __init__(self, db: Session) -> None:
|
||||
self.db = db
|
||||
self.repository = AdminsRepository(db)
|
||||
self.wallet_service = WalletService(db)
|
||||
|
||||
def login(self, payload, response: Response) -> dict:
|
||||
admin = self.repository.get_admin_by_username(payload.username)
|
||||
if not admin or not verify_password(payload.password, admin.password_hash):
|
||||
raise AuthenticationError("invalid admin credentials")
|
||||
admin.last_login_at = datetime.utcnow()
|
||||
access_token = create_access_token(admin.username, scope="admin")
|
||||
refresh_token = create_refresh_token(admin.username, scope="admin")
|
||||
set_auth_cookies(response, access_token, refresh_token, prefix="admin")
|
||||
self.db.commit()
|
||||
return self.serialize_admin(admin)
|
||||
|
||||
def logout(self, response: Response) -> None:
|
||||
clear_auth_cookies(response, prefix="admin")
|
||||
|
||||
@staticmethod
|
||||
def serialize_admin(admin) -> dict:
|
||||
return {
|
||||
"id": admin.id,
|
||||
"username": admin.username,
|
||||
"nickname": admin.nickname,
|
||||
"isSuperAdmin": admin.is_super_admin,
|
||||
}
|
||||
|
||||
def dashboard(self) -> dict:
|
||||
total_tasks = self.repository.count_tasks()
|
||||
success_tasks = self.repository.count_success_tasks()
|
||||
return {
|
||||
"users": self.repository.count_users(),
|
||||
"paidOrders": self.repository.count_paid_orders(),
|
||||
"tasks": total_tasks,
|
||||
"successRate": round(success_tasks / total_tasks * 100, 2) if total_tasks else 0,
|
||||
}
|
||||
|
||||
def list_users(self) -> list[dict]:
|
||||
rows = self.repository.list_users().limit(200).all()
|
||||
return [
|
||||
{
|
||||
"id": item.id,
|
||||
"publicId": item.public_id,
|
||||
"username": item.username or "",
|
||||
"nickname": item.nickname,
|
||||
"email": item.email or "",
|
||||
"status": item.status,
|
||||
"createdAt": item.created_at.isoformat(),
|
||||
}
|
||||
for item in rows
|
||||
]
|
||||
|
||||
def get_user_detail(self, user_id: int) -> dict:
|
||||
user = self.repository.get_user(user_id)
|
||||
if not user:
|
||||
raise NotFoundAppError("user not found", code=10020)
|
||||
wallet = self.wallet_service.get_wallet_summary(user.id)
|
||||
return {
|
||||
"id": user.id,
|
||||
"publicId": user.public_id,
|
||||
"username": user.username or "",
|
||||
"nickname": user.nickname,
|
||||
"email": user.email or "",
|
||||
"status": user.status,
|
||||
"wallet": wallet,
|
||||
}
|
||||
|
||||
def manual_adjust_wallet(self, user_id: int, amount_points: int, reason: str) -> dict:
|
||||
user = self.repository.get_user(user_id)
|
||||
if not user:
|
||||
raise NotFoundAppError("user not found", code=10020)
|
||||
tx = self.wallet_service.add_points(
|
||||
user.id,
|
||||
amount_points,
|
||||
biz_type="manual_adjust",
|
||||
related_type="user",
|
||||
related_id=user.id,
|
||||
remark=reason,
|
||||
operator_type="admin",
|
||||
)
|
||||
self.db.commit()
|
||||
return {"transactionNo": tx.transaction_no, "amountPoints": amount_points}
|
||||
|
||||
def user_invite_relations(self, user_id: int) -> list[dict]:
|
||||
rows = self.repository.invite_relations().filter(
|
||||
(InviteRelation.inviter_user_id == user_id) | (InviteRelation.invitee_user_id == user_id)
|
||||
).all()
|
||||
return [
|
||||
{
|
||||
"id": item.id,
|
||||
"inviterUserId": item.inviter_user_id,
|
||||
"inviteeUserId": item.invitee_user_id,
|
||||
"rewardStatus": item.reward_status,
|
||||
"rewardPoints": item.reward_points,
|
||||
"createdAt": item.created_at.isoformat(),
|
||||
}
|
||||
for item in rows
|
||||
]
|
||||
|
||||
def list_invite_relations(self) -> list[dict]:
|
||||
rows = self.repository.invite_relations().limit(200).all()
|
||||
return [
|
||||
{
|
||||
"id": item.id,
|
||||
"inviterUserId": item.inviter_user_id,
|
||||
"inviteeUserId": item.invitee_user_id,
|
||||
"rewardStatus": item.reward_status,
|
||||
"rewardPoints": item.reward_points,
|
||||
"rewardedAt": item.rewarded_at.isoformat() if item.rewarded_at else None,
|
||||
"createdAt": item.created_at.isoformat(),
|
||||
}
|
||||
for item in rows
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user