// Package wallet 提供代理主钱包复杂写用例。 package wallet import ( "context" domainwallet "github.com/break/junhong_cmp_fiber/internal/domain/wallet" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/internal/model/dto" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" "gorm.io/gorm" ) // ChangeCreditService 调整既有店铺主钱包实际信用额度。 type ChangeCreditService struct { db *gorm.DB } // NewChangeCreditService 创建实际信用额度调整服务。 func NewChangeCreditService(db *gorm.DB) *ChangeCreditService { return &ChangeCreditService{db: db} } // Execute 按主钱包类型和版本条件更新,不修改余额、冻结金额或钱包流水。 func (s *ChangeCreditService) Execute(ctx context.Context, shopID uint, enabled bool, limit int64, version int) (*dto.ShopCreditLimitResponse, error) { var result *dto.ShopCreditLimitResponse err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { var stored model.AgentWallet if err := tx.Where("shop_id = ? AND wallet_type = ?", shopID, constants.AgentWalletTypeMain).First(&stored).Error; err != nil { return errors.New(errors.CodeWalletNotFound, "店铺主钱包不存在") } aggregate := domainwallet.AgentWallet{ID: stored.ID, ShopID: stored.ShopID, WalletType: stored.WalletType, Balance: stored.Balance, FrozenBalance: stored.FrozenBalance, CreditEnabled: stored.CreditEnabled, CreditLimit: stored.CreditLimit, Status: stored.Status, Version: stored.Version} if err := aggregate.ChangeCredit(enabled, limit); err != nil { return err } update := tx.Model(&model.AgentWallet{}). Where("id = ? AND wallet_type = ? AND version = ? AND balance::numeric - frozen_balance::numeric + ?::numeric >= 0", stored.ID, constants.AgentWalletTypeMain, version, aggregate.EffectiveCredit()). Updates(map[string]any{"credit_enabled": enabled, "credit_limit": limit, "version": gorm.Expr("version + 1")}) if update.Error != nil { return errors.Wrap(errors.CodeInternalError, update.Error, "更新店铺信用额度失败") } if update.RowsAffected != 1 { var current model.AgentWallet if err := tx.Where("id = ? AND wallet_type = ?", stored.ID, constants.AgentWalletTypeMain).First(¤t).Error; err != nil { return errors.New(errors.CodeWalletNotFound, "店铺主钱包不存在") } if current.Version != version { return errors.New(errors.CodeConflict, "钱包版本已变化,请刷新后重试") } return errors.New(errors.CodeInsufficientQuota, "当前资金占用无法降低或关闭信用额度") } available, _ := aggregate.AvailableBalance() result = &dto.ShopCreditLimitResponse{ShopID: shopID, WalletID: stored.ID, Balance: stored.Balance, FrozenBalance: stored.FrozenBalance, CreditEnabled: enabled, CreditLimit: limit, AvailableBalance: available, Version: version + 1} return nil }) return result, err }