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,607 @@
from __future__ import annotations
import uuid
from datetime import datetime
from decimal import Decimal
from typing import Any
from sqlalchemy import (
Boolean,
CheckConstraint,
DateTime,
ForeignKeyConstraint,
Index,
Integer,
Numeric,
String,
UniqueConstraint,
func,
text,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.types import JSON
from app.db.base_class import Base
def _new_id() -> str:
return str(uuid.uuid4())
class TenantCommercialPlan(Base):
"""租户已协商的商业套餐版本,不与平台内部成本或客户节省事实混用。"""
__tablename__ = "tenant_commercial_plans"
__table_args__ = (
UniqueConstraint("tenant_id", "id", name="uq_tenant_commercial_plans_tenant_id"),
UniqueConstraint(
"tenant_id",
"plan_code",
"version",
name="uq_tenant_commercial_plans_tenant_code_version",
),
CheckConstraint(
"pricing_model IN ('subscription', 'usage', 'hybrid', 'pilot', 'custom')",
name="ck_tenant_commercial_plans_pricing_model",
),
CheckConstraint(
"billing_interval IN ('monthly', 'quarterly', 'annual', 'contract')",
name="ck_tenant_commercial_plans_billing_interval",
),
CheckConstraint(
"status IN ('draft', 'active', 'retired')",
name="ck_tenant_commercial_plans_status",
),
CheckConstraint(
"base_fee >= 0 AND included_seats >= 0 AND version >= 1",
name="ck_tenant_commercial_plans_values",
),
CheckConstraint(
"length(trim(plan_code)) > 0 AND length(trim(name)) > 0",
name="ck_tenant_commercial_plans_keys",
),
CheckConstraint(
"length(trim(currency)) = 3",
name="ck_tenant_commercial_plans_currency",
),
CheckConstraint(
"effective_to IS NULL OR effective_to > effective_from",
name="ck_tenant_commercial_plans_effective_window",
),
Index(
"uq_tenant_commercial_plans_active_code",
"tenant_id",
"plan_code",
unique=True,
postgresql_where=text("status = 'active'"),
),
Index(
"ix_tenant_commercial_plans_tenant_status",
"tenant_id",
"status",
"effective_from",
),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
plan_code: Mapped[str] = mapped_column(String(80), nullable=False)
name: Mapped[str] = mapped_column(String(160), nullable=False)
pricing_model: Mapped[str] = mapped_column(String(24), nullable=False)
billing_interval: Mapped[str] = mapped_column(String(20), nullable=False)
currency: Mapped[str] = mapped_column(String(3), nullable=False)
base_fee: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
included_seats: Mapped[int] = mapped_column(
Integer, nullable=False, default=0, server_default="0"
)
overage_enabled: Mapped[bool] = mapped_column(
Boolean, nullable=False, default=False, server_default="false"
)
status: Mapped[str] = mapped_column(
String(16), nullable=False, default="draft", server_default="draft"
)
effective_from: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
effective_to: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
contract_terms_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
created_by: Mapped[str] = mapped_column(String(120), nullable=False)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
)
subscriptions = relationship("TenantSubscription", back_populates="plan", passive_deletes=True)
class TenantSubscription(Base):
"""租户订阅及当期计费快照,避免套餐后续变化污染历史账期。"""
__tablename__ = "tenant_subscriptions"
__table_args__ = (
UniqueConstraint("tenant_id", "id", name="uq_tenant_subscriptions_tenant_id"),
UniqueConstraint(
"tenant_id", "subscription_key", name="uq_tenant_subscriptions_tenant_key"
),
UniqueConstraint(
"tenant_id",
"external_provider",
"external_subscription_id",
name="uq_tenant_subscriptions_external_ref",
),
ForeignKeyConstraint(
["tenant_id", "plan_id"],
["tenant_commercial_plans.tenant_id", "tenant_commercial_plans.id"],
name="fk_tenant_subscriptions_tenant_plan",
ondelete="RESTRICT",
),
CheckConstraint(
"status IN ('trialing', 'active', 'past_due', 'suspended', 'canceled', 'expired')",
name="ck_tenant_subscriptions_status",
),
CheckConstraint(
"billing_interval IN ('monthly', 'quarterly', 'annual', 'contract')",
name="ck_tenant_subscriptions_billing_interval",
),
CheckConstraint(
"seats > 0 AND base_fee_snapshot >= 0 AND version >= 1",
name="ck_tenant_subscriptions_values",
),
CheckConstraint(
"length(trim(subscription_key)) > 0 AND length(trim(currency)) = 3",
name="ck_tenant_subscriptions_keys",
),
CheckConstraint(
"current_period_end > current_period_start",
name="ck_tenant_subscriptions_period",
),
CheckConstraint(
"ends_at IS NULL OR ends_at > starts_at",
name="ck_tenant_subscriptions_contract_window",
),
CheckConstraint(
"(external_provider IS NULL AND external_subscription_id IS NULL) OR "
"(external_provider IS NOT NULL AND external_subscription_id IS NOT NULL)",
name="ck_tenant_subscriptions_external_pair",
),
CheckConstraint(
"status != 'canceled' OR canceled_at IS NOT NULL",
name="ck_tenant_subscriptions_cancellation",
),
Index(
"uq_tenant_subscriptions_current",
"tenant_id",
unique=True,
postgresql_where=text("status IN ('trialing', 'active', 'past_due', 'suspended')"),
),
Index(
"ix_tenant_subscriptions_tenant_status_period",
"tenant_id",
"status",
"current_period_end",
),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
subscription_key: Mapped[str] = mapped_column(String(120), nullable=False)
plan_id: Mapped[str] = mapped_column(String(36), nullable=False)
status: Mapped[str] = mapped_column(String(20), nullable=False)
starts_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
ends_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
current_period_start: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
current_period_end: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
seats: Mapped[int] = mapped_column(Integer, nullable=False)
base_fee_snapshot: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
currency: Mapped[str] = mapped_column(String(3), nullable=False)
billing_interval: Mapped[str] = mapped_column(String(20), nullable=False)
auto_renew: Mapped[bool] = mapped_column(
Boolean, nullable=False, default=False, server_default="false"
)
external_provider: Mapped[str | None] = mapped_column(String(60))
external_subscription_id: Mapped[str | None] = mapped_column(String(160))
canceled_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
metadata_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
created_by: Mapped[str] = mapped_column(String(120), nullable=False)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
)
plan = relationship("TenantCommercialPlan", back_populates="subscriptions")
entitlements = relationship(
"CommercialEntitlement", back_populates="subscription", passive_deletes=True
)
class CommercialEntitlement(Base):
"""订阅的功能权益与可计量配额定义。"""
__tablename__ = "commercial_entitlements"
__table_args__ = (
UniqueConstraint("tenant_id", "id", name="uq_commercial_entitlements_tenant_id"),
UniqueConstraint(
"tenant_id",
"subscription_id",
"id",
name="uq_commercial_entitlements_tenant_subscription_id",
),
UniqueConstraint(
"tenant_id",
"subscription_id",
"entitlement_key",
name="uq_commercial_entitlements_subscription_key",
),
ForeignKeyConstraint(
["tenant_id", "subscription_id"],
["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
name="fk_commercial_entitlements_tenant_subscription",
ondelete="RESTRICT",
),
CheckConstraint(
"entitlement_type IN ('feature', 'metered', 'unlimited')",
name="ck_commercial_entitlements_type",
),
CheckConstraint(
"reset_interval IN ('none', 'monthly', 'quarterly', 'annual', 'contract')",
name="ck_commercial_entitlements_reset_interval",
),
CheckConstraint(
"overage_policy IN ('block', 'allow', 'alert')",
name="ck_commercial_entitlements_overage_policy",
),
CheckConstraint(
"status IN ('active', 'suspended', 'expired')",
name="ck_commercial_entitlements_status",
),
CheckConstraint(
"version >= 1 AND (included_quantity IS NULL OR included_quantity >= 0) "
"AND (hard_limit_quantity IS NULL OR hard_limit_quantity >= 0)",
name="ck_commercial_entitlements_values",
),
CheckConstraint(
"(entitlement_type = 'unlimited' AND included_quantity IS NULL "
"AND hard_limit_quantity IS NULL) OR "
"(entitlement_type = 'feature' AND included_quantity IN (0, 1) "
"AND (hard_limit_quantity IS NULL OR hard_limit_quantity IN (0, 1))) OR "
"(entitlement_type = 'metered' AND included_quantity IS NOT NULL "
"AND (hard_limit_quantity IS NULL OR hard_limit_quantity >= included_quantity))",
name="ck_commercial_entitlements_quota_shape",
),
CheckConstraint(
"length(trim(entitlement_key)) > 0 AND length(trim(metric_key)) > 0 "
"AND length(trim(unit)) > 0",
name="ck_commercial_entitlements_keys",
),
CheckConstraint(
"effective_to IS NULL OR effective_to > effective_from",
name="ck_commercial_entitlements_effective_window",
),
Index(
"ix_commercial_entitlements_subscription_status",
"tenant_id",
"subscription_id",
"status",
),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
subscription_id: Mapped[str] = mapped_column(String(36), nullable=False)
entitlement_key: Mapped[str] = mapped_column(String(120), nullable=False)
metric_key: Mapped[str] = mapped_column(String(120), nullable=False)
entitlement_type: Mapped[str] = mapped_column(String(20), nullable=False)
unit: Mapped[str] = mapped_column(String(40), nullable=False)
included_quantity: Mapped[Decimal | None] = mapped_column(Numeric(20, 6))
hard_limit_quantity: Mapped[Decimal | None] = mapped_column(Numeric(20, 6))
reset_interval: Mapped[str] = mapped_column(String(20), nullable=False)
overage_policy: Mapped[str] = mapped_column(String(16), nullable=False)
status: Mapped[str] = mapped_column(String(16), nullable=False)
effective_from: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
effective_to: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
config_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
)
subscription = relationship("TenantSubscription", back_populates="entitlements")
class UsageMeterEvent(Base):
"""追加只读的客户用量事实;同一来源幂等键只能产生一条事件。"""
__tablename__ = "usage_meter_events"
__table_args__ = (
UniqueConstraint("tenant_id", "id", name="uq_usage_meter_events_tenant_id"),
UniqueConstraint(
"tenant_id",
"subscription_id",
"id",
name="uq_usage_meter_events_tenant_subscription_id",
),
UniqueConstraint(
"tenant_id",
"subscription_id",
"entitlement_id",
"id",
name="uq_usage_meter_events_entitlement_id",
),
UniqueConstraint(
"tenant_id",
"source_system",
"idempotency_key",
name="uq_usage_meter_events_source_request",
),
ForeignKeyConstraint(
["tenant_id", "subscription_id"],
["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
name="fk_usage_meter_events_tenant_subscription",
ondelete="RESTRICT",
),
ForeignKeyConstraint(
["tenant_id", "subscription_id", "entitlement_id"],
[
"commercial_entitlements.tenant_id",
"commercial_entitlements.subscription_id",
"commercial_entitlements.id",
],
name="fk_usage_meter_events_tenant_entitlement",
ondelete="RESTRICT",
),
ForeignKeyConstraint(
["tenant_id", "subscription_id", "billing_period_id"],
[
"commercial_billing_periods.tenant_id",
"commercial_billing_periods.subscription_id",
"commercial_billing_periods.id",
],
name="fk_usage_meter_events_tenant_billing_period",
ondelete="RESTRICT",
),
ForeignKeyConstraint(
["tenant_id", "subscription_id", "entitlement_id", "reversal_of_event_id"],
[
"usage_meter_events.tenant_id",
"usage_meter_events.subscription_id",
"usage_meter_events.entitlement_id",
"usage_meter_events.id",
],
name="fk_usage_meter_events_tenant_reversal",
ondelete="RESTRICT",
),
CheckConstraint(
"event_type IN ('usage', 'credit', 'adjustment', 'reversal')",
name="ck_usage_meter_events_type",
),
CheckConstraint(
"(event_type = 'usage' AND quantity > 0) OR "
"(event_type = 'credit' AND quantity < 0) OR "
"(event_type IN ('adjustment', 'reversal') AND quantity <> 0)",
name="ck_usage_meter_events_quantity",
),
CheckConstraint(
"(event_type = 'reversal' AND reversal_of_event_id IS NOT NULL) OR "
"(event_type != 'reversal' AND reversal_of_event_id IS NULL)",
name="ck_usage_meter_events_reversal",
),
CheckConstraint(
"(subject_type IS NULL AND subject_id IS NULL) OR "
"(subject_type IS NOT NULL AND subject_id IS NOT NULL)",
name="ck_usage_meter_events_subject_pair",
),
CheckConstraint(
"actor_type IN ('system', 'user', 'integration', 'admin')",
name="ck_usage_meter_events_actor_type",
),
CheckConstraint(
"length(trim(metric_key)) > 0 AND length(trim(unit)) > 0 "
"AND length(trim(period_key)) > 0 AND length(trim(source_system)) > 0 "
"AND length(trim(quota_period_key)) > 0 "
"AND length(trim(idempotency_key)) > 0 "
"AND length(trim(request_fingerprint)) > 0",
name="ck_usage_meter_events_keys",
),
Index(
"ix_usage_meter_events_quota_window",
"tenant_id",
"subscription_id",
"metric_key",
"quota_period_key",
"occurred_at",
),
Index(
"ix_usage_meter_events_billing_period",
"tenant_id",
"billing_period_id",
"occurred_at",
),
Index(
"ix_usage_meter_events_correlation",
"tenant_id",
"correlation_id",
"occurred_at",
),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
subscription_id: Mapped[str] = mapped_column(String(36), nullable=False)
entitlement_id: Mapped[str] = mapped_column(String(36), nullable=False)
billing_period_id: Mapped[str] = mapped_column(String(36), nullable=False)
event_type: Mapped[str] = mapped_column(String(16), nullable=False)
metric_key: Mapped[str] = mapped_column(String(120), nullable=False)
quantity: Mapped[Decimal] = mapped_column(Numeric(20, 6), nullable=False)
unit: Mapped[str] = mapped_column(String(40), nullable=False)
period_key: Mapped[str] = mapped_column(String(64), nullable=False)
quota_period_key: Mapped[str] = mapped_column(String(64), nullable=False)
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
source_system: Mapped[str] = mapped_column(String(80), nullable=False)
idempotency_key: Mapped[str] = mapped_column(String(160), nullable=False)
request_fingerprint: Mapped[str] = mapped_column(String(80), nullable=False)
reversal_of_event_id: Mapped[str | None] = mapped_column(String(36))
subject_type: Mapped[str | None] = mapped_column(String(60))
subject_id: Mapped[str | None] = mapped_column(String(160))
actor_type: Mapped[str] = mapped_column(String(20), nullable=False)
actor_id: Mapped[str] = mapped_column(String(120), nullable=False)
correlation_id: Mapped[str | None] = mapped_column(String(120))
trace_id: Mapped[str | None] = mapped_column(String(120))
metadata_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
recorded_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
class CommercialCostEvent(Base):
"""平台内部成本事实;物理上独立于客户节省、价值机会和价值实现。"""
__tablename__ = "commercial_cost_events"
__table_args__ = (
UniqueConstraint("tenant_id", "id", name="uq_commercial_cost_events_tenant_id"),
UniqueConstraint(
"tenant_id",
"source_system",
"idempotency_key",
name="uq_commercial_cost_events_source_request",
),
ForeignKeyConstraint(
["tenant_id", "subscription_id"],
["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
name="fk_commercial_cost_events_tenant_subscription",
ondelete="RESTRICT",
),
ForeignKeyConstraint(
["tenant_id", "subscription_id", "usage_event_id"],
[
"usage_meter_events.tenant_id",
"usage_meter_events.subscription_id",
"usage_meter_events.id",
],
name="fk_commercial_cost_events_tenant_usage",
ondelete="RESTRICT",
),
ForeignKeyConstraint(
["tenant_id", "subscription_id", "billing_period_id"],
[
"commercial_billing_periods.tenant_id",
"commercial_billing_periods.subscription_id",
"commercial_billing_periods.id",
],
name="fk_commercial_cost_events_tenant_billing_period",
ondelete="RESTRICT",
),
ForeignKeyConstraint(
["tenant_id", "reversal_of_cost_event_id"],
["commercial_cost_events.tenant_id", "commercial_cost_events.id"],
name="fk_commercial_cost_events_tenant_reversal",
ondelete="RESTRICT",
),
CheckConstraint(
"event_type IN ('incurred', 'credit', 'adjustment', 'reversal')",
name="ck_commercial_cost_events_type",
),
CheckConstraint(
"cost_category IN ('ai_inference', 'ocr', 'storage', 'connector', "
"'support', 'implementation', 'infrastructure', 'payment', 'other')",
name="ck_commercial_cost_events_category",
),
CheckConstraint(
"quantity > 0 AND unit_cost >= 0 AND fx_rate > 0",
name="ck_commercial_cost_events_values",
),
CheckConstraint(
"(event_type = 'incurred' AND cost_amount >= 0 AND reporting_amount >= 0) OR "
"(event_type = 'credit' AND cost_amount <= 0 AND reporting_amount <= 0) OR "
"(event_type IN ('adjustment', 'reversal') AND cost_amount <> 0 "
"AND reporting_amount <> 0)",
name="ck_commercial_cost_events_amount_direction",
),
CheckConstraint(
"(event_type = 'reversal' AND reversal_of_cost_event_id IS NOT NULL) OR "
"(event_type != 'reversal' AND reversal_of_cost_event_id IS NULL)",
name="ck_commercial_cost_events_reversal",
),
CheckConstraint(
"usage_event_id IS NULL OR subscription_id IS NOT NULL",
name="ck_commercial_cost_events_usage_pair",
),
CheckConstraint(
"(subscription_id IS NULL AND billing_period_id IS NULL) OR "
"(subscription_id IS NOT NULL AND billing_period_id IS NOT NULL)",
name="ck_commercial_cost_events_billing_period_pair",
),
CheckConstraint(
"length(trim(unit)) > 0 AND length(trim(source_system)) > 0 "
"AND length(trim(idempotency_key)) > 0 "
"AND length(trim(request_fingerprint)) > 0 "
"AND length(trim(original_currency)) = 3 "
"AND length(trim(reporting_currency)) = 3",
name="ck_commercial_cost_events_keys",
),
Index(
"ix_commercial_cost_events_tenant_period",
"tenant_id",
"occurred_at",
"cost_category",
),
Index(
"ix_commercial_cost_events_subscription_period",
"tenant_id",
"subscription_id",
"billing_period_id",
"occurred_at",
),
Index(
"ix_commercial_cost_events_allocation",
"tenant_id",
"allocation_key",
"occurred_at",
),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
subscription_id: Mapped[str | None] = mapped_column(String(36))
billing_period_id: Mapped[str | None] = mapped_column(String(36))
usage_event_id: Mapped[str | None] = mapped_column(String(36))
event_type: Mapped[str] = mapped_column(String(16), nullable=False)
cost_category: Mapped[str] = mapped_column(String(32), nullable=False)
quantity: Mapped[Decimal] = mapped_column(Numeric(20, 6), nullable=False)
unit: Mapped[str] = mapped_column(String(40), nullable=False)
unit_cost: Mapped[Decimal] = mapped_column(Numeric(20, 8), nullable=False)
cost_amount: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
original_currency: Mapped[str] = mapped_column(String(3), nullable=False)
reporting_amount: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
reporting_currency: Mapped[str] = mapped_column(String(3), nullable=False)
fx_rate: Mapped[Decimal] = mapped_column(Numeric(20, 8), nullable=False)
provider: Mapped[str | None] = mapped_column(String(120))
sku: Mapped[str | None] = mapped_column(String(120))
model_name: Mapped[str | None] = mapped_column(String(120))
allocation_key: Mapped[str] = mapped_column(String(160), nullable=False)
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
source_system: Mapped[str] = mapped_column(String(80), nullable=False)
idempotency_key: Mapped[str] = mapped_column(String(160), nullable=False)
request_fingerprint: Mapped[str] = mapped_column(String(80), nullable=False)
reversal_of_cost_event_id: Mapped[str | None] = mapped_column(String(36))
correlation_id: Mapped[str | None] = mapped_column(String(120))
trace_id: Mapped[str | None] = mapped_column(String(120))
metadata_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
recorded_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
__all__ = [
"CommercialCostEvent",
"CommercialEntitlement",
"TenantCommercialPlan",
"TenantSubscription",
"UsageMeterEvent",
]