266 lines
9.4 KiB
Python
266 lines
9.4 KiB
Python
"""
|
|
商品相关模型
|
|
"""
|
|
from datetime import datetime
|
|
from config.database import db
|
|
import json
|
|
|
|
|
|
class Category(db.Model):
|
|
"""商品分类模型"""
|
|
__tablename__ = 'categories'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
name = db.Column(db.String(50), nullable=False)
|
|
parent_id = db.Column(db.Integer, default=0)
|
|
level = db.Column(db.Integer, default=1)
|
|
sort_order = db.Column(db.Integer, default=0)
|
|
icon_url = db.Column(db.String(255))
|
|
is_active = db.Column(db.Integer, default=1)
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
def to_dict(self):
|
|
return {
|
|
'id': self.id,
|
|
'name': self.name,
|
|
'parent_id': self.parent_id,
|
|
'level': self.level,
|
|
'sort_order': self.sort_order,
|
|
'icon_url': self.icon_url,
|
|
'is_active': self.is_active,
|
|
'created_at': self.created_at.isoformat() if self.created_at else None
|
|
}
|
|
|
|
def __repr__(self):
|
|
return f'<Category {self.name}>'
|
|
|
|
|
|
class SpecName(db.Model):
|
|
"""规格名称模型"""
|
|
__tablename__ = 'spec_names'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
name = db.Column(db.String(50), nullable=False)
|
|
sort_order = db.Column(db.Integer, default=0)
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
|
|
def to_dict(self):
|
|
return {
|
|
'id': self.id,
|
|
'name': self.name,
|
|
'sort_order': self.sort_order
|
|
}
|
|
|
|
def __repr__(self):
|
|
return f'<SpecName {self.name}>'
|
|
|
|
|
|
class SpecValue(db.Model):
|
|
"""规格值模型"""
|
|
__tablename__ = 'spec_values'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
spec_name_id = db.Column(db.Integer, db.ForeignKey('spec_names.id'), nullable=False)
|
|
value = db.Column(db.String(100), nullable=False)
|
|
sort_order = db.Column(db.Integer, default=0)
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
|
|
spec_name = db.relationship('SpecName', backref='values')
|
|
|
|
def to_dict(self):
|
|
return {
|
|
'id': self.id,
|
|
'spec_name_id': self.spec_name_id,
|
|
'value': self.value,
|
|
'sort_order': self.sort_order
|
|
}
|
|
|
|
def __repr__(self):
|
|
return f'<SpecValue {self.value}>'
|
|
|
|
|
|
class Product(db.Model):
|
|
"""商品模型"""
|
|
__tablename__ = 'products'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
name = db.Column(db.String(200), nullable=False)
|
|
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'), nullable=False)
|
|
brand = db.Column(db.String(100))
|
|
price = db.Column(db.Numeric(10, 2), nullable=False)
|
|
original_price = db.Column(db.Numeric(10, 2))
|
|
description = db.Column(db.Text)
|
|
main_image = db.Column(db.String(255))
|
|
status = db.Column(db.Integer, default=1) # 0-下架 1-上架
|
|
has_specs = db.Column(db.Integer, default=0) # 0-无规格 1-有规格
|
|
sales_count = db.Column(db.Integer, default=0)
|
|
view_count = db.Column(db.Integer, default=0)
|
|
weight = db.Column(db.Numeric(8, 2))
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
category = db.relationship('Category', backref='products')
|
|
|
|
def to_dict(self):
|
|
return {
|
|
'id': self.id,
|
|
'name': self.name,
|
|
'category_id': self.category_id,
|
|
'category_name': self.category.name if self.category else '',
|
|
'brand': self.brand,
|
|
'price': float(self.price) if self.price else 0,
|
|
'original_price': float(self.original_price) if self.original_price else None,
|
|
'description': self.description,
|
|
'main_image': self.main_image,
|
|
'status': self.status,
|
|
'has_specs': self.has_specs,
|
|
'sales_count': self.sales_count,
|
|
'view_count': self.view_count,
|
|
'weight': float(self.weight) if self.weight else None,
|
|
'created_at': self.created_at.isoformat() if self.created_at else None,
|
|
'updated_at': self.updated_at.isoformat() if self.updated_at else None
|
|
}
|
|
|
|
def __repr__(self):
|
|
return f'<Product {self.name}>'
|
|
|
|
|
|
class ProductImage(db.Model):
|
|
"""商品图片模型"""
|
|
__tablename__ = 'product_images'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
|
|
image_url = db.Column(db.String(255), nullable=False)
|
|
sort_order = db.Column(db.Integer, default=0)
|
|
is_main = db.Column(db.Integer, default=0) # 0-否 1-是
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
|
|
product = db.relationship('Product', backref='images')
|
|
|
|
def to_dict(self):
|
|
return {
|
|
'id': self.id,
|
|
'product_id': self.product_id,
|
|
'image_url': self.image_url,
|
|
'sort_order': self.sort_order,
|
|
'is_main': self.is_main,
|
|
'created_at': self.created_at.isoformat() if self.created_at else None
|
|
}
|
|
|
|
def __repr__(self):
|
|
return f'<ProductImage {self.id}>'
|
|
|
|
|
|
class ProductSpecRelation(db.Model):
|
|
"""商品规格关联模型"""
|
|
__tablename__ = 'product_spec_relations'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
|
|
spec_name_id = db.Column(db.Integer, db.ForeignKey('spec_names.id'), nullable=False)
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
|
|
product = db.relationship('Product', backref='spec_relations')
|
|
spec_name = db.relationship('SpecName')
|
|
|
|
def __repr__(self):
|
|
return f'<ProductSpecRelation {self.product_id}-{self.spec_name_id}>'
|
|
|
|
|
|
class ProductInventory(db.Model):
|
|
"""商品库存模型(SKU)"""
|
|
__tablename__ = 'product_inventory'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
|
|
sku_code = db.Column(db.String(100), unique=True, nullable=False)
|
|
spec_combination = db.Column(db.JSON)
|
|
price_adjustment = db.Column(db.Numeric(10, 2), default=0)
|
|
stock = db.Column(db.Integer, nullable=False, default=0)
|
|
warning_stock = db.Column(db.Integer, default=10)
|
|
is_default = db.Column(db.Integer, default=0)
|
|
status = db.Column(db.Integer, default=1)
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
product = db.relationship('Product', backref='inventory')
|
|
|
|
def get_final_price(self):
|
|
"""获取最终价格"""
|
|
base_price = float(self.product.price) if self.product and self.product.price else 0
|
|
adjustment = float(self.price_adjustment) if self.price_adjustment else 0
|
|
return base_price + adjustment
|
|
|
|
def to_dict(self):
|
|
return {
|
|
'id': self.id,
|
|
'product_id': self.product_id,
|
|
'sku_code': self.sku_code,
|
|
'spec_combination': self.spec_combination,
|
|
'price_adjustment': float(self.price_adjustment) if self.price_adjustment else 0,
|
|
'final_price': self.get_final_price(),
|
|
'stock': self.stock,
|
|
'warning_stock': self.warning_stock,
|
|
'is_default': self.is_default,
|
|
'status': self.status,
|
|
'created_at': self.created_at.isoformat() if self.created_at else None
|
|
}
|
|
|
|
def __repr__(self):
|
|
return f'<ProductInventory {self.sku_code}>'
|
|
|
|
|
|
class InventoryLog(db.Model):
|
|
"""库存变更日志模型"""
|
|
__tablename__ = 'inventory_logs'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
product_id = db.Column(db.Integer, db.ForeignKey('products.id'), nullable=False)
|
|
sku_code = db.Column(db.String(100), nullable=False)
|
|
change_type = db.Column(db.Integer, nullable=False) # 1-入库 2-出库 3-调整
|
|
change_quantity = db.Column(db.Integer, nullable=False)
|
|
before_stock = db.Column(db.Integer, nullable=False)
|
|
after_stock = db.Column(db.Integer, nullable=False)
|
|
related_order_id = db.Column(db.Integer)
|
|
remark = db.Column(db.String(255))
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
|
|
product = db.relationship('Product')
|
|
|
|
@classmethod
|
|
def create_log(cls, product_id, sku_code, change_type, change_quantity,
|
|
before_stock, after_stock, related_order_id=None, remark=None):
|
|
"""创建库存变更日志"""
|
|
log = cls(
|
|
product_id=product_id,
|
|
sku_code=sku_code,
|
|
change_type=change_type,
|
|
change_quantity=change_quantity,
|
|
before_stock=before_stock,
|
|
after_stock=after_stock,
|
|
related_order_id=related_order_id,
|
|
remark=remark
|
|
)
|
|
db.session.add(log)
|
|
db.session.commit()
|
|
return log
|
|
|
|
def to_dict(self):
|
|
return {
|
|
'id': self.id,
|
|
'product_id': self.product_id,
|
|
'sku_code': self.sku_code,
|
|
'change_type': self.change_type,
|
|
'change_quantity': self.change_quantity,
|
|
'before_stock': self.before_stock,
|
|
'after_stock': self.after_stock,
|
|
'related_order_id': self.related_order_id,
|
|
'remark': self.remark,
|
|
'created_at': self.created_at.isoformat() if self.created_at else None
|
|
}
|
|
|
|
def __repr__(self):
|
|
return f'<InventoryLog {self.sku_code}>'
|