package service import ( "encoding/json" "errors" "strings" "time" "github.com/spf13/cast" "gorm.io/gorm" "bkb-seller/global" "bkb-seller/model" "bkb-seller/model/request" "bkb-seller/model/response" "bkb-seller/utils" ) func GetTbGoods(uuid string, id uint) (error, model.TbGoodsDetail) { var ( err error result model.TbGoodsDetail ) err = global.MG_DB.Table("tb_goods").Select("id,category_id,title,title_eng,images,content,content_text,retail_price,stock,sales,tags,online,created_at,updated_at").Where("create_by = ? AND id = ?", uuid, id). Preload("Specs", func(db *gorm.DB) *gorm.DB { return db.Select("id,goods_id,specs,sku_no,stock,price,image,goods_no").Where("status = 1") }).First(&result).Error if err != nil { return err, result } for i := 0; i < len(result.Specs); i++ { err = json.Unmarshal([]byte(result.Specs[i].Specs), &result.Specs[i].Attributes) if err != nil { return err, result } } _, result.Breadcrumb = GetTbCategoryBreadcrumb(result.CategoryId) // 获取分类面包屑 return nil, result } func GetTbGoodsList(info request.SearchTbGoods) (err error, list any, total int64) { limit := info.PageSize offset := info.PageSize * (info.Page - 1) db := global.MG_DB.Model(&model.TbGoods{}).Where("create_by = ?", info.CreateBy) if info.SpuNo != "" { db = db.Where("spu_no = ?", info.SpuNo) } if info.Title != "" { db = db.Where("title LIKE ?", "%"+info.Title+"%") } if info.Status == 1 { // 在售中 db = db.Where("stock > 0 AND online = 'on'") } else if info.Status == 2 { // 已下架 db = db.Where("online = 'off'") } else if info.Status == 3 { // 已售罄 db = db.Where("stock <= 0") } err = db.Count(&total).Error var res []model.TbGoods4List err = db.Select("id,spu_no,title,title_eng,images,retail_price,price_min,price_max,stock,sales,online,created_at,updated_at").Order("id DESC").Limit(limit).Offset(offset).Find(&res).Error if err != nil { return err, nil, 0 } var ids []uint for i := 0; i < len(res); i++ { ids = append(ids, res[i].ID) if res[i].Online == "on" { if res[i].Stock == 0 { res[i].Status = 3 } else { res[i].Status = 1 } } else { // 下架 res[i].Status = 2 } // res[i].CreatedAtUnix = res[i].CreatedAt.Unix() // res[i].UpdatedAtUnix = res[i].UpdatedAt.Unix() } if len(ids) != 0 { // 计算30天销量 endTime, _ := time.Parse(utils.DateFormat, utils.GetNow().Format(utils.DateFormat)) startTime := endTime.AddDate(0, 0, -29) countMap := batchGetGoodsOrderCount(info.CreateBy, ids, &startTime, &endTime) for i := 0; i < len(res); i++ { if val, ok := countMap[res[i].ID]; ok { res[i].Sales30 = val } } } return err, res, total } func CreateTbGoodsSeries(uuid, storeNo string, info request.CreateTbGoods) (id uint,spuNo string,err error) { // check attributes // code := makeCode(int(goodsId), specValues) // newSpecs, _ := json.Marshal(specValues) imgs := strings.Split(info.Images, ",") var goods model.TbGoods tx := global.MG_DB.Begin() if goods.SpuNo == "" { goods.SpuNo = utils.GetCurrentTimeStr() } goods.StoreNo = storeNo goods.CategoryId = info.CategoryId goods.Title = info.Title goods.TitleEng = info.TitleEng goods.Images = info.Images goods.Content = info.Content goods.ContentText = info.ContentText goods.RetailPrice = info.RetailPrice goods.Sales = 0 goods.Views = 0 goods.Favourites = 0 goods.Tags = info.Tags goods.Online = info.Online goods.CreateBy = uuid goods.Cover = imgs[0] err = tx.Model(&model.TbGoods{}).Create(&goods).Error if err != nil { tx.Rollback() return 0,"",err } var specs []model.TbGoodsSpecs for i := 0; i < len(info.Specs); i++ { var spec model.TbGoodsSpecs spec.GoodsId = goods.ID spec.SkuNo = utils.GetCurrentTimeStr() specValues, _ := ToSpecValues(info.Specs[i].Attributes) code := makeCode(goods.ID, specValues) attribute, _ := json.Marshal(specValues) // attribute, _ := json.Marshal(info.Specs[i].Attributes) spec.Code = code spec.Specs = string(attribute) spec.Stock = info.Specs[i].Stock spec.Price = info.Specs[i].Price spec.CreateBy = uuid spec.StoreNo = storeNo if info.Specs[i].Image == "" { spec.Image = "https://minio.sumweal.com/nft/221109/Ddvz989FB85u31TToa2xUT8WeYZ5q2g0678GGPRx7NXKM11F1U.png" } else { spec.Image = info.Specs[i].Image } spec.GoodsNo = info.Specs[i].GoodsNo spec.Status = 1 goods.Stock += info.Specs[i].Stock if goods.PriceMin == 0 || goods.PriceMin > info.Specs[i].Price { // 获取最低规格价格 goods.PriceMin = info.Specs[i].Price } if goods.PriceMax == 0 || goods.PriceMax < info.Specs[i].Price { // 获取最高规格价格 goods.PriceMax = info.Specs[i].Price } specs = append(specs, spec) } if len(specs) != 0 { err = tx.Model(&model.TbGoodsSpecs{}).Create(&specs).Error if err != nil { tx.Rollback() return 0,"",err } } goods.ListPrice = goods.PriceMin err = tx.Model(&model.TbGoods{}).Where("id = ?", goods.ID).UpdateColumns(map[string]any{"stock": goods.Stock, "list_price": goods.ListPrice, "price_min": goods.PriceMin, "price_max": goods.PriceMax}).Error if err != nil { tx.Rollback() return 0,"",err } tx.Commit() id = goods.ID return id, goods.SpuNo, nil } func UpdateTbGoodsSeries(uuid string, info request.UpdateTbGoods) (err error, id uint) { var ( tmp, goods model.TbGoods ) err = global.MG_DB.Model(&model.TbGoods{}).Where("create_by = ? AND id = ?", uuid, info.ID).First(&tmp).Error if err != nil { return errors.New("商品不存在"), info.ID } imgs := strings.Split(info.Images, ",") tx := global.MG_DB.Begin() goods.CategoryId = info.CategoryId goods.Title = info.Title goods.TitleEng = info.TitleEng goods.Images = info.Images goods.Content = info.Content goods.ContentText = info.ContentText goods.RetailPrice = info.RetailPrice goods.Tags = info.Tags goods.Online = info.Online goods.Cover = imgs[0] err = tx.Where("id = ?", info.ID).Select("CategoryId", "Title", "TitleEng", "Images", "Content", "ContentText", "RetailPrice", "Tags", "Online", "Cover").Updates(&goods).Error if err != nil { tx.Rollback() return err, 0 } var ( delSpecIds []uint specs []model.TbGoodsSpecs ) for i := 0; i < len(info.Specs); i++ { if info.Specs[i].ID == 0 { // 新建规格 var spec model.TbGoodsSpecs spec.GoodsId = tmp.ID spec.SkuNo = utils.GetCurrentTimeStr() specValues, _ := ToSpecValues(info.Specs[i].Attributes) code := makeCode(spec.GoodsId, specValues) // attribute, _ := json.Marshal(info.Specs[i].Attributes) attribute, _ := json.Marshal(specValues) spec.Code = code spec.Specs = string(attribute) spec.Stock = info.Specs[i].Stock spec.Price = info.Specs[i].Price if info.Specs[i].Image == "" { spec.Image = "https://minio.sumweal.com/nft/221109/Ddvz989FB85u31TToa2xUT8WeYZ5q2g0678GGPRx7NXKM11F1U.png" } else { spec.Image = info.Specs[i].Image } spec.GoodsNo = info.Specs[i].GoodsNo spec.Status = 1 specs = append(specs, spec) } else { // 编辑 var spec model.TbGoodsSpecs spec.GoodsId = tmp.ID if info.Specs[i].SkuNo == "" { spec.SkuNo = utils.GetCurrentTimeStr() } specValues, _ := ToSpecValues(info.Specs[i].Attributes) code := makeCode(spec.GoodsId, specValues) // attribute, _ := json.Marshal(info.Specs[i].Attributes) attribute, _ := json.Marshal(specValues) spec.Code = code spec.Specs = string(attribute) spec.Stock = info.Specs[i].Stock spec.Price = info.Specs[i].Price if info.Specs[i].Image == "" { spec.Image = "https://minio.sumweal.com/nft/221109/Ddvz989FB85u31TToa2xUT8WeYZ5q2g0678GGPRx7NXKM11F1U.png" } else { spec.Image = info.Specs[i].Image } spec.GoodsNo = info.Specs[i].GoodsNo err = tx.Model(&model.TbGoodsSpecs{}).Where("create_by = ? AND id = ?",uuid, info.Specs[i].ID).Select("GoodsNo", "Specs", "Stock", "Price", "Image", "Code").Updates(&spec).Error if err != nil { tx.Rollback() return err, 0 } delSpecIds = append(delSpecIds, info.Specs[i].ID) } goods.Stock += info.Specs[i].Stock if goods.PriceMin == 0 || goods.PriceMin > info.Specs[i].Price { // 获取最低规格价格 goods.PriceMin = info.Specs[i].Price } if goods.PriceMax == 0 || goods.PriceMax < info.Specs[i].Price { // 获取最高规格价格 goods.PriceMax = info.Specs[i].Price } } if len(delSpecIds) == 0 { err = tx.Model(&model.TbGoodsSpecs{}).Where("goods_id = ?", tmp.ID).Delete(&[]model.TbGoodsSpecs{}).Error if err != nil { tx.Rollback() return err, 0 } } else { err = tx.Model(&model.TbGoodsSpecs{}).Where("goods_id = ? AND id NOT IN (?)", tmp.ID, delSpecIds).Delete(&[]model.TbGoodsSpecs{}).Error if err != nil { tx.Rollback() return err, 0 } } if len(specs) != 0 { err = tx.Model(&model.TbGoodsSpecs{}).Create(&specs).Error if err != nil { tx.Rollback() return err, 0 } } goods.ListPrice = goods.PriceMin err = tx.Model(&model.TbGoods{}).Where("id = ?", info.ID).UpdateColumns(map[string]any{"stock": goods.Stock, "list_price": goods.ListPrice, "price_min": goods.PriceMin, "price_max": goods.PriceMax}).Error if err != nil { tx.Rollback() return err, 0 } tx.Commit() DealGoodsStatus([]uint{tmp.ID}) id = tmp.ID return } func BatchUpdateTbGoodsOnline(uuid string, info request.BatchOnline) (err error) { err = global.MG_DB.Model(&model.TbGoods{}).Where("create_by = ? AND id IN (?)", uuid, info.Ids).Update("online", info.Online).Error if err != nil { return err } DealGoodsStatus(info.Ids) return nil } func BatchDeleteTbGoods(uuid string, info request.IdsReq) (err error) { err = global.MG_DB.Model(&model.TbGoods{}).Where("create_by = ? AND id IN (?)", uuid, info.Ids).Delete(&[]model.TbGoods{}).Error if err != nil { return err } DealDeleteGoodsStatus(info.Ids) return nil } func DealGoodsStatus(ids []uint) { var ( err error list []model.TbGoods ) err = global.MG_DB.Model(&model.TbGoods{}).Where("id IN (?)", ids).Find(&list).Error if err != nil { return } for i := 0; i < len(list); i++ { if list[i].Online == "on" && list[i].Stock > 0 { // 上架且库存为0 // 更新关联任务商品状态 tx := global.MG_DB.Begin() err = tx.Model(&model.Mission{}).Where("goods_id = ?", list[i].ID).Update("goods_status", 1).Error if err != nil { tx.Rollback() return } tx.Commit() } else { // 更新关联任务商品状态 tx := global.MG_DB.Begin() err = tx.Model(&model.Mission{}).Where("goods_id = ?", list[i].ID).Update("goods_status", 2).Error if err != nil { tx.Rollback() return } tx.Commit() } } } func DealDeleteGoodsStatus(ids []uint) { // 更新关联任务商品状态 tx := global.MG_DB.Begin() err := tx.Model(&model.Mission{}).Where("goods_id IN (?)", ids).Update("goods_status", 2).Error if err != nil { tx.Rollback() return } tx.Commit() } func ToSpecValues(attrs []request.TbGoodsAttributeValue) (specValues []request.GoodsAttributeValue, err error) { specValues = make([]request.GoodsAttributeValue, 0) for _, v := range attrs { var attrModel model.TbAttribute var valueId uint err = global.MG_DB.Model(&model.TbAttribute{}).Where("id=?", v.AttributeId).First(&attrModel).Error if err != nil { return } var specValue model.SpecValue global.MG_DB.Model(&model.SpecValue{}).Where("value=? and spec_id=?", v.Value, v.AttributeId).First(&specValue) valueId = specValue.ID if specValue.ID == 0 { newSpecValue := model.SpecValue{ Value: v.Value, SpecId: v.AttributeId, } err = global.MG_DB.Model(&model.SpecValue{}).Create(&newSpecValue).Error if err != nil { return } valueId = newSpecValue.ID } specValues = append(specValues, request.GoodsAttributeValue{ AttributeId: v.AttributeId, Key: attrModel.Name, Value: v.Value, ValueId: uint(valueId), }) } return } func makeCode(goodsId uint, specValues []request.GoodsAttributeValue) string { var code = cast.ToString(goodsId) + "$" for k, v := range specValues { code = code + cast.ToString(v.AttributeId) + "-" + cast.ToString(v.ValueId) if k != len(specValues)-1 { code = code + "#" } } return code } func ListSku(goodsId uint) (list []model.TbGoodsSpecs, err error) { list = make([]model.TbGoodsSpecs, 0) err = global.MG_DB.Model(&model.TbGoodsSpecs{}).Where("goods_id = ? and status=1", goodsId).Find(&list).Error if err != nil { return } return } func UpdateSkuList(info request.UpdateSku) (err error) { var ( stock int64 priceMax,priceMin float64 check model.TbGoodsSpecs ) if len(info.SkuList) == 0 { return } err = global.MG_DB.Model(&model.TbGoodsSpecs{}).Where("id = ?",info.SkuList[0].ID).First(&check).Error if err != nil { return } tx := global.MG_DB.Begin() for _, v := range info.SkuList { err = tx.Model(&model.TbGoodsSpecs{}).Where("id = ?", v.ID).Updates(map[string]any{"price": v.Price, "stock": v.Stock}).Error if err != nil { tx.Rollback() return } stock += v.Stock if v.Price > priceMax { priceMax = v.Price } if (v.Price < priceMin && priceMin > 0) || priceMin == 0 { priceMin = v.Price } } err = tx.Model(&model.TbGoods{}).Where("id = ?", check.GoodsId).Updates(map[string]any{"price_min": priceMin, "stock": stock,"price_max":priceMax}).Error if err != nil { tx.Rollback() return } tx.Commit() return } type GoodsSpecs struct { GoodsId uint ` json:"goods_id"` // spu id Title string ` json:"title"` // 商品标题 Stock int64 ` json:"stock"` // 库存 Specs json.RawMessage ` json:"specs"` // 规格 } func GetSale(uuid string,page, size int, title string) (list []*response.GoodsSaleItem, total int64, err error) { skuList := make([]GoodsSpecs, 0) db := global.MG_DB.Model(&model.TbGoodsSpecs{}).Select("tb_goods_specs.stock", "title", "specs", "goods_id").Joins("JOIN tb_goods on tb_goods_specs.goods_id=tb_goods.id"). Where("tb_goods.create_by = ? AND tb_goods.title like ?",uuid, "%"+title+"%") db.Count(&total) err = db.Limit(size).Offset(size * (page - 1)).Find(&skuList).Error if err != nil { return } list = make([]*response.GoodsSaleItem, 0) for _, v := range skuList { specList := make([]request.GoodsAttributeValue, 0) json.Unmarshal(v.Specs, &specList) values := make([]string, 0) for _, spec := range specList { values = append(values, spec.Value) } item := &response.GoodsSaleItem{ GoodsId: cast.ToString(v.GoodsId), Title: v.Title, SkuName: strings.Join(values, " "), Stock: uint(v.Stock), TodaySale: 0,//TODO:: 统计 sku 销售量 SevenSale: 0, ThirtySale: 0, } list = append(list, item) } return }