dingdanquanliucheng/backend/app/models/business.py
wsb1224 4d8cc9a21c fix: 自发订单填入快递单号后自动识别并保存快递公司,修复物流签收状态无法自动更新
自发订单(self-delivery)的 tracking_number 存储在 sales_order 表,
但 express_company 字段缺失,导致定时任务查询快递100时缺少 com 参数
而静默失败,订单永远停在"运输中"。

- SalesOrder 模型新增 express_company 字段及数据库迁移
- create_order/update_order 时自动识别并保存快递公司
- update_tracking_number 修改单号时同步更新快递公司
- _load_third_party_traces 回退读取订单表 express_company 并自动回填
- check_delivery_by_tracking 增加详细日志便于排查

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 11:10:19 +08:00

462 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""业务核心 ORM 模型。
定义订单管理系统的所有业务数据表,包括客户、产品、供应商、
订单、物流、提醒、文件附件、定价规则等。
"""
from sqlalchemy import Boolean, Date, DateTime, ForeignKey, Integer, Numeric, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column
from backend.app.db import Base
from backend.app.models.base import AuditMixin, TimestampMixin
class Customer(TimestampMixin, AuditMixin, Base):
"""客户表customer
存储客户基本信息、结算方式、所属业务员、信用额度等。
支持软删除AuditMixin.deleted
被调用方: customer_repository、customer_service、order_service
"""
__tablename__ = "customer"
id: Mapped[int] = mapped_column(primary_key=True)
customer_name: Mapped[str] = mapped_column(String(64), index=True)
mobile: Mapped[str] = mapped_column(String(32), index=True)
address: Mapped[str | None] = mapped_column(String(255), nullable=True)
settlement_type: Mapped[str | None] = mapped_column(String(32), nullable=True)
settlement_days: Mapped[int] = mapped_column(default=0)
settlement_day_of_month: Mapped[int | None] = mapped_column(Integer, nullable=True) # 每月几号结算
reminder_day_of_month: Mapped[int | None] = mapped_column(Integer, nullable=True) # 每月几号提醒
customer_type: Mapped[str | None] = mapped_column(String(32), nullable=True)
price_tier: Mapped[str | None] = mapped_column(String(32), nullable=True, default="default")
salesman_id: Mapped[int | None] = mapped_column(nullable=True)
credit_limit: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
class ProductCategory(TimestampMixin, AuditMixin, Base):
"""产品分类表product_category
管理产品的一级分类,如"铝板""钢材"等。
category_code 唯一标识sort_no 控制前端排序。
"""
__tablename__ = "product_category"
id: Mapped[int] = mapped_column(primary_key=True)
category_name: Mapped[str] = mapped_column(String(64))
category_code: Mapped[str] = mapped_column(String(64), unique=True, index=True)
sort_no: Mapped[int] = mapped_column(default=0)
status: Mapped[int] = mapped_column(default=1)
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
class Product(TimestampMixin, AuditMixin, Base):
"""产品表product
存储产品名称、规格、单位、成本价、售价等信息。
category_id 关联 product_category 表is_default 标记默认规格。
被调用方: product_repository、product_service、pricing_engine
"""
__tablename__ = "product"
id: Mapped[int] = mapped_column(primary_key=True)
product_name: Mapped[str] = mapped_column(String(64), index=True)
specification: Mapped[str] = mapped_column(String(64), default="")
unit: Mapped[str] = mapped_column(String(32), default="")
category: Mapped[str | None] = mapped_column(String(64), nullable=True)
category_id: Mapped[int | None] = mapped_column(ForeignKey("product_category.id"), nullable=True)
cost_price: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
sale_price: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
pricing_type: Mapped[str | None] = mapped_column(String(32), nullable=True)
pricing_unit: Mapped[str | None] = mapped_column(String(16), nullable=True, default="")
thickness: Mapped[str | None] = mapped_column(String(32), nullable=True)
weight_gsm: Mapped[int | None] = mapped_column(Integer, nullable=True)
default_width_m: Mapped[float | None] = mapped_column(Numeric(10, 4), nullable=True)
status: Mapped[int] = mapped_column(default=1)
is_default: Mapped[int] = mapped_column(default=0)
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
class Supplier(TimestampMixin, AuditMixin, Base):
"""供应商表supplier
存储供应商/工厂基本信息supplier_type 区分"factory"等类型。
template_type 用于生成发厂文本时选择对应的模板。
"""
__tablename__ = "supplier"
id: Mapped[int] = mapped_column(primary_key=True)
supplier_name: Mapped[str] = mapped_column(String(64), index=True)
supplier_type: Mapped[str] = mapped_column(String(32), default="factory")
contact_name: Mapped[str | None] = mapped_column(String(64), nullable=True)
contact_mobile: Mapped[str | None] = mapped_column(String(32), nullable=True)
address: Mapped[str | None] = mapped_column(String(255), nullable=True)
template_type: Mapped[str | None] = mapped_column(String(64), nullable=True)
status: Mapped[int] = mapped_column(default=1)
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
class SalesOrder(TimestampMixin, AuditMixin, Base):
"""销售订单主表sales_order
订单全流程的核心表,包含客户信息快照、金额汇总、状态流转、
取消审批、发厂确认等字段。order_status 驱动整个业务流程。
状态流转(有司机): draft → pending_approve → approved → pending_driver → accepted → picked_up → pending_logistics → (快递100) → delivered → completed → settled
状态流转(无司机): draft → pending_approve → approved → pending_logistics → (快递100) → delivered → completed → settled
被调用方: order_repository、order_service、reminder_service
"""
__tablename__ = "sales_order"
id: Mapped[int] = mapped_column(primary_key=True)
order_no: Mapped[str] = mapped_column(String(64), unique=True, index=True)
customer_id: Mapped[int | None] = mapped_column(ForeignKey("customer.id"), nullable=True)
customer_name: Mapped[str] = mapped_column(String(64))
customer_mobile: Mapped[str] = mapped_column(String(32))
customer_address: Mapped[str | None] = mapped_column(String(255), nullable=True)
salesman_id: Mapped[int | None] = mapped_column(nullable=True)
order_status: Mapped[str] = mapped_column(String(32), index=True, default="draft")
order_source: Mapped[str | None] = mapped_column(String(64), nullable=True)
delivery_type: Mapped[str | None] = mapped_column(String(64), nullable=True)
factory_id: Mapped[int | None] = mapped_column(ForeignKey("supplier.id"), nullable=True)
contract_amount: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
sale_price_total: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
cost_price_total: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
rebate_total: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
freight_total: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
tax_total: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
other_fee_total: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
profit_total: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
profit_rate: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
commission_amount: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
cancel_requested_by: Mapped[int | None] = mapped_column(nullable=True)
cancel_requested_at: Mapped[DateTime | None] = mapped_column(DateTime, nullable=True)
cancel_reason: Mapped[str | None] = mapped_column(String(255), nullable=True)
cancel_opinion: Mapped[str | None] = mapped_column(Text, nullable=True)
cancel_previous_status: Mapped[str | None] = mapped_column(String(32), nullable=True)
supplier_text_confirmed_at: Mapped[DateTime | None] = mapped_column(DateTime, nullable=True)
supplier_text_confirmed_by: Mapped[int | None] = mapped_column(nullable=True)
payment_method: Mapped[str | None] = mapped_column(String(64), nullable=True)
tax_amount: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
need_invoice: Mapped[int] = mapped_column(Integer, default=0)
order_type: Mapped[str | None] = mapped_column(String(32), nullable=True) # industry/daily
self_delivery: Mapped[int] = mapped_column(Integer, default=0)
tracking_number: Mapped[str | None] = mapped_column(String(128), nullable=True)
express_company: Mapped[str | None] = mapped_column(String(64), nullable=True)
customer_demand: Mapped[str | None] = mapped_column(Text, nullable=True)
remark: Mapped[str | None] = mapped_column(Text, nullable=True)
class SalesOrderItem(TimestampMixin, AuditMixin, Base):
"""订单明细行表sales_order_item
每个订单包含多条明细,记录产品、数量、单价、面积、附加费用等。
pricing_type 区分按面积/按重量等计价方式。
被调用方: order_repository、order_service
"""
__tablename__ = "sales_order_item"
id: Mapped[int] = mapped_column(primary_key=True)
order_id: Mapped[int] = mapped_column(ForeignKey("sales_order.id"), index=True)
product_id: Mapped[int | None] = mapped_column(nullable=True)
product_name: Mapped[str] = mapped_column(String(64))
specification: Mapped[str] = mapped_column(String(64), default="")
unit: Mapped[str] = mapped_column(String(32), default="")
quantity: Mapped[float] = mapped_column(Numeric(18, 4), default=0)
sale_price: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
cost_price: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
rebate_amount: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
freight_amount: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
tax_amount: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
other_fee_amount: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
pricing_type: Mapped[str | None] = mapped_column(String(32), nullable=True)
length_m: Mapped[float | None] = mapped_column(Numeric(10, 4), nullable=True)
width_m: Mapped[float | None] = mapped_column(Numeric(10, 4), nullable=True)
area_sqm: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True)
surcharge_detail: Mapped[str | None] = mapped_column(Text, nullable=True)
processing_detail: Mapped[str | None] = mapped_column(Text, nullable=True)
supplier_id: Mapped[int | None] = mapped_column(ForeignKey("supplier.id"), nullable=True)
supplier_model: Mapped[str | None] = mapped_column(String(64), nullable=True)
price_tier: Mapped[str | None] = mapped_column(String(32), nullable=True)
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
demand_specification: Mapped[str | None] = mapped_column(String(128), nullable=True)
class SalesOrderApproveLog(TimestampMixin, AuditMixin, Base):
"""订单审批日志表sales_order_approve_log
记录每次审批操作的结果(通过/驳回)、审批前后状态、审批意见。
approve_type 区分"order"(订单审批)和"cancel_order"(取消审批)。
被调用方: order_service
"""
__tablename__ = "sales_order_approve_log"
id: Mapped[int] = mapped_column(primary_key=True)
order_id: Mapped[int] = mapped_column(ForeignKey("sales_order.id"), index=True)
approve_type: Mapped[str] = mapped_column(String(32), default="order")
approve_result: Mapped[str] = mapped_column(String(32))
before_status: Mapped[str | None] = mapped_column(String(32), nullable=True)
after_status: Mapped[str | None] = mapped_column(String(32), nullable=True)
approve_opinion: Mapped[str | None] = mapped_column(Text, nullable=True)
operator_id: Mapped[int | None] = mapped_column(nullable=True)
class OrderSupplierTextLog(TimestampMixin, AuditMixin, Base):
"""发厂文本日志表order_supplier_text_log
记录为供应商生成的发厂文本内容及确认状态。
confirmed 标记工厂是否已确认文本内容。
被调用方: order_service
"""
__tablename__ = "order_supplier_text_log"
id: Mapped[int] = mapped_column(primary_key=True)
order_id: Mapped[int] = mapped_column(ForeignKey("sales_order.id"), index=True)
supplier_id: Mapped[int] = mapped_column(ForeignKey("supplier.id"), index=True)
template_type: Mapped[str | None] = mapped_column(String(64), nullable=True)
text_content: Mapped[str] = mapped_column(Text)
confirmed: Mapped[int] = mapped_column(default=0)
confirmed_by: Mapped[int | None] = mapped_column(nullable=True)
confirmed_at: Mapped[DateTime | None] = mapped_column(DateTime, nullable=True)
created_by: Mapped[int | None] = mapped_column(nullable=True)
class LogisticsTask(TimestampMixin, AuditMixin, Base):
"""物流任务表logistics_task
记录每个订单的物流配送任务,包含取货/送货地址、司机、
快递单号、任务状态等。status: pending → accepted → picked_up → delivered。
被调用方: logistics_repository、logistics_service、reminder_service
"""
__tablename__ = "logistics_task"
id: Mapped[int] = mapped_column(primary_key=True)
task_no: Mapped[str | None] = mapped_column(String(64), nullable=True, unique=True)
order_id: Mapped[int] = mapped_column(ForeignKey("sales_order.id"), index=True)
driver_id: Mapped[int] = mapped_column(index=True)
factory_id: Mapped[int | None] = mapped_column(ForeignKey("supplier.id"), nullable=True)
pickup_address: Mapped[str] = mapped_column(String(255))
delivery_address: Mapped[str] = mapped_column(String(255))
pickup_content: Mapped[str] = mapped_column(String(255))
quantity: Mapped[float] = mapped_column(Numeric(18, 4), default=0)
status: Mapped[str] = mapped_column(String(32), default="pending")
tracking_number: Mapped[str | None] = mapped_column(String(64), nullable=True)
express_company: Mapped[str | None] = mapped_column(String(32), nullable=True)
created_by: Mapped[int | None] = mapped_column(nullable=True)
canceled_by: Mapped[int | None] = mapped_column(nullable=True)
cancel_reason: Mapped[str | None] = mapped_column(String(255), nullable=True)
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
class LogisticsTrace(Base):
"""物流轨迹表logistics_trace
记录物流节点的时间、描述、类型、数据来源平台。
支持从快递100等第三方平台同步轨迹数据。
"""
__tablename__ = "logistics_trace"
id: Mapped[int] = mapped_column(primary_key=True)
order_id: Mapped[int] = mapped_column(ForeignKey("sales_order.id"), index=True)
task_id: Mapped[int | None] = mapped_column(ForeignKey("logistics_task.id"), nullable=True, index=True)
node_time: Mapped[DateTime] = mapped_column(DateTime)
node_desc: Mapped[str] = mapped_column(String(500))
node_type: Mapped[str | None] = mapped_column(String(64), nullable=True)
source_platform: Mapped[str | None] = mapped_column(String(64), nullable=True)
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
created_at: Mapped[DateTime] = mapped_column(DateTime, server_default=func.now())
class FileAttachment(Base):
"""文件附件表file_attachment
通用文件关联表,通过 biz_type + biz_id 关联任意业务对象。
存储文件 URL、类型、大小、阿里云 OSS 存储信息等。
被调用方: file_repository、file_service
"""
__tablename__ = "file_attachment"
id: Mapped[int] = mapped_column(primary_key=True)
biz_type: Mapped[str] = mapped_column(String(64), index=True)
biz_id: Mapped[int] = mapped_column(index=True)
file_name: Mapped[str] = mapped_column(String(255))
file_url: Mapped[str] = mapped_column(String(1000))
file_type: Mapped[str | None] = mapped_column(String(64), nullable=True)
file_size: Mapped[int | None] = mapped_column(nullable=True)
created_by: Mapped[int | None] = mapped_column(nullable=True)
created_at: Mapped[DateTime] = mapped_column(DateTime, server_default=func.now())
storage_provider: Mapped[str | None] = mapped_column(String(32), nullable=True, default="aliyun_oss")
bucket_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
object_key: Mapped[str | None] = mapped_column(String(1000), nullable=True)
content_type: Mapped[str | None] = mapped_column(String(128), nullable=True)
file_ext: Mapped[str | None] = mapped_column(String(32), nullable=True)
file_category: Mapped[str | None] = mapped_column(String(32), nullable=True)
class CustomerArrears(Base):
"""客户欠款表customer_arrears
记录客户未结清的欠款信息,包括欠款金额、账期起始日、到期日。
status: pending → overdue超期后触发提醒。
被调用方: reminder_repository、reminder_service
"""
__tablename__ = "customer_arrears"
id: Mapped[int] = mapped_column(primary_key=True)
customer_id: Mapped[int] = mapped_column(ForeignKey("customer.id"), index=True)
order_id: Mapped[int] = mapped_column(ForeignKey("sales_order.id"), index=True)
arrears_amount: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
settlement_start_date: Mapped[Date | None] = mapped_column(Date, nullable=True)
due_date: Mapped[Date | None] = mapped_column(Date, nullable=True)
status: Mapped[str] = mapped_column(String(32), default="pending")
reminded_at: Mapped[DateTime | None] = mapped_column(DateTime, nullable=True)
created_at: Mapped[DateTime] = mapped_column(DateTime, server_default=func.now())
class SystemReminder(Base):
"""系统提醒表system_reminder
存储系统自动生成的提醒消息,如物流超时、欠款逾期、沉默客户等。
reminder_type 区分提醒类型receiver_user_id 指定接收人。
status: pending → read。
被调用方: reminder_repository、reminder_service
"""
__tablename__ = "system_reminder"
id: Mapped[int] = mapped_column(primary_key=True)
reminder_type: Mapped[str] = mapped_column(String(64), index=True)
biz_type: Mapped[str] = mapped_column(String(64), index=True)
biz_id: Mapped[int] = mapped_column(index=True)
receiver_user_id: Mapped[int] = mapped_column(index=True)
reminder_title: Mapped[str] = mapped_column(String(255))
reminder_content: Mapped[str] = mapped_column(String(2000))
status: Mapped[str] = mapped_column(String(32), default="pending")
sent_at: Mapped[DateTime | None] = mapped_column(DateTime, nullable=True)
created_at: Mapped[DateTime] = mapped_column(DateTime, server_default=func.now())
class AIRecognitionLog(Base):
"""AI 识别日志表ai_recognition_log
记录 AI 图片识别的原始结果、置信度、人工修正结果。
用于 AI 识别功能的结果追踪和模型优化。
被调用方: ai_repository、ai_service
"""
__tablename__ = "ai_recognition_log"
id: Mapped[int] = mapped_column(primary_key=True)
biz_type: Mapped[str] = mapped_column(String(64), index=True)
biz_id: Mapped[int] = mapped_column(index=True)
image_url: Mapped[str] = mapped_column(String(1000))
raw_result: Mapped[str | None] = mapped_column(Text, nullable=True)
confidence: Mapped[float | None] = mapped_column(Numeric(10, 4), nullable=True)
corrected_result: Mapped[str | None] = mapped_column(Text, nullable=True)
created_by: Mapped[int | None] = mapped_column(nullable=True)
created_at: Mapped[DateTime] = mapped_column(DateTime, server_default=func.now())
class ProductPricingRule(TimestampMixin, AuditMixin, Base):
"""产品定价规则表product_pricing_rule
定义每个产品的计价方式(按面积/按重量)、基准单价、
计算公式、附加费用等。被 pricing_engine 在创建订单时调用。
"""
__tablename__ = "product_pricing_rule"
id: Mapped[int] = mapped_column(primary_key=True)
product_id: Mapped[int] = mapped_column(unique=True, index=True)
product_name: Mapped[str] = mapped_column(String(128))
pricing_type: Mapped[str] = mapped_column(String(32), default="area")
base_unit_price: Mapped[float] = mapped_column(Numeric(18, 4), default=0)
pricing_unit: Mapped[str] = mapped_column(String(16), default="")
pricing_inputs: Mapped[str | None] = mapped_column(Text, nullable=True)
formula_expr: Mapped[str | None] = mapped_column(Text, nullable=True)
formula_constants: Mapped[str | None] = mapped_column(Text, nullable=True)
surcharge_json: Mapped[str | None] = mapped_column(Text, nullable=True)
formula_note: Mapped[str | None] = mapped_column(Text, nullable=True)
tax_rate: Mapped[float] = mapped_column(Numeric(5, 2), default=0)
tax_inclusive: Mapped[int] = mapped_column(default=0)
status: Mapped[int] = mapped_column(default=1)
class SupplierProductCost(TimestampMixin, AuditMixin, Base):
"""供应商产品成本表supplier_product_cost
记录每个供应商对每个产品的成本价格,支持按厚度、克重等维度定价。
is_primary 标记主供应商。被 pricing_engine 在计算订单成本时调用。
"""
__tablename__ = "supplier_product_cost"
id: Mapped[int] = mapped_column(primary_key=True)
product_id: Mapped[int] = mapped_column(index=True)
supplier_id: Mapped[int] = mapped_column(index=True)
supplier_model: Mapped[str | None] = mapped_column(String(64), nullable=True)
our_model: Mapped[str | None] = mapped_column(String(64), nullable=True)
thickness: Mapped[str | None] = mapped_column(String(32), nullable=True)
weight_gsm: Mapped[int | None] = mapped_column(Integer, nullable=True)
base_fabric_weight: Mapped[int | None] = mapped_column(Integer, nullable=True)
cost_price: Mapped[float] = mapped_column(Numeric(18, 4))
cost_unit: Mapped[str] = mapped_column(String(16), default="")
is_primary: Mapped[int] = mapped_column(default=0)
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
status: Mapped[int] = mapped_column(default=1)
class ProductPriceTier(TimestampMixin, AuditMixin, Base):
"""产品价格层级表product_price_tier
定义产品的多级价格体系如普通客户价、VIP客户价
tier_code 区分层级,前端根据客户的价格层级选择对应售价。
"""
__tablename__ = "product_price_tier"
id: Mapped[int] = mapped_column(primary_key=True)
product_id: Mapped[int] = mapped_column(index=True)
tier_code: Mapped[str] = mapped_column(String(32))
tier_name: Mapped[str] = mapped_column(String(64))
price: Mapped[float] = mapped_column(Numeric(18, 4))
price_unit: Mapped[str] = mapped_column(String(16), default="")
remark: Mapped[str | None] = mapped_column(String(255), nullable=True)
status: Mapped[int] = mapped_column(default=1)
class LogisticsWaybill(TimestampMixin, AuditMixin, Base):
"""物流运单号表logistics_waybill
记录一个物流任务关联的多个运单号(一票货分多件场景)。
通过 task_id 关联 logistics_taskorder_id 冗余关联 sales_order。
被调用方: logistics_repository、logistics_service
"""
__tablename__ = "logistics_waybill"
id: Mapped[int] = mapped_column(primary_key=True)
task_id: Mapped[int] = mapped_column(ForeignKey("logistics_task.id"), index=True)
order_id: Mapped[int] = mapped_column(ForeignKey("sales_order.id"), index=True)
tracking_number: Mapped[str] = mapped_column(String(64), index=True)
express_company: Mapped[str | None] = mapped_column(String(32), nullable=True)
express_name: Mapped[str | None] = mapped_column(String(64), nullable=True)
image_url: Mapped[str | None] = mapped_column(String(1000), nullable=True)
ocr_raw: Mapped[str | None] = mapped_column(Text, nullable=True)
status: Mapped[str] = mapped_column(String(32), default="pending")
created_by: Mapped[int | None] = mapped_column(nullable=True)
class PerformanceStatCache(Base):
"""业绩统计缓存表performance_stat_cache
存储业绩统计结果缓存,避免重复查询数据库。
created_at 超过 24 小时视为过期。
"""
__tablename__ = "performance_stat_cache"
id: Mapped[int] = mapped_column(primary_key=True)
stat_type: Mapped[str] = mapped_column(String(16), index=True)
stat_period: Mapped[str] = mapped_column(String(32), index=True)
category_id: Mapped[int | None] = mapped_column(nullable=True)
order_count: Mapped[int] = mapped_column(Integer, default=0)
order_amount: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
commission_amount: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
total_profit: Mapped[float] = mapped_column(Numeric(18, 2), default=0)
category_amounts_json: Mapped[str | None] = mapped_column(Text, nullable=True)
created_at: Mapped[DateTime] = mapped_column(DateTime, server_default=func.now())