Files
WonderQ-Project/WonderQ-Admin/app/models.py
2026-08-25 22:47:59 +08:00

474 lines
24 KiB
Python

from datetime import datetime, timezone
from uuid import uuid4
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.dialects.postgresql import ARRAY, JSONB
from sqlalchemy.orm import Mapped, mapped_column, relationship
from .database import Base
def utc_now() -> datetime:
return datetime.now(timezone.utc).replace(tzinfo=None)
def new_id() -> str:
return str(uuid4())
class AdminUser(Base):
__tablename__ = "AdminUser"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
email: Mapped[str] = mapped_column(String, unique=True, nullable=False)
name: Mapped[str] = mapped_column(String, nullable=False)
passwordHash: Mapped[str] = mapped_column(String, nullable=False)
role: Mapped[str] = mapped_column(String, default="admin", nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
auditLogs: Mapped[list["AuditLog"]] = relationship(back_populates="actor")
assignedLeads: Mapped[list["Lead"]] = relationship(back_populates="assignedUser", foreign_keys="Lead.assignedUserId")
class AdminRole(Base):
__tablename__ = "AdminRole"
__table_args__ = (UniqueConstraint("code", name="uq_AdminRole_code"),)
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
name: Mapped[str] = mapped_column(String, nullable=False)
code: Mapped[str] = mapped_column(String, nullable=False)
dataScope: Mapped[str] = mapped_column(String, default="all", nullable=False)
description: Mapped[str | None] = mapped_column(Text)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class AdminMenu(Base):
__tablename__ = "AdminMenu"
__table_args__ = (UniqueConstraint("permissionCode", name="uq_AdminMenu_permissionCode"),)
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
parentId: Mapped[str | None] = mapped_column(String, ForeignKey("AdminMenu.id", ondelete="CASCADE"))
name: Mapped[str] = mapped_column(String, nullable=False)
type: Mapped[str] = mapped_column(String, default="page", nullable=False)
path: Mapped[str | None] = mapped_column(String)
componentKey: Mapped[str | None] = mapped_column(String)
permissionCode: Mapped[str | None] = mapped_column(String)
icon: Mapped[str | None] = mapped_column(String)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
isVisible: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class AdminDepartment(Base):
__tablename__ = "AdminDepartment"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
parentId: Mapped[str | None] = mapped_column(String, ForeignKey("AdminDepartment.id", ondelete="SET NULL"))
name: Mapped[str] = mapped_column(String, nullable=False)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class AdminUserRole(Base):
__tablename__ = "AdminUserRole"
userId: Mapped[str] = mapped_column(String, ForeignKey("AdminUser.id", ondelete="CASCADE"), primary_key=True)
roleId: Mapped[str] = mapped_column(String, ForeignKey("AdminRole.id", ondelete="CASCADE"), primary_key=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
class AdminRoleMenu(Base):
__tablename__ = "AdminRoleMenu"
roleId: Mapped[str] = mapped_column(String, ForeignKey("AdminRole.id", ondelete="CASCADE"), primary_key=True)
menuId: Mapped[str] = mapped_column(String, ForeignKey("AdminMenu.id", ondelete="CASCADE"), primary_key=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
class AdminUserDepartment(Base):
__tablename__ = "AdminUserDepartment"
userId: Mapped[str] = mapped_column(String, ForeignKey("AdminUser.id", ondelete="CASCADE"), primary_key=True)
deptId: Mapped[str] = mapped_column(String, ForeignKey("AdminDepartment.id", ondelete="CASCADE"), primary_key=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
class AdminRoleDepartment(Base):
__tablename__ = "AdminRoleDepartment"
roleId: Mapped[str] = mapped_column(String, ForeignKey("AdminRole.id", ondelete="CASCADE"), primary_key=True)
deptId: Mapped[str] = mapped_column(String, ForeignKey("AdminDepartment.id", ondelete="CASCADE"), primary_key=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
DEFAULT_ADMIN_DEPT_ID = "00000000-0000-0000-0000-000000000001"
class AdminOwnedContent:
deptId: Mapped[str] = mapped_column(
String,
ForeignKey("AdminDepartment.id", ondelete="RESTRICT"),
nullable=False,
index=True,
server_default=DEFAULT_ADMIN_DEPT_ID,
)
createdById: Mapped[str | None] = mapped_column(
String,
ForeignKey("AdminUser.id", ondelete="SET NULL"),
nullable=True,
index=True,
)
class MediaAsset(AdminOwnedContent, Base):
__tablename__ = "MediaAsset"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
url: Mapped[str] = mapped_column(String, unique=True, nullable=False)
name: Mapped[str | None] = mapped_column(String)
mimeType: Mapped[str | None] = mapped_column(String)
sizeBytes: Mapped[int | None] = mapped_column(Integer)
group: Mapped[str | None] = mapped_column(String)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class HeroSlide(AdminOwnedContent, Base):
__tablename__ = "HeroSlide"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
title: Mapped[str] = mapped_column(String, nullable=False)
kicker: Mapped[str | None] = mapped_column(String)
actionLabel: Mapped[str | None] = mapped_column(String)
image: Mapped[str] = mapped_column(String, nullable=False)
targetType: Mapped[str | None] = mapped_column(String)
targetValue: Mapped[str | None] = mapped_column(String)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class DestinationHero(AdminOwnedContent, Base):
__tablename__ = "DestinationHero"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
title: Mapped[str] = mapped_column(String, nullable=False)
kicker: Mapped[str | None] = mapped_column(String)
image: Mapped[str | None] = mapped_column(String)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class VehicleOption(AdminOwnedContent, Base):
__tablename__ = "VehicleOption"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
title: Mapped[str] = mapped_column(String, nullable=False)
description: Mapped[str | None] = mapped_column(Text)
image: Mapped[str | None] = mapped_column(String)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class VehicleServiceConfig(AdminOwnedContent, Base):
__tablename__ = "VehicleServiceConfig"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
introTitle: Mapped[str] = mapped_column(String, nullable=False)
intro: Mapped[str] = mapped_column(Text, nullable=False)
serviceSections: Mapped[list[dict[str, str]]] = mapped_column(JSONB, default=list, nullable=False)
advantages: Mapped[list[str]] = mapped_column(JSONB, default=list, nullable=False)
processSteps: Mapped[list[dict[str, str]]] = mapped_column(JSONB, default=list, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class HomeExperience(AdminOwnedContent, Base):
__tablename__ = "HomeExperience"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
badge: Mapped[str] = mapped_column(String, nullable=False)
category: Mapped[str] = mapped_column(String, nullable=False)
title: Mapped[str] = mapped_column(String, nullable=False)
englishTitle: Mapped[str] = mapped_column(String, nullable=False)
image: Mapped[str] = mapped_column(String, nullable=False)
demandKeyword: Mapped[str] = mapped_column(String, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False, index=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class HomeTeamBuilding(AdminOwnedContent, Base):
__tablename__ = "HomeTeamBuilding"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
tag: Mapped[str] = mapped_column(String, nullable=False)
title: Mapped[str] = mapped_column(String, nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=False)
image: Mapped[str] = mapped_column(String, nullable=False)
demandKeyword: Mapped[str] = mapped_column(String, nullable=False)
detailSubtitle: Mapped[str] = mapped_column(String, default="", nullable=False)
detailParagraphs: Mapped[list[str]] = mapped_column(JSONB, default=list, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False, index=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class HomeWildArchive(AdminOwnedContent, Base):
__tablename__ = "HomeWildArchive"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
title: Mapped[str] = mapped_column(String, nullable=False)
image: Mapped[str] = mapped_column(String, nullable=False)
images: Mapped[list[str]] = mapped_column(JSONB, default=list, nullable=False)
demandKeyword: Mapped[str] = mapped_column(String, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False, index=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class HomeWanfaRecommendation(AdminOwnedContent, Base):
__tablename__ = "HomeWanfaRecommendation"
__table_args__ = (
UniqueConstraint("categoryId", name="uq_HomeWanfaRecommendation_categoryId"),
)
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
categoryId: Mapped[str] = mapped_column(
String,
ForeignKey("WanfaCategory.id", ondelete="RESTRICT"),
nullable=False,
index=True,
)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False, index=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
category: Mapped["WanfaCategory"] = relationship(back_populates="recommendations")
class WanfaCategory(AdminOwnedContent, Base):
__tablename__ = "WanfaCategory"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
label: Mapped[str] = mapped_column(String, nullable=False)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False, index=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
routes: Mapped[list["WanfaRoute"]] = relationship(
back_populates="category",
order_by="WanfaRoute.sortOrder.asc()",
)
recommendations: Mapped[list[HomeWanfaRecommendation]] = relationship(back_populates="category")
class WanfaRoute(AdminOwnedContent, Base):
__tablename__ = "WanfaRoute"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
categoryId: Mapped[str] = mapped_column(
String,
ForeignKey("WanfaCategory.id", ondelete="RESTRICT"),
nullable=False,
index=True,
)
title: Mapped[str] = mapped_column(String, nullable=False)
subtitle: Mapped[str] = mapped_column(Text, nullable=False)
image: Mapped[str] = mapped_column(String, nullable=False)
routeCount: Mapped[int] = mapped_column(Integer, nullable=False)
demandKeyword: Mapped[str] = mapped_column(String, nullable=False)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False, index=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
category: Mapped[WanfaCategory] = relationship(back_populates="routes")
class DetailRecord(AdminOwnedContent, Base):
__tablename__ = "DetailRecord"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
key: Mapped[str] = mapped_column(String, nullable=False, unique=True)
eyebrow: Mapped[str] = mapped_column(String, nullable=False)
duration: Mapped[str] = mapped_column(String, nullable=False)
title: Mapped[str] = mapped_column(String, nullable=False)
subtitle: Mapped[str] = mapped_column(Text, nullable=False)
intro: Mapped[str] = mapped_column(Text, nullable=False)
highlights: Mapped[list[str]] = mapped_column(JSONB, default=list, nullable=False)
included: Mapped[list[str]] = mapped_column(JSONB, default=list, nullable=False)
excluded: Mapped[list[str]] = mapped_column(JSONB, default=list, nullable=False)
notes: Mapped[list[str]] = mapped_column(JSONB, default=list, nullable=False)
gallery: Mapped[list[str]] = mapped_column(JSONB, default=list, nullable=False)
conciergeAdvisorId: Mapped[str | None] = mapped_column(String, nullable=True)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False, index=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class ConciergeAdvisor(AdminOwnedContent, Base):
__tablename__ = "ConciergeAdvisor"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
avatar: Mapped[str] = mapped_column(String, nullable=False)
name: Mapped[str] = mapped_column(String, nullable=False)
role: Mapped[str] = mapped_column(String, nullable=False)
details: Mapped[list[dict[str, str]]] = mapped_column(JSONB, default=list, nullable=False)
qrImage: Mapped[str] = mapped_column(String, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False, index=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class DemandHero(AdminOwnedContent, Base):
__tablename__ = "DemandHero"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
title: Mapped[str] = mapped_column(String, nullable=False)
kicker: Mapped[str | None] = mapped_column(String)
description: Mapped[str | None] = mapped_column(Text)
steps: Mapped[list[str]] = mapped_column(ARRAY(String), default=list, nullable=False)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class DemandFeatureCard(AdminOwnedContent, Base):
__tablename__ = "DemandFeatureCard"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
title: Mapped[str] = mapped_column(String, nullable=False)
description: Mapped[str | None] = mapped_column(Text)
sortOrder: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class DemandForm(AdminOwnedContent, Base):
__tablename__ = "DemandForm"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
destinationLabel: Mapped[str] = mapped_column(String, default="目的地/玩法", nullable=False)
destinationPlaceholder: Mapped[str | None] = mapped_column(String)
phoneLabel: Mapped[str] = mapped_column(String, default="联系方式", nullable=False)
phonePlaceholder: Mapped[str | None] = mapped_column(String)
noteLabel: Mapped[str] = mapped_column(String, default="补充说明", nullable=False)
notePlaceholder: Mapped[str | None] = mapped_column(String)
submitLabel: Mapped[str] = mapped_column(String, default="提交出行需求", nullable=False)
chips: Mapped[list[str]] = mapped_column(ARRAY(String), default=list, nullable=False)
isActive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
class Lead(AdminOwnedContent, Base):
__tablename__ = "Lead"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
leadType: Mapped[str] = mapped_column(String, default="general", nullable=False, index=True)
contactName: Mapped[str | None] = mapped_column(String)
customerId: Mapped[str | None] = mapped_column(String, ForeignKey("Customer.id", ondelete="SET NULL"), index=True)
destination: Mapped[str | None] = mapped_column(String)
phone: Mapped[str] = mapped_column(String, nullable=False)
travelDate: Mapped[datetime | None] = mapped_column(DateTime)
peopleCount: Mapped[int | None] = mapped_column(Integer)
budgetMin: Mapped[int | None] = mapped_column(Integer)
budgetMax: Mapped[int | None] = mapped_column(Integer)
note: Mapped[str | None] = mapped_column(Text)
vehicleDemand: Mapped[dict | None] = mapped_column(JSONB)
sourcePage: Mapped[str | None] = mapped_column(String)
status: Mapped[str] = mapped_column(String, default="new", nullable=False)
assignedUserId: Mapped[str | None] = mapped_column(String, ForeignKey("AdminUser.id", ondelete="SET NULL"))
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
assignedUser: Mapped[AdminUser | None] = relationship(back_populates="assignedLeads", foreign_keys=[assignedUserId])
followups: Mapped[list["LeadFollowup"]] = relationship(back_populates="lead", cascade="all, delete-orphan")
class LeadFollowup(Base):
__tablename__ = "LeadFollowup"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
leadId: Mapped[str] = mapped_column(String, ForeignKey("Lead.id", ondelete="CASCADE"), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
nextAt: Mapped[datetime | None] = mapped_column(DateTime)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
lead: Mapped[Lead] = relationship(back_populates="followups")
class Customer(Base):
__tablename__ = "Customer"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
phone: Mapped[str] = mapped_column(String, unique=True, nullable=False)
name: Mapped[str | None] = mapped_column(String)
wechat: Mapped[str | None] = mapped_column(String)
note: Mapped[str | None] = mapped_column(Text)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
orders: Mapped[list["Order"]] = relationship(back_populates="customer")
class Order(Base):
__tablename__ = "Order"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
customerId: Mapped[str | None] = mapped_column(String, ForeignKey("Customer.id", ondelete="SET NULL"))
status: Mapped[str] = mapped_column(String, default="draft", nullable=False)
travelDate: Mapped[datetime | None] = mapped_column(DateTime)
amount: Mapped[int | None] = mapped_column(Integer)
note: Mapped[str | None] = mapped_column(Text)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, onupdate=utc_now, nullable=False)
customer: Mapped[Customer | None] = relationship(back_populates="orders")
class SiteVersion(Base):
__tablename__ = "SiteVersion"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
title: Mapped[str] = mapped_column(String, nullable=False)
status: Mapped[str] = mapped_column(String, default="draft", nullable=False)
snapshot: Mapped[dict | list] = mapped_column(JSONB, nullable=False)
publishedAt: Mapped[datetime | None] = mapped_column(DateTime)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
class AuditLog(Base):
__tablename__ = "AuditLog"
id: Mapped[str] = mapped_column(String, primary_key=True, default=new_id)
actorId: Mapped[str | None] = mapped_column(String, ForeignKey("AdminUser.id", ondelete="SET NULL"))
action: Mapped[str] = mapped_column(String, nullable=False)
entity: Mapped[str] = mapped_column(String, nullable=False)
entityId: Mapped[str | None] = mapped_column(String)
before: Mapped[dict | list | None] = mapped_column(JSONB)
after: Mapped[dict | list | None] = mapped_column(JSONB)
createdAt: Mapped[datetime] = mapped_column(DateTime, default=utc_now, nullable=False)
actor: Mapped[AdminUser | None] = relationship(back_populates="auditLogs")