feat: 接入起卦后端流程并完善积分扣减链路
This commit is contained in:
@@ -17,6 +17,7 @@ from core.agentscope.schemas.agui_input import extract_latest_user_payload
|
||||
from core.config.settings import config
|
||||
from core.logging import get_logger
|
||||
from schemas.agent.forwarded_props import (
|
||||
parse_forwarded_props_divination_payload,
|
||||
parse_forwarded_props_runtime_mode,
|
||||
RuntimeMode,
|
||||
)
|
||||
@@ -93,11 +94,22 @@ class AgentService:
|
||||
forwarded_props = getattr(run_input, "forwarded_props", None)
|
||||
try:
|
||||
runtime_mode = parse_forwarded_props_runtime_mode(forwarded_props)
|
||||
divination_payload = parse_forwarded_props_divination_payload(
|
||||
forwarded_props
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise ApiProblemError(
|
||||
status_code=422,
|
||||
detail=problem_payload(code="AGENT_PAYLOAD_INVALID", detail=str(exc)),
|
||||
) from exc
|
||||
if divination_payload is None:
|
||||
raise ApiProblemError(
|
||||
status_code=422,
|
||||
detail=problem_payload(
|
||||
code="AGENT_DIVINATION_PAYLOAD_REQUIRED",
|
||||
detail="forwardedProps.divinationPayload is required",
|
||||
),
|
||||
)
|
||||
|
||||
if runtime_config is None:
|
||||
from v1.agent.system_agents_config import (
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from core.db import get_db
|
||||
from v1.points.repository import PointsRepository
|
||||
from v1.points.service import PointsService
|
||||
|
||||
|
||||
def get_points_service(session: AsyncSession = Depends(get_db)) -> PointsService:
|
||||
return PointsService(repository=PointsRepository(session))
|
||||
@@ -56,3 +56,14 @@ class PointsRepository:
|
||||
)
|
||||
self._session.add(entry)
|
||||
await self._session.flush()
|
||||
|
||||
async def get_user_points(self, *, user_id: UUID) -> UserPoints:
|
||||
insert_stmt = (
|
||||
insert(UserPoints)
|
||||
.values(user_id=user_id)
|
||||
.on_conflict_do_nothing(index_elements=[UserPoints.user_id])
|
||||
)
|
||||
await self._session.execute(insert_stmt)
|
||||
|
||||
stmt = select(UserPoints).where(UserPoints.user_id == user_id)
|
||||
return (await self._session.execute(stmt)).scalar_one()
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
from core.auth.models import CurrentUser
|
||||
from v1.points.dependencies import get_points_service
|
||||
from v1.points.schemas import PointsBalanceResponse
|
||||
from v1.points.service import PointsService
|
||||
from v1.users.dependencies import get_current_user
|
||||
|
||||
router = APIRouter(prefix="/points", tags=["points"])
|
||||
|
||||
|
||||
@router.get("/balance", response_model=PointsBalanceResponse)
|
||||
async def get_points_balance(
|
||||
service: Annotated[PointsService, Depends(get_points_service)],
|
||||
current_user: Annotated[CurrentUser, Depends(get_current_user)],
|
||||
) -> PointsBalanceResponse:
|
||||
result = await service.get_points_balance(user_id=current_user.id)
|
||||
return PointsBalanceResponse(
|
||||
balance=result.balance,
|
||||
frozenBalance=result.frozen_balance,
|
||||
availableBalance=result.available_balance,
|
||||
runCost=result.run_cost,
|
||||
canRun=result.can_run,
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class PointsBalanceResponse(BaseModel):
|
||||
model_config = ConfigDict(populate_by_name=True, serialize_by_alias=True)
|
||||
|
||||
balance: int = Field(ge=0)
|
||||
frozen_balance: int = Field(alias="frozenBalance", ge=0)
|
||||
available_balance: int = Field(alias="availableBalance", ge=0)
|
||||
run_cost: int = Field(alias="runCost", gt=0)
|
||||
can_run: bool = Field(alias="canRun")
|
||||
@@ -22,6 +22,15 @@ class RunChargeResult:
|
||||
event_id: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PointsBalanceResult:
|
||||
balance: int
|
||||
frozen_balance: int
|
||||
available_balance: int
|
||||
run_cost: int
|
||||
can_run: bool
|
||||
|
||||
|
||||
class PointsService:
|
||||
def __init__(self, repository: PointsRepository) -> None:
|
||||
self._repository = repository
|
||||
@@ -51,6 +60,23 @@ class PointsService:
|
||||
)
|
||||
return available
|
||||
|
||||
async def get_points_balance(
|
||||
self,
|
||||
*,
|
||||
user_id: UUID,
|
||||
) -> PointsBalanceResult:
|
||||
account = await self._repository.get_user_points(user_id=user_id)
|
||||
balance = int(account.balance)
|
||||
frozen_balance = int(account.frozen_balance)
|
||||
available = max(balance - frozen_balance, 0)
|
||||
return PointsBalanceResult(
|
||||
balance=balance,
|
||||
frozen_balance=frozen_balance,
|
||||
available_balance=available,
|
||||
run_cost=RUN_POINTS_COST,
|
||||
can_run=available >= RUN_POINTS_COST,
|
||||
)
|
||||
|
||||
async def consume_successful_run_points(
|
||||
self,
|
||||
*,
|
||||
|
||||
@@ -4,8 +4,10 @@ from fastapi import APIRouter
|
||||
|
||||
from v1.agent.router import router as agent_router
|
||||
from v1.auth.router import router as auth_router
|
||||
from v1.points.router import router as points_router
|
||||
|
||||
|
||||
router = APIRouter(prefix="/api/v1")
|
||||
router.include_router(auth_router)
|
||||
router.include_router(agent_router)
|
||||
router.include_router(points_router)
|
||||
|
||||
Reference in New Issue
Block a user