213 lines
9.5 KiB
Go
213 lines
9.5 KiB
Go
package sys
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"hotgo/internal/consts"
|
|
"hotgo/internal/dao"
|
|
"hotgo/internal/library/contexts"
|
|
"hotgo/internal/library/dict"
|
|
"hotgo/internal/library/hgorm/handler"
|
|
"hotgo/internal/library/hgorm/hook"
|
|
"hotgo/internal/model"
|
|
"hotgo/internal/model/entity"
|
|
"hotgo/internal/model/input/sysin"
|
|
"hotgo/internal/service"
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
"github.com/gogf/gf/v2/util/grand"
|
|
)
|
|
|
|
type sSysProduct struct{}
|
|
|
|
func NewSysProduct() *sSysProduct { return &sSysProduct{} }
|
|
func init() {
|
|
service.RegisterSysProduct(NewSysProduct())
|
|
dict.RegisterFunc("productCategoryOption", "商品分类选项", service.SysProduct().CategoryOption, true)
|
|
}
|
|
func (s *sSysProduct) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
|
if len(option) == 0 {
|
|
option = append(option, &handler.Option{FilterTenant: true})
|
|
}
|
|
return handler.Model(dao.Product.Ctx(ctx), option...)
|
|
}
|
|
func (s *sSysProduct) categoryModel(ctx context.Context) *gdb.Model {
|
|
return handler.Model(dao.ProductCategory.Ctx(ctx), &handler.Option{FilterTenant: true})
|
|
}
|
|
func (s *sSysProduct) listModel(ctx context.Context) *gdb.Model {
|
|
pc, cc := dao.Product.Columns(), dao.ProductCategory.Columns()
|
|
return s.Model(ctx).As("p").LeftJoin(dao.ProductCategory.Table()+" c", col("c", cc.Id)+"="+col("p", pc.CategoryId)).Fields("p.*", fmt.Sprintf("COALESCE(%s,'') category_name", col("c", cc.Name)))
|
|
}
|
|
func (s *sSysProduct) List(ctx context.Context, in *sysin.ProductListInp) (list []*sysin.ProductListModel, total int, err error) {
|
|
pc := dao.Product.Columns()
|
|
mod := s.listModel(ctx).Where(col("p", pc.ProductType), consts.ProductTypeStandard)
|
|
if in.Name != "" {
|
|
mod = mod.WhereLike(col("p", pc.Name), "%"+in.Name+"%")
|
|
}
|
|
if in.PaymentType != "" {
|
|
mod = mod.Where(col("p", pc.PaymentType), in.PaymentType)
|
|
}
|
|
if in.CategoryId > 0 {
|
|
mod = mod.Where(col("p", pc.CategoryId), in.CategoryId)
|
|
}
|
|
var minAmount, maxAmount int64
|
|
if in.PriceMin != "" {
|
|
minAmount, err = parseCents(in.PriceMin)
|
|
if err != nil {
|
|
return nil, 0, gerror.New("最低价格格式不正确")
|
|
}
|
|
mod = mod.WhereGTE(col("p", pc.PriceAmount), minAmount)
|
|
}
|
|
if in.PriceMax != "" {
|
|
maxAmount, err = parseCents(in.PriceMax)
|
|
if err != nil {
|
|
return nil, 0, gerror.New("最高价格格式不正确")
|
|
}
|
|
mod = mod.WhereLTE(col("p", pc.PriceAmount), maxAmount)
|
|
}
|
|
if in.PriceMin != "" && in.PriceMax != "" && minAmount > maxAmount {
|
|
return nil, 0, gerror.New("最低价格不能高于最高价格")
|
|
}
|
|
if in.PublishStatus > 0 {
|
|
mod = mod.Where(col("p", pc.PublishStatus), in.PublishStatus)
|
|
}
|
|
if err = mod.Page(in.Page, in.PerPage).OrderAsc(col("p", pc.Sort)).OrderDesc(col("p", pc.Id)).ScanAndCount(&list, &total, false); err != nil {
|
|
return nil, 0, gerror.Wrap(err, "获取商品列表失败")
|
|
}
|
|
for _, row := range list {
|
|
row.Price = formatCents(row.PriceAmount)
|
|
count, e := handler.Model(dao.TradeOrder.Ctx(ctx), &handler.Option{FilterTenant: true}).Where(dao.TradeOrder.Columns().ProductId, row.Id).Count()
|
|
if e != nil {
|
|
return nil, 0, e
|
|
}
|
|
row.Referenced = count > 0
|
|
}
|
|
return
|
|
}
|
|
func (s *sSysProduct) View(ctx context.Context, in *sysin.ProductViewInp) (res *sysin.ProductListModel, err error) {
|
|
if err = s.listModel(ctx).Where(col("p", dao.Product.Columns().Id), in.Id).Scan(&res); err != nil {
|
|
return nil, gerror.Wrap(err, "获取商品详情失败")
|
|
}
|
|
if res == nil {
|
|
return nil, gerror.New("商品不存在")
|
|
}
|
|
res.Price = formatCents(res.PriceAmount)
|
|
count, err := handler.Model(dao.TradeOrder.Ctx(ctx), &handler.Option{FilterTenant: true}).Where(dao.TradeOrder.Columns().ProductId, res.Id).Count()
|
|
res.Referenced = count > 0
|
|
return
|
|
}
|
|
func (s *sSysProduct) Edit(ctx context.Context, in *sysin.ProductEditInp) (id uint64, err error) {
|
|
amount, err := parseCents(in.Price)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
in.PriceAmount = amount
|
|
if in.PaymentType == "" {
|
|
in.PaymentType = consts.PayTypeBalance
|
|
}
|
|
if in.Currency == "" {
|
|
in.Currency = "CNY"
|
|
}
|
|
cc := dao.ProductCategory.Columns()
|
|
count, err := s.categoryModel(ctx).WherePri(in.CategoryId).Where(cc.Status, consts.StatusEnabled).Count()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if count == 0 {
|
|
return 0, gerror.New("商品分类不存在或已停用")
|
|
}
|
|
c := dao.Product.Columns()
|
|
uid := contexts.GetUserId(ctx)
|
|
var before *entity.Product
|
|
if in.Id > 0 {
|
|
if err = s.Model(ctx).WherePri(in.Id).Scan(&before); err != nil {
|
|
return
|
|
}
|
|
if before == nil {
|
|
return 0, gerror.New("商品不存在")
|
|
}
|
|
result, e := s.Model(ctx).WherePri(in.Id).Where(c.Version, in.Version).Fields(sysin.ProductUpdateFields{}).Data(g.Map{c.CategoryId: in.CategoryId, c.ProductType: in.ProductType, c.PaymentType: in.PaymentType, c.Name: in.Name, c.ShopName: in.ShopName, c.PriceAmount: in.PriceAmount, c.Currency: in.Currency, c.CoverAttachmentId: in.CoverAttachmentId, c.CoverUrl: in.CoverUrl, c.DetailHtml: in.DetailHtml, c.IsRecommended: in.IsRecommended, c.StartAt: in.StartAt, c.EndAt: in.EndAt, c.Sort: in.Sort, c.Remark: in.Remark, c.UpdatedBy: uid, c.Version: gdb.Raw(c.Version + "+1")}).Update()
|
|
if e != nil {
|
|
return 0, gerror.Wrap(e, "更新商品失败")
|
|
}
|
|
rows, _ := result.RowsAffected()
|
|
if rows != 1 {
|
|
return 0, gerror.New("商品数据已更新,请刷新后重试")
|
|
}
|
|
id = in.Id
|
|
} else {
|
|
in.ProductNo = "PD" + gtime.Now().Format("YmdHis") + grand.S(5)
|
|
result, e := s.Model(ctx).Fields(c.ProductNo, c.CategoryId, c.ProductType, c.PaymentType, c.Name, c.ShopName, c.PriceAmount, c.Currency, c.CoverAttachmentId, c.CoverUrl, c.DetailHtml, c.IsRecommended, c.ClaimedCount, c.PublishStatus, c.StartAt, c.EndAt, c.Sort, c.Remark, c.CreatedBy, c.UpdatedBy).Hook(hook.SaveTenant).Data(g.Map{c.ProductNo: in.ProductNo, c.CategoryId: in.CategoryId, c.ProductType: in.ProductType, c.PaymentType: in.PaymentType, c.Name: in.Name, c.ShopName: in.ShopName, c.PriceAmount: in.PriceAmount, c.Currency: in.Currency, c.CoverAttachmentId: in.CoverAttachmentId, c.CoverUrl: in.CoverUrl, c.DetailHtml: in.DetailHtml, c.IsRecommended: in.IsRecommended, c.ClaimedCount: grand.N(100, 500), c.PublishStatus: consts.BizPublishDraft, c.StartAt: in.StartAt, c.EndAt: in.EndAt, c.Sort: in.Sort, c.Remark: in.Remark, c.CreatedBy: uid, c.UpdatedBy: uid}).Insert()
|
|
if e != nil {
|
|
return 0, gerror.Wrap(e, "创建商品失败")
|
|
}
|
|
last, _ := result.LastInsertId()
|
|
id = uint64(last)
|
|
}
|
|
err = service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "product", Action: "product.edit", TargetType: "product", TargetId: id, Before: before, After: g.Map{"name": in.Name, "priceAmount": in.PriceAmount, "categoryId": in.CategoryId}})
|
|
return
|
|
}
|
|
func (s *sSysProduct) Status(ctx context.Context, in *sysin.ProductStatusInp) error {
|
|
c := dao.Product.Columns()
|
|
result, err := s.Model(ctx).WherePri(in.Id).Where(c.Version, in.Version).Data(g.Map{c.PublishStatus: in.PublishStatus, c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
|
|
if err != nil {
|
|
return gerror.Wrap(err, "更新商品状态失败")
|
|
}
|
|
rows, _ := result.RowsAffected()
|
|
if rows != 1 {
|
|
return gerror.New("商品数据已更新,请刷新后重试")
|
|
}
|
|
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "product", Action: "product.status", TargetType: "product", TargetId: in.Id, After: g.Map{"publishStatus": in.PublishStatus}})
|
|
}
|
|
func (s *sSysProduct) Recommend(ctx context.Context, in *sysin.ProductRecommendInp) error {
|
|
c := dao.Product.Columns()
|
|
result, err := s.Model(ctx).WherePri(in.Id).Where(c.Version, in.Version).Data(g.Map{c.IsRecommended: in.IsRecommended, c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
|
|
if err != nil {
|
|
return gerror.Wrap(err, "更新商品推荐状态失败")
|
|
}
|
|
rows, _ := result.RowsAffected()
|
|
if rows != 1 {
|
|
return gerror.New("商品数据已更新,请刷新后重试")
|
|
}
|
|
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "product", Action: "product.recommend", TargetType: "product", TargetId: in.Id, After: g.Map{"isRecommended": in.IsRecommended}})
|
|
}
|
|
func (s *sSysProduct) Delete(ctx context.Context, in *sysin.ProductDeleteInp) error {
|
|
c := dao.Product.Columns()
|
|
var row *entity.Product
|
|
if err := s.Model(ctx).WherePri(in.Id).Scan(&row); err != nil {
|
|
return err
|
|
}
|
|
if row == nil {
|
|
return gerror.New("商品不存在")
|
|
}
|
|
count, err := handler.Model(dao.TradeOrder.Ctx(ctx), &handler.Option{FilterTenant: true}).Where(dao.TradeOrder.Columns().ProductId, in.Id).Count()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return gerror.New("商品已被订单引用,只允许下架,不能删除")
|
|
}
|
|
if row.PublishStatus == consts.BizPublishPublished {
|
|
_, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{c.PublishStatus: consts.BizPublishOffline, c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
|
|
return err
|
|
}
|
|
_, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{c.DeletedAt: gtime.Now(), c.DeletedBy: contexts.GetUserId(ctx), c.UpdatedBy: contexts.GetUserId(ctx)}).Update()
|
|
return gerror.Wrap(err, "删除商品失败")
|
|
}
|
|
func (s *sSysProduct) CategoryOption(ctx context.Context) (opts []*model.Option, err error) {
|
|
var rows []*entity.ProductCategory
|
|
c := dao.ProductCategory.Columns()
|
|
if err = s.categoryModel(ctx).Fields(c.Id, c.Name).Where(c.Status, consts.StatusEnabled).OrderAsc(c.Sort).Scan(&rows); err != nil {
|
|
return nil, gerror.Wrap(err, "获取商品分类选项失败")
|
|
}
|
|
for _, row := range rows {
|
|
opts = append(opts, dict.GenHashOption(row.Id, gconv.String(row.Name)))
|
|
}
|
|
return
|
|
}
|