diff --git a/banban-mini/src/services/child.ts b/banban-mini/src/services/child.ts index f645901..64e814e 100644 --- a/banban-mini/src/services/child.ts +++ b/banban-mini/src/services/child.ts @@ -88,3 +88,9 @@ export async function updateChild( data, }) } + +export async function deleteChild(childId: number): Promise { + return request(`/banban/children/${childId}`, { + method: 'DELETE', + }) +} diff --git a/talkingq-url/banban/dao/child.py b/talkingq-url/banban/dao/child.py index 71233ac..cd79377 100644 --- a/talkingq-url/banban/dao/child.py +++ b/talkingq-url/banban/dao/child.py @@ -42,11 +42,29 @@ class ChildDAO(BaseDAO): async def get_by_id(self, child_id: int) -> Optional[Mapping]: return ( await self.execute( - "SELECT * FROM children WHERE child_id = :child_id", + "SELECT * FROM children WHERE child_id = :child_id AND status = 1", {"child_id": child_id}, ) ).mappings().first() + async def get_by_parent(self, child_id: int, user_id: int) -> Optional[Mapping]: + return ( + await self.execute( + """ + SELECT c.* + FROM children AS c + JOIN parent_child_relations AS pcr + ON pcr.child_id = c.child_id + WHERE c.child_id = :child_id + AND pcr.user_id = :user_id + AND c.status = 1 + AND pcr.status = 1 + LIMIT 1 + """, + {"child_id": child_id, "user_id": user_id}, + ) + ).mappings().first() + async def list_by_parent(self, user_id: int, limit: int = 20, cursor: int = None) -> list[Mapping]: params = {"user_id": user_id, "limit": limit + 1} where = "pcr.user_id = :user_id AND pcr.status = 1 AND c.status = 1" @@ -104,3 +122,41 @@ class ChildDAO(BaseDAO): {"child_id": child_id, "user_id": user_id}, ) return result.scalar_one_or_none() is not None + + async def soft_delete_for_parent(self, child_id: int, user_id: int) -> bool: + if not await self.has_access(child_id, user_id): + return False + + await self.execute( + """ + UPDATE children + SET status = 0, + updated_at = CURRENT_TIMESTAMP + WHERE child_id = :child_id + AND status = 1 + """, + {"child_id": child_id}, + ) + await self.execute( + """ + UPDATE parent_child_relations + SET status = 0, + updated_at = CURRENT_TIMESTAMP + WHERE child_id = :child_id + AND user_id = :user_id + AND status = 1 + """, + {"child_id": child_id, "user_id": user_id}, + ) + await self.execute( + """ + UPDATE device_bindings + SET child_id = NULL, + updated_at = CURRENT_TIMESTAMP + WHERE child_id = :child_id + AND owner_user_id = :user_id + AND status = 1 + """, + {"child_id": child_id, "user_id": user_id}, + ) + return True diff --git a/talkingq-url/banban/routers/children.py b/talkingq-url/banban/routers/children.py index 6925b86..47c29e3 100644 --- a/talkingq-url/banban/routers/children.py +++ b/talkingq-url/banban/routers/children.py @@ -72,8 +72,9 @@ async def get_child( request: Request, current_user_id: int = Depends(get_current_user_id), ) -> ChildResponse: + del request service = ChildService() - child = await service.get(child_id) + child = await service.get(child_id, current_user_id) if not child: raise HTTPException(status_code=404, detail="child not found") return ChildResponse(**child) @@ -94,3 +95,16 @@ async def update_child( if not child: raise HTTPException(status_code=404, detail="child not found") return ChildResponse(**child) + + +@router.delete("/{child_id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete_child( + child_id: int, + request: Request, + current_user_id: int = Depends(get_current_user_id), +) -> None: + del request + service = ChildService() + deleted = await service.delete(child_id, current_user_id) + if not deleted: + raise HTTPException(status_code=404, detail="child not found") diff --git a/talkingq-url/banban/service/child.py b/talkingq-url/banban/service/child.py index d304cdf..2ddd19b 100644 --- a/talkingq-url/banban/service/child.py +++ b/talkingq-url/banban/service/child.py @@ -52,11 +52,11 @@ class ChildService(DatabaseServiceBase): finally: await db_session.close() - async def get(self, child_id: int) -> Optional[Mapping]: + async def get(self, child_id: int, user_id: int) -> Optional[Mapping]: db_session = await self.get_session() try: dao = ChildDAO(db_session) - return await dao.get_by_id(child_id) + return await dao.get_by_parent(child_id, user_id) finally: await db_session.close() @@ -75,6 +75,21 @@ class ChildService(DatabaseServiceBase): raise PermissionError("No access to this child") await dao.update(child_id, child_name, child_gender, child_birthday) await db_session.commit() - return await self.get(child_id) + return await self.get(child_id, user_id) + finally: + await db_session.close() + + async def delete(self, child_id: int, user_id: int) -> bool: + db_session = await self.get_session() + try: + dao = ChildDAO(db_session) + deleted = await dao.soft_delete_for_parent(child_id, user_id) + if not deleted: + return False + await db_session.commit() + return True + except Exception: + await db_session.rollback() + raise finally: await db_session.close()