186 lines
5.9 KiB
Python
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()}",
|
|
)
|