feat: initialize aivideo project

This commit is contained in:
2026-04-17 18:33:05 +08:00
commit 14b18d67fe
162 changed files with 26251 additions and 0 deletions

View 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())

View 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())

View 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

View 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
]