Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
143 lines
4.3 KiB
Python
143 lines
4.3 KiB
Python
"""商业不可变账期和管理审计历史。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.api.deps import (
|
|
CurrentUserContext,
|
|
get_current_user,
|
|
get_db,
|
|
require_platform_admin_user,
|
|
)
|
|
from app.schemas.commercial_billing import (
|
|
CommercialAdminEventRead,
|
|
CommercialBillingPeriodRead,
|
|
CommercialBillingPeriodTenantRead,
|
|
)
|
|
from app.services.commercial_access_policy import (
|
|
CommercialAccessPolicy,
|
|
CommercialPermissionError,
|
|
)
|
|
from app.services.commercial_billing_periods import CommercialBillingPeriodService
|
|
from app.services.commercial_queries import CommercialQueryService
|
|
|
|
router = APIRouter(prefix="/commercial")
|
|
DbSession = Annotated[Session, Depends(get_db)]
|
|
CurrentUser = Annotated[CurrentUserContext, Depends(get_current_user)]
|
|
PlatformAdmin = Annotated[CurrentUserContext, Depends(require_platform_admin_user)]
|
|
|
|
|
|
@router.get(
|
|
"/billing-periods",
|
|
response_model=list[CommercialBillingPeriodTenantRead],
|
|
summary="读取当前租户不可变账期历史",
|
|
)
|
|
def list_current_tenant_billing_periods(
|
|
db: DbSession,
|
|
current_user: CurrentUser,
|
|
subscription_id: str | None = None,
|
|
as_of: datetime | None = None,
|
|
limit: int = Query(default=200, ge=1, le=500),
|
|
offset: int = Query(default=0, ge=0),
|
|
) -> list[CommercialBillingPeriodTenantRead]:
|
|
try:
|
|
tenant_id = CommercialAccessPolicy.require_account_read(current_user)
|
|
rows = _period_reads(
|
|
db,
|
|
tenant_id,
|
|
subscription_id=subscription_id,
|
|
as_of=as_of,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
return [CommercialBillingPeriodTenantRead.model_validate(row.model_dump()) for row in rows]
|
|
except CommercialPermissionError as error:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(error)) from error
|
|
|
|
|
|
@router.get(
|
|
"/admin/tenants/{tenant_id}/billing-periods",
|
|
response_model=list[CommercialBillingPeriodRead],
|
|
summary="平台管理员读取租户不可变账期历史",
|
|
)
|
|
def list_tenant_billing_periods(
|
|
tenant_id: str,
|
|
db: DbSession,
|
|
current_user: PlatformAdmin,
|
|
subscription_id: str | None = None,
|
|
as_of: datetime | None = None,
|
|
limit: int = Query(default=200, ge=1, le=500),
|
|
offset: int = Query(default=0, ge=0),
|
|
) -> list[CommercialBillingPeriodRead]:
|
|
target = CommercialAccessPolicy.require_platform_admin(
|
|
current_user,
|
|
target_tenant_id=tenant_id,
|
|
)
|
|
return _period_reads(
|
|
db,
|
|
target,
|
|
subscription_id=subscription_id,
|
|
as_of=as_of,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
|
|
|
|
@router.get(
|
|
"/admin/tenants/{tenant_id}/admin-events",
|
|
response_model=list[CommercialAdminEventRead],
|
|
summary="平台管理员读取商业配置与续期审计",
|
|
)
|
|
def list_tenant_commercial_admin_events(
|
|
tenant_id: str,
|
|
db: DbSession,
|
|
current_user: PlatformAdmin,
|
|
action: str | None = None,
|
|
resource_type: str | None = None,
|
|
resource_id: str | None = None,
|
|
start: datetime | None = None,
|
|
end: datetime | None = None,
|
|
limit: int = Query(default=200, ge=1, le=500),
|
|
offset: int = Query(default=0, ge=0),
|
|
) -> list[CommercialAdminEventRead]:
|
|
target = CommercialAccessPolicy.require_platform_admin(
|
|
current_user,
|
|
target_tenant_id=tenant_id,
|
|
)
|
|
try:
|
|
return CommercialQueryService(db).list_admin_events(
|
|
target,
|
|
action=action,
|
|
resource_type=resource_type,
|
|
resource_id=resource_id,
|
|
start=start,
|
|
end=end,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
except ValueError as error:
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(error)) from error
|
|
|
|
|
|
def _period_reads(
|
|
db: Session,
|
|
tenant_id: str,
|
|
*,
|
|
subscription_id: str | None,
|
|
as_of: datetime | None,
|
|
limit: int,
|
|
offset: int,
|
|
) -> list[CommercialBillingPeriodRead]:
|
|
rows = CommercialQueryService(db).list_billing_periods(
|
|
tenant_id,
|
|
subscription_id=subscription_id,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
return [CommercialBillingPeriodService.to_read(row, as_of=as_of) for row in rows]
|