218 lines
6.8 KiB
Python
218 lines
6.8 KiB
Python
"""
|
||
StarSystem Service
|
||
恒星系统服务层
|
||
"""
|
||
from typing import List, Optional
|
||
from sqlalchemy import select, func, update, delete, or_
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from app.models.db.star_system import StarSystem
|
||
from app.models.db.celestial_body import CelestialBody
|
||
|
||
|
||
class StarSystemService:
|
||
"""恒星系统服务"""
|
||
|
||
@staticmethod
|
||
async def get_all(
|
||
db: AsyncSession,
|
||
skip: int = 0,
|
||
limit: int = 100,
|
||
exclude_solar: bool = False,
|
||
search: Optional[str] = None
|
||
) -> List[StarSystem]:
|
||
"""
|
||
获取所有恒星系统
|
||
|
||
Args:
|
||
db: 数据库会话
|
||
skip: 跳过记录数
|
||
limit: 返回记录数
|
||
exclude_solar: 是否排除太阳系
|
||
search: 搜索关键词(匹配名称)
|
||
"""
|
||
query = select(StarSystem).order_by(StarSystem.distance_pc.asc().nulls_first())
|
||
|
||
# 排除太阳系
|
||
if exclude_solar:
|
||
query = query.where(StarSystem.id != 1)
|
||
|
||
# 搜索
|
||
if search:
|
||
search_pattern = f"%{search}%"
|
||
query = query.where(
|
||
or_(
|
||
StarSystem.name.ilike(search_pattern),
|
||
StarSystem.name_zh.ilike(search_pattern),
|
||
StarSystem.host_star_name.ilike(search_pattern)
|
||
)
|
||
)
|
||
|
||
query = query.offset(skip).limit(limit)
|
||
result = await db.execute(query)
|
||
return list(result.scalars().all())
|
||
|
||
@staticmethod
|
||
async def get_by_id(db: AsyncSession, system_id: int) -> Optional[StarSystem]:
|
||
"""根据ID获取恒星系统"""
|
||
result = await db.execute(
|
||
select(StarSystem).where(StarSystem.id == system_id)
|
||
)
|
||
return result.scalar_one_or_none()
|
||
|
||
@staticmethod
|
||
async def get_by_name(db: AsyncSession, name: str) -> Optional[StarSystem]:
|
||
"""根据名称获取恒星系统"""
|
||
result = await db.execute(
|
||
select(StarSystem).where(StarSystem.name == name)
|
||
)
|
||
return result.scalar_one_or_none()
|
||
|
||
@staticmethod
|
||
async def create(db: AsyncSession, system_data: dict) -> StarSystem:
|
||
"""创建恒星系统"""
|
||
system = StarSystem(**system_data)
|
||
db.add(system)
|
||
await db.commit()
|
||
await db.refresh(system)
|
||
return system
|
||
|
||
@staticmethod
|
||
async def update(db: AsyncSession, system_id: int, system_data: dict) -> Optional[StarSystem]:
|
||
"""更新恒星系统"""
|
||
result = await db.execute(
|
||
select(StarSystem).where(StarSystem.id == system_id)
|
||
)
|
||
system = result.scalar_one_or_none()
|
||
|
||
if not system:
|
||
return None
|
||
|
||
for key, value in system_data.items():
|
||
if hasattr(system, key):
|
||
setattr(system, key, value)
|
||
|
||
await db.commit()
|
||
await db.refresh(system)
|
||
return system
|
||
|
||
@staticmethod
|
||
async def delete_system(db: AsyncSession, system_id: int) -> bool:
|
||
"""
|
||
删除恒星系统(级联删除所有关联天体)
|
||
不允许删除太阳系(id=1)
|
||
"""
|
||
if system_id == 1:
|
||
raise ValueError("不能删除太阳系")
|
||
|
||
result = await db.execute(
|
||
delete(StarSystem).where(StarSystem.id == system_id)
|
||
)
|
||
await db.commit()
|
||
return result.rowcount > 0
|
||
|
||
@staticmethod
|
||
async def get_with_bodies(db: AsyncSession, system_id: int) -> Optional[dict]:
|
||
"""
|
||
获取恒星系统及其所有天体
|
||
|
||
Returns:
|
||
包含 system 和 bodies 的字典
|
||
"""
|
||
# 获取恒星系统
|
||
system_result = await db.execute(
|
||
select(StarSystem).where(StarSystem.id == system_id)
|
||
)
|
||
system = system_result.scalar_one_or_none()
|
||
|
||
if not system:
|
||
return None
|
||
|
||
# 获取关联的天体
|
||
bodies_result = await db.execute(
|
||
select(CelestialBody)
|
||
.where(CelestialBody.system_id == system_id)
|
||
.order_by(CelestialBody.type, CelestialBody.name)
|
||
)
|
||
bodies = list(bodies_result.scalars().all())
|
||
|
||
return {
|
||
"system": system,
|
||
"bodies": bodies,
|
||
"body_count": len(bodies)
|
||
}
|
||
|
||
@staticmethod
|
||
async def update_planet_count(db: AsyncSession, system_id: int) -> None:
|
||
"""更新恒星系统的行星数量统计"""
|
||
result = await db.execute(
|
||
select(func.count(CelestialBody.id))
|
||
.where(CelestialBody.system_id == system_id)
|
||
.where(CelestialBody.type != 'star') # 排除恒星本身
|
||
)
|
||
count = result.scalar()
|
||
|
||
await db.execute(
|
||
update(StarSystem)
|
||
.where(StarSystem.id == system_id)
|
||
.values(planet_count=count)
|
||
)
|
||
await db.commit()
|
||
|
||
@staticmethod
|
||
async def get_statistics(db: AsyncSession) -> dict:
|
||
"""获取恒星系统统计信息"""
|
||
# 总恒星系统数
|
||
total_systems_result = await db.execute(select(func.count(StarSystem.id)))
|
||
total_systems = total_systems_result.scalar()
|
||
|
||
# 系外恒星系统数
|
||
exo_systems_result = await db.execute(
|
||
select(func.count(StarSystem.id)).where(StarSystem.id != 1)
|
||
)
|
||
exo_systems = exo_systems_result.scalar()
|
||
|
||
# 总行星数
|
||
total_planets_result = await db.execute(
|
||
select(func.count(CelestialBody.id))
|
||
.where(CelestialBody.type == 'planet')
|
||
)
|
||
total_planets = total_planets_result.scalar()
|
||
|
||
# 系外行星数
|
||
exo_planets_result = await db.execute(
|
||
select(func.count(CelestialBody.id))
|
||
.where(CelestialBody.type == 'planet')
|
||
.where(CelestialBody.system_id > 1)
|
||
)
|
||
exo_planets = exo_planets_result.scalar()
|
||
|
||
# 距离最近的10个恒星系统
|
||
nearest_systems_result = await db.execute(
|
||
select(StarSystem.name, StarSystem.name_zh, StarSystem.distance_ly, StarSystem.planet_count)
|
||
.where(StarSystem.id != 1)
|
||
.order_by(StarSystem.distance_pc.asc())
|
||
.limit(10)
|
||
)
|
||
nearest_systems = [
|
||
{
|
||
"name": name,
|
||
"name_zh": name_zh,
|
||
"distance_ly": distance_ly,
|
||
"planet_count": planet_count
|
||
}
|
||
for name, name_zh, distance_ly, planet_count in nearest_systems_result
|
||
]
|
||
|
||
return {
|
||
"total_systems": total_systems,
|
||
"exo_systems": exo_systems,
|
||
"total_planets": total_planets,
|
||
"exo_planets": exo_planets,
|
||
"solar_system_planets": total_planets - exo_planets,
|
||
"nearest_systems": nearest_systems
|
||
}
|
||
|
||
|
||
# 创建服务实例
|
||
star_system_service = StarSystemService()
|