feat: add mqtt raw data management with UTC+8 range UI
All checks were successful
Deploy to Production / deploy (push) Successful in 1m19s

This commit is contained in:
魏风
2026-03-20 17:46:37 +08:00
parent e81aadcd99
commit ff971e2cea
15 changed files with 1279 additions and 7 deletions

View File

@@ -0,0 +1,223 @@
from datetime import date, datetime, timezone
from fastapi.testclient import TestClient
from sqlmodel import Session, delete, func, select
from app.core.config import settings
from app.models import TerminalMessageRaw
from app.mqtt.repository import insert_raw_message
def _reset_raw_data(db: Session) -> None:
db.execute(delete(TerminalMessageRaw))
db.commit()
def _count_raw_rows(db: Session) -> int:
statement = select(func.count()).select_from(TerminalMessageRaw)
return int(db.exec(statement).one())
def test_list_mqtt_raw_days(
client: TestClient, superuser_token_headers: dict[str, str], db: Session
) -> None:
_reset_raw_data(db)
insert_raw_message(
session=db,
device_id="device-a",
topic="terminal/device-a/raw",
payload=b"A1",
qos=1,
retain=False,
received_at=datetime(2026, 3, 10, 8, 0, tzinfo=timezone.utc),
)
insert_raw_message(
session=db,
device_id="device-a",
topic="terminal/device-a/raw",
payload=b"A2",
qos=1,
retain=False,
received_at=datetime(2026, 3, 10, 21, 0, tzinfo=timezone.utc),
)
insert_raw_message(
session=db,
device_id="device-b",
topic="terminal/device-b/raw",
payload=b"B1",
qos=1,
retain=False,
received_at=datetime(2026, 3, 11, 9, 0, tzinfo=timezone.utc),
)
response = client.get(
f"{settings.API_V1_STR}/mqtt-raw/days?skip=0&limit=10",
headers=superuser_token_headers,
)
assert response.status_code == 200
content = response.json()
assert content["count"] == 2
assert content["data"][0]["day"] == "2026-03-11"
assert content["data"][0]["message_count"] == 2
assert content["data"][1]["day"] == "2026-03-10"
assert content["data"][1]["message_count"] == 1
def test_download_mqtt_raw_by_day_or_time_range(
client: TestClient, superuser_token_headers: dict[str, str], db: Session
) -> None:
_reset_raw_data(db)
target_day = date(2026, 3, 12)
insert_raw_message(
session=db,
device_id="device-a",
topic="terminal/device-a/raw",
payload=b"EARLY\n",
qos=1,
retain=False,
received_at=datetime(2026, 3, 11, 20, 0, tzinfo=timezone.utc),
)
insert_raw_message(
session=db,
device_id="device-a",
topic="terminal/device-a/raw",
payload=b"MID\n",
qos=1,
retain=False,
received_at=datetime(2026, 3, 12, 3, 0, tzinfo=timezone.utc),
)
insert_raw_message(
session=db,
device_id="device-a",
topic="terminal/device-a/raw",
payload=b"LATE\n",
qos=1,
retain=False,
received_at=datetime(2026, 3, 12, 12, 0, tzinfo=timezone.utc),
)
insert_raw_message(
session=db,
device_id="device-a",
topic="terminal/device-a/raw",
payload=b"NEXT_DAY\n",
qos=1,
retain=False,
received_at=datetime(2026, 3, 12, 18, 0, tzinfo=timezone.utc),
)
full_day_response = client.get(
f"{settings.API_V1_STR}/mqtt-raw/download",
headers=superuser_token_headers,
params={"day": target_day.isoformat()},
)
assert full_day_response.status_code == 200
assert full_day_response.content == b"EARLY\nMID\nLATE\n"
range_response = client.get(
f"{settings.API_V1_STR}/mqtt-raw/download",
headers=superuser_token_headers,
params={
"day": target_day.isoformat(),
"start_seconds": 10 * 3600,
"end_seconds": 14 * 3600,
},
)
assert range_response.status_code == 200
assert range_response.content == b"MID\n"
def test_delete_mqtt_raw_by_day(
client: TestClient, superuser_token_headers: dict[str, str], db: Session
) -> None:
_reset_raw_data(db)
insert_raw_message(
session=db,
device_id="device-x",
topic="terminal/device-x/raw",
payload=b"X",
qos=1,
retain=False,
received_at=datetime(2026, 3, 14, 20, 30, tzinfo=timezone.utc),
)
insert_raw_message(
session=db,
device_id="device-a",
topic="terminal/device-a/raw",
payload=b"A",
qos=1,
retain=False,
received_at=datetime(2026, 3, 15, 1, 0, tzinfo=timezone.utc),
)
insert_raw_message(
session=db,
device_id="device-b",
topic="terminal/device-b/raw",
payload=b"B",
qos=1,
retain=False,
received_at=datetime(2026, 3, 15, 12, 0, tzinfo=timezone.utc),
)
insert_raw_message(
session=db,
device_id="device-c",
topic="terminal/device-c/raw",
payload=b"C",
qos=1,
retain=False,
received_at=datetime(2026, 3, 15, 16, 30, tzinfo=timezone.utc),
)
response = client.delete(
f"{settings.API_V1_STR}/mqtt-raw/day/2026-03-15",
headers=superuser_token_headers,
)
assert response.status_code == 200
content = response.json()
assert content["deleted_count"] == 3
assert _count_raw_rows(db) == 1
def test_delete_all_mqtt_raw_requires_confirmation(
client: TestClient, superuser_token_headers: dict[str, str], db: Session
) -> None:
_reset_raw_data(db)
insert_raw_message(
session=db,
device_id="device-a",
topic="terminal/device-a/raw",
payload=b"A",
qos=1,
retain=False,
received_at=datetime(2026, 3, 15, 1, 0, tzinfo=timezone.utc),
)
insert_raw_message(
session=db,
device_id="device-b",
topic="terminal/device-b/raw",
payload=b"B",
qos=1,
retain=False,
received_at=datetime(2026, 3, 16, 1, 0, tzinfo=timezone.utc),
)
reject_response = client.delete(
f"{settings.API_V1_STR}/mqtt-raw/all",
headers=superuser_token_headers,
)
assert reject_response.status_code == 400
assert _count_raw_rows(db) == 2
confirmed_response = client.delete(
f"{settings.API_V1_STR}/mqtt-raw/all?confirm=true",
headers=superuser_token_headers,
)
assert confirmed_response.status_code == 200
content = confirmed_response.json()
assert content["deleted_count"] == 2
assert _count_raw_rows(db) == 0