341 lines
10 KiB
Python
341 lines
10 KiB
Python
"""组织管理API"""
|
||
from fastapi import APIRouter, Depends, HTTPException
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from sqlalchemy import select, and_
|
||
from typing import List
|
||
|
||
from app.database import get_db
|
||
from app.models.relationship import Organization, OrganizationMember
|
||
from app.models.character import Character
|
||
from app.schemas.relationship import (
|
||
OrganizationCreate,
|
||
OrganizationUpdate,
|
||
OrganizationResponse,
|
||
OrganizationDetailResponse,
|
||
OrganizationMemberCreate,
|
||
OrganizationMemberUpdate,
|
||
OrganizationMemberResponse,
|
||
OrganizationMemberDetailResponse
|
||
)
|
||
from app.logger import get_logger
|
||
|
||
router = APIRouter(prefix="/organizations", tags=["组织管理"])
|
||
logger = get_logger(__name__)
|
||
|
||
|
||
@router.get("/project/{project_id}", response_model=List[OrganizationDetailResponse], summary="获取项目的所有组织")
|
||
async def get_project_organizations(
|
||
project_id: str,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
获取项目中的所有组织及其详情
|
||
|
||
返回组织的基本信息和统计数据
|
||
"""
|
||
result = await db.execute(
|
||
select(Organization).where(Organization.project_id == project_id)
|
||
)
|
||
organizations = result.scalars().all()
|
||
|
||
# 获取每个组织的角色信息
|
||
org_list = []
|
||
for org in organizations:
|
||
char_result = await db.execute(
|
||
select(Character).where(Character.id == org.character_id)
|
||
)
|
||
char = char_result.scalar_one_or_none()
|
||
|
||
if char:
|
||
org_list.append(OrganizationDetailResponse(
|
||
id=org.id,
|
||
character_id=org.character_id,
|
||
name=char.name,
|
||
type=char.organization_type,
|
||
purpose=char.organization_purpose,
|
||
member_count=org.member_count,
|
||
power_level=org.power_level,
|
||
location=org.location,
|
||
motto=org.motto,
|
||
color=org.color
|
||
))
|
||
|
||
logger.info(f"获取项目 {project_id} 的组织列表,共 {len(org_list)} 个")
|
||
return org_list
|
||
|
||
|
||
@router.get("/{org_id}", response_model=OrganizationResponse, summary="获取组织详情")
|
||
async def get_organization(
|
||
org_id: str,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""获取组织的详细信息"""
|
||
result = await db.execute(
|
||
select(Organization).where(Organization.id == org_id)
|
||
)
|
||
org = result.scalar_one_or_none()
|
||
|
||
if not org:
|
||
raise HTTPException(status_code=404, detail="组织不存在")
|
||
|
||
return org
|
||
|
||
|
||
@router.post("/", response_model=OrganizationResponse, summary="创建组织")
|
||
async def create_organization(
|
||
organization: OrganizationCreate,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
创建新组织
|
||
|
||
- 需要关联到一个已存在的角色记录(is_organization=True)
|
||
- 可以设置父组织、势力等级等属性
|
||
"""
|
||
# 验证角色是否存在且是组织
|
||
char_result = await db.execute(
|
||
select(Character).where(Character.id == organization.character_id)
|
||
)
|
||
char = char_result.scalar_one_or_none()
|
||
|
||
if not char:
|
||
raise HTTPException(status_code=404, detail="关联的角色不存在")
|
||
if not char.is_organization:
|
||
raise HTTPException(status_code=400, detail="关联的角色不是组织类型")
|
||
|
||
# 检查是否已存在
|
||
existing = await db.execute(
|
||
select(Organization).where(Organization.character_id == organization.character_id)
|
||
)
|
||
if existing.scalar_one_or_none():
|
||
raise HTTPException(status_code=400, detail="该角色已有组织详情记录")
|
||
|
||
# 创建组织
|
||
db_org = Organization(**organization.model_dump())
|
||
db.add(db_org)
|
||
await db.commit()
|
||
await db.refresh(db_org)
|
||
|
||
logger.info(f"创建组织成功:{db_org.id} - {char.name}")
|
||
return db_org
|
||
|
||
|
||
@router.put("/{org_id}", response_model=OrganizationResponse, summary="更新组织")
|
||
async def update_organization(
|
||
org_id: str,
|
||
organization: OrganizationUpdate,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""更新组织的属性"""
|
||
result = await db.execute(
|
||
select(Organization).where(Organization.id == org_id)
|
||
)
|
||
db_org = result.scalar_one_or_none()
|
||
|
||
if not db_org:
|
||
raise HTTPException(status_code=404, detail="组织不存在")
|
||
|
||
# 更新字段
|
||
update_data = organization.model_dump(exclude_unset=True)
|
||
for field, value in update_data.items():
|
||
setattr(db_org, field, value)
|
||
|
||
await db.commit()
|
||
await db.refresh(db_org)
|
||
|
||
logger.info(f"更新组织成功:{org_id}")
|
||
return db_org
|
||
|
||
|
||
@router.delete("/{org_id}", summary="删除组织")
|
||
async def delete_organization(
|
||
org_id: str,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""删除组织(会级联删除所有成员关系)"""
|
||
result = await db.execute(
|
||
select(Organization).where(Organization.id == org_id)
|
||
)
|
||
db_org = result.scalar_one_or_none()
|
||
|
||
if not db_org:
|
||
raise HTTPException(status_code=404, detail="组织不存在")
|
||
|
||
await db.delete(db_org)
|
||
await db.commit()
|
||
|
||
logger.info(f"删除组织成功:{org_id}")
|
||
return {"message": "组织删除成功", "id": org_id}
|
||
|
||
|
||
# ============ 组织成员管理 ============
|
||
|
||
@router.get("/{org_id}/members", response_model=List[OrganizationMemberDetailResponse], summary="获取组织成员")
|
||
async def get_organization_members(
|
||
org_id: str,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
获取组织的所有成员
|
||
|
||
按职位等级(rank)降序排列
|
||
"""
|
||
# 验证组织存在
|
||
org_result = await db.execute(
|
||
select(Organization).where(Organization.id == org_id)
|
||
)
|
||
if not org_result.scalar_one_or_none():
|
||
raise HTTPException(status_code=404, detail="组织不存在")
|
||
|
||
# 获取成员列表
|
||
result = await db.execute(
|
||
select(OrganizationMember)
|
||
.where(OrganizationMember.organization_id == org_id)
|
||
.order_by(OrganizationMember.rank.desc(), OrganizationMember.created_at)
|
||
)
|
||
members = result.scalars().all()
|
||
|
||
# 获取成员角色信息
|
||
member_list = []
|
||
for member in members:
|
||
char_result = await db.execute(
|
||
select(Character).where(Character.id == member.character_id)
|
||
)
|
||
char = char_result.scalar_one_or_none()
|
||
|
||
if char:
|
||
member_list.append(OrganizationMemberDetailResponse(
|
||
id=member.id,
|
||
character_id=member.character_id,
|
||
character_name=char.name,
|
||
position=member.position,
|
||
rank=member.rank,
|
||
loyalty=member.loyalty,
|
||
contribution=member.contribution,
|
||
status=member.status,
|
||
joined_at=member.joined_at,
|
||
left_at=member.left_at,
|
||
notes=member.notes
|
||
))
|
||
|
||
logger.info(f"获取组织 {org_id} 的成员列表,共 {len(member_list)} 人")
|
||
return member_list
|
||
|
||
|
||
@router.post("/{org_id}/members", response_model=OrganizationMemberResponse, summary="添加组织成员")
|
||
async def add_organization_member(
|
||
org_id: str,
|
||
member: OrganizationMemberCreate,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
添加角色到组织
|
||
|
||
- 一个角色在同一组织中只能有一个职位
|
||
- 会自动更新组织的成员计数
|
||
"""
|
||
# 验证组织存在
|
||
org_result = await db.execute(
|
||
select(Organization).where(Organization.id == org_id)
|
||
)
|
||
org = org_result.scalar_one_or_none()
|
||
if not org:
|
||
raise HTTPException(status_code=404, detail="组织不存在")
|
||
|
||
# 验证角色存在
|
||
char_result = await db.execute(
|
||
select(Character).where(Character.id == member.character_id)
|
||
)
|
||
char = char_result.scalar_one_or_none()
|
||
if not char:
|
||
raise HTTPException(status_code=404, detail="角色不存在")
|
||
if char.is_organization:
|
||
raise HTTPException(status_code=400, detail="不能将组织添加为成员")
|
||
|
||
# 检查是否已存在
|
||
existing = await db.execute(
|
||
select(OrganizationMember).where(
|
||
and_(
|
||
OrganizationMember.organization_id == org_id,
|
||
OrganizationMember.character_id == member.character_id
|
||
)
|
||
)
|
||
)
|
||
if existing.scalar_one_or_none():
|
||
raise HTTPException(status_code=400, detail="该角色已在组织中")
|
||
|
||
# 创建成员关系
|
||
db_member = OrganizationMember(
|
||
organization_id=org_id,
|
||
**member.model_dump(),
|
||
source="manual"
|
||
)
|
||
db.add(db_member)
|
||
|
||
# 更新组织成员计数
|
||
org.member_count += 1
|
||
|
||
await db.commit()
|
||
await db.refresh(db_member)
|
||
|
||
logger.info(f"添加成员成功:{char.name} 加入组织 {org_id}")
|
||
return db_member
|
||
|
||
|
||
@router.put("/members/{member_id}", response_model=OrganizationMemberResponse, summary="更新成员信息")
|
||
async def update_organization_member(
|
||
member_id: str,
|
||
member: OrganizationMemberUpdate,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""更新组织成员的职位、忠诚度等信息"""
|
||
result = await db.execute(
|
||
select(OrganizationMember).where(OrganizationMember.id == member_id)
|
||
)
|
||
db_member = result.scalar_one_or_none()
|
||
|
||
if not db_member:
|
||
raise HTTPException(status_code=404, detail="成员记录不存在")
|
||
|
||
# 更新字段
|
||
update_data = member.model_dump(exclude_unset=True)
|
||
for field, value in update_data.items():
|
||
setattr(db_member, field, value)
|
||
|
||
await db.commit()
|
||
await db.refresh(db_member)
|
||
|
||
logger.info(f"更新成员信息成功:{member_id}")
|
||
return db_member
|
||
|
||
|
||
@router.delete("/members/{member_id}", summary="移除组织成员")
|
||
async def remove_organization_member(
|
||
member_id: str,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""
|
||
从组织中移除成员
|
||
|
||
会自动更新组织的成员计数
|
||
"""
|
||
result = await db.execute(
|
||
select(OrganizationMember).where(OrganizationMember.id == member_id)
|
||
)
|
||
db_member = result.scalar_one_or_none()
|
||
|
||
if not db_member:
|
||
raise HTTPException(status_code=404, detail="成员记录不存在")
|
||
|
||
# 更新组织成员计数
|
||
org_result = await db.execute(
|
||
select(Organization).where(Organization.id == db_member.organization_id)
|
||
)
|
||
org = org_result.scalar_one()
|
||
org.member_count = max(0, org.member_count - 1)
|
||
|
||
await db.delete(db_member)
|
||
await db.commit()
|
||
|
||
logger.info(f"移除成员成功:{member_id}")
|
||
return {"message": "成员移除成功", "id": member_id} |