Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 69 additions & 3 deletions internal/apps/oauth/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,19 @@ package oauth
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"

"github.com/google/uuid"
"github.com/hibiken/asynq"
"github.com/linux-do/cdk/internal/config"
"github.com/linux-do/cdk/internal/db"
"github.com/linux-do/cdk/internal/logger"
"github.com/linux-do/cdk/internal/task"
"github.com/linux-do/cdk/internal/task/schedule"
"github.com/linux-do/cdk/internal/utils"
"net/http"
"time"

"gorm.io/gorm"
)

Expand Down Expand Up @@ -128,3 +133,64 @@ func (u *User) UpdateUserScore(ctx context.Context, newScore int) error {
logger.InfoF(ctx, "用户[%s]徽章分数更新成功: %d -> %d", u.Username, u.Score, newScore)
return nil
}

// UpdateFromOAuthInfo 根据 OAuth 信息更新用户数据
func (u *User) UpdateFromOAuthInfo(oauthInfo *OAuthUserInfo) {
u.Username = oauthInfo.Username
u.Nickname = oauthInfo.Name
u.AvatarUrl = oauthInfo.AvatarUrl
u.IsActive = oauthInfo.Active
u.TrustLevel = oauthInfo.TrustLevel
u.LastLoginAt = time.Now()
}

// CheckActive 检查用户账户是否激活,未激活则返回错误
func (u *User) CheckActive() error {
if !u.IsActive {
return errors.New(BannedAccount)
}
return nil
}

// EnqueueBadgeScoreTask 为用户下发徽章分数计算任务
func (u *User) EnqueueBadgeScoreTask(ctx context.Context) {
payload, _ := json.Marshal(map[string]interface{}{
"user_id": u.ID,
})
if _, err := schedule.AsynqClient.Enqueue(asynq.NewTask(task.UpdateSingleUserBadgeScoreTask, payload)); err != nil {
logger.ErrorF(ctx, "下发用户[%s]徽章分数计算任务失败: %v", u.Username, err)
} else {
logger.InfoF(ctx, "下发用户[%s]徽章分数计算任务成功", u.Username)
}
}

// MarkAsDeactivatedAndCreateNew 将当前用户标记为已注销,并创建新用户
func (u *User) MarkAsDeactivatedAndCreateNew(ctx context.Context, oauthInfo *OAuthUserInfo) (*User, error) {
err := db.DB(ctx).Transaction(func(tx *gorm.DB) error {
// 将旧用户名修改为注销状态
oldUsername := fmt.Sprintf("%s已注销: %s", u.Username, uuid.NewString())
if err := tx.Model(u).Updates(map[string]interface{}{
"username": oldUsername,
"is_active": false,
}).Error; err != nil {
return err
}

// 创建新用户
newUser := User{
ID: oauthInfo.Id,
Username: oauthInfo.Username,
Nickname: oauthInfo.Name,
AvatarUrl: oauthInfo.AvatarUrl,
IsActive: oauthInfo.Active,
TrustLevel: oauthInfo.TrustLevel,
LastLoginAt: time.Now(),
}
if err := tx.Create(&newUser).Error; err != nil {
return err
}
*u = newUser
return nil
})
return u, err
}
105 changes: 32 additions & 73 deletions internal/apps/oauth/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,9 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"time"

"github.com/google/uuid"
"github.com/hibiken/asynq"
"github.com/linux-do/cdk/internal/logger"
"github.com/linux-do/cdk/internal/task"
"github.com/linux-do/cdk/internal/task/schedule"

"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/linux-do/cdk/internal/config"
Expand Down Expand Up @@ -113,12 +106,24 @@ func doOAuth(ctx context.Context, code string) (*User, error) {
return nil, err
}

// save to db
// 处理用户信息同步逻辑
var user User
tx := db.DB(ctx).Where("username = ?", userInfo.Username).First(&user)
if tx.Error != nil {
// create user
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {

txByUsername := db.DB(ctx).Where("username = ?", userInfo.Username).First(&user)
if txByUsername.Error != nil {
txByID := db.DB(ctx).Where("id = ?", userInfo.Id).First(&user)
if txByID.Error == nil {
// ID 存在但 username 不匹配(用户改名)
if err = user.CheckActive(); err != nil {
span.SetStatus(codes.Error, err.Error())
return nil, err
}
user.UpdateFromOAuthInfo(&userInfo)
if err = db.DB(ctx).Save(&user).Error; err != nil {
span.SetStatus(codes.Error, err.Error())
return nil, err
}
} else if errors.Is(txByUsername.Error, gorm.ErrRecordNotFound) {
user = User{
ID: userInfo.Id,
Username: userInfo.Username,
Expand All @@ -128,79 +133,33 @@ func doOAuth(ctx context.Context, code string) (*User, error) {
TrustLevel: userInfo.TrustLevel,
LastLoginAt: time.Now(),
}
tx = db.DB(ctx).Create(&user)
if tx.Error != nil {
span.SetStatus(codes.Error, tx.Error.Error())
return nil, tx.Error
}
payload, _ := json.Marshal(map[string]interface{}{
"user_id": user.ID,
})

if _, errTask := schedule.AsynqClient.Enqueue(asynq.NewTask(task.UpdateSingleUserBadgeScoreTask, payload)); errTask != nil {
logger.ErrorF(ctx, "下发用户[%s]徽章分数计算任务失败: %v", user.Username, errTask)
} else {
logger.InfoF(ctx, "下发用户[%s]徽章分数计算任务成功", user.Username)
if err = db.DB(ctx).Create(&user).Error; err != nil {
span.SetStatus(codes.Error, err.Error())
return nil, err
}
user.EnqueueBadgeScoreTask(ctx)
} else {
// response failed
span.SetStatus(codes.Error, tx.Error.Error())
return nil, tx.Error
// query failed
span.SetStatus(codes.Error, txByUsername.Error.Error())
return nil, txByUsername.Error
}
} else {
if user.ID != userInfo.Id {
err = db.DB(ctx).Transaction(func(tx *gorm.DB) error {
oldUsername := fmt.Sprintf("%s已注销: %s", user.Username, uuid.NewString())
if errUpdate := tx.Model(&user).Updates(map[string]interface{}{
"username": oldUsername,
"is_active": false,
}).Error; errUpdate != nil {
return errUpdate
}
// create user
user = User{
ID: userInfo.Id,
Username: userInfo.Username,
Nickname: userInfo.Name,
AvatarUrl: userInfo.AvatarUrl,
IsActive: userInfo.Active,
TrustLevel: userInfo.TrustLevel,
LastLoginAt: time.Now(),
}
if errCreate := tx.Create(&user).Error; errCreate != nil {
return errCreate
}
return nil
})
if err != nil {
// username 相同但 ID 不同(账户注销后被新用户占用)
if _, err = user.MarkAsDeactivatedAndCreateNew(ctx, &userInfo); err != nil {
span.SetStatus(codes.Error, err.Error())
return nil, err
}
payload, _ := json.Marshal(map[string]interface{}{
"user_id": user.ID,
})
if _, errTask := schedule.AsynqClient.Enqueue(asynq.NewTask(task.UpdateSingleUserBadgeScoreTask, payload)); errTask != nil {
logger.ErrorF(ctx, "下发用户[%s]徽章分数计算任务失败: %v", user.Username, errTask)
} else {
logger.InfoF(ctx, "下发用户[%s]徽章分数计算任务成功", user.Username)
}
user.EnqueueBadgeScoreTask(ctx)
} else {
if !user.IsActive {
err = errors.New(BannedAccount)
if err = user.CheckActive(); err != nil {
span.SetStatus(codes.Error, err.Error())
return nil, err
}
// update user
user.Username = userInfo.Username
user.Nickname = userInfo.Name
user.AvatarUrl = userInfo.AvatarUrl
user.IsActive = userInfo.Active
user.TrustLevel = userInfo.TrustLevel
user.LastLoginAt = time.Now()
tx = db.DB(ctx).Save(&user)
if tx.Error != nil {
span.SetStatus(codes.Error, tx.Error.Error())
return nil, tx.Error
user.UpdateFromOAuthInfo(&userInfo)
if err = db.DB(ctx).Save(&user).Error; err != nil {
span.SetStatus(codes.Error, err.Error())
return nil, err
}
}
}
Expand Down
Loading