173 lines
5.9 KiB
Go
173 lines
5.9 KiB
Go
package liveclient
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
|
|
biz "kra/internal/biz/liveclient"
|
|
"kra/internal/data/ledger"
|
|
)
|
|
|
|
type Provider interface{ DB() *gorm.DB }
|
|
|
|
type repo struct{ data Provider }
|
|
|
|
func NewRepo(data Provider) biz.Repo { return &repo{data: data} }
|
|
|
|
type userPO struct {
|
|
ID uint `gorm:"primaryKey"`
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
|
UUID string
|
|
Username string
|
|
Password string
|
|
NickName string `gorm:"column:nick_name"`
|
|
HeaderImg string `gorm:"column:header_img"`
|
|
AuthorityID uint `gorm:"column:authority_id"`
|
|
Enable int
|
|
PasswordUpdatedAt *time.Time
|
|
MustChangePassword bool
|
|
}
|
|
|
|
func (userPO) TableName() string { return "sys_users" }
|
|
|
|
type authorityPO struct {
|
|
AuthorityID uint `gorm:"primaryKey;column:authority_id"`
|
|
AuthorityName string
|
|
ParentID *uint
|
|
DataScope int
|
|
DefaultRouter string
|
|
}
|
|
|
|
func (authorityPO) TableName() string { return "sys_authorities" }
|
|
|
|
type userAuthorityPO struct {
|
|
UserID uint `gorm:"column:sys_user_id"`
|
|
AuthorityID uint `gorm:"column:sys_authority_authority_id"`
|
|
}
|
|
|
|
func (userAuthorityPO) TableName() string { return "sys_user_authority" }
|
|
|
|
type policyPO struct {
|
|
ID uint `gorm:"primaryKey;autoIncrement"`
|
|
Ptype string
|
|
V0, V1, V2 string
|
|
V3, V4, V5 string
|
|
}
|
|
|
|
func (policyPO) TableName() string { return "casbin_rule" }
|
|
|
|
func (r *repo) Register(ctx context.Context, account *biz.Account, passwordHash string, initialDiamonds int64) (*biz.Account, error) {
|
|
now := time.Now()
|
|
user := userPO{UUID: uuid.NewString(), Username: account.Username, Password: passwordHash, NickName: account.Nickname, HeaderImg: account.Avatar, AuthorityID: biz.ClientAuthorityID, Enable: 1, PasswordUpdatedAt: &now}
|
|
err := r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
var count int64
|
|
if err := tx.Model(&userPO{}).Where("username = ?", account.Username).Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return biz.ErrUsernameExists
|
|
}
|
|
root := uint(0)
|
|
authority := authorityPO{AuthorityID: biz.ClientAuthorityID, AuthorityName: "直播用户", ParentID: &root, DataScope: 4, DefaultRouter: "404"}
|
|
if err := tx.Where("authority_id = ?", authority.AuthorityID).FirstOrCreate(&authority).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, route := range clientRoutes() {
|
|
policy := policyPO{Ptype: "p", V0: strconv.FormatUint(uint64(biz.ClientAuthorityID), 10), V1: route.path, V2: route.method}
|
|
if err := tx.Where("ptype = ? AND v0 = ? AND v1 = ? AND v2 = ?", policy.Ptype, policy.V0, policy.V1, policy.V2).FirstOrCreate(&policy).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := tx.Create(&user).Error; err != nil {
|
|
if duplicate(err) {
|
|
return biz.ErrUsernameExists
|
|
}
|
|
return err
|
|
}
|
|
if err := tx.Create(&userAuthorityPO{UserID: user.ID, AuthorityID: biz.ClientAuthorityID}).Error; err != nil {
|
|
return err
|
|
}
|
|
return tx.Create(&ledger.WalletPO{UserID: user.ID, DiamondBalance: initialDiamonds}).Error
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return r.Get(ctx, user.ID)
|
|
}
|
|
|
|
func clientRoutes() []struct{ method, path string } {
|
|
return []struct{ method, path string }{
|
|
{method: "POST", path: "/live/client/auth/logout"},
|
|
{method: "GET", path: "/live/client/me"},
|
|
{method: "GET", path: "/live/client/balance"},
|
|
{method: "PUT", path: "/live/client/me"},
|
|
{method: "PUT", path: "/live/client/me/password"},
|
|
{method: "POST", path: "/live/rooms/:id/gifts"},
|
|
{method: "GET", path: "/live/rooms/:id/follow"},
|
|
{method: "POST", path: "/live/rooms/:id/follow"},
|
|
{method: "DELETE", path: "/live/rooms/:id/follow"},
|
|
{method: "POST", path: "/live/recharge/orders"},
|
|
{method: "GET", path: "/live/recharge/orders/:orderNo"},
|
|
{method: "GET", path: "/live/recharge/orders/:orderNo/status"},
|
|
}
|
|
}
|
|
|
|
func (r *repo) Get(ctx context.Context, userID uint) (*biz.Account, error) {
|
|
var row struct {
|
|
ID uint
|
|
CreatedAt time.Time
|
|
UUID string
|
|
Username string
|
|
Nickname string
|
|
Avatar string
|
|
AuthorityID uint
|
|
Enable int
|
|
DiamondBalance int64
|
|
PasswordUpdatedAt *time.Time
|
|
}
|
|
err := r.data.DB().WithContext(ctx).Table("sys_users").
|
|
Select("sys_users.id, sys_users.created_at, sys_users.uuid, sys_users.username, sys_users.nick_name AS nickname, sys_users.header_img AS avatar, sys_users.authority_id, sys_users.enable, COALESCE(live_wallets.diamond_balance, 0) AS diamond_balance, sys_users.password_updated_at").
|
|
Joins("LEFT JOIN live_wallets ON live_wallets.user_id = sys_users.id").
|
|
Where("sys_users.id = ? AND sys_users.deleted_at IS NULL", userID).Scan(&row).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if row.ID == 0 {
|
|
return nil, biz.ErrAccountNotFound
|
|
}
|
|
return &biz.Account{UserID: row.ID, UUID: row.UUID, Username: row.Username, Nickname: row.Nickname, Avatar: row.Avatar, AuthorityID: row.AuthorityID, Enabled: row.Enable == 1, DiamondBalance: row.DiamondBalance, CreatedAt: row.CreatedAt, PasswordUpdatedAt: row.PasswordUpdatedAt}, nil
|
|
}
|
|
|
|
func (r *repo) UpdateProfile(ctx context.Context, in *biz.ProfileInput) (*biz.Account, error) {
|
|
result := r.data.DB().WithContext(ctx).Model(&userPO{}).Where("id = ?", in.UserID).Updates(map[string]any{"nick_name": in.Nickname, "header_img": in.Avatar})
|
|
if result.Error != nil {
|
|
return nil, result.Error
|
|
}
|
|
if result.RowsAffected == 0 {
|
|
var count int64
|
|
if err := r.data.DB().WithContext(ctx).Model(&userPO{}).Where("id = ?", in.UserID).Count(&count).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
if count == 0 {
|
|
return nil, biz.ErrAccountNotFound
|
|
}
|
|
}
|
|
return r.Get(ctx, in.UserID)
|
|
}
|
|
|
|
func duplicate(err error) bool {
|
|
if err == nil || errors.Is(err, biz.ErrUsernameExists) {
|
|
return false
|
|
}
|
|
text := strings.ToLower(err.Error())
|
|
return strings.Contains(text, "unique") || strings.Contains(text, "duplicate")
|
|
}
|