package user import ( "bytes" "context" "github.com/saas-mingyang/mingyang-admin-common/i18n" "mingyang-admin-app-api/internal/svc" "mingyang-admin-app-api/internal/types" "mingyang-admin-app-rpc/types/app" "net/http" "runtime" "strconv" "strings" "time" "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 在API层添加调用栈信息 func (l *LogoutLogic) Logout() (resp *types.BaseMsgResp, err error) { // 打印调用栈 buf := make([]byte, 1024) n := runtime.Stack(buf, false) l.Logger.Infof("Logout call stack:\n%s", buf[:n]) token := l.getTokenFromContext() l.Infof("Starting Logout RPC call at: %v", time.Now()) // 记录goroutine ID goID := getGoroutineID() l.Logger.Infof("Goroutine ID: %d", goID) _, err = l.svcCtx.AppRpc.LogoutUser(l.ctx, &app.UserToken{ AccessToken: token, UserId: l.getUserIDFromContext(), }) l.Logger.Infof("Finished Logout RPC call at: %v", time.Now()) if err != nil { return nil, err } return &types.BaseMsgResp{Msg: l.svcCtx.Trans.Trans(l.ctx, i18n.Success)}, nil } // 获取goroutine ID func getGoroutineID() uint64 { b := make([]byte, 64) b = b[:runtime.Stack(b, false)] b = bytes.TrimPrefix(b, []byte("goroutine ")) b = b[:bytes.IndexByte(b, ' ')] n, _ := strconv.ParseUint(string(b), 10, 64) return n } // 从上下文中获取 Token func (l *LogoutLogic) getTokenFromContext() string { // 尝试不同的 key 从上下文中获取 Token keys := []string{"token", "access_token", "jwt_token", "auth_token"} for _, key := range keys { if token, ok := l.ctx.Value(key).(string); ok && token != "" { return l.cleanToken(token) } } return "" } // 清理 Token 字符串,移除 "Bearer " 前缀 func (l *LogoutLogic) cleanToken(token string) string { if token == "" { return "" } token = strings.TrimSpace(token) // 移除 "Bearer " 前缀 if strings.HasPrefix(strings.ToLower(token), "bearer ") { token = token[7:] } return token } // 从 Authorization Header 中提取 Token func (l *LogoutLogic) extractTokenFromAuthHeader(ctx context.Context) string { // 尝试从请求上下文获取 Request 对象 if req, ok := ctx.Value("http_request").(*http.Request); ok { authHeader := req.Header.Get("Authorization") if authHeader != "" { return l.cleanToken(authHeader) } } return "" } // 从上下文中获取用户ID func (l *LogoutLogic) getUserIDFromContext() uint64 { // 在AuthMiddleware中,我们将用户ID放入了context if userID, ok := l.ctx.Value("userId").(uint64); ok { return userID } return 0 } // 获取 Token 过期时间 func (l *LogoutLogic) getTokenExpireTime(token string) (time.Time, error) { // 使用 JWTManager 验证并获取 Claims claims, err := l.svcCtx.AppRpc.AuthToken(l.ctx, &app.AuthReq{Token: token}) if err != nil { return time.Time{}, err } return claims.Claims.ExpiresAt.Timestamp.AsTime(), nil }