"""场内基金模拟交易域(fin_*)只读 ORM 映射。 设计约定(强约束): 1. 本模块只做「列映射」:不提供任何写辅助方法(无 insert/update/delete/save/merge), 底座、Agent 与业务组员都不得借本模块下单、改持仓或改交易数据(项目硬性业务红线)。 2. 列名、类型、可空性严格对齐 MySQL 实际结构(information_schema.COLUMNS 逐列核对), 不允许任何 DDL / Alembic 迁移改动库结构;回归校验由 tools/audit_constraints.py 负责。 3. 不声明 ForeignKey:跨表关系由 app/repository/fund_query_repository.py 的 客户数据范围解析器表达,避免 ORM 引入 flush 期级联写语义。 4. 未映射 MySQL 生成列(GENERATION_EXPRESSION 非空)——本域当前没有生成列。 5. 只读查询统一走 app/repository/fund_query_repository.py,Agent(Service 层) 不得直接访问本模块的 ORM 实体。 """ from datetime import date, datetime from decimal import Decimal from typing import Any from sqlalchemy import JSON, Date, Integer, Numeric, String, Text from sqlalchemy.dialects.mysql import BIGINT, DATETIME, TINYINT from sqlalchemy.orm import Mapped, mapped_column from app.model.base import Base __all__ = [ "FundCapitalFlow", "FundCashLedger", "FundCustomerProfile", "FundFeeRule", "FundHolding", "FundMarketPrice", "FundNavHistory", "FundProduct", "FundRiskAlert", "FundRiskAssessment", "FundRiskNotification", "FundSimAccount", "FundSimOrder", "FundTransaction", ] class FundProduct(Base): """场内基金产品主数据(fin_product)。""" __tablename__ = "fin_product" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) product_code: Mapped[str] = mapped_column(String(32), nullable=False) product_name: Mapped[str] = mapped_column(String(128), nullable=False) exchange_code: Mapped[str] = mapped_column(String(16), nullable=False) product_category: Mapped[str] = mapped_column(String(32), nullable=False) risk_level: Mapped[str] = mapped_column(String(8), nullable=False) fund_manager: Mapped[str | None] = mapped_column(String(64), nullable=True) currency: Mapped[str] = mapped_column(String(8), nullable=False) lot_size: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) price_tick: Mapped[Decimal] = mapped_column(Numeric(18, 6), nullable=False) current_nav: Mapped[Decimal | None] = mapped_column(Numeric(18, 6), nullable=True) current_nav_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) min_amount: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) open_start_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) open_end_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) open_period_start: Mapped[date | None] = mapped_column(Date, nullable=True) open_period_end: Mapped[date | None] = mapped_column(Date, nullable=True) transaction_fee_rate: Mapped[Decimal | None] = mapped_column(Numeric(10, 6), nullable=True) single_investor_max_holding_ratio: Mapped[Decimal] = mapped_column( Numeric(7, 4), nullable=False ) management_fee_rate: Mapped[Decimal | None] = mapped_column(Numeric(7, 4), nullable=True) custodian_fee_rate: Mapped[Decimal | None] = mapped_column(Numeric(7, 4), nullable=True) risk_disclosure_required: Mapped[int] = mapped_column(TINYINT(display_width=1), nullable=False) second_confirmation_required: Mapped[int] = mapped_column( TINYINT(display_width=1), nullable=False ) recording_required: Mapped[int] = mapped_column(TINYINT(display_width=1), nullable=False) status: Mapped[str] = mapped_column(String(16), nullable=False) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundMarketPrice(Base): """场内日行情(fin_market_price),行情为公共数据,无客户归属。""" __tablename__ = "fin_market_price" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) product_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) trade_date: Mapped[date] = mapped_column(Date, nullable=False) open_price: Mapped[Decimal] = mapped_column(Numeric(18, 6), nullable=False) high_price: Mapped[Decimal] = mapped_column(Numeric(18, 6), nullable=False) low_price: Mapped[Decimal] = mapped_column(Numeric(18, 6), nullable=False) close_price: Mapped[Decimal] = mapped_column(Numeric(18, 6), nullable=False) volume: Mapped[Decimal | None] = mapped_column(Numeric(24, 4), nullable=True) turnover_amount: Mapped[Decimal | None] = mapped_column(Numeric(24, 2), nullable=True) total_fund_shares: Mapped[Decimal] = mapped_column(Numeric(24, 4), nullable=False) source: Mapped[str] = mapped_column(String(32), nullable=False) source_updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundNavHistory(Base): """基金净值历史(fin_nav_history),公共数据。""" __tablename__ = "fin_nav_history" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) product_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) nav_date: Mapped[date] = mapped_column(Date, nullable=False) nav: Mapped[Decimal] = mapped_column(Numeric(12, 6), nullable=False) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundSimAccount(Base): """模拟资金账户(fin_sim_account)。""" __tablename__ = "fin_sim_account" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) account_no: Mapped[str] = mapped_column(String(32), nullable=False) customer_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) currency: Mapped[str] = mapped_column(String(8), nullable=False) cash_balance: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) available_cash: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) frozen_cash: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) initial_balance: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) status: Mapped[str] = mapped_column(String(16), nullable=False) version: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundCashLedger(Base): """资金流水(fin_cash_ledger)。无 customer_id,客户归属经 account_id 间接解析。""" __tablename__ = "fin_cash_ledger" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) ledger_no: Mapped[str] = mapped_column(String(64), nullable=False) account_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) transaction_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) entry_type: Mapped[str] = mapped_column(String(24), nullable=False) amount: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) balance_after: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) available_cash_after: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) frozen_cash_after: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) idempotency_key: Mapped[str] = mapped_column(String(128), nullable=False) occurred_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundCapitalFlow(Base): """资金划转/出入金流水(fin_capital_flow)。""" __tablename__ = "fin_capital_flow" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) flow_no: Mapped[str] = mapped_column(String(64), nullable=False) customer_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) account_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) transaction_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) flow_type: Mapped[str] = mapped_column(String(16), nullable=False) amount: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) balance_after: Mapped[Decimal | None] = mapped_column(Numeric(18, 2), nullable=True) status: Mapped[str] = mapped_column(String(16), nullable=False) settled_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) occurred_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) payer_name: Mapped[str | None] = mapped_column(String(64), nullable=True) source_type: Mapped[str] = mapped_column(String(16), nullable=False) related_work_order_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) match_status: Mapped[str] = mapped_column(String(16), nullable=False) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundFeeRule(Base): """费率规则(fin_fee_rule),配置型公共数据。""" __tablename__ = "fin_fee_rule" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) rule_code: Mapped[str] = mapped_column(String(64), nullable=False) product_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) exchange_code: Mapped[str | None] = mapped_column(String(16), nullable=True) order_side: Mapped[str] = mapped_column(String(8), nullable=False) customer_tier: Mapped[str | None] = mapped_column(String(16), nullable=True) min_trade_amount: Mapped[Decimal | None] = mapped_column(Numeric(18, 2), nullable=True) max_trade_amount: Mapped[Decimal | None] = mapped_column(Numeric(18, 2), nullable=True) fee_rate: Mapped[Decimal] = mapped_column(Numeric(10, 6), nullable=False) minimum_fee: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) fixed_fee: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) priority: Mapped[int] = mapped_column(Integer, nullable=False) effective_from: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) effective_until: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) status: Mapped[str] = mapped_column(String(16), nullable=False) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundSimOrder(Base): """模拟委托单(fin_sim_order)。只读查询,禁止在此写入委托。""" __tablename__ = "fin_sim_order" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) order_no: Mapped[str] = mapped_column(String(64), nullable=False) customer_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) account_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) product_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) order_side: Mapped[str] = mapped_column(String(8), nullable=False) price_type: Mapped[str] = mapped_column(String(8), nullable=False) quantity: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) limit_price: Mapped[Decimal | None] = mapped_column(Numeric(18, 6), nullable=True) quote_price: Mapped[Decimal] = mapped_column(Numeric(18, 6), nullable=False) quote_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) quote_source: Mapped[str] = mapped_column(String(32), nullable=False) channel: Mapped[str | None] = mapped_column(String(32), nullable=True) advisor_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) filled_quantity: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) average_executed_price: Mapped[Decimal | None] = mapped_column(Numeric(18, 6), nullable=True) status: Mapped[str] = mapped_column(String(24), nullable=False) risk_rule_hits: Mapped[Any | None] = mapped_column(JSON, nullable=True) risk_disclosure_ack_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) second_confirmation_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) recording_reference: Mapped[str | None] = mapped_column(String(64), nullable=True) ops_handler_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) ops_handled_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) compliance_handler_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) compliance_handled_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) reject_reason: Mapped[str | None] = mapped_column(Text, nullable=True) submitted_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) cancelled_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundTransaction(Base): """成交记录(fin_transaction)。只读查询,禁止在此写入成交。""" __tablename__ = "fin_transaction" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) transaction_no: Mapped[str] = mapped_column(String(64), nullable=False) order_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) work_order_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) customer_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) account_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) product_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) order_side: Mapped[str] = mapped_column(String(8), nullable=False) transaction_type: Mapped[str] = mapped_column(String(16), nullable=False) executed_price: Mapped[Decimal] = mapped_column(Numeric(18, 6), nullable=False) nav: Mapped[Decimal] = mapped_column(Numeric(12, 6), nullable=False) executed_quantity: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) shares: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) gross_amount: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) amount: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) fee_rule_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) fee_rate_snapshot: Mapped[Decimal] = mapped_column(Numeric(10, 6), nullable=False) fee_amount: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) fee: Mapped[Decimal | None] = mapped_column(Numeric(18, 2), nullable=True) net_amount: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) quote_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) quote_source: Mapped[str] = mapped_column(String(32), nullable=False) executed_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) confirmed_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) confirmed_by: Mapped[int | None] = mapped_column(Integer, nullable=True) auto_confirmed: Mapped[int] = mapped_column(TINYINT(display_width=1), nullable=False) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundHolding(Base): """持仓(fin_holding)。只读查询,禁止在此修改持仓。""" __tablename__ = "fin_holding" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) customer_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) trade_account: Mapped[str] = mapped_column(String(32), nullable=False) product_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) total_quantity: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) shares: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) available_quantity: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) frozen_quantity: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) average_cost: Mapped[Decimal] = mapped_column(Numeric(18, 6), nullable=False) cost_amount: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) market_value: Mapped[Decimal | None] = mapped_column(Numeric(18, 2), nullable=True) current_value: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) profit_loss: Mapped[Decimal | None] = mapped_column(Numeric(18, 2), nullable=True) profit_loss_ratio: Mapped[Decimal | None] = mapped_column(Numeric(10, 4), nullable=True) status: Mapped[str] = mapped_column(String(16), nullable=False) first_acquired_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) version: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundCustomerProfile(Base): """客户画像(fin_customer_profile),主键为 customer_id。""" __tablename__ = "fin_customer_profile" customer_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) trade_account: Mapped[str] = mapped_column(String(32), nullable=False) real_name: Mapped[str] = mapped_column(String(64), nullable=False) birth_date: Mapped[date | None] = mapped_column(Date, nullable=True) occupation: Mapped[str | None] = mapped_column(String(64), nullable=True) mobile_masked: Mapped[str | None] = mapped_column(String(32), nullable=True) investor_type: Mapped[str] = mapped_column(String(8), nullable=False) investment_horizon: Mapped[str | None] = mapped_column(String(32), nullable=True) preferred_asset_class: Mapped[Any | None] = mapped_column(JSON, nullable=True) trading_frequency: Mapped[str | None] = mapped_column(String(32), nullable=True) last_active_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) total_asset: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False) behavior_score: Mapped[int] = mapped_column(Integer, nullable=False) risk_tags: Mapped[Any | None] = mapped_column(JSON, nullable=True) opened_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundRiskAssessment(Base): """风险测评记录(fin_risk_assessment)。""" __tablename__ = "fin_risk_assessment" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) customer_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) questionnaire_version: Mapped[str] = mapped_column(String(32), nullable=False) answers: Mapped[Any] = mapped_column(JSON, nullable=False) total_score: Mapped[int] = mapped_column(Integer, nullable=False) investor_type: Mapped[str] = mapped_column(String(8), nullable=False) assessed_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) valid_until: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundRiskAlert(Base): """风险预警事件(fin_risk_alert)。""" __tablename__ = "fin_risk_alert" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) alert_no: Mapped[str] = mapped_column(String(64), nullable=False) customer_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) related_transaction_id: Mapped[int | None] = mapped_column( BIGINT(unsigned=True), nullable=True ) related_order_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) related_work_order_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) primary_risk_work_order_id: Mapped[int | None] = mapped_column( BIGINT(unsigned=True), nullable=True ) alert_type: Mapped[str] = mapped_column(String(64), nullable=False) alert_level: Mapped[str] = mapped_column(String(8), nullable=False) trigger_rule_codes: Mapped[Any] = mapped_column(JSON, nullable=False) evidence_summary: Mapped[str] = mapped_column(Text, nullable=False) evidence_snapshot: Mapped[Any] = mapped_column(JSON, nullable=False) priority_score: Mapped[int] = mapped_column(Integer, nullable=False) event_status: Mapped[str] = mapped_column(String(16), nullable=False) status: Mapped[str] = mapped_column(String(16), nullable=False) ack_status: Mapped[str] = mapped_column(String(16), nullable=False) ack_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) handler_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) due_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) is_escalated: Mapped[int] = mapped_column(TINYINT(display_width=1), nullable=False) escalated_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) escalation_reason: Mapped[str | None] = mapped_column(Text, nullable=True) handle_result: Mapped[str | None] = mapped_column(Text, nullable=True) closed_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) close_reason: Mapped[str | None] = mapped_column(Text, nullable=True) ai_analysis: Mapped[Any | None] = mapped_column(JSON, nullable=True) manual_remark: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) updated_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False) class FundRiskNotification(Base): """风险通知(fin_risk_notification)。无 customer_id,客户归属经 alert_id 间接解析。""" __tablename__ = "fin_risk_notification" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True) notification_no: Mapped[str] = mapped_column(String(64), nullable=False) alert_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), nullable=False) channel: Mapped[str] = mapped_column(String(16), nullable=False) receiver_user_id: Mapped[int | None] = mapped_column(BIGINT(unsigned=True), nullable=True) receiver_email: Mapped[str | None] = mapped_column(String(128), nullable=True) title: Mapped[str] = mapped_column(String(128), nullable=False) content: Mapped[str] = mapped_column(Text, nullable=False) send_status: Mapped[str] = mapped_column(String(16), nullable=False) sent_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) read_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) acknowledged_at: Mapped[datetime | None] = mapped_column(DATETIME(fsp=0), nullable=True) fail_reason: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DATETIME(fsp=0), nullable=False)