first commit
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-manage-backend/global"
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-manage-backend/internal/dto"
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-manage-backend/internal/mysqldao"
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-manage-backend/internal/mysqlmodel"
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-manage-backend/pkg/errcode"
|
||||
)
|
||||
|
||||
// GoodsService 商品管理服务。
|
||||
type GoodsService struct {
|
||||
goodsDao *mysqldao.GoodsDao
|
||||
}
|
||||
|
||||
// NewGoodsService 创建商品服务。
|
||||
func NewGoodsService() *GoodsService {
|
||||
return &GoodsService{goodsDao: mysqldao.NewGoodsDao()}
|
||||
}
|
||||
|
||||
// List 分页查询商品(管理端含下架商品)。
|
||||
func (s *GoodsService) List(ctx context.Context, request *dto.GoodsListRequest) (*dto.GoodsListResponse, *errcode.Error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "svc.AdminGoodsList")
|
||||
defer span.End()
|
||||
|
||||
page, pageSize := normalizePage(request.Page, request.PageSize)
|
||||
|
||||
list, total, err := s.goodsDao.GoodsList(ctx, mysqldao.GoodsListParams{
|
||||
GoodsType: request.GoodsType,
|
||||
Status: request.Status,
|
||||
Keyword: strings.TrimSpace(request.Keyword),
|
||||
Page: page,
|
||||
Size: pageSize,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errcode.DatabaseError
|
||||
}
|
||||
|
||||
items := make([]dto.GoodsItem, 0, len(list))
|
||||
for i := range list {
|
||||
items = append(items, buildGoodsItem(&list[i]))
|
||||
}
|
||||
return &dto.GoodsListResponse{
|
||||
PageResponse: dto.PageResponse{Total: total, Page: page, PageSize: pageSize},
|
||||
List: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create 创建商品。
|
||||
func (s *GoodsService) Create(ctx context.Context, adminID int64, request *dto.GoodsCreateRequest) (*dto.GoodsItem, *errcode.Error) {
|
||||
ctx, span := global.Tracer.Start(ctx, "svc.AdminGoodsCreate")
|
||||
defer span.End()
|
||||
|
||||
code := strings.TrimSpace(request.GoodsCode)
|
||||
if code == "" {
|
||||
return nil, errcode.InvalidParams.WithDetails("商品编码不能为空")
|
||||
}
|
||||
if request.DiscountPrice > request.Price {
|
||||
return nil, errcode.GoodsPriceInvalid.WithDetails("售价不能高于原价")
|
||||
}
|
||||
|
||||
// 编码唯一性校验(含软删,防历史编码占用)。
|
||||
if _, err := s.goodsDao.GoodsGetByCode(ctx, code); err == nil {
|
||||
return nil, errcode.GoodsCodeExists
|
||||
} else if !isNotFound(err) {
|
||||
return nil, errcode.DatabaseError
|
||||
}
|
||||
|
||||
status := request.Status
|
||||
if status == 0 {
|
||||
status = GoodsStatusOffShelf // 新建默认下架,运营确认后再上架。
|
||||
}
|
||||
|
||||
goods := &mysqlmodel.Goods{
|
||||
GoodsCode: code,
|
||||
GoodsName: strings.TrimSpace(request.GoodsName),
|
||||
GoodsType: request.GoodsType,
|
||||
Price: request.Price,
|
||||
DiscountPrice: request.DiscountPrice,
|
||||
MemberDays: request.MemberDays,
|
||||
CosQuota: request.UploadQuota,
|
||||
AIRecommendQuota: request.AIRecommendQuota,
|
||||
ClosetShareQuota: request.ClosetShareQuota,
|
||||
OutfitShareQuota: request.OutfitShareQuota,
|
||||
ValidDays: request.ValidDays,
|
||||
Description: request.Description,
|
||||
Status: status,
|
||||
}
|
||||
goods.CreatedBy = adminID
|
||||
goods.UpdatedBy = adminID
|
||||
if err := s.goodsDao.GoodsCreate(ctx, goods); err != nil {
|
||||
return nil, errcode.DatabaseError
|
||||
}
|
||||
|
||||
item := buildGoodsItem(goods)
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
// Update 更新商品(传什么改什么;不修改编码与类型)。
|
||||
func (s *GoodsService) Update(ctx context.Context, adminID int64, id int64, request *dto.GoodsUpdateRequest) *errcode.Error {
|
||||
ctx, span := global.Tracer.Start(ctx, "svc.AdminGoodsUpdate")
|
||||
defer span.End()
|
||||
|
||||
if _, err := s.goodsDao.GoodsGetByID(ctx, id); err != nil {
|
||||
if isNotFound(err) {
|
||||
return errcode.GoodsNotFound
|
||||
}
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
|
||||
updates := map[string]any{"updated_by": adminID}
|
||||
if request.GoodsName != nil {
|
||||
updates["goods_name"] = strings.TrimSpace(*request.GoodsName)
|
||||
}
|
||||
if request.Price != nil {
|
||||
updates["price"] = *request.Price
|
||||
}
|
||||
if request.DiscountPrice != nil {
|
||||
updates["discount_price"] = *request.DiscountPrice
|
||||
}
|
||||
if request.MemberDays != nil {
|
||||
updates["member_days"] = *request.MemberDays
|
||||
}
|
||||
if request.UploadQuota != nil {
|
||||
updates["cos_quota"] = *request.UploadQuota
|
||||
}
|
||||
if request.AIRecommendQuota != nil {
|
||||
updates["ai_recommend_quota"] = *request.AIRecommendQuota
|
||||
}
|
||||
if request.ClosetShareQuota != nil {
|
||||
updates["closet_share_quota"] = *request.ClosetShareQuota
|
||||
}
|
||||
if request.OutfitShareQuota != nil {
|
||||
updates["outfit_share_quota"] = *request.OutfitShareQuota
|
||||
}
|
||||
if request.ValidDays != nil {
|
||||
updates["valid_days"] = *request.ValidDays
|
||||
}
|
||||
if request.Description != nil {
|
||||
updates["description"] = *request.Description
|
||||
}
|
||||
if len(updates) == 1 {
|
||||
return nil // 无可更新字段。
|
||||
}
|
||||
if err := s.goodsDao.GoodsUpdate(ctx, id, updates); err != nil {
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateStatus 商品上下架。
|
||||
func (s *GoodsService) UpdateStatus(ctx context.Context, adminID int64, id int64, request *dto.GoodsStatusRequest) *errcode.Error {
|
||||
ctx, span := global.Tracer.Start(ctx, "svc.AdminGoodsUpdateStatus")
|
||||
defer span.End()
|
||||
|
||||
if _, err := s.goodsDao.GoodsGetByID(ctx, id); err != nil {
|
||||
if isNotFound(err) {
|
||||
return errcode.GoodsNotFound
|
||||
}
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
if err := s.goodsDao.GoodsUpdate(ctx, id, map[string]any{"status": request.Status, "updated_by": adminID}); err != nil {
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 软删除商品(历史订单已快照商品信息,删除不影响历史订单)。
|
||||
func (s *GoodsService) Delete(ctx context.Context, adminID int64, id int64) *errcode.Error {
|
||||
ctx, span := global.Tracer.Start(ctx, "svc.AdminGoodsDelete")
|
||||
defer span.End()
|
||||
|
||||
if _, err := s.goodsDao.GoodsGetByID(ctx, id); err != nil {
|
||||
if isNotFound(err) {
|
||||
return errcode.GoodsNotFound
|
||||
}
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
if err := s.goodsDao.GoodsSoftDelete(ctx, id); err != nil {
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildGoodsItem 商品模型 → 响应(注意 dto.UploadQuota 对应 model.CosQuota)。
|
||||
func buildGoodsItem(g *mysqlmodel.Goods) dto.GoodsItem {
|
||||
return dto.GoodsItem{
|
||||
ID: g.ID,
|
||||
GoodsCode: g.GoodsCode,
|
||||
GoodsName: g.GoodsName,
|
||||
GoodsType: g.GoodsType,
|
||||
Price: g.Price,
|
||||
DiscountPrice: g.DiscountPrice,
|
||||
MemberDays: g.MemberDays,
|
||||
UploadQuota: g.CosQuota,
|
||||
AIRecommendQuota: g.AIRecommendQuota,
|
||||
ClosetShareQuota: g.ClosetShareQuota,
|
||||
OutfitShareQuota: g.OutfitShareQuota,
|
||||
ValidDays: g.ValidDays,
|
||||
Description: g.Description,
|
||||
Status: g.Status,
|
||||
ProductID: g.ProductID,
|
||||
ProductStatus: g.ProductStatus,
|
||||
CreatedAt: g.CreatedAt,
|
||||
UpdatedAt: g.UpdatedAt,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user