feat(merchant): 新建商户自动复制自营商户的 27 个默认商品
- CreateMerchant 事务内调用 copyDefaultProducts,将自营商户全部可售商品复制给新商户 - 新增 model.MerchantCodeSelfOperated 常量,消除散落的 self-operated 魔法字符串 - tenant.go 替换魔法字符串为常量 - 实测:新建商户后自动获得 27 个商品,价格/货币/库存/关联商品均正确
This commit is contained in:
@@ -10,6 +10,9 @@ const (
|
|||||||
MerchantStatusActive = "active"
|
MerchantStatusActive = "active"
|
||||||
MerchantStatusDisabled = "disabled"
|
MerchantStatusDisabled = "disabled"
|
||||||
|
|
||||||
|
// MerchantCodeSelfOperated 是平台自营商户编码,作为默认商品目录的模板来源。
|
||||||
|
MerchantCodeSelfOperated = "self-operated"
|
||||||
|
|
||||||
MerchantFeatureProducts = "products"
|
MerchantFeatureProducts = "products"
|
||||||
MerchantFeatureOrders = "orders"
|
MerchantFeatureOrders = "orders"
|
||||||
MerchantFeatureWallet = "wallet"
|
MerchantFeatureWallet = "wallet"
|
||||||
|
|||||||
@@ -103,6 +103,9 @@ func (s *MerchantService) CreateMerchant(in CreateMerchantInput, actorUserID uin
|
|||||||
}).Error; err != nil {
|
}).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := copyDefaultProducts(tx, merchant.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return writeAudit(tx, &merchant.ID, &actorUserID, nil, "merchant.create", "merchant", fmt.Sprint(merchant.ID), map[string]string{"code": merchant.Code})
|
return writeAudit(tx, &merchant.ID, &actorUserID, nil, "merchant.create", "merchant", fmt.Sprint(merchant.ID), map[string]string{"code": merchant.Code})
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -532,6 +535,45 @@ func (s *MerchantService) UpdateAPIClientStatus(merchantID, id uint, status stri
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copyDefaultProducts 将自营商户的全部可售商品复制给新建商户,作为默认商品目录。
|
||||||
|
// 自营商户(self-operated)充当平台默认商品模板,新商户开箱即用。
|
||||||
|
// 使用 OnConflict DoNothing 保证幂等:即使重复调用也不会报唯一索引冲突。
|
||||||
|
func copyDefaultProducts(tx *gorm.DB, merchantID uint) error {
|
||||||
|
var selfMerchant model.Merchant
|
||||||
|
if err := tx.Where("code = ?", model.MerchantCodeSelfOperated).First(&selfMerchant).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil // 自营商户不存在时跳过,不阻断建商户
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if selfMerchant.ID == merchantID {
|
||||||
|
return nil // 自营商户自身无需复制
|
||||||
|
}
|
||||||
|
var templates []model.MerchantProduct
|
||||||
|
if err := tx.Where("merchant_id = ?", selfMerchant.ID).Find(&templates).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(templates) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
products := make([]model.MerchantProduct, 0, len(templates))
|
||||||
|
for _, t := range templates {
|
||||||
|
products = append(products, model.MerchantProduct{
|
||||||
|
MerchantID: merchantID,
|
||||||
|
ProductID: t.ProductID,
|
||||||
|
SKU: t.SKU,
|
||||||
|
DisplayName: t.DisplayName,
|
||||||
|
PriceAmount: t.PriceAmount,
|
||||||
|
CostAmount: t.CostAmount,
|
||||||
|
Currency: t.Currency,
|
||||||
|
Stock: t.Stock,
|
||||||
|
Status: t.Status,
|
||||||
|
FulfillmentConfig: t.FulfillmentConfig,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&products).Error
|
||||||
|
}
|
||||||
|
|
||||||
func ensureProduct(tx *gorm.DB, in CreateMerchantProductInput) (*model.Product, error) {
|
func ensureProduct(tx *gorm.DB, in CreateMerchantProductInput) (*model.Product, error) {
|
||||||
if in.ProductCode != "" {
|
if in.ProductCode != "" {
|
||||||
var product model.Product
|
var product model.Product
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (s *TenantService) ResolveMember(userID uint, merchantRef string) (*model.M
|
|||||||
|
|
||||||
func (s *TenantService) EnsureSelfMember(userID uint, role string, status int) error {
|
func (s *TenantService) EnsureSelfMember(userID uint, role string, status int) error {
|
||||||
var merchant model.Merchant
|
var merchant model.Merchant
|
||||||
if err := s.db.Where("code = ?", "self-operated").First(&merchant).Error; err != nil {
|
if err := s.db.Where("code = ?", model.MerchantCodeSelfOperated).First(&merchant).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.EnsureMember(merchant.ID, userID, role, status, role == model.MemberRoleOwner)
|
return s.EnsureMember(merchant.ID, userID, role, status, role == model.MemberRoleOwner)
|
||||||
|
|||||||
Reference in New Issue
Block a user