feat(user): implement soft deletion for user accounts and update related logic
This commit is contained in:
parent
e027cbb5de
commit
a9c832cb7c
@ -32,7 +32,6 @@ type (
|
|||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at"`
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
DeletedAt int64 `json:"deleted_at,omitempty"`
|
DeletedAt int64 `json:"deleted_at,omitempty"`
|
||||||
IsDel bool `json:"is_del,omitempty"`
|
|
||||||
}
|
}
|
||||||
Follow {
|
Follow {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
|
|||||||
@ -68,6 +68,10 @@ func (l *UserLoginLogic) UserLogin(req *types.UserLoginRequest) (resp *types.Log
|
|||||||
|
|
||||||
userInfo, err = l.svcCtx.UserModel.FindOneByEmail(l.ctx, req.Email)
|
userInfo, err = l.svcCtx.UserModel.FindOneByEmail(l.ctx, req.Email)
|
||||||
|
|
||||||
|
if userInfo.DeletedAt.Valid {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserNotExist), "user email deleted: %v", req.Email)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.As(err, &gorm.ErrRecordNotFound) {
|
if errors.As(err, &gorm.ErrRecordNotFound) {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserNotExist), "user email not exist: %v", req.Email)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserNotExist), "user email not exist: %v", req.Email)
|
||||||
|
|||||||
@ -79,13 +79,16 @@ func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterRequest) (resp *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if the user exists
|
// Check if the user exists
|
||||||
_, err = l.svcCtx.UserModel.FindOneByEmail(l.ctx, req.Email)
|
u, err := l.svcCtx.UserModel.FindOneByEmail(l.ctx, req.Email)
|
||||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
l.Errorw("FindOneByEmail Error", logger.Field("error", err))
|
l.Errorw("FindOneByEmail Error", logger.Field("error", err))
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query user info failed: %v", err.Error())
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query user info failed: %v", err.Error())
|
||||||
} else if err == nil {
|
} else if err == nil && !u.DeletedAt.Valid {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserExist), "user email exist: %v", req.Email)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserExist), "user email exist: %v", req.Email)
|
||||||
|
} else if err == nil && u.DeletedAt.Valid {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserDisabled), "user email deleted: %v", req.Email)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate password
|
// Generate password
|
||||||
pwd := tool.EncodePassWord(req.Password)
|
pwd := tool.EncodePassWord(req.Password)
|
||||||
userInfo := &user.User{
|
userInfo := &user.User{
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/pkg/cache"
|
"github.com/perfect-panel/server/pkg/cache"
|
||||||
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@ -72,7 +73,7 @@ func (m *defaultUserModel) FindOneByEmail(ctx context.Context, email string) (*U
|
|||||||
if err := conn.Model(&AuthMethods{}).Where("`auth_type` = 'email' AND `auth_identifier` = ?", email).First(&data).Error; err != nil {
|
if err := conn.Model(&AuthMethods{}).Where("`auth_type` = 'email' AND `auth_identifier` = ?", email).First(&data).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return conn.Model(&User{}).Where("`id` = ?", data.UserId).Preload("UserDevices").Preload("AuthMethods").First(v).Error
|
return conn.Model(&User{}).Unscoped().Where("`id` = ?", data.UserId).Preload("UserDevices").Preload("AuthMethods").First(v).Error
|
||||||
})
|
})
|
||||||
return &user, err
|
return &user, err
|
||||||
}
|
}
|
||||||
@ -91,7 +92,7 @@ func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error)
|
|||||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
||||||
var resp User
|
var resp User
|
||||||
err := m.QueryCtx(ctx, &resp, userIdKey, func(conn *gorm.DB, v interface{}) error {
|
err := m.QueryCtx(ctx, &resp, userIdKey, func(conn *gorm.DB, v interface{}) error {
|
||||||
return conn.Model(&User{}).Where("`id` = ?", id).Preload("UserDevices").Preload("AuthMethods").First(&resp).Error
|
return conn.Model(&User{}).Unscoped().Where("`id` = ?", id).Preload("UserDevices").Preload("AuthMethods").First(&resp).Error
|
||||||
})
|
})
|
||||||
return &resp, err
|
return &resp, err
|
||||||
}
|
}
|
||||||
@ -119,10 +120,11 @@ func (m *defaultUserModel) Delete(ctx context.Context, id int64, tx ...*gorm.DB)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用批量相关缓存清理,包含所有相关数据的缓存
|
// Use batch related cache cleaning, including a cache of all relevant data
|
||||||
defer func() {
|
defer func() {
|
||||||
if clearErr := m.BatchClearRelatedCache(ctx, data); clearErr != nil {
|
if clearErr := m.BatchClearRelatedCache(ctx, data); clearErr != nil {
|
||||||
// 记录清理缓存错误,但不阻断删除操作
|
// Record cache cleaning errors, but do not block deletion operations
|
||||||
|
logger.Errorf("failed to clear related cache for user %d: %v", id, clearErr.Error())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -130,24 +132,11 @@ func (m *defaultUserModel) Delete(ctx context.Context, id int64, tx ...*gorm.DB)
|
|||||||
if len(tx) > 0 {
|
if len(tx) > 0 {
|
||||||
db = tx[0]
|
db = tx[0]
|
||||||
}
|
}
|
||||||
|
// Soft deletion of user information without any processing of other information (Determine whether to allow login/subscription based on the user's deletion status)
|
||||||
// 删除用户相关的所有数据
|
|
||||||
if err := db.Model(&User{}).Where("`id` = ?", id).Delete(&User{}).Error; err != nil {
|
if err := db.Model(&User{}).Where("`id` = ?", id).Delete(&User{}).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.Model(&AuthMethods{}).Where("`user_id` = ?", id).Delete(&AuthMethods{}).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Model(&Subscribe{}).Where("`user_id` = ?", id).Delete(&Subscribe{}).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Model(&Device{}).Where("`user_id` = ?", id).Delete(&Device{}).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,32 +2,35 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Id int64 `gorm:"primaryKey"`
|
Id int64 `gorm:"primaryKey"`
|
||||||
Password string `gorm:"type:varchar(100);not null;comment:User Password"`
|
Password string `gorm:"type:varchar(100);not null;comment:User Password"`
|
||||||
Algo string `gorm:"type:varchar(20);default:'default';comment:Encryption Algorithm"`
|
Algo string `gorm:"type:varchar(20);default:'default';comment:Encryption Algorithm"`
|
||||||
Salt string `gorm:"type:varchar(20);default:null;comment:Password Salt"`
|
Salt string `gorm:"type:varchar(20);default:null;comment:Password Salt"`
|
||||||
Avatar string `gorm:"type:MEDIUMTEXT;comment:User Avatar"`
|
Avatar string `gorm:"type:MEDIUMTEXT;comment:User Avatar"`
|
||||||
Balance int64 `gorm:"default:0;comment:User Balance"` // User Balance Amount
|
Balance int64 `gorm:"default:0;comment:User Balance"` // User Balance Amount
|
||||||
ReferCode string `gorm:"type:varchar(20);default:'';comment:Referral Code"`
|
ReferCode string `gorm:"type:varchar(20);default:'';comment:Referral Code"`
|
||||||
RefererId int64 `gorm:"index:idx_referer;comment:Referrer ID"`
|
RefererId int64 `gorm:"index:idx_referer;comment:Referrer ID"`
|
||||||
Commission int64 `gorm:"default:0;comment:Commission"` // Commission Amount
|
Commission int64 `gorm:"default:0;comment:Commission"` // Commission Amount
|
||||||
ReferralPercentage uint8 `gorm:"default:0;comment:Referral"` // Referral Percentage
|
ReferralPercentage uint8 `gorm:"default:0;comment:Referral"` // Referral Percentage
|
||||||
OnlyFirstPurchase *bool `gorm:"default:true;not null;comment:Only First Purchase"` // Only First Purchase Referral
|
OnlyFirstPurchase *bool `gorm:"default:true;not null;comment:Only First Purchase"` // Only First Purchase Referral
|
||||||
GiftAmount int64 `gorm:"default:0;comment:User Gift Amount"`
|
GiftAmount int64 `gorm:"default:0;comment:User Gift Amount"`
|
||||||
Enable *bool `gorm:"default:true;not null;comment:Is Account Enabled"`
|
Enable *bool `gorm:"default:true;not null;comment:Is Account Enabled"`
|
||||||
IsAdmin *bool `gorm:"default:false;not null;comment:Is Admin"`
|
IsAdmin *bool `gorm:"default:false;not null;comment:Is Admin"`
|
||||||
EnableBalanceNotify *bool `gorm:"default:false;not null;comment:Enable Balance Change Notifications"`
|
EnableBalanceNotify *bool `gorm:"default:false;not null;comment:Enable Balance Change Notifications"`
|
||||||
EnableLoginNotify *bool `gorm:"default:false;not null;comment:Enable Login Notifications"`
|
EnableLoginNotify *bool `gorm:"default:false;not null;comment:Enable Login Notifications"`
|
||||||
EnableSubscribeNotify *bool `gorm:"default:false;not null;comment:Enable Subscription Notifications"`
|
EnableSubscribeNotify *bool `gorm:"default:false;not null;comment:Enable Subscription Notifications"`
|
||||||
EnableTradeNotify *bool `gorm:"default:false;not null;comment:Enable Trade Notifications"`
|
EnableTradeNotify *bool `gorm:"default:false;not null;comment:Enable Trade Notifications"`
|
||||||
AuthMethods []AuthMethods `gorm:"foreignKey:UserId;references:Id"`
|
AuthMethods []AuthMethods `gorm:"foreignKey:UserId;references:Id"`
|
||||||
UserDevices []Device `gorm:"foreignKey:UserId;references:Id"`
|
UserDevices []Device `gorm:"foreignKey:UserId;references:Id"`
|
||||||
Rules string `gorm:"type:TEXT;comment:User Rules"`
|
Rules string `gorm:"type:TEXT;comment:User Rules"`
|
||||||
CreatedAt time.Time `gorm:"<-:create;comment:Creation Time"`
|
CreatedAt time.Time `gorm:"<-:create;comment:Creation Time"`
|
||||||
UpdatedAt time.Time `gorm:"comment:Update Time"`
|
UpdatedAt time.Time `gorm:"comment:Update Time"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index;comment:Deletion Time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*User) TableName() string {
|
func (*User) TableName() string {
|
||||||
|
|||||||
@ -2540,7 +2540,6 @@ type User struct {
|
|||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at"`
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
DeletedAt int64 `json:"deleted_at,omitempty"`
|
DeletedAt int64 `json:"deleted_at,omitempty"`
|
||||||
IsDel bool `json:"is_del,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserAffiliate struct {
|
type UserAffiliate struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user