自发订单(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>
462 lines
24 KiB
Python
462 lines
24 KiB
Python
"""业务核心 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_task,order_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())
|