feat(platform): close AI expense value loop

Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
caoxiaozhu
2026-07-17 14:14:08 +08:00
parent 242d68c36f
commit 787bc3a481
507 changed files with 82072 additions and 6344 deletions

View File

@@ -0,0 +1,142 @@
"""商业不可变账期和管理审计历史。"""
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]