Files
banban/talkingq-url/banban/service/location.py
2026-05-06 03:33:28 +08:00

161 lines
5.5 KiB
Python

from collections.abc import Mapping
from dataclasses import dataclass
from datetime import datetime
from typing import Any
from services.database_service_base import DatabaseServiceBase
from database.models import ChildLocationCurrent
try:
from banban.dao.location import LocationDAO, ParentDeviceAccess
from banban.schemas.location import DeviceLocationReportRequest
from banban.service.im import im_service
except ModuleNotFoundError:
from banban.dao.location import LocationDAO, ParentDeviceAccess
from banban.schemas.location import DeviceLocationReportRequest
from banban.service.im import im_service
class LocationService(DatabaseServiceBase):
def __init__(self):
super().__init__(service_name="location_service")
async def assert_parent_device_access(self, *, device_id: str, user_id: int) -> ParentDeviceAccess:
db_session = await self.get_session()
try:
dao = LocationDAO(db_session)
return await dao.assert_parent_device_access(device_id=device_id, user_id=user_id)
finally:
await db_session.close()
async def report_device_location(
self,
*,
device_id: str,
serial_number: str,
payload: DeviceLocationReportRequest,
) -> tuple[Any, Mapping[str, Any]]:
device_identity = await im_service.authenticate_device_identity(
device_id=device_id,
serial_number=serial_number,
)
db_session = await self.get_session()
try:
dao = LocationDAO(db_session)
current_row = await dao.report_device_location(
device_id=device_id,
child_id=device_identity.child_id,
payload=payload,
)
return device_identity, current_row
finally:
await db_session.close()
async def get_device_current_location(
self,
*,
device_id: str,
user_id: int,
) -> Mapping[str, Any]:
db_session = await self.get_session()
try:
dao = LocationDAO(db_session)
return await dao.get_device_current_location(device_id=device_id, user_id=user_id)
finally:
await db_session.close()
async def get_device_trajectory(
self,
*,
device_id: str,
user_id: int,
start_at: datetime | None,
end_at: datetime | None,
limit: int,
) -> tuple[ParentDeviceAccess, list[Mapping[str, Any]]]:
db_session = await self.get_session()
try:
dao = LocationDAO(db_session)
return await dao.get_device_trajectory(
device_id=device_id,
user_id=user_id,
start_at=start_at,
end_at=end_at,
limit=limit,
)
finally:
await db_session.close()
async def insert_or_update(self, device_id: str, location: ChildLocationCurrent) -> None:
db_session = await self.get_session()
try:
dao = LocationDAO(db_session)
current_row = await dao.get_current_location_by_device_id(device_id=device_id)
if current_row:
await dao.update(device_id=device_id, latitude=location.lat, longitude=location.lng)
# 更新成功加到历史记录表
else:
await dao.insert(device_id=device_id, latitude=location.lat, longitude=location.lng)
finally:
await db_session.close()
async def report_mqtt_device_location(
self,
*,
device_id: str,
latitude: float | None,
longitude: float | None,
coord_type: str | None = None,
accuracy_m: int | None = None,
altitude_m: float | None = None,
speed_mps: float | None = None,
heading_deg: int | None = None,
source: int | None = None,
battery_pct: int | None = None,
device_time: datetime | None = None,
) -> Mapping[str, Any] | None:
if latitude is None or longitude is None:
return None
@dataclass(frozen=True)
class _MQTTLocationPayload:
coord_type: str
lat: float
lng: float
accuracy_m: int | None
altitude_m: float | None
speed_mps: float | None
heading_deg: int | None
source: int
battery_pct: int | None
device_time: datetime
db_session = await self.get_session()
try:
dao = LocationDAO(db_session)
binding_row = await dao.get_active_binding_by_device(device_id=device_id)
if not binding_row or binding_row["child_id"] is None:
return None
payload = _MQTTLocationPayload(
coord_type=(coord_type or "gcj02").strip() or "gcj02",
lat=float(latitude),
lng=float(longitude),
accuracy_m=accuracy_m,
altitude_m=altitude_m,
speed_mps=speed_mps,
heading_deg=heading_deg,
source=source if source is not None else 0,
battery_pct=battery_pct,
device_time=device_time or datetime.now(),
)
return await dao.report_device_location(
device_id=device_id,
child_id=int(binding_row["child_id"]),
payload=payload,
)
finally:
await db_session.close()
# 创建全局 LocationService 实例
location_service = LocationService()