feat(domains): identity, billing, task services and domain placeholders

Billing implements reserve -> commit -> refund against bl_quota and bl_ledger
with row-level locking and idempotency keys; the refund commits on its own
transaction so a failed request cannot silently keep a user's quota. Task
implements the unified task centre over the state machine. Identity issues and
resolves users. Remaining domains are placeholders whose READMEs state what
belongs there and what must not.
This commit is contained in:
xsl
2026-08-14 00:26:39 +08:00
parent 54dc741811
commit 7c67506474
36 changed files with 1213 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
"""Data access for the task domain. **Only this module may touch tk_task.**"""
from __future__ import annotations
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from packages.shared.models import Task
async def get(session: AsyncSession, task_id: int) -> Task | None:
return await session.get(Task, task_id)
async def get_for_update(session: AsyncSession, task_id: int) -> Task | None:
result = await session.execute(select(Task).where(Task.task_id == task_id).with_for_update())
return result.scalar_one_or_none()
async def find_by_idempotency_key(session: AsyncSession, key: str) -> Task | None:
result = await session.execute(select(Task).where(Task.idempotency_key == key))
return result.scalar_one_or_none()
async def list_for_user(session: AsyncSession, user_id: int, limit: int = 20) -> list[Task]:
result = await session.execute(
select(Task)
.where(Task.user_id == user_id)
.order_by(Task.submitted_at.desc(), Task.task_id.desc())
.limit(limit)
)
return list(result.scalars().all())
def add(session: AsyncSession, task: Task) -> Task:
session.add(task)
return task