Files
video-gen/video-gen-api/app/services/team_manager_service.py
T
2026-07-06 14:06:25 +08:00

186 lines
5.9 KiB
Python

from __future__ import annotations
from datetime import datetime, timezone
from typing import Any
from fastapi import HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.enums.team import TeamStatus
from app.enums.user import UserType
from app.models.team import Team
from app.models.user import User
from app.services.credits import add_credits, deduct_credits
from app.utils.id_gen import generate_id
async def set_team_manager(db: AsyncSession, team_id: str, user_id: str | None) -> Team:
"""设置团队管理人。user_id 为 None 表示取消管理人。"""
result = await db.execute(
select(Team).where(Team.id == team_id, Team.deleted_at.is_(None)).limit(1)
)
team = result.scalar_one_or_none()
if not team:
raise HTTPException(status_code=404, detail="团队不存在")
if user_id is None:
team.manager_id = None
await db.flush()
await db.refresh(team)
return team
# 验证用户存在、是前台用户、属于该团队
user_result = await db.execute(
select(User).where(User.id == user_id, User.is_active.is_(True)).limit(1)
)
user = user_result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
if user.user_type != UserType.FRONTEND.value:
raise HTTPException(status_code=400, detail="仅前台用户可设为团队管理人")
if user.team_id != team_id:
raise HTTPException(status_code=400, detail="用户不属于该团队,请先将其加入团队")
team.manager_id = user_id
await db.flush()
await db.refresh(team)
return team
async def is_team_manager(db: AsyncSession, user_id: str, team_id: str | None) -> bool:
"""判断用户是否是指定团队的管理人。"""
if not team_id:
return False
result = await db.execute(
select(Team.manager_id).where(Team.id == team_id, Team.deleted_at.is_(None)).limit(1)
)
manager_id = result.scalar_one_or_none()
return manager_id == user_id
async def get_managed_team(db: AsyncSession, user_id: str) -> Team | None:
"""获取用户管理的团队。"""
result = await db.execute(
select(Team).where(Team.manager_id == user_id, Team.deleted_at.is_(None)).limit(1)
)
return result.scalar_one_or_none()
async def get_team_members(
db: AsyncSession,
team_id: str,
*,
page: int = 1,
page_size: int = 20,
) -> dict[str, Any]:
"""列出团队成员(仅前台用户)。"""
page = max(int(page or 1), 1)
page_size = min(max(int(page_size or 20), 1), 100)
# 验证团队存在
team_result = await db.execute(
select(Team).where(Team.id == team_id, Team.deleted_at.is_(None)).limit(1)
)
if not team_result.scalar_one_or_none():
raise HTTPException(status_code=404, detail="团队不存在")
# 总数
from sqlalchemy import func
total = (await db.execute(
select(func.count(User.id)).where(
User.user_type == UserType.FRONTEND.value,
User.team_id == team_id,
User.is_active.is_(True),
)
)).scalar() or 0
# 列表
result = await db.execute(
select(User).where(
User.user_type == UserType.FRONTEND.value,
User.team_id == team_id,
User.is_active.is_(True),
)
.order_by(User.created_at.asc())
.offset((page - 1) * page_size)
.limit(page_size)
)
members = list(result.scalars().all())
return {
"items": [
{
"id": m.id,
"username": m.username,
"phone": m.phone,
"credits": m.credits,
"is_active": m.is_active,
"joined_at": m.created_at,
}
for m in members
],
"total": total,
}
async def transfer_credits_to_member(
db: AsyncSession,
manager_id: str,
target_member_id: str,
amount: float,
description: str | None = None,
) -> None:
"""管理人从自己余额转积分给团队成员。"""
if amount <= 0:
raise HTTPException(status_code=400, description="转账积分必须大于0")
# 获取管理人
manager_result = await db.execute(
select(User).where(User.id == manager_id, User.is_active.is_(True)).limit(1)
)
manager = manager_result.scalar_one_or_none()
if not manager:
raise HTTPException(status_code=404, detail="管理员不存在")
# 获取目标成员
member_result = await db.execute(
select(User).where(User.id == target_member_id, User.is_active.is_(True)).limit(1)
)
member = member_result.scalar_one_or_none()
if not member:
raise HTTPException(status_code=404, detail="成员不存在")
# 验证管理人是该团队的管理人且目标是同团队成员
if not manager.team_id:
raise HTTPException(status_code=400, detail="您不在任何团队中")
if member.team_id != manager.team_id:
raise HTTPException(status_code=400, detail="只能转账给同团队成员")
team_result = await db.execute(
select(Team).where(Team.id == manager.team_id, Team.deleted_at.is_(None)).limit(1)
)
team = team_result.scalar_one_or_none()
if not team or team.manager_id != manager_id:
raise HTTPException(status_code=403, detail="只有团队管理人才能分配积分")
desc = description or "团队积分发放"
# 从管理人扣减
await deduct_credits(
db,
manager_id,
amount,
f"分配给成员 {member.username}: {desc}",
biz_key=f"mgr_xfer_out:{manager_id}:{target_member_id}:{generate_id()}",
)
# 给成员增加
await add_credits(
db,
target_member_id,
amount,
f"来自团队管理人: {desc}",
record_type="admin",
biz_key=f"mgr_xfer_in:{manager_id}:{target_member_id}:{generate_id()}",
)