Files
banban/talkingq-url/banban/dao/device_alarm.py
2026-06-22 14:33:10 +08:00

254 lines
8.1 KiB
Python

from collections.abc import Mapping
from datetime import datetime
from typing import Any
from sqlalchemy import text
from banban.dao import BaseDAO
class DeviceAlarmDAO(BaseDAO):
async def create(
self,
*,
device_id: str,
owner_user_id: int | None,
child_id: int | None,
source_msg_id: str = "010",
location: Mapping[str, Any] | None = None,
) -> int:
location = location or {}
address = str(location.get("address") or "").strip() or None
address_status = location.get("address_resolve_status")
if address and address_status is None:
address_status = 1
elif address_status is None and location.get("lat") is not None and location.get("lng") is not None:
address_status = 0
result = await self.execute(
"""
INSERT INTO device_alarm_events (
device_id,
owner_user_id,
child_id,
source_msg_id,
coord_type,
lat,
lng,
location_updated_at,
address,
address_resolved_at,
address_resolve_status,
created_at
)
VALUES (
:device_id,
:owner_user_id,
:child_id,
:source_msg_id,
:coord_type,
:lat,
:lng,
:location_updated_at,
:address,
:address_resolved_at,
:address_resolve_status,
CURRENT_TIMESTAMP
)
""",
{
"device_id": device_id,
"owner_user_id": owner_user_id,
"child_id": child_id,
"source_msg_id": source_msg_id,
"coord_type": location.get("coord_type"),
"lat": location.get("lat"),
"lng": location.get("lng"),
"location_updated_at": location.get("updated_at"),
"address": address,
"address_resolved_at": location.get("address_resolved_at"),
"address_resolve_status": address_status,
},
)
await self.commit()
return int(result.lastrowid)
async def get_by_id(self, *, alarm_id: int, device_id: str | None = None) -> Mapping[str, Any] | None:
where_device = "AND device_id = :device_id" if device_id is not None else ""
params: dict[str, Any] = {"alarm_id": alarm_id}
if device_id is not None:
params["device_id"] = device_id
result = await self.execute(
text(
f"""
SELECT
alarm_id,
device_id,
owner_user_id,
child_id,
source_msg_id,
coord_type,
lat,
lng,
location_updated_at,
address,
address_resolved_at,
address_resolve_status,
created_at
FROM device_alarm_events
WHERE alarm_id = :alarm_id
{where_device}
LIMIT 1
"""
),
params,
)
return result.mappings().first()
async def get_current_location_snapshot(self, *, child_id: int) -> Mapping[str, Any] | None:
result = await self.execute(
text(
"""
SELECT
coord_type,
lat,
lng,
updated_at,
address,
address_resolved_at,
address_resolve_status
FROM child_location_current
WHERE child_id = :child_id
LIMIT 1
"""
),
{"child_id": child_id},
)
return result.mappings().first()
async def update_resolved_address(
self,
*,
alarm_id: int,
address: str | None,
status: int,
) -> bool:
result = await self.execute(
text(
"""
UPDATE device_alarm_events
SET address = :address,
address_resolve_status = :status,
address_resolved_at = CURRENT_TIMESTAMP
WHERE alarm_id = :alarm_id
"""
),
{
"alarm_id": alarm_id,
"address": address,
"status": status,
},
)
await self.commit()
return bool(result.rowcount)
async def update_location_snapshot(
self,
*,
alarm_id: int,
location: Mapping[str, Any],
) -> bool:
lat = location.get("lat")
lng = location.get("lng")
address = str(location.get("address") or "").strip() or None
address_status = location.get("address_resolve_status")
address_resolved_at = location.get("address_resolved_at")
if address and address_status is None:
address_status = 1
elif address_status is None and lat is not None and lng is not None:
address_status = 0
if address and address_resolved_at is None:
address_resolved_at = datetime.now()
result = await self.execute(
text(
"""
UPDATE device_alarm_events
SET coord_type = :coord_type,
lat = :lat,
lng = :lng,
location_updated_at = :location_updated_at,
address = :address,
address_resolved_at = :address_resolved_at,
address_resolve_status = :address_resolve_status
WHERE alarm_id = :alarm_id
"""
),
{
"alarm_id": alarm_id,
"coord_type": location.get("coord_type"),
"lat": lat,
"lng": lng,
"location_updated_at": location.get("updated_at"),
"address": address,
"address_resolved_at": address_resolved_at,
"address_resolve_status": address_status,
},
)
await self.commit()
return bool(result.rowcount)
async def get_active_binding_context(self, *, device_id: str) -> Mapping[str, Any] | None:
result = await self.execute(
text(
"""
SELECT owner_user_id, child_id
FROM device_bindings
WHERE device_id = :device_id
AND status = 1
LIMIT 1
"""
),
{"device_id": device_id},
)
return result.mappings().first()
async def list_by_device(
self,
*,
device_id: str,
limit: int,
) -> list[Mapping[str, Any]]:
result = await self.execute(
text(
"""
SELECT
dae.alarm_id,
dae.device_id,
dae.owner_user_id,
dae.child_id,
dae.source_msg_id,
dae.coord_type,
dae.lat,
dae.lng,
dae.location_updated_at,
dae.address,
dae.address_resolved_at,
dae.address_resolve_status,
dae.created_at,
c.child_name
FROM device_alarm_events AS dae
LEFT JOIN children AS c
ON c.child_id = dae.child_id
AND c.status = 1
WHERE dae.device_id = :device_id
ORDER BY dae.created_at DESC, dae.alarm_id DESC
LIMIT :limit
"""
),
{
"device_id": device_id,
"limit": limit,
},
)
return list(result.mappings().all())