package business import ( "context" "github.com/go-kratos/kratos/v3/errors" "strings" "time" ) var ( ErrPlatform = errors.BadRequest("SW_PLATFORM_INVALID", "所属平台不正确") ErrPlatformUnavailable = errors.BadRequest("SW_PLATFORM_UNAVAILABLE", "平台数据源未配置或暂不可用") ErrPlatformPasswordConfig = errors.BadRequest("SW_PLATFORM_PASSWORD_CONFIG", "平台密码兼容配置未就绪") ErrPlatformAccount = errors.BadRequest("SW_PLATFORM_ACCOUNT", "游戏商务账号不存在或已禁用") ErrPlatformPassword = errors.BadRequest("SW_PLATFORM_PASSWORD", "游戏商务账号或密码错误") ErrBindingLevel = errors.BadRequest("SW_BINDING_LEVEL", "仅三级商务可绑定游戏 SDK 商务") ErrBindingOccupied = errors.BadRequest("SW_BINDING_OCCUPIED", "该游戏商务已被其他账号绑定或审核中") ErrBindingExists = errors.BadRequest("SW_BINDING_EXISTS", "该游戏商务已绑定") ErrBindingPending = errors.BadRequest("SW_BINDING_PENDING", "该游戏商务已提交,请等待 OA 审核") ErrBindingNotFound = errors.NotFound("SW_BINDING_NOT_FOUND", "绑定记录不存在") ) type PlatformKey struct { SourceDB string BusierID uint64 } type PlatformAccount struct { ID uint64 Account, Password string Status int } type PlatformAccountRepo interface { FindByAccount(context.Context, string, string) (*PlatformAccount, error) } type PlatformPasswordVerifier interface { Verify(string, string) (bool, error) } func NormalizePlatform(value string) (string, error) { value = strings.ToLower(strings.TrimSpace(value)) switch value { case "lx", "hw", "yq": return value, nil default: return "", ErrPlatform } } func PlatformName(value string) string { switch strings.ToLower(strings.TrimSpace(value)) { case "lx": return "龙翔" case "hw": return "汇玩" case "yq": return "游趣" default: return value } } type Binding struct { UserID uint PlatformKey Account, UserName string Status string RejectReason *string CreatedAt time.Time ReviewTime *time.Time ReviewAccount *string PromoteCount *int64 } type BindingFilter struct { UserID uint SourceDBs []string Account string Status string } type BindingRepo interface { List(context.Context, UserScope, BindingFilter, ...ListOption) ([]*Binding, int64, error) WithPlatformLock(context.Context, PlatformKey, func(context.Context) error) error ForPlatform(context.Context, PlatformKey) ([]*Binding, error) Save(context.Context, *Binding) error Delete(context.Context, uint, PlatformKey) error } type BindingUsecase struct { repo BindingRepo accounts *AccountUsecase platforms PlatformAccountRepo passwords PlatformPasswordVerifier } func NewBindingUsecase(repo BindingRepo, accounts *AccountUsecase, platforms PlatformAccountRepo, passwords PlatformPasswordVerifier) *BindingUsecase { return &BindingUsecase{repo: repo, accounts: accounts, platforms: platforms, passwords: passwords} } func (uc *BindingUsecase) List(ctx context.Context, q BindingFilter, options ...ListOption) ([]*Binding, int64, error) { actor, current, err := uc.accounts.actor(ctx) if err != nil { return nil, 0, err } scope := UserScope{All: actor.AuthorityID == 888} if !scope.All { scope.SelfID = current.ID if current.Level >= 3 { scope.OnlySelf = true } else { scope.DescendantPath = descendantPath(current) } } return uc.repo.List(ctx, scope, q, options...) } func (uc *BindingUsecase) ForUser(ctx context.Context, id uint) ([]*Binding, error) { if _, err := uc.accounts.Find(ctx, id); err != nil { return nil, err } rows, _, err := uc.repo.List(ctx, UserScope{SelfID: id, OnlySelf: true}, BindingFilter{UserID: id, Status: "0"}, ListLimit(-1)) return rows, err } func (uc *BindingUsecase) Bind(ctx context.Context, source, account, password string) error { _, current, err := uc.accounts.actor(ctx) if err != nil { return err } if current.Level != 3 { return ErrBindingLevel } source, err = NormalizePlatform(source) if err != nil { return err } account = strings.TrimSpace(account) platform, err := uc.platforms.FindByAccount(ctx, source, account) if err != nil { return err } if platform == nil || platform.Status != 1 { return ErrPlatformAccount } valid, err := uc.passwords.Verify(password, platform.Password) if err != nil { return err } if !valid { return ErrPlatformPassword } key := PlatformKey{SourceDB: source, BusierID: platform.ID} // 平台验密完成后才开启主库事务,避免跨库事务切源。 return uc.repo.WithPlatformLock(ctx, key, func(txctx context.Context) error { bindings, err := uc.repo.ForPlatform(txctx, key) if err != nil { return err } for _, b := range bindings { if b.UserID != current.ID && (b.Status == "0" || b.Status == "3") { return ErrBindingOccupied } } for _, b := range bindings { if b.UserID == current.ID { if b.Status == "0" { return ErrBindingExists } if b.Status == "3" { return ErrBindingPending } } } return uc.repo.Save(txctx, &Binding{UserID: current.ID, PlatformKey: key, Account: account, Status: "3", CreatedAt: time.Now()}) }) } func (uc *BindingUsecase) Unbind(ctx context.Context, key PlatformKey) error { _, current, err := uc.accounts.actor(ctx) if err != nil { return err } key.SourceDB, err = NormalizePlatform(key.SourceDB) if err != nil { return err } if key.BusierID == 0 { return ErrBindingNotFound } return uc.repo.WithPlatformLock(ctx, key, func(txctx context.Context) error { return uc.repo.Delete(txctx, current.ID, key) }) }