You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

488 lines
14 KiB

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
}