dev #1

Merged
huangyicao merged 12 commits from dev into main 2026-04-27 09:39:05 +08:00
3 changed files with 79 additions and 1 deletions
Showing only changes of commit fb78863ee6 - Show all commits

View File

@@ -252,6 +252,40 @@ class BindingDAO(BaseDAO):
.first() .first()
) )
def list_by_user(self, user_id: int, limit: int = 20, cursor: int = None) -> list[Mapping]:
params = {"user_id": user_id, "limit": limit + 1}
where = "db.owner_user_id = :user_id AND db.status = 1"
if cursor is not None:
where += " AND db.id < :cursor"
params["cursor"] = cursor
rows = (
self.db.execute(
text(
f"""
SELECT
db.id,
db.device_id,
db.child_id,
c.child_name,
db.status,
db.bound_at
FROM device_bindings AS db
LEFT JOIN children AS c
ON c.child_id = db.child_id
AND c.status = 1
WHERE {where}
ORDER BY db.id DESC
LIMIT :limit
"""
),
params,
)
.mappings()
.all()
)
return rows
def get_by_device(self, device_id: str, user_id: int) -> Optional[Mapping]: def get_by_device(self, device_id: str, user_id: int) -> Optional[Mapping]:
return ( return (
self.db.execute( self.db.execute(

View File

@@ -1,7 +1,7 @@
import logging import logging
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, Request, status from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
from pydantic import BaseModel from pydantic import BaseModel
try: try:
@@ -45,6 +45,20 @@ class BindingGetResponse(BaseModel):
bound_at: datetime bound_at: datetime
class BindingListItem(BaseModel):
device_id: str
child_id: int | None
child_name: str | None = None
status: int
bound_at: datetime
class BindingListResponse(BaseModel):
items: list[BindingListItem]
total: int
next_cursor: int | None = None
class BindHistoryItem(BaseModel): class BindHistoryItem(BaseModel):
device_id: str device_id: str
child_id: int | None child_id: int | None
@@ -140,6 +154,30 @@ def get_current_binding(
return binding return binding
@router.get("", response_model=BindingListResponse)
def list_bindings(
request: Request,
cursor: int | None = Query(default=None, ge=1),
limit: int = Query(default=20, ge=1, le=100),
current_user_id: int = Depends(get_current_user_id),
db=Depends(get_db_session),
) -> BindingListResponse:
service = BindingService(db)
rows, has_more = service.list_bindings(current_user_id, limit, cursor)
next_cursor = int(rows[-1]["id"]) if has_more and rows else None
items = [
BindingListItem(
device_id=row["device_id"],
child_id=row["child_id"],
child_name=row["child_name"],
status=row["status"],
bound_at=row["bound_at"],
)
for row in rows
]
return BindingListResponse(items=items, total=len(items), next_cursor=next_cursor)
@router.get("/{device_id}", response_model=BindingGetResponse) @router.get("/{device_id}", response_model=BindingGetResponse)
def get_binding( def get_binding(
device_id: str, device_id: str,

View File

@@ -31,6 +31,12 @@ class BindingService:
def get_current_binding(self, user_id: int) -> Optional[Mapping]: def get_current_binding(self, user_id: int) -> Optional[Mapping]:
return self.dao.get_current_by_user(user_id) return self.dao.get_current_by_user(user_id)
def list_bindings(self, user_id: int, limit: int = 20, cursor: int = None) -> tuple[list, bool]:
rows = self.dao.list_by_user(user_id, limit, cursor)
has_more = len(rows) > limit
rows = rows[:limit]
return rows, has_more
def direct_bind(self, device_id: str, child_id: int | None, user_id: int) -> Mapping: def direct_bind(self, device_id: str, child_id: int | None, user_id: int) -> Mapping:
self.dao.direct_bind(device_id, child_id, user_id) self.dao.direct_bind(device_id, child_id, user_id)
return {"device_id": device_id, "child_id": child_id} return {"device_id": device_id, "child_id": child_id}