feat(用户): 添加账号注销功能
Some checks failed
Build docker and publish / build (20.15.1) (push) Has been cancelled
Some checks failed
Build docker and publish / build (20.15.1) (push) Has been cancelled
实现用户账号注销逻辑,包括删除用户所有关联数据并根据原设备信息创建新账号
This commit is contained in:
parent
c5d59b86b0
commit
70c8811406
23
internal/handler/public/user/deleteAccountHandler.go
Normal file
23
internal/handler/public/user/deleteAccountHandler.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/perfect-panel/server/internal/logic/public/user"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeleteAccountHandler 注销账号处理器
|
||||||
|
// 根据当前token删除所有关联设备,然后根据各自设备ID重新新建账号
|
||||||
|
func DeleteAccountHandler(serverCtx *svc.ServiceContext) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
l := user.NewDeleteAccountLogic(c.Request.Context(), serverCtx)
|
||||||
|
resp, err := l.DeleteAccount()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -769,13 +769,16 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Query User Affiliate Count
|
// Query User Affiliate Count
|
||||||
publicUserGroupRouter.GET("/affiliate/count", publicUser.QueryUserAffiliateHandler(serverCtx))
|
publicUserGroupRouter.GET("/affiliate/count", publicUser.QueryUserAffiliateHandler(serverCtx))
|
||||||
|
|
||||||
// Query User Affiliate List
|
// Query User Affiliate List
|
||||||
publicUserGroupRouter.GET("/affiliate/list", publicUser.QueryUserAffiliateListHandler(serverCtx))
|
publicUserGroupRouter.GET("/affiliate/list", publicUser.QueryUserAffiliateListHandler(serverCtx))
|
||||||
|
|
||||||
// Query User Balance Log
|
// Delete Account
|
||||||
publicUserGroupRouter.GET("/balance_log", publicUser.QueryUserBalanceLogHandler(serverCtx))
|
publicUserGroupRouter.GET("/delete_account", publicUser.DeleteAccountHandler(serverCtx))
|
||||||
|
|
||||||
|
// Query User Balance Log
|
||||||
|
publicUserGroupRouter.GET("/balance_log", publicUser.QueryUserBalanceLogHandler(serverCtx))
|
||||||
|
|
||||||
// Update Bind Email
|
// Update Bind Email
|
||||||
publicUserGroupRouter.PUT("/bind_email", publicUser.UpdateBindEmailHandler(serverCtx))
|
publicUserGroupRouter.PUT("/bind_email", publicUser.UpdateBindEmailHandler(serverCtx))
|
||||||
|
|||||||
156
internal/logic/public/user/deleteAccountLogic.go
Normal file
156
internal/logic/public/user/deleteAccountLogic.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/model/user"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/constant"
|
||||||
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
"github.com/perfect-panel/server/pkg/uuidx"
|
||||||
|
"github.com/perfect-panel/server/pkg/xerr"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeleteAccountLogic struct {
|
||||||
|
logger.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDeleteAccountLogic 创建注销账号逻辑实例
|
||||||
|
func NewDeleteAccountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteAccountLogic {
|
||||||
|
return &DeleteAccountLogic{
|
||||||
|
Logger: logger.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAccount 注销账号逻辑
|
||||||
|
// 1. 获取当前用户信息
|
||||||
|
// 2. 删除所有关联数据(用户、认证方式、设备)
|
||||||
|
// 3. 根据原设备信息创建全新账号
|
||||||
|
// 4. 返回新账号信息
|
||||||
|
func (l *DeleteAccountLogic) DeleteAccount() (resp *types.DeleteAccountResponse, err error) {
|
||||||
|
// 获取当前用户
|
||||||
|
currentUser, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = &types.DeleteAccountResponse{}
|
||||||
|
var newUserId int64
|
||||||
|
|
||||||
|
// 开始数据库事务
|
||||||
|
err = l.svcCtx.UserModel.Transaction(l.ctx, func(tx *gorm.DB) error {
|
||||||
|
// 1. 查找用户的所有设备(用于后续创建新账号)
|
||||||
|
devices, _, err := l.svcCtx.UserModel.QueryDeviceList(l.ctx, currentUser.Id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "查询用户设备失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 删除用户的所有认证方式
|
||||||
|
err = tx.Model(&user.AuthMethods{}).Where("`user_id` = ?", currentUser.Id).Delete(&user.AuthMethods{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "删除用户认证方式失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 删除用户的所有设备
|
||||||
|
err = tx.Model(&user.Device{}).Where("`user_id` = ?", currentUser.Id).Delete(&user.Device{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "删除用户设备失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 删除用户的订阅信息
|
||||||
|
err = tx.Model(&user.Subscribe{}).Where("`user_id` = ?", currentUser.Id).Delete(&user.Subscribe{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "删除用户订阅信息失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 删除用户本身
|
||||||
|
err = tx.Model(&user.User{}).Where("`id` = ?", currentUser.Id).Delete(&user.User{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "删除用户失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 为每个原设备创建新的用户(使用同一事务)
|
||||||
|
for _, oldDevice := range devices {
|
||||||
|
userInfo, err := l.registerUserAndDevice(tx, oldDevice.Identifier)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "创建新用户失败")
|
||||||
|
}
|
||||||
|
newUserId = userInfo.Id // 保留最后一个新用户ID
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Success = true
|
||||||
|
resp.Message = "账户注销成功,已创建新账户"
|
||||||
|
resp.UserId = newUserId
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateReferCode 生成推荐码
|
||||||
|
func generateReferCode() string {
|
||||||
|
bytes := make([]byte, 4)
|
||||||
|
rand.Read(bytes)
|
||||||
|
return hex.EncodeToString(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *DeleteAccountLogic) registerUserAndDevice(tx *gorm.DB, identifier string) (*user.User, error) {
|
||||||
|
// 1. 创建新用户
|
||||||
|
userInfo := &user.User{
|
||||||
|
Salt: "default",
|
||||||
|
OnlyFirstPurchase: &l.svcCtx.Config.Invite.OnlyFirstPurchase,
|
||||||
|
}
|
||||||
|
if err := tx.Create(userInfo).Error; err != nil {
|
||||||
|
l.Errorw("failed to create user", logger.Field("error", err.Error()))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create user failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 更新推荐码
|
||||||
|
userInfo.ReferCode = uuidx.UserInviteCode(userInfo.Id)
|
||||||
|
if err := tx.Model(&user.User{}).Where("id = ?", userInfo.Id).Update("refer_code", userInfo.ReferCode).Error; err != nil {
|
||||||
|
l.Errorw("failed to update refer code", logger.Field("user_id", userInfo.Id), logger.Field("error", err.Error()))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update refer code failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 创建设备认证方式
|
||||||
|
authMethod := &user.AuthMethods{
|
||||||
|
UserId: userInfo.Id,
|
||||||
|
AuthType: "device",
|
||||||
|
AuthIdentifier: identifier,
|
||||||
|
Verified: true,
|
||||||
|
}
|
||||||
|
if err := tx.Create(authMethod).Error; err != nil {
|
||||||
|
l.Errorw("failed to create device auth method", logger.Field("user_id", userInfo.Id), logger.Field("identifier", identifier), logger.Field("error", err.Error()))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create device auth method failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 插入设备记录
|
||||||
|
deviceInfo := &user.Device{
|
||||||
|
Ip: "",
|
||||||
|
UserId: userInfo.Id,
|
||||||
|
UserAgent: "",
|
||||||
|
Identifier: identifier,
|
||||||
|
Enabled: true,
|
||||||
|
Online: false,
|
||||||
|
}
|
||||||
|
if err := tx.Create(deviceInfo).Error; err != nil {
|
||||||
|
l.Errorw("failed to insert device", logger.Field("user_id", userInfo.Id), logger.Field("identifier", identifier), logger.Field("error", err.Error()))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert device failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return userInfo, nil
|
||||||
|
}
|
||||||
@ -150,6 +150,10 @@ type BatchDeleteSubscribeGroupRequest struct {
|
|||||||
Ids []int64 `json:"ids" validate:"required"`
|
Ids []int64 `json:"ids" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeleteSubscribeRequest struct {
|
||||||
|
Id int64 `json:"id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type BatchDeleteSubscribeRequest struct {
|
type BatchDeleteSubscribeRequest struct {
|
||||||
Ids []int64 `json:"ids" validate:"required"`
|
Ids []int64 `json:"ids" validate:"required"`
|
||||||
}
|
}
|
||||||
@ -517,19 +521,21 @@ type DeleteSubscribeGroupRequest struct {
|
|||||||
Id int64 `json:"id" validate:"required"`
|
Id int64 `json:"id" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeleteSubscribeRequest struct {
|
type DeleteUserDeivceRequest struct {
|
||||||
Id int64 `json:"id" validate:"required"`
|
Id int64 `json:"id" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeleteAccountResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
UserId int64 `json:"user_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type DeleteUserAuthMethodRequest struct {
|
type DeleteUserAuthMethodRequest struct {
|
||||||
UserId int64 `json:"user_id"`
|
UserId int64 `json:"user_id"`
|
||||||
AuthType string `json:"auth_type"`
|
AuthType string `json:"auth_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeleteUserDeivceRequest struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteUserSubscribeRequest struct {
|
type DeleteUserSubscribeRequest struct {
|
||||||
UserSubscribeId int64 `json:"user_subscribe_id"`
|
UserSubscribeId int64 `json:"user_subscribe_id"`
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user