// Code scaffolded by goctl. Safe to edit. // goctl 1.10.0 package auth import ( "context" "errors" "fmt" "perms-system-server/internal/middleware" userModel "perms-system-server/internal/model/user" "perms-system-server/internal/response" "perms-system-server/internal/svc" "github.com/zeromicro/go-zero/core/limit" "github.com/zeromicro/go-zero/core/logx" ) type LogoutLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogic { return &LogoutLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } // Logout 用户注销。递增当前用户的 tokenVersion 使所有已签发的 access/refresh 令牌立即失效,并清除用户缓存。 func (l *LogoutLogic) Logout() error { // 审计 M-R11-2:从 middleware 的 UserDetails 直接取 userId/username, // 不再到 IncrementTokenVersion 内部再 FindOne 一次仅为构造缓存键。 ud := middleware.GetUserDetails(l.ctx) if ud == nil || ud.UserId == 0 { return response.ErrUnauthorized("未登录") } userId := ud.UserId if l.svcCtx.TokenOpLimiter != nil { code, _ := l.svcCtx.TokenOpLimiter.Take(fmt.Sprintf("logout:%d", userId)) if code == limit.OverQuota { return response.ErrTooManyRequests("操作过于频繁,请稍后再试") } } if _, err := l.svcCtx.SysUserModel.IncrementTokenVersion(l.ctx, userId, ud.Username); err != nil { // 审计 L-R10-3:IncrementTokenVersion 在目标用户已被并发删除时会返 ErrUpdateConflict。 // Logout 的语义目标本就是"让该账号的旧令牌立即失效",用户已经消失等同语义已达成, // 按幂等成功处理并继续清缓存,不要让一次正常的注销因为极罕见的删号竞态回 500。 if !errors.Is(err, userModel.ErrUpdateConflict) { return err } logx.WithContext(l.ctx).Infof("logout on already-deleted user userId=%d, treated as idempotent success", userId) } l.svcCtx.UserDetailsLoader.Clean(l.ctx, userId) return nil }