Files
bdrp-server/app/main/api/internal/logic/user/canceloutlogic.go
2026-04-20 11:34:35 +08:00

158 lines
5.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package user
import (
"context"
"database/sql"
"errors"
"fmt"
"os"
"time"
"bdrp-server/app/main/api/internal/svc"
"bdrp-server/app/main/api/internal/types"
"bdrp-server/app/main/model"
"bdrp-server/common/ctxdata"
"bdrp-server/common/xerr"
"bdrp-server/pkg/lzkit/crypto"
pkgerrors "github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type CancelOutLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewCancelOutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CancelOutLogic {
return &CancelOutLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// cancelledMobilePlain 生成唯一占位明文手机号11 位),用于释放真实手机号唯一约束
func cancelledMobilePlain(userID int64) string {
return fmt.Sprintf("199%09d", userID%1000000000)
}
func (l *CancelOutLogic) CancelOut(req *types.CancelOutReq) error {
userID, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return pkgerrors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
}
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
return pkgerrors.Wrapf(xerr.NewErrCode(xerr.USER_NOT_FOUND), "用户不存在")
}
return pkgerrors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败: %v", err)
}
if user.Disable == 1 {
return pkgerrors.Wrapf(xerr.NewErrCode(xerr.USER_DISABLED), "账号已被封禁")
}
if user.CancelledAt.Valid {
return pkgerrors.Wrapf(xerr.NewErrCode(xerr.USER_CANCELLED), "账号已注销")
}
if !user.Mobile.Valid || user.Mobile.String == "" {
return pkgerrors.Wrapf(xerr.NewErrMsg("请先绑定手机号后再注销账号"), "注销账号, 未绑定手机号 userId=%d", userID)
}
secretKey := l.svcCtx.Config.Encrypt.SecretKey
encryptedMobile := user.Mobile.String
if os.Getenv("ENV") != "development" {
codeKey := fmt.Sprintf("%s:%s", "cancelAccount", encryptedMobile)
cacheCode, err := l.svcCtx.Redis.Get(codeKey)
if err != nil {
if errors.Is(err, redis.Nil) {
return pkgerrors.Wrapf(xerr.NewErrMsg("验证码已过期"), "注销账号, 验证码过期")
}
return pkgerrors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "注销账号, 读取验证码失败: %v", err)
}
if cacheCode != req.Code {
return pkgerrors.Wrapf(xerr.NewErrMsg("验证码不正确"), "注销账号, 验证码不正确")
}
}
placeholderPlain := cancelledMobilePlain(userID)
placeholderEnc, err := crypto.EncryptMobile(placeholderPlain, secretKey)
if err != nil {
return pkgerrors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "注销账号, 生成占位手机号失败: %v", err)
}
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return pkgerrors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
}
err = l.svcCtx.UserModel.Trans(l.ctx, func(tranCtx context.Context, session sqlx.Session) error {
dbUser, err := l.svcCtx.UserModel.FindOne(tranCtx, userID)
if err != nil {
return err
}
if dbUser.CancelledAt.Valid {
return xerr.NewErrCode(xerr.USER_CANCELLED)
}
dbUser.CancelledAt = sql.NullTime{Time: time.Now(), Valid: true}
dbUser.Mobile = sql.NullString{String: placeholderEnc, Valid: true}
dbUser.Nickname = sql.NullString{String: "已注销用户", Valid: true}
dbUser.Password = sql.NullString{Valid: false}
dbUser.Info = ""
if err := l.svcCtx.UserModel.UpdateWithVersion(tranCtx, session, dbUser); err != nil {
return pkgerrors.Wrapf(err, "更新用户注销状态失败")
}
authBuilder := l.svcCtx.UserAuthModel.SelectBuilder().Where("user_id = ?", userID)
userAuths, err := l.svcCtx.UserAuthModel.FindAll(tranCtx, authBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return pkgerrors.Wrapf(err, "查询用户授权失败")
}
for _, ua := range userAuths {
if err := l.svcCtx.UserAuthModel.Delete(tranCtx, session, ua.Id); err != nil {
return pkgerrors.Wrapf(err, "删除用户授权失败 id=%d", ua.Id)
}
}
queryBuilder := l.svcCtx.QueryModel.SelectBuilder().Where("user_id = ?", userID)
queries, err := l.svcCtx.QueryModel.FindAll(tranCtx, queryBuilder, "")
if err != nil && !errors.Is(err, model.ErrNotFound) {
return pkgerrors.Wrapf(err, "查询用户查询记录失败")
}
for _, q := range queries {
if err := l.svcCtx.QueryModel.Delete(tranCtx, session, q.Id); err != nil {
return pkgerrors.Wrapf(err, "删除查询记录失败 id=%d", q.Id)
}
}
if agentModel != nil {
ag, err := l.svcCtx.AgentModel.FindOne(tranCtx, agentModel.Id)
if err != nil {
return err
}
ag.Mobile = placeholderEnc
ag.WechatId = sql.NullString{Valid: false}
if err := l.svcCtx.AgentModel.UpdateWithVersion(tranCtx, session, ag); err != nil {
return pkgerrors.Wrapf(err, "更新代理占位信息失败")
}
}
return nil
})
if err != nil {
var ce *xerr.CodeError
if errors.As(err, &ce) {
return ce
}
return pkgerrors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户注销失败: %v", err)
}
return nil
}