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="当前版本积分暂未开放团队转账功能")