190 lines
5.6 KiB
Go
190 lines
5.6 KiB
Go
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) })
|
|
}
|