136 lines
4.3 KiB
Python
136 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from fastapi import HTTPException
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.enums.user import UserType
|
|
from app.models.team import Team
|
|
from app.models.user import User
|
|
from app.services.credit.query_service import attach_credit_snapshot, get_user_credit_map
|
|
|
|
|
|
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())
|
|
credit_map = await get_user_credit_map(db, [item.id for item in members])
|
|
for item in members:
|
|
attach_credit_snapshot(item, credit_map.get(item.id, 0.0))
|
|
|
|
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,
|
|
direction: str = "increase", # "increase" 管理人→成员; "decrease" 成员扣减
|
|
description: str | None = None,
|
|
) -> None:
|
|
raise HTTPException(status_code=409, detail="当前版本积分暂未开放团队转账功能")
|