feat: 添加 points_audit_ledger 及 JSON 字段 Pydantic Schema 约束

This commit is contained in:
qzl
2026-04-10 12:28:18 +08:00
parent 46513829cd
commit 0ac8b81a66
34 changed files with 2595 additions and 1757 deletions
+4
View File
@@ -6,8 +6,10 @@ from .auth_user import AuthUser
from .invite_code import InviteCode
from .llm import Llm
from .llm_factory import LlmFactory
from .points_audit_ledger import PointsAuditLedger
from .points_ledger import PointsLedger
from .profile import Profile
from .register_bonus_claims import RegisterBonusClaims
from .system_agents import SystemAgents
from .user_points import UserPoints
@@ -18,8 +20,10 @@ __all__ = [
"InviteCode",
"Llm",
"LlmFactory",
"PointsAuditLedger",
"PointsLedger",
"Profile",
"RegisterBonusClaims",
"SystemAgents",
"UserPoints",
]
+96
View File
@@ -0,0 +1,96 @@
from __future__ import annotations
import uuid
from decimal import Decimal
from sqlalchemy import (
BigInteger,
CheckConstraint,
Index,
Integer,
JSON,
Numeric,
SmallInteger,
String,
Text,
UniqueConstraint,
text,
)
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column
from core.db.base import Base, TimestampMixin
class PointsAuditLedger(TimestampMixin, Base):
__tablename__ = "points_audit_ledger"
__table_args__ = (
CheckConstraint(
"amount >= 0", name="ck_points_audit_ledger_amount_non_negative"
),
CheckConstraint(
"direction in (1, 0, -1)", name="ck_points_audit_ledger_direction_valid"
),
CheckConstraint(
"balance_after >= 0",
name="ck_points_audit_ledger_balance_after_non_negative",
),
CheckConstraint(
"change_type in ('register', 'consume', 'grant', 'adjust')",
name="ck_points_audit_ledger_change_type",
),
CheckConstraint(
"biz_type is null or biz_type = 'chat'",
name="ck_points_audit_ledger_biz_type",
),
CheckConstraint(
"billed_to in ('user', 'platform')",
name="ck_points_audit_ledger_billed_to",
),
CheckConstraint(
"jsonb_typeof(metadata) = 'object'",
name="ck_points_audit_ledger_metadata_object",
),
UniqueConstraint("event_id", name="uq_points_audit_ledger_event_id"),
Index(
"ix_points_audit_ledger_user_id_created_at",
"user_id_snapshot",
text("created_at DESC"),
),
Index(
"ix_points_audit_ledger_change_type_created_at",
"change_type",
text("created_at DESC"),
),
)
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
primary_key=True,
default=uuid.uuid4,
)
event_id: Mapped[str] = mapped_column(String(64), nullable=False)
user_id_snapshot: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True),
nullable=True,
)
user_email_snapshot: Mapped[str | None] = mapped_column(Text, nullable=True)
change_type: Mapped[str] = mapped_column(String(16), nullable=False)
biz_type: Mapped[str | None] = mapped_column(String(16), nullable=True)
biz_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), nullable=True)
direction: Mapped[int] = mapped_column(SmallInteger, nullable=False)
amount: Mapped[int] = mapped_column(BigInteger, nullable=False)
balance_after: Mapped[int] = mapped_column(BigInteger, nullable=False)
billed_to: Mapped[str] = mapped_column(String(16), nullable=False)
run_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
request_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
input_tokens: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
output_tokens: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
cost: Mapped[Decimal] = mapped_column(Numeric(12, 6), nullable=False, default=0)
metadata_json: Mapped[dict[str, object]] = mapped_column(
"metadata",
JSON().with_variant(JSONB, "postgresql"),
nullable=False,
server_default=text("'{}'::jsonb"),
default=dict,
)
@@ -0,0 +1,33 @@
from __future__ import annotations
import uuid
from sqlalchemy import ForeignKey, String, Text, UniqueConstraint
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
from core.db.base import Base, TimestampMixin
class RegisterBonusClaims(TimestampMixin, Base):
__tablename__ = "register_bonus_claims"
__table_args__ = (
UniqueConstraint("email_hash", name="uq_register_bonus_claims_email_hash"),
UniqueConstraint(
"grant_event_id", name="uq_register_bonus_claims_grant_event_id"
),
)
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
primary_key=True,
default=uuid.uuid4,
)
email_hash: Mapped[str] = mapped_column(String(64), nullable=False)
user_email_snapshot: Mapped[str] = mapped_column(Text, nullable=False)
first_user_id: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True),
ForeignKey("auth.users.id", ondelete="SET NULL"),
nullable=True,
)
grant_event_id: Mapped[str] = mapped_column(String(64), nullable=False)