161 lines
5.5 KiB
Python
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()
|