2026-08-09 10:43:45 +08:00
|
|
|
|
// Models generated by mesh dev cli,@Author: David Yan(david.yan@qq.com).
|
|
|
|
|
|
package models
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2026-08-09 12:41:08 +08:00
|
|
|
|
"bsm/full/module/ec/mall/internal/impl"
|
2026-08-09 10:43:45 +08:00
|
|
|
|
"git.apinb.com/bsm-sdk/core/database"
|
|
|
|
|
|
"git.apinb.com/bsm-sdk/core/types"
|
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* MallProduct
|
|
|
|
|
|
* Comment: 商品基本信息
|
|
|
|
|
|
* Version: 10
|
|
|
|
|
|
* Created: 2022-04-12 14:34:27 , Updated:0001-01-01 00:00:00
|
|
|
|
|
|
*/
|
|
|
|
|
|
type MallProduct struct {
|
|
|
|
|
|
types.Std_IICUDS
|
|
|
|
|
|
Std_Store
|
|
|
|
|
|
SerialId string `gorm:"column:serial_id;type:varchar(36);Index;" json:"serial_id"` // 产品序列号
|
|
|
|
|
|
SupplyId int64 `gorm:"column:supply_id;default:0;supply_id;"` // 供应商ID
|
|
|
|
|
|
Title string `gorm:"column:title;type:varchar(255);not null;" json:"title"` // 商品副标题
|
|
|
|
|
|
MeasuringName string `gorm:"column:measuring_name;type:varchar(255);default:'';" json:"measuring_name"` // 测量单位名称
|
|
|
|
|
|
SellMax int32 `gorm:"column:sell_max;default:0;" json:"sell_max"` // 最高限购
|
|
|
|
|
|
SellMin int32 `gorm:"column:sell_min;default:0;" json:"sell_min"` // 最低限购
|
|
|
|
|
|
Wastage int32 `gorm:"column:wastage;default:0;" json:"wastage"` // 损耗率
|
|
|
|
|
|
StandardNumber string `gorm:"column:standard_number;type:varchar(255);default:'';" json:"standard_number"` // 商品标准编号
|
|
|
|
|
|
Brand string `gorm:"column:brand;type:varchar(255);default:'';" json:"brand"` // 品牌
|
|
|
|
|
|
StockType int32 `gorm:"column:stock_type;default:1;" json:"stock_type"` // 库存计数方式:1-买方下单减库存,2-完成签约减库存
|
|
|
|
|
|
Status int32 `gorm:"column:status;default:0;" json:"status"` // 商品状态:5-未上架,1-已上架,2-自动上架等待中,3-禁用 ,4-草稿
|
|
|
|
|
|
PutawayTime string `gorm:"column:putaway_time;type:varchar(255);default:'';" json:"putaway_time"` // 预定的上架时间
|
|
|
|
|
|
CoverImage string `gorm:"column:cover_image;type:varchar(255);default:'';" json:"cover_image"` // 商品代表性图片
|
|
|
|
|
|
TemplateID int64 `gorm:"column:template_id;default:0;" json:"template_id"` // 运费模板id
|
|
|
|
|
|
RecentNumber int64 `gorm:"column:recent_number;default:0;" json:"recent_number"` // 最近30天的销量
|
|
|
|
|
|
Limitation int64 `gorm:"column:limitation;default:0;" json:"limitation"` // 区域限制:1不限制,2限制
|
|
|
|
|
|
LiTemplateID int64 `gorm:"column:li_template_id;default:0;" json:"li_template_id"` // 区域限制的模板id
|
|
|
|
|
|
Content string `gorm:"column:content;type:text;default:'';" json:"content"` // 详情
|
|
|
|
|
|
Notes string `gorm:"column:notes;type:varchar(255);default:'';" json:"notes"` // 备注说明:发货方式及时间
|
|
|
|
|
|
SaleTotal int32 `gorm:"column:sale_total;default:0;" json:"sale_total"` // 销售总数
|
|
|
|
|
|
SalesPrice int64 `gorm:"column:sales_price;default:0;" json:"sales_price"` // 商品售价
|
|
|
|
|
|
Stock int32 `gorm:"column:stock;default:0;" json:"stock"` // 库存
|
|
|
|
|
|
|
|
|
|
|
|
CategoryPaths string `gorm:"column:category_paths;type:varchar(255);default:'';" json:"category_paths"` // 商品类别ID路径(多个分类ID使用半角逗号分割)
|
|
|
|
|
|
SubTitle string `gorm:"column:sub_title;type:varchar(255);not null;" json:"sub_title"` // 商品副标题
|
|
|
|
|
|
Keyword string `gorm:"column:keyword;type:varchar(255);default:'';" json:"keyword"` // 商品关键词
|
|
|
|
|
|
CostPrice int64 `gorm:"column:cost_price;default:0;" json:"cost_price"` // 成本价格
|
|
|
|
|
|
Args string `gorm:"column:args;type:varchar(255);default:'';" json:"args"` // 商品相关参数JSON
|
|
|
|
|
|
Star int64 `gorm:"column:star;default:0;" json:"star"` // 评分
|
|
|
|
|
|
CommentTotal int32 `gorm:"column:comment_total;default:0;" json:"comment_total"` // 评论总数
|
|
|
|
|
|
ViewTotal int32 `gorm:"column:view_total;default:0;" json:"view_total"` // 浏览总数
|
|
|
|
|
|
ImageTotal int32 `gorm:"column:image_total;default:0;" json:"image_total"` // 图片总数
|
|
|
|
|
|
PartnerPer int64 `gorm:"column:partner_per;default:0;" json:"partner_per"` // 分销人员利润占比
|
|
|
|
|
|
Recommend int8 `gorm:"column:recommend;default:0;" json:"recommend"` // 2不推荐,1推荐的状态,0默认
|
|
|
|
|
|
Sort int32 `gorm:"column:sort;default:0;" json:"sort"` // 排序
|
|
|
|
|
|
Types int32 `gorm:"column:types;default:0;" json:"types"` // 商品类型:Product=1 实体商品,Service=2 服务,Membership=3 会员服务,Other=4 其它
|
|
|
|
|
|
GasType int32 `gorm:"column:gas_types;default:1;" json:"gas_types"` // 商品类型:1其它商品 2按瓶计费,3 按kg计费
|
|
|
|
|
|
|
|
|
|
|
|
Images []MallProductPhotos `gorm:"foreignKey:ProductIdentity;references:Identity;" json:"images"`
|
|
|
|
|
|
Specs []ProductSpec `gorm:"foreignKey:ProductIdentity;references:Identity;" json:"spec"`
|
|
|
|
|
|
Categories []ProductCategory `gorm:"foreignKey:ProductIdentity;references:Identity;" json:"category"`
|
|
|
|
|
|
CategoryId []int64 `gorm:"-"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
|
database.AppendMigrate(&MallProduct{})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TableName .
|
|
|
|
|
|
func (table *MallProduct) TableName() string {
|
|
|
|
|
|
return "mall_product" //对应数据库表名
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ForbiddenProduct 批量停用产品
|
|
|
|
|
|
func ForbiddenProduct(list []string, storeIdentity string) (int64, error) {
|
|
|
|
|
|
tx := impl.DBService.Model(&MallProduct{}).Where("identity in ?", list).Where("store_identity = ?", storeIdentity).Updates(&MallProduct{Status: 3})
|
|
|
|
|
|
return tx.RowsAffected, tx.Error
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ProductDetail 获取产品详情
|
|
|
|
|
|
func ProductDetail(identity string) (*MallProduct, error) {
|
|
|
|
|
|
var data = &MallProduct{}
|
|
|
|
|
|
tx := impl.DBService.Preload("Specs").Preload("Images").Preload("Categories").Where("identity = ?", identity).First(&data)
|
|
|
|
|
|
return data, tx.Error
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateProduct 创建新产品
|
|
|
|
|
|
func CreateProduct(product *MallProduct, specId []*ProductSpec, photo []*MallProductPhotos, categoryId []*ProductCategory) error {
|
|
|
|
|
|
tx := impl.DBService.Begin()
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
// 创建主产品记录
|
|
|
|
|
|
if err := tx.Create(product).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建规格关联
|
|
|
|
|
|
if len(specId) > 0 {
|
|
|
|
|
|
for _, v := range specId {
|
|
|
|
|
|
v.ProductId = product.ID
|
|
|
|
|
|
v.ProductIdentity = product.Identity
|
|
|
|
|
|
}
|
|
|
|
|
|
if err := tx.Create(&specId).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建图片关联
|
|
|
|
|
|
if len(photo) > 0 {
|
|
|
|
|
|
if err := tx.Create(&photo).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建分类关联
|
|
|
|
|
|
if len(categoryId) > 0 {
|
|
|
|
|
|
for _, v := range categoryId {
|
|
|
|
|
|
v.ProductId = product.ID
|
|
|
|
|
|
v.ProductIdentity = product.Identity
|
|
|
|
|
|
}
|
|
|
|
|
|
if err := tx.Create(&categoryId).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return tx.Commit().Error
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ModifyProduct 修改产品信息
|
|
|
|
|
|
func ModifyProduct(product *MallProduct, specId []*ProductSpec, photo []*MallProductPhotos, categoryId []*ProductCategory) error {
|
|
|
|
|
|
// 先查询原产品信息
|
|
|
|
|
|
pt := MallProduct{}
|
|
|
|
|
|
err := impl.DBService.Where("identity = ?", product.Identity).First(&pt).Error
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tx := impl.DBService.Begin()
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
// 更新主产品信息
|
|
|
|
|
|
if err := tx.Where("identity = ?", product.Identity).Updates(product).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新规格关联 - 先删除后创建
|
|
|
|
|
|
if len(specId) > 0 {
|
|
|
|
|
|
if err := tx.Where("product_identity = ?", product.Identity).Delete(&ProductSpec{}).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, v := range specId {
|
|
|
|
|
|
v.ProductId = pt.ID
|
|
|
|
|
|
v.ProductIdentity = pt.Identity
|
|
|
|
|
|
}
|
|
|
|
|
|
if err := tx.Create(&specId).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新图片关联 - 先删除后创建
|
|
|
|
|
|
if len(photo) > 0 {
|
|
|
|
|
|
if err := tx.Where("product_identity = ?", product.Identity).Delete(&MallProductPhotos{}).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
if err := tx.Create(&photo).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新分类关联 - 先删除后创建
|
|
|
|
|
|
if len(categoryId) > 0 {
|
|
|
|
|
|
if err := tx.Where("product_identity = ?", product.Identity).Delete(&ProductCategory{}).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, v := range categoryId {
|
|
|
|
|
|
v.ProductId = pt.ID
|
|
|
|
|
|
v.ProductIdentity = pt.Identity
|
|
|
|
|
|
}
|
|
|
|
|
|
if err := tx.Create(&categoryId).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return tx.Commit().Error
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DelProduct 删除产品及其关联数据
|
|
|
|
|
|
func DelProduct(identity string) error {
|
|
|
|
|
|
tx := impl.DBService.Begin()
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
// 按依赖关系顺序删除关联数据
|
|
|
|
|
|
// 1. 删除评论
|
|
|
|
|
|
if err := tx.Where("product_identity = ?", identity).Delete(&MallProductComment{}).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 删除图片
|
|
|
|
|
|
if err := tx.Where("product_identity = ?", identity).Delete(&MallProductPhotos{}).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 删除规格
|
|
|
|
|
|
if err := tx.Where("product_identity = ?", identity).Delete(&MallProductSpec{}).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 删除分类关联
|
|
|
|
|
|
if err := tx.Where("product_identity = ?", identity).Delete(&ProductCategory{}).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 删除主产品记录
|
|
|
|
|
|
if err := tx.Where("identity = ?", identity).Delete(&MallProduct{}).Error; err != nil {
|
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return tx.Commit().Error
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetProductList 获取产品列表
|
|
|
|
|
|
func GetProductList(identity []string, page, size, status, category, recommend int64, keyword, timeStart, timeEnd string) ([]*MallProduct, int32, error) {
|
|
|
|
|
|
tx := impl.DBService.Select("*")
|
|
|
|
|
|
|
|
|
|
|
|
// 店铺筛选
|
|
|
|
|
|
if len(identity) == 1 {
|
|
|
|
|
|
tx = tx.Where("store_identity = ?", identity[0])
|
|
|
|
|
|
} else if len(identity) > 1 {
|
|
|
|
|
|
tx = tx.Where("store_identity in ?", identity)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 分类筛选
|
|
|
|
|
|
if category != 0 {
|
|
|
|
|
|
tx = tx.Where("id in (SELECT product_id FROM product_category WHERE category_id = ?)", category)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 关键词搜索
|
|
|
|
|
|
if keyword != "" {
|
|
|
|
|
|
tx = tx.Where("title LIKE ? OR sub_title LIKE ?", "%"+keyword+"%", "%"+keyword+"%")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 时间范围筛选
|
|
|
|
|
|
if timeStart != "" && timeEnd != "" {
|
|
|
|
|
|
tx = tx.Where("created_at BETWEEN ? AND ?", timeStart, timeEnd)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 状态筛选
|
|
|
|
|
|
if status != 0 {
|
|
|
|
|
|
tx = tx.Where("status = ?", status)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 推荐状态筛选
|
|
|
|
|
|
if recommend != 0 {
|
|
|
|
|
|
tx = tx.Where("recommend = ?", recommend)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
posts := make([]*MallProduct, 0)
|
|
|
|
|
|
var cnt int64 = 0
|
|
|
|
|
|
|
|
|
|
|
|
// 预加载关联数据,包括最低价格的规格
|
|
|
|
|
|
tx.Order("created_at desc").Preload("Specs", func(db *gorm.DB) *gorm.DB {
|
|
|
|
|
|
return db.Table("mall_product_spec as s, (?) as b",
|
|
|
|
|
|
db.Model(&MallProductSpec{}).Select("product_identity,min(price) price").Group("product_identity")).
|
|
|
|
|
|
Select("s.*").Where("s.product_identity = b.product_identity and s.price = b.price").Order("s.product_identity")
|
|
|
|
|
|
}).Limit(int(size)).Offset(int((page - 1) * size)).Find(&posts).Count(&cnt)
|
|
|
|
|
|
|
|
|
|
|
|
err := tx.Error
|
|
|
|
|
|
return posts, int32(cnt), err
|
|
|
|
|
|
}
|