package service import ( "errors" "fmt" "strings" "affiliate_dash/internal/model" "gorm.io/gorm" ) type CreateMerchantProductInput struct { ProductCode string ProductName string Category string Description string Attributes string SKU string DisplayName string PriceAmount int64 CostAmount int64 Currency string Stock int64 Status string FulfillmentConfig string } func (s *MerchantService) CreateMerchantProduct(merchantID uint, in CreateMerchantProductInput, actorUserID uint) (*model.MerchantProduct, error) { in.SKU = strings.TrimSpace(in.SKU) in.ProductCode = strings.TrimSpace(in.ProductCode) in.ProductName = strings.TrimSpace(in.ProductName) if in.SKU == "" { return nil, errors.New("商户商品 SKU 不能为空") } if in.PriceAmount < 0 || in.CostAmount < 0 { return nil, errors.New("商品金额不能小于零") } if in.Stock < -1 { return nil, errors.New("库存只能为 -1 或非负整数") } if in.Currency == "" { in.Currency = "POINT" } in.Currency = strings.ToUpper(in.Currency) if in.Status == "" { in.Status = model.ProductStatusActive } if in.Status != model.ProductStatusActive && in.Status != model.ProductStatusInactive { return nil, errors.New("无效的商品状态") } merchantProduct := &model.MerchantProduct{} err := s.db.Transaction(func(tx *gorm.DB) error { var merchant model.Merchant if err := tx.Where("id = ? AND status = ?", merchantID, model.MerchantStatusActive).First(&merchant).Error; err != nil { return errors.New("商户不存在或已禁用") } product, err := ensureProduct(tx, in) if err != nil { return err } merchantProduct = &model.MerchantProduct{ MerchantID: merchantID, ProductID: product.ID, SKU: in.SKU, DisplayName: fallbackName(in.DisplayName, product.Name), PriceAmount: in.PriceAmount, CostAmount: in.CostAmount, Currency: in.Currency, Stock: in.Stock, Status: in.Status, FulfillmentConfig: in.FulfillmentConfig, } if err := tx.Create(merchantProduct).Error; err != nil { return err } return writeAudit(tx, &merchantID, &actorUserID, nil, "merchant_product.create", "merchant_product", fmt.Sprint(merchantProduct.ID), map[string]string{"sku": in.SKU}) }) if err != nil { return nil, err } return merchantProduct, nil } func (s *MerchantService) ListMerchantProducts(merchantID uint, page, size int, activeOnly bool) ([]model.MerchantProduct, int64, error) { page, size = normalizePage(page, size) tx := s.db.Model(&model.MerchantProduct{}).Where("merchant_id = ?", merchantID) if activeOnly { tx = tx.Where("status = ?", model.ProductStatusActive) } var total int64 if err := tx.Count(&total).Error; err != nil { return nil, 0, err } var products []model.MerchantProduct err := tx.Preload("Product").Order("id DESC").Offset((page - 1) * size).Limit(size).Find(&products).Error return products, total, err } type UpdateMerchantProductInput struct { DisplayName *string PriceAmount *int64 CostAmount *int64 Stock *int64 Status *string FulfillmentConfig *string } func (s *MerchantService) UpdateMerchantProduct(merchantID, id uint, in UpdateMerchantProductInput, actorUserID uint) error { updates := make(map[string]interface{}) if in.DisplayName != nil { updates["display_name"] = *in.DisplayName } if in.PriceAmount != nil { if *in.PriceAmount < 0 { return errors.New("商品售价不能小于零") } updates["price_amount"] = *in.PriceAmount } if in.CostAmount != nil { if *in.CostAmount < 0 { return errors.New("商品成本不能小于零") } updates["cost_amount"] = *in.CostAmount } if in.Stock != nil { if *in.Stock < -1 { return errors.New("库存只能为 -1 或非负整数") } updates["stock"] = *in.Stock } if in.Status != nil { if *in.Status != model.ProductStatusActive && *in.Status != model.ProductStatusInactive { return errors.New("无效的商品状态") } updates["status"] = *in.Status } if in.FulfillmentConfig != nil { updates["fulfillment_config"] = *in.FulfillmentConfig } if len(updates) == 0 { return errors.New("没有可更新字段") } return s.db.Transaction(func(tx *gorm.DB) error { result := tx.Model(&model.MerchantProduct{}).Where("id = ? AND merchant_id = ?", id, merchantID).Updates(updates) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.New("商户商品不存在") } return writeAudit(tx, &merchantID, &actorUserID, nil, "merchant_product.update", "merchant_product", fmt.Sprint(id), nil) }) }