feat(app): 添加用户退出登录功能

- 在 app.go 中新增 LogoutUser 方法及对应请求响应结构体
- 更新 app.json API 文档,移除旧 /logout 接口,新增 /user/logout 接口
- 在 app.proto 中定义 LogoutUserRequest 和 UserToken 消息类型
- 生成新的 app.pb.go 和 app_grpc.pb.go 文件以支持新接口
- 配置文件 app.yaml 中增加 RPC 调用的超时和重试设置
- 实现 gRPC 客户端和服务端对 LogoutUser 接口的支持
This commit is contained in:
huanglei19951029 2025-12-15 19:44:12 +08:00
parent 65ff8ddb62
commit c3406ce964
19 changed files with 1173 additions and 398 deletions

View File

@ -92,34 +92,6 @@
} }
} }
}, },
"/logout": {
"post": {
"description": "登出",
"tags": [
"user"
],
"summary": "登出",
"operationId": "Logout",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"$ref": "#/definitions/IDReq"
}
}
],
"responses": {
"200": {
"description": "BaseDataInfo",
"schema": {
"$ref": "#/definitions/BaseDataInfo"
}
}
}
}
},
"/register": { "/register": {
"post": { "post": {
"description": "Register | 注册", "description": "Register | 注册",
@ -206,11 +178,10 @@
}, },
"/user/info": { "/user/info": {
"post": { "post": {
"description": "@ Get UserInfo detail By Token | 获取用户信息", "description": "Get UserInfo detail By Token | 获取用户信息",
"tags": [ "tags": [
"user" "user"
], ],
"summary": "@ Get UserInfo detail By Token | 获取用户信息",
"operationId": "GetUserInfoByToken", "operationId": "GetUserInfoByToken",
"responses": { "responses": {
"200": { "200": {
@ -250,6 +221,24 @@
} }
} }
}, },
"/user/logout": {
"post": {
"description": "Logout | 退出登录 不传参数,默认使用请求头的token",
"tags": [
"user"
],
"summary": "Logout | 退出登录 不传参数,默认使用请求头的token",
"operationId": "Logout",
"responses": {
"200": {
"description": "BaseMsgResp",
"schema": {
"$ref": "#/definitions/BaseMsgResp"
}
}
}
}
},
"/user/oauthAuthorize": { "/user/oauthAuthorize": {
"post": { "post": {
"description": "OauthAuthorize | 第三方登录接口", "description": "OauthAuthorize | 第三方登录接口",

View File

@ -22,7 +22,6 @@ type (
CaptchaCode string `json:"captchaCode,optional"` CaptchaCode string `json:"captchaCode,optional"`
// 过期时间 // 过期时间
ExpireTime uint32 `json:"expireTime,optional"` ExpireTime uint32 `json:"expireTime,optional"`
} }
RegisterReq { RegisterReq {
// 账户注册类型:手机或邮箱 // 账户注册类型:手机或邮箱
@ -31,89 +30,83 @@ type (
Value string `json:"value,optional"` Value string `json:"value,optional"`
// 昵称 // 昵称
NickName string `json:"nickName,optional"` NickName string `json:"nickName,optional"`
// 性别 // 性别
Sex uint8 `json:"sex,optional"` Sex uint8 `json:"sex,optional"`
//生日 //生日
Birthday uint32 `json:"birthday,optional"` Birthday uint32 `json:"birthday,optional"`
// 密码加密后的值 // 密码加密后的值
PasswordHash *string `json:"passwordHash,optional"` PasswordHash *string `json:"passwordHash,optional"`
//验证码 //验证码
Captcha string `json:"captcha,optional"` Captcha string `json:"captcha,optional"`
// 验证码ID // 验证码ID
CaptchaId string `json:"captchaId,optional"` CaptchaId string `json:"captchaId,optional"`
//VerificationType 类型 1 手机 2 邮箱 //VerificationType 类型 1 手机 2 邮箱
VerificationType *uint32 `json:"verificationType,optional"` VerificationType *uint32 `json:"verificationType,optional"`
// Gender male or female or other , 男,女,其他 // Gender male or female or other , 男,女,其他
Gender *string `json:"gender,optional"` Gender *string `json:"gender,optional"`
// 注册来源 app,pc // 注册来源 app,pc
RegisterSource *string `json:"registerSource,optional"` RegisterSource *string `json:"registerSource,optional"`
} }
RegisterResp {
// token信息
RegisterResp{ AuthToken *AuthToken `json:"authToken,optional"`
// token信息 User *UserInfo `json:"userInfo,optional"`
AuthToken *AuthToken `json:"authToken,optional"`
User *UserInfo `json:"userInfo,optional"`
} }
AuthToken {
AuthToken{ // access_token
// access_token AccessToken string `json:"accessToken,optional"`
AccessToken string `json:"accessToken,optional"` // refresh_token
// refresh_token RefreshToken string `json:"refreshToken,optional"`
RefreshToken string `json:"refreshToken,optional"` // token_type 类型
// token_type 类型 TokenType string `json:"tokenType,optional"`
TokenType string `json:"tokenType,optional"` //access_token_expires
//access_token_expires AccessTokenExpires int64 `json:"accessTokenExpires,optional"`
AccessTokenExpires int64 `json:"accessTokenExpires,optional"` //refresh_token_expires
//refresh_token_expires RefreshTokenExpires int64 `json:"refreshTokenExpires,optional"`
RefreshTokenExpires int64 `json:"refreshTokenExpires,optional"`
} }
UserInfo {
UserInfo{
BaseIDInfo
// 昵称
NickName string `json:"nickName,optional"`
//生日
Birthday *int64 `json:"birthday,optional"`
// 头像
Avatar *string `json:"avatar,optional"`
// Gender male or female or other , 男,女,其他
Gender *string `json:"gender,optional"`
// 账户注册类型:
// 1 手机 2 邮箱
RegisterType *uint32 `json:"registerType,optional"`
// 手机号
Mobile *string `json:"mobile,optional"`
// 用户名
Username *string `json:"username,optional"`
//邮箱
Email *string `json:"email,optional"`
}
LoginReq{
// UserName or Email or Mobile
UserName string `json:"userName,optional"`
// Password
Password string `json:"password"`
// ClientIP
ClientIP string `json:"clientIp"`
// 登录方式
LoginType string `json:"loginType"`
// 登录系统
LoginPlatform string `json:"loginPlatform"`
}
LoginResp{
BaseMsgResp
AuthToken *AuthToken
UserInfo *UserInfo
}
UserInfoResp{
BaseIDInfo BaseIDInfo
// UserInfo information | UserInfo信息数据 // 昵称
Data UserInfo `json:"data"` NickName string `json:"nickName,optional"`
//生日
Birthday *int64 `json:"birthday,optional"`
// 头像
Avatar *string `json:"avatar,optional"`
// Gender male or female or other , 男,女,其他
Gender *string `json:"gender,optional"`
// 账户注册类型:
// 1 手机 2 邮箱
RegisterType *uint32 `json:"registerType,optional"`
// 手机号
Mobile *string `json:"mobile,optional"`
// 用户名
Username *string `json:"username,optional"`
//邮箱
Email *string `json:"email,optional"`
} }
LoginReq {
UserListReq{ // UserName or Email or Mobile
UserName string `json:"userName,optional"`
// Password
Password string `json:"password"`
// ClientIP
ClientIP string `json:"clientIp"`
// 登录方式
LoginType string `json:"loginType"`
// 登录系统
LoginPlatform string `json:"loginPlatform"`
}
LoginResp {
BaseMsgResp
AuthToken *AuthToken
UserInfo *UserInfo
}
UserInfoResp {
BaseIDInfo
// UserInfo information | UserInfo信息数据
Data UserInfo `json:"data"`
}
UserListReq {
// Page // Page
PageInfo PageInfo
// UserName or Email or Mobile // UserName or Email or Mobile
@ -125,89 +118,88 @@ type (
// Status 1: normal 2: ban | 状态 1 正常 2 禁用 // Status 1: normal 2: ban | 状态 1 正常 2 禁用
Status *string `json:"status,optional"` Status *string `json:"status,optional"`
} }
// The response data of user list | User信息列表数据
// The response data of user list | User信息列表数据
UserListResp { UserListResp {
BaseDataInfo BaseDataInfo
// The device list data | USer信息列表数据 // The device list data | USer信息列表数据
Data UserListInfo `json:"data"` Data UserListInfo `json:"data"`
} }
// The device list data | User信息列表数据 // The device list data | User信息列表数据
UserListInfo { UserListInfo {
BaseListInfo BaseListInfo
// The device list data | User信息列表数据 // The device list data | User信息列表数据
Data []UserInfo `json:"data"` Data []UserInfo `json:"data"`
} }
OauthAuthorizeReq { OauthAuthorizeReq {}
OauthAuthorizeResp {
BaseMsgResp
}
PassWordResetReq {
// UserName or Email or Mobile
UserName string `json:"userName,optional"`
// Password
Password string `json:"password"`
}
}
OauthAuthorizeResp {
BaseMsgResp
}
PassWordResetReq {
// UserName or Email or Mobile
UserName string `json:"userName,optional"`
// Password
Password string `json:"password"`
}
) )
@server ( @server (
group: user_public group: user_public
) )
service App { service App {
// GetVerifyCode | 获取验证码 // GetVerifyCode | 获取验证码
@handler getVerifyCode @handler getVerifyCode
post /get/verifyCode (VerifyCodeReq) returns (VerifyCodeResp) post /get/verifyCode (VerifyCodeReq) returns (VerifyCodeResp)
// Register | 注册 // Register | 注册
@handler register @handler register
post /register (RegisterReq) returns (RegisterResp) post /register (RegisterReq) returns (RegisterResp)
// Login | 登录
@handler login
post /login (LoginReq) returns (LoginResp)
}
// Login | 登录
@handler login
post /login (LoginReq) returns (LoginResp)
}
@server ( @server (
group: user group: user
middleware: Authority middleware: Authority
) )
service App { service App {
// Get userInfo detail by ID | 获取用户信息 // Get userInfo detail by ID | 获取用户信息
@handler getUserInfoById @handler getUserInfoById
post /user (IDReq) returns (UserInfo) post /user (IDReq) returns (UserInfo)
// Get UserInfo detail By Token | 获取用户信息 // Get UserInfo detail By Token | 获取用户信息
@handler getUserInfoByToken @handler getUserInfoByToken
post /user/info () returns (UserInfo) post /user/info returns (UserInfo)
//List userInfo | 获取用户列表 //List userInfo | 获取用户列表
@handler listUserInfo @handler listUserInfo
post /user/list (UserListReq) returns (UserListResp) post /user/list (UserListReq) returns (UserListResp)
//Update userInfo | 更新用户信息 //Update userInfo | 更新用户信息
@handler updateUserInfo @handler updateUserInfo
post /user/update (UserInfo) returns (BaseDataInfo) post /user/update (UserInfo) returns (BaseMsgResp)
// Logout | 退出登录 // Logout | 退出登录 不传参数,默认使用请求头的token
@handler logout @handler logout
post /user/logout (IDReq) returns (BaseDataInfo) post /user/logout () returns (BaseMsgResp)
// PassWordReset | 修改密码 // PassWordReset | 修改密码
@handler passWordReset @handler passWordReset
post /user/passWordReset (PassWordResetReq) returns (BaseDataInfo) post /user/passWordReset (PassWordResetReq) returns (BaseDataInfo)
// CheckLogin | 检测登录状态 // CheckLogin | 检测登录状态
@handler checkLogin @handler checkLogin
post /user/checkLogin (IDReq) returns (BaseDataInfo) post /user/checkLogin (IDReq) returns (BaseDataInfo)
//OauthAuthorize | 第三方登录接口 //OauthAuthorize | 第三方登录接口
@handler oauthAuthorize @handler oauthAuthorize
post /user/oauthAuthorize (OauthAuthorizeReq) returns (OauthAuthorizeResp) post /user/oauthAuthorize (OauthAuthorizeReq) returns (OauthAuthorizeResp)
} }

View File

@ -33,4 +33,7 @@ AppRpc:
Hosts: Hosts:
- 192.168.201.58:2379 # 共享etcd地址 - 192.168.201.58:2379 # 共享etcd地址
Key: app.rpc Key: app.rpc
Enabled: true Enabled: true
NonBlock: true
Timeout: 3000
RetryCount: 1 # 重试次数如果为1则最多调用2次包括第一次

View File

@ -11,9 +11,8 @@ import (
// swagger:route post /user/info user GetUserInfoByToken // swagger:route post /user/info user GetUserInfoByToken
// //
//@ Get UserInfo detail By Token | 获取用户信息
// //
//@ Get UserInfo detail By Token | 获取用户信息 // Get UserInfo detail By Token | 获取用户信息
// //
// Responses: // Responses:
// 200: UserInfo // 200: UserInfo

View File

@ -7,34 +7,21 @@ import (
"mingyang-admin-app-api/internal/logic/user" "mingyang-admin-app-api/internal/logic/user"
"mingyang-admin-app-api/internal/svc" "mingyang-admin-app-api/internal/svc"
"mingyang-admin-app-api/internal/types"
) )
// swagger:route post /logout user Logout // swagger:route post /user/logout user Logout
// //
//登出 // Logout | 退出登录 不传参数,默认使用请求头的token
// //
//登出 // Logout | 退出登录 不传参数,默认使用请求头的token
//
// Parameters:
// + name: body
// require: true
// in: body
// type: IDReq
// //
// Responses: // Responses:
// 200: BaseDataInfo // 200: BaseMsgResp
func LogoutHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { func LogoutHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var req types.IDReq
if err := httpx.Parse(r, &req, true); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := user.NewLogoutLogic(r.Context(), svcCtx) l := user.NewLogoutLogic(r.Context(), svcCtx)
resp, err := l.Logout(&req) resp, err := l.Logout()
if err != nil { if err != nil {
err = svcCtx.Trans.TransError(r.Context(), err) err = svcCtx.Trans.TransError(r.Context(), err)
httpx.ErrorCtx(r.Context(), w, err) httpx.ErrorCtx(r.Context(), w, err)

View File

@ -1,10 +1,17 @@
package user package user
import ( import (
"bytes"
"context" "context"
"github.com/saas-mingyang/mingyang-admin-common/i18n"
"mingyang-admin-app-api/internal/svc" "mingyang-admin-app-api/internal/svc"
"mingyang-admin-app-api/internal/types" "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" "github.com/zeromicro/go-zero/core/logx"
) )
@ -23,8 +30,96 @@ func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogi
} }
} }
func (l *LogoutLogic) Logout(req *types.IDReq) (resp *types.BaseDataInfo, err error) { // Logout 在API层添加调用栈信息
// todo: add your logic here and delete this line 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])
return 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
} }

View File

@ -7,6 +7,9 @@ import (
"fmt" "fmt"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"github.com/saas-mingyang/mingyang-admin-common/config"
jwt2 "github.com/saas-mingyang/mingyang-admin-common/utils/jwt"
"github.com/zeromicro/go-zero/rest/enum"
"mingyang-admin-app-api/internal/types" "mingyang-admin-app-api/internal/types"
"mingyang-admin-app-rpc/appclient" "mingyang-admin-app-rpc/appclient"
"mingyang-admin-app-rpc/types/app" "mingyang-admin-app-rpc/types/app"
@ -16,12 +19,6 @@ import (
) )
// 定义上下文键类型 // 定义上下文键类型
type contextKey string
const (
UserIDKey contextKey = "user_id"
UserInfoKey contextKey = "user_info"
)
type AuthorityMiddleware struct { type AuthorityMiddleware struct {
Rds redis.UniversalClient Rds redis.UniversalClient
@ -61,36 +58,21 @@ func (m *AuthorityMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
writeError(w, 401, "Authorization header is required") writeError(w, 401, "Authorization header is required")
return return
} }
fromToken := jwt2.StripBearerPrefixFromToken(r.Header.Get("Authorization"))
// 2. 验证 Bearer Token 格式
if !strings.HasPrefix(authHeader, "Bearer ") {
writeError(w, 401, "Invalid authorization format, must be 'Bearer <token>'")
return
}
// 3. 提取 Token
tokenString := strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer"))
if tokenString == "" {
writeError(w, 401, "Token cannot be empty")
return
}
// 4. 检查 Redis 缓存(可选)
if m.Rds != nil { if m.Rds != nil {
cacheKey := fmt.Sprintf("token:%s", tokenString) cacheKey := config.RedisTokenPrefix + fromToken
fmt.Printf("cacheKey: %s\n", cacheKey)
cachedUserID, err := m.Rds.Get(r.Context(), cacheKey).Result() cachedUserID, err := m.Rds.Get(r.Context(), cacheKey).Result()
if err == nil && cachedUserID != "" { if err == nil && cachedUserID != "" {
// 从缓存中获取到用户ID // 从缓存中获取到用户ID
ctx := context.WithValue(r.Context(), UserIDKey, cachedUserID) ctx := context.WithValue(r.Context(), enum.UserIdRpcCtxKey, cachedUserID)
r = r.WithContext(ctx) r = r.WithContext(ctx)
next(w, r) next(w, r)
return return
} }
} }
// 5. 调用 RPC 验证 Token // 5. 调用 RPC 验证 Token
fmt.Printf("Validating token: %s\n", tokenString) token, err := m.AppRpc.AuthToken(r.Context(), &app.AuthReq{Token: fromToken})
token, err := m.AppRpc.AuthToken(r.Context(), &app.AuthReq{Token: tokenString})
if err != nil { if err != nil {
fmt.Printf("Error validating token: %v\n", err) fmt.Printf("Error validating token: %v\n", err)
// 根据错误类型返回不同的错误信息 // 根据错误类型返回不同的错误信息
@ -120,15 +102,30 @@ func (m *AuthorityMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
// 7. 缓存到 Redis可选 // 7. 缓存到 Redis可选
if m.Rds != nil { if m.Rds != nil {
cacheKey := fmt.Sprintf("token:%s", tokenString) cacheKey := fmt.Sprintf("token:%s", fromToken)
// 设置缓存过期时间30分钟 // 设置缓存过期时间30分钟
m.Rds.Set(r.Context(), cacheKey, id, 30*time.Minute) m.Rds.Set(r.Context(), cacheKey, id, 30*time.Minute)
} }
// 8. 设置到上下文 // 创建新的上下文,包含 Token 和用户信息
ctx := r.Context() ctx := r.Context()
ctx = context.WithValue(ctx, UserIDKey, id) ctx = context.WithValue(ctx, "token", fromToken)
ctx = context.WithValue(ctx, UserInfoKey, token) ctx = context.WithValue(ctx, "userId", token.UserId)
ctx = context.WithValue(ctx, "tokenClaims", token)
// 获取客户端 IP
clientIP := getClientIP(r)
ctx = context.WithValue(ctx, "client_ip", clientIP)
// 获取 User-Agent
userAgent := r.UserAgent()
ctx = context.WithValue(ctx, "user_agent", userAgent)
// 将新上下文设置到请求中
r = r.WithContext(ctx)
// 调用下一个处理器
next(w, r)
// 9. 记录请求日志(可选) // 9. 记录请求日志(可选)
fmt.Printf("[%s] %s - UserID: %d - Duration: %v\n", fmt.Printf("[%s] %s - UserID: %d - Duration: %v\n",
@ -136,9 +133,25 @@ func (m *AuthorityMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
r.URL.Path, r.URL.Path,
id, id,
time.Since(startTime)) time.Since(startTime))
// 10. 继续处理请求
r = r.WithContext(ctx)
next(w, r)
} }
} }
// 获取客户端 IP
func getClientIP(r *http.Request) string {
// 尝试从 X-Forwarded-For 获取
if forwarded := r.Header.Get("X-Forwarded-For"); forwarded != "" {
// 可能有多个 IP取第一个
ips := strings.Split(forwarded, ",")
if len(ips) > 0 {
return strings.TrimSpace(ips[0])
}
}
// 从 RemoteAddr 获取
ip := r.RemoteAddr
if colonIndex := strings.LastIndex(ip, ":"); colonIndex != -1 {
ip = ip[:colonIndex]
}
return ip
}

View File

@ -95,6 +95,10 @@ message LoginResponse {
AuthToken auth_token = 2; AuthToken auth_token = 2;
} }
message LogoutUserRequest {
string access_token = 1;
}
// NumericDate 使 timestamp JWT // NumericDate 使 timestamp JWT
message NumericDate { message NumericDate {
google.protobuf.Timestamp timestamp = 1; google.protobuf.Timestamp timestamp = 1;
@ -191,6 +195,11 @@ message UserInfo {
optional int64 updated_at = 24; optional int64 updated_at = 24;
} }
message UserToken {
string access_token = 1;
uint64 user_id = 2;
}
message VerifyCodeReq { message VerifyCodeReq {
VerifyCodeType type = 1; VerifyCodeType type = 1;
AccountType account_type = 2; AccountType account_type = 2;
@ -219,6 +228,9 @@ service App {
// //
// group: user // group: user
rpc ListUsers(PageUserRequest) returns (PageUserResponse); rpc ListUsers(PageUserRequest) returns (PageUserResponse);
// 退
// group: user
rpc LogoutUser(UserToken) returns (LogoutUserRequest);
// group: base // group: base
rpc InitDatabase(Empty) returns (BaseResp); rpc InitDatabase(Empty) returns (BaseResp);
} }

View File

@ -26,6 +26,7 @@ type (
IDsReq = app.IDsReq IDsReq = app.IDsReq
LoginRequest = app.LoginRequest LoginRequest = app.LoginRequest
LoginResponse = app.LoginResponse LoginResponse = app.LoginResponse
LogoutUserRequest = app.LogoutUserRequest
NumericDate = app.NumericDate NumericDate = app.NumericDate
PageInfoReq = app.PageInfoReq PageInfoReq = app.PageInfoReq
PageUserRequest = app.PageUserRequest PageUserRequest = app.PageUserRequest
@ -36,6 +37,7 @@ type (
UUIDReq = app.UUIDReq UUIDReq = app.UUIDReq
UUIDsReq = app.UUIDsReq UUIDsReq = app.UUIDsReq
UserInfo = app.UserInfo UserInfo = app.UserInfo
UserToken = app.UserToken
VerifyCodeReq = app.VerifyCodeReq VerifyCodeReq = app.VerifyCodeReq
VerifyCodeResp = app.VerifyCodeResp VerifyCodeResp = app.VerifyCodeResp
@ -50,6 +52,8 @@ type (
LoginUser(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) LoginUser(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error)
// 分页查询用户信息 // 分页查询用户信息
ListUsers(ctx context.Context, in *PageUserRequest, opts ...grpc.CallOption) (*PageUserResponse, error) ListUsers(ctx context.Context, in *PageUserRequest, opts ...grpc.CallOption) (*PageUserResponse, error)
// 用户退出登录
LogoutUser(ctx context.Context, in *UserToken, opts ...grpc.CallOption) (*LogoutUserRequest, error)
InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error) InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error)
} }
@ -94,6 +98,12 @@ func (m *defaultApp) ListUsers(ctx context.Context, in *PageUserRequest, opts ..
return client.ListUsers(ctx, in, opts...) return client.ListUsers(ctx, in, opts...)
} }
// 用户退出登录
func (m *defaultApp) LogoutUser(ctx context.Context, in *UserToken, opts ...grpc.CallOption) (*LogoutUserRequest, error) {
client := app.NewAppClient(m.cli.Conn())
return client.LogoutUser(ctx, in, opts...)
}
func (m *defaultApp) InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error) { func (m *defaultApp) InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error) {
client := app.NewAppClient(m.cli.Conn()) client := app.NewAppClient(m.cli.Conn())
return client.InitDatabase(ctx, in, opts...) return client.InitDatabase(ctx, in, opts...)

View File

@ -84,8 +84,13 @@ message PageUserResponse {
uint64 total = 1; uint64 total = 1;
repeated UserInfo data = 2; repeated UserInfo data = 2;
} }
message UserToken {
string access_token = 1;
uint64 user_id = 2;
}
message LogoutUserRequest {
string access_token = 1;
}
// App // App
@ -99,4 +104,7 @@ service App {
// //
// group: user // group: user
rpc ListUsers(PageUserRequest) returns (PageUserResponse); rpc ListUsers(PageUserRequest) returns (PageUserResponse);
// 退
// group: user
rpc LogoutUser(UserToken) returns (LogoutUserRequest);
} }

View File

@ -1,111 +1,100 @@
package jwt_manager package jwt_manager
import ( import (
"context"
"crypto/md5"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"github.com/golang-jwt/jwt/v5"
"mingyang-admin-app-rpc/internal/config" "mingyang-admin-app-rpc/internal/config"
"time" "time"
"github.com/golang-jwt/jwt/v5"
"github.com/redis/go-redis/v9"
) )
type JWTManager struct { // TokenType 令牌类型
accessTokenSecret []byte type TokenType string
refreshTokenSecret []byte
accessTokenExpiry time.Duration
refreshTokenExpiry time.Duration
issuer string
}
const (
AccessToken TokenType = "access"
RefreshToken TokenType = "refresh"
)
// JWTConfig JWT 配置
// Claims JWT Claims 结构体
type Claims struct { type Claims struct {
UserID uint64 `json:"user_id"` UserID uint64 `json:"user_id"`
Type string `json:"type"` // "access" or "refresh" Type TokenType `json:"type"`
jwt.RegisteredClaims jwt.RegisteredClaims
} }
func NewJWTManager(config *config.JWTConfig) *JWTManager { // TokenPair 令牌对
type TokenPair struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
AccessTokenExpiresAt time.Time `json:"access_token_expires_at"`
RefreshTokenExpiresAt time.Time `json:"refresh_token_expires_at"`
TokenType string `json:"token_type"`
}
// JWTManager JWT 管理器
type JWTManager struct {
accessSecret []byte
refreshSecret []byte
accessExpiry time.Duration
refreshExpiry time.Duration
issuer string
redis redis.UniversalClient
prefix string // Redis 键前缀
ctx context.Context
}
// NewJWTManager 创建新的 JWT 管理器
func NewJWTManager(config *config.JWTConfig, redisClient redis.UniversalClient) *JWTManager {
if config.AccessTokenSecret == "" || config.RefreshTokenSecret == "" {
panic("JWT 配置错误")
}
return &JWTManager{ return &JWTManager{
accessTokenSecret: []byte(config.AccessTokenSecret), accessSecret: []byte(config.AccessTokenSecret),
refreshTokenSecret: []byte(config.RefreshTokenSecret), refreshSecret: []byte(config.RefreshTokenSecret),
accessTokenExpiry: config.AccessTokenExpiry, accessExpiry: config.AccessTokenExpiry,
refreshTokenExpiry: config.RefreshTokenExpiry, refreshExpiry: config.RefreshTokenExpiry,
issuer: config.Issuer, issuer: config.Issuer,
redis: redisClient,
prefix: "jwt:",
ctx: context.Background(),
} }
} }
// GenerateAccessToken 生成访问令牌 // WithContext 设置上下文
func (m *JWTManager) GenerateAccessToken(userID uint64) (string, *Claims, error) { func (m *JWTManager) WithContext(ctx context.Context) *JWTManager {
return m.generateToken(userID, "access", m.accessTokenExpiry, m.accessTokenSecret) m.ctx = ctx
return m
} }
// GenerateRefreshToken 生成刷新令牌 // ==================== Token 生成方法 ====================
func (m *JWTManager) GenerateRefreshToken(userID uint64) (string, *Claims, error) {
return m.generateToken(userID, "refresh", m.refreshTokenExpiry, m.refreshTokenSecret)
}
func (m *JWTManager) generateToken(userID uint64, tokenType string, expiry time.Duration, secret []byte) (string, *Claims, error) { // GenerateTokenPair 生成访问令牌和刷新令牌对
fmt.Printf("userID: %d, tokenType: %s, expiry: %s, secret: %s\n", userID, tokenType, expiry, secret)
claims := &Claims{
UserID: userID,
Type: tokenType,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(expiry)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: m.issuer,
Subject: fmt.Sprint(userID),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(secret)
if err != nil {
return "", nil, err
}
return tokenString, claims, nil
}
// VerifyAccessToken 验证访问令牌
func (m *JWTManager) VerifyAccessToken(tokenString string) (*Claims, error) {
return m.verifyToken(tokenString, m.accessTokenSecret, "access")
}
// VerifyRefreshToken 验证刷新令牌
func (m *JWTManager) VerifyRefreshToken(tokenString string) (*Claims, error) {
return m.verifyToken(tokenString, m.refreshTokenSecret, "refresh")
}
func (m *JWTManager) verifyToken(tokenString string, secret []byte, expectedType string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return secret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
if claims.Type != expectedType {
return nil, errors.New("invalid token type")
}
return claims, nil
}
return nil, errors.New("invalid token")
}
// GenerateTokenPair 生成令牌对
func (m *JWTManager) GenerateTokenPair(userID uint64) (*TokenPair, error) { func (m *JWTManager) GenerateTokenPair(userID uint64) (*TokenPair, error) {
accessToken, accessClaims, err := m.GenerateAccessToken(userID) // 生成访问令牌
accessToken, accessClaims, err := m.generateToken(userID, AccessToken, m.accessSecret, m.accessExpiry)
fmt.Printf("accessToken: %v\n", accessToken)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("生成访问令牌失败: %w", err)
} }
refreshToken, refreshClaims, err := m.GenerateRefreshToken(userID) // 生成刷新令牌
refreshToken, refreshClaims, err := m.generateToken(userID, RefreshToken, m.refreshSecret, m.refreshExpiry)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("生成刷新令牌失败: %w", err)
}
// 存储令牌到 Redis用于追踪和管理
if m.redis != nil {
m.storeToken(userID, accessToken, accessClaims.ExpiresAt.Time)
m.storeToken(userID, refreshToken, refreshClaims.ExpiresAt.Time)
} }
return &TokenPair{ return &TokenPair{
@ -117,10 +106,484 @@ func (m *JWTManager) GenerateTokenPair(userID uint64) (*TokenPair, error) {
}, nil }, nil
} }
type TokenPair struct { // GenerateAccessToken 生成访问令牌
AccessToken string `json:"access_token"` func (m *JWTManager) GenerateAccessToken(userID uint64) (string, *Claims, error) {
RefreshToken string `json:"refresh_token"` token, claims, err := m.generateToken(userID, AccessToken, m.accessSecret, m.accessExpiry)
AccessTokenExpiresAt time.Time `json:"access_token_expires_at"` if err != nil {
RefreshTokenExpiresAt time.Time `json:"refresh_token_expires_at"` return "", nil, err
TokenType string `json:"token_type"` }
// 存储到 Redis
if m.redis != nil {
m.storeToken(userID, token, claims.ExpiresAt.Time)
}
return token, claims, nil
}
// GenerateRefreshToken 生成刷新令牌
func (m *JWTManager) GenerateRefreshToken(userID uint64) (string, *Claims, error) {
token, claims, err := m.generateToken(userID, RefreshToken, m.refreshSecret, m.refreshExpiry)
if err != nil {
return "", nil, err
}
// 存储到 Redis
if m.redis != nil {
m.storeToken(userID, token, claims.ExpiresAt.Time)
}
return token, claims, nil
}
// generateToken 内部方法:生成令牌
func (m *JWTManager) generateToken(userID uint64, tokenType TokenType, secret []byte, expiry time.Duration) (string, *Claims, error) {
now := time.Now()
expireAt := now.Add(expiry)
claims := &Claims{
UserID: userID,
Type: tokenType,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expireAt),
IssuedAt: jwt.NewNumericDate(now),
Issuer: m.issuer,
Subject: fmt.Sprintf("%d", userID),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(secret)
if err != nil {
return "", nil, fmt.Errorf("签名令牌失败: %w", err)
}
return tokenString, claims, nil
}
// ==================== Token 验证方法 ====================
// VerifyAccessToken 验证访问令牌(包含黑名单检查)
func (m *JWTManager) VerifyAccessToken(tokenString string) (*Claims, error) {
return m.verifyToken(tokenString, AccessToken, m.accessSecret)
}
// VerifyRefreshToken 验证刷新令牌(包含黑名单检查)
func (m *JWTManager) VerifyRefreshToken(tokenString string) (*Claims, error) {
return m.verifyToken(tokenString, RefreshToken, m.refreshSecret)
}
// verifyToken 内部方法:验证令牌
func (m *JWTManager) verifyToken(tokenString string, expectedType TokenType, secret []byte) (*Claims, error) {
// 1. 检查令牌是否在黑名单中
if m.redis != nil {
blacklisted, err := m.isTokenBlacklisted(tokenString, expectedType)
if err != nil {
return nil, fmt.Errorf("检查黑名单失败: %w", err)
}
if blacklisted {
return nil, errors.New("令牌已失效,请重新登录")
}
}
// 2. 验证 JWT
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("不支持的签名方法: %v", token.Header["alg"])
}
return secret, nil
})
if err != nil {
return nil, fmt.Errorf("解析令牌失败: %w", err)
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
// 3. 检查令牌类型
if claims.Type != expectedType {
return nil, errors.New("令牌类型不匹配")
}
// 4. 检查令牌是否过期
if claims.ExpiresAt.Time.Before(time.Now()) {
return nil, errors.New("令牌已过期")
}
return claims, nil
}
return nil, errors.New("无效的令牌")
}
// ==================== 黑名单管理 ====================
// BlacklistToken 将令牌加入黑名单
func (m *JWTManager) BlacklistToken(tokenString string) error {
if m.redis == nil {
return errors.New("redis 客户端未初始化")
}
// 尝试解析令牌
claims, err := m.parseTokenWithoutBlacklistCheck(tokenString)
if err != nil {
return fmt.Errorf("无法识别令牌: %w", err)
}
return m.addToBlacklist(tokenString, claims.Type, claims.ExpiresAt.Time)
}
// BlacklistAccessToken 将访问令牌加入黑名单
func (m *JWTManager) BlacklistAccessToken(tokenString string) error {
if m.redis == nil {
return errors.New("redis 客户端未初始化")
}
claims, err := m.VerifyAccessToken(tokenString)
if err != nil {
return fmt.Errorf("无效的访问令牌: %w", err)
}
return m.addToBlacklist(tokenString, AccessToken, claims.ExpiresAt.Time)
}
// BlacklistRefreshToken 将刷新令牌加入黑名单
func (m *JWTManager) BlacklistRefreshToken(tokenString string) error {
if m.redis == nil {
return errors.New("redis 客户端未初始化")
}
claims, err := m.VerifyRefreshToken(tokenString)
if err != nil {
return fmt.Errorf("无效的刷新令牌: %w", err)
}
return m.addToBlacklist(tokenString, RefreshToken, claims.ExpiresAt.Time)
}
// addToBlacklist 内部方法:将令牌加入黑名单
func (m *JWTManager) addToBlacklist(tokenString string, tokenType TokenType, expireTime time.Time) error {
remaining := time.Until(expireTime)
if remaining <= 0 {
// 令牌已过期,不需要加入黑名单
return nil
}
tokenHash := m.hashToken(tokenString)
blacklistKey := m.getBlacklistKey(tokenHash, tokenType)
// 设置黑名单,过期时间等于令牌剩余有效期
err := m.redis.Set(m.ctx, blacklistKey, "1", remaining).Err()
if err != nil {
return fmt.Errorf("设置黑名单失败: %w", err)
}
// 记录用户与令牌的关联(用于按用户清理)
if claims, err := m.parseTokenWithoutBlacklistCheck(tokenString); err == nil {
userTokenKey := m.getUserTokenKey(claims.UserID, tokenHash)
m.redis.Set(m.ctx, userTokenKey, tokenString, remaining)
}
return nil
}
// IsTokenBlacklisted 检查令牌是否在黑名单中
func (m *JWTManager) IsTokenBlacklisted(tokenString string) (bool, error) {
if m.redis == nil {
return false, errors.New("redis 客户端未初始化")
}
// 检查所有类型的黑名单
tokenHash := m.hashToken(tokenString)
// 检查访问令牌黑名单
accessBlacklistKey := m.getBlacklistKey(tokenHash, AccessToken)
exists, err := m.redis.Exists(m.ctx, accessBlacklistKey).Result()
if err != nil {
return false, fmt.Errorf("检查访问令牌黑名单失败: %w", err)
}
if exists > 0 {
return true, nil
}
// 检查刷新令牌黑名单
refreshBlacklistKey := m.getBlacklistKey(tokenHash, RefreshToken)
exists, err = m.redis.Exists(m.ctx, refreshBlacklistKey).Result()
if err != nil {
return false, fmt.Errorf("检查刷新令牌黑名单失败: %w", err)
}
return exists > 0, nil
}
// isTokenBlacklisted 内部方法:检查令牌是否在黑名单中
func (m *JWTManager) isTokenBlacklisted(tokenString string, tokenType TokenType) (bool, error) {
if m.redis == nil {
return false, nil
}
tokenHash := m.hashToken(tokenString)
blacklistKey := m.getBlacklistKey(tokenHash, tokenType)
exists, err := m.redis.Exists(m.ctx, blacklistKey).Result()
if err != nil {
return false, err
}
return exists > 0, nil
}
// RemoveFromBlacklist 从黑名单中移除令牌
func (m *JWTManager) RemoveFromBlacklist(tokenString string) error {
if m.redis == nil {
return errors.New("redis 客户端未初始化")
}
tokenHash := m.hashToken(tokenString)
// 尝试移除两种类型的黑名单
accessBlacklistKey := m.getBlacklistKey(tokenHash, AccessToken)
refreshBlacklistKey := m.getBlacklistKey(tokenHash, RefreshToken)
err := m.redis.Del(m.ctx, accessBlacklistKey, refreshBlacklistKey).Err()
return err
}
// RevokeUserTokens 撤销用户所有令牌
func (m *JWTManager) RevokeUserTokens(userID uint64) error {
if m.redis == nil {
return errors.New("redis 客户端未初始化")
}
// 获取用户的所有令牌
tokens, err := m.GetUserTokens(userID)
if err != nil {
return fmt.Errorf("获取用户令牌失败: %w", err)
}
// 将所有令牌加入黑名单
for _, token := range tokens {
m.BlacklistToken(token)
}
// 清理用户令牌集合
userTokenSetKey := m.getUserTokenSetKey(userID)
err = m.redis.Del(m.ctx, userTokenSetKey).Err()
if err != nil {
return fmt.Errorf("清理用户令牌集合失败: %w", err)
}
return nil
}
// ==================== 用户令牌管理 ====================
// storeToken 存储用户令牌
func (m *JWTManager) storeToken(userID uint64, tokenString string, expireTime time.Time) {
if m.redis == nil {
return
}
// 将令牌添加到用户的令牌集合
userTokenSetKey := m.getUserTokenSetKey(userID)
err := m.redis.SAdd(m.ctx, userTokenSetKey, tokenString).Err()
if err != nil {
// 记录错误但继续执行
fmt.Printf("存储用户令牌失败: %v\n", err)
return
}
// 设置集合的过期时间(比令牌晚 1 小时)
expireAt := expireTime.Add(time.Hour)
err = m.redis.ExpireAt(m.ctx, userTokenSetKey, expireAt).Err()
if err != nil {
fmt.Printf("设置集合过期时间失败: %v\n", err)
}
}
// GetUserTokens 获取用户的所有令牌
func (m *JWTManager) GetUserTokens(userID uint64) ([]string, error) {
if m.redis == nil {
return nil, errors.New("redis 客户端未初始化")
}
userTokenSetKey := m.getUserTokenSetKey(userID)
return m.redis.SMembers(m.ctx, userTokenSetKey).Result()
}
// RemoveUserToken 移除用户的特定令牌
func (m *JWTManager) RemoveUserToken(userID uint64, tokenString string) error {
if m.redis == nil {
return errors.New("redis 客户端未初始化")
}
userTokenSetKey := m.getUserTokenSetKey(userID)
err := m.redis.SRem(m.ctx, userTokenSetKey, tokenString).Err()
return err
}
// ==================== 令牌刷新 ====================
// RefreshTokens 使用刷新令牌获取新的令牌对
func (m *JWTManager) RefreshTokens(refreshToken string) (*TokenPair, error) {
// 验证刷新令牌
claims, err := m.VerifyRefreshToken(refreshToken)
if err != nil {
return nil, fmt.Errorf("刷新令牌验证失败: %w", err)
}
// 将旧的刷新令牌加入黑名单
if err := m.BlacklistRefreshToken(refreshToken); err != nil {
// 记录日志但继续执行
fmt.Printf("警告:将旧刷新令牌加入黑名单失败: %v\n", err)
}
// 生成新的令牌对
return m.GenerateTokenPair(claims.UserID)
}
// ==================== 辅助方法 ====================
// parseTokenWithoutBlacklistCheck 解析令牌但不检查黑名单
func (m *JWTManager) parseTokenWithoutBlacklistCheck(tokenString string) (*Claims, error) {
// 尝试作为访问令牌解析
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return m.accessSecret, nil
})
if err == nil && token.Valid {
if claims, ok := token.Claims.(*Claims); ok {
return claims, nil
}
}
// 尝试作为刷新令牌解析
token, err = jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return m.refreshSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("无法解析令牌")
}
// hashToken 生成令牌的哈希值
func (m *JWTManager) hashToken(token string) string {
hash := md5.Sum([]byte(token))
return hex.EncodeToString(hash[:])
}
// getBlacklistKey 获取黑名单键名
func (m *JWTManager) getBlacklistKey(tokenHash string, tokenType TokenType) string {
return fmt.Sprintf("%sblacklist:%s:%s", m.prefix, tokenType, tokenHash)
}
// getUserTokenSetKey 获取用户令牌集合键名
func (m *JWTManager) getUserTokenSetKey(userID uint64) string {
return fmt.Sprintf("%suser:tokens:%d", m.prefix, userID)
}
// getUserTokenKey 获取用户令牌键名
func (m *JWTManager) getUserTokenKey(userID uint64, tokenHash string) string {
return fmt.Sprintf("%suser:%d:token:%s", m.prefix, userID, tokenHash)
}
// ==================== 工具方法 ====================
// GetTokenInfo 获取令牌信息
func (m *JWTManager) GetTokenInfo(tokenString string) (map[string]interface{}, error) {
claims, err := m.parseTokenWithoutBlacklistCheck(tokenString)
if err != nil {
return nil, err
}
info := map[string]interface{}{
"user_id": claims.UserID,
"token_type": claims.Type,
"issued_at": claims.IssuedAt.Time.Format(time.RFC3339),
"expires_at": claims.ExpiresAt.Time.Format(time.RFC3339),
"issuer": claims.Issuer,
"subject": claims.Subject,
"remaining": time.Until(claims.ExpiresAt.Time).String(),
}
// 检查是否在黑名单中
if m.redis != nil {
blacklisted, _ := m.IsTokenBlacklisted(tokenString)
info["blacklisted"] = blacklisted
}
return info, nil
}
// ValidateAndExtract 验证令牌并提取用户ID
func (m *JWTManager) ValidateAndExtract(tokenString string) (uint64, error) {
claims, err := m.VerifyAccessToken(tokenString)
if err != nil {
return 0, err
}
return claims.UserID, nil
}
// Cleanup 清理过期的令牌数据
func (m *JWTManager) Cleanup() error {
if m.redis == nil {
return nil
}
// Redis 会自动清理过期的键
return nil
}
// GetBlacklistStats 获取黑名单统计信息
func (m *JWTManager) GetBlacklistStats() (map[string]interface{}, error) {
if m.redis == nil {
return nil, errors.New("redis 客户端未初始化")
}
stats := make(map[string]interface{})
// 获取访问令牌黑名单数量
accessPattern := fmt.Sprintf("%sblacklist:access:*", m.prefix)
accessKeys, err := m.redis.Keys(m.ctx, accessPattern).Result()
if err == nil {
stats["access_token_blacklist_count"] = len(accessKeys)
} else {
stats["access_token_blacklist_count"] = 0
}
// 获取刷新令牌黑名单数量
refreshPattern := fmt.Sprintf("%sblacklist:refresh:*", m.prefix)
refreshKeys, err := m.redis.Keys(m.ctx, refreshPattern).Result()
if err == nil {
stats["refresh_token_blacklist_count"] = len(refreshKeys)
} else {
stats["refresh_token_blacklist_count"] = 0
}
return stats, nil
}
// Ping 检查 Redis 连接
func (m *JWTManager) Ping() error {
if m.redis == nil {
return errors.New("redis 客户端未初始化")
}
_, err := m.redis.Ping(m.ctx).Result()
return err
}
// Close 关闭 Redis 连接
func (m *JWTManager) Close() error {
if m.redis == nil {
return nil
}
return m.redis.Close()
} }

View File

@ -23,7 +23,7 @@ func NewAuthTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *TokenLo
ctx: ctx, ctx: ctx,
svcCtx: svcCtx, svcCtx: svcCtx,
Logger: logx.WithContext(ctx), Logger: logx.WithContext(ctx),
jwtManager: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf), jwtManager: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf, svcCtx.Redis),
} }
} }
@ -36,7 +36,7 @@ func (l *TokenLogic) AuthToken(in *app.AuthReq) (*app.AuthInfoResp, error) {
} }
return &app.AuthInfoResp{ return &app.AuthInfoResp{
UserId: token.UserID, UserId: token.UserID,
Type: token.Type, Type: string(token.Type),
Claims: &app.RegisteredClaims{ Claims: &app.RegisteredClaims{
IssuedAt: &app.NumericDate{ IssuedAt: &app.NumericDate{
Timestamp: &timestamppb.Timestamp{ Timestamp: &timestamppb.Timestamp{

View File

@ -34,7 +34,7 @@ func NewLoginUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginUs
ctx: ctx, ctx: ctx,
svcCtx: svcCtx, svcCtx: svcCtx,
Logger: logx.WithContext(ctx), Logger: logx.WithContext(ctx),
jwt: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf), jwt: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf, nil),
} }
} }

View File

@ -0,0 +1,50 @@
package user
import (
"context"
"mingyang-admin-app-rpc/internal/jwt_manager"
"mingyang-admin-app-rpc/internal/util/dberrorhandler"
"time"
"mingyang-admin-app-rpc/internal/svc"
"mingyang-admin-app-rpc/types/app"
"github.com/zeromicro/go-zero/core/logx"
)
type LogoutUserLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
jwtManager *jwt_manager.JWTManager
}
func NewLogoutUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutUserLogic {
return &LogoutUserLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
jwtManager: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf, svcCtx.Redis),
}
}
// LogoutUser 用户退出登录
func (l *LogoutUserLogic) LogoutUser(in *app.UserToken) (*app.LogoutUserRequest, error) {
// 记录开始时间
start := time.Now()
l.Logger.Info("LogoutUser started token =", in.AccessToken)
accessToken := in.GetAccessToken()
_, err := l.jwtManager.VerifyAccessToken(accessToken)
if err != nil {
l.Logger.Error("Verify access token failed", "error", err, "time", time.Since(start))
return nil, dberrorhandler.DefaultEntError(l.Logger, err, in)
}
err = l.jwtManager.BlacklistToken(accessToken)
if err != nil {
l.Logger.Error("Blacklist token failed", "error", err, "time", time.Since(start))
return nil, dberrorhandler.DefaultEntError(l.Logger, err, in)
}
return &app.LogoutUserRequest{
AccessToken: accessToken,
}, nil
}

View File

@ -30,7 +30,7 @@ func NewRegisterUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Regi
return &RegisterUserLogic{ return &RegisterUserLogic{
ctx: ctx, ctx: ctx,
svcCtx: svcCtx, svcCtx: svcCtx,
jwtManager: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf), jwtManager: jwt_manager.NewJWTManager(&svcCtx.Config.JWTConf, svcCtx.Redis),
Logger: logx.WithContext(ctx), Logger: logx.WithContext(ctx),
cacheRepo: cacherepo.NewCacheRepository(ctx, svcCtx), cacheRepo: cacherepo.NewCacheRepository(ctx, svcCtx),
} }

View File

@ -55,6 +55,12 @@ func (s *AppServer) ListUsers(ctx context.Context, in *app.PageUserRequest) (*ap
return l.ListUsers(in) return l.ListUsers(in)
} }
// 用户退出登录
func (s *AppServer) LogoutUser(ctx context.Context, in *app.UserToken) (*app.LogoutUserRequest, error) {
l := user.NewLogoutUserLogic(ctx, s.svcCtx)
return l.LogoutUser(in)
}
func (s *AppServer) InitDatabase(ctx context.Context, in *app.Empty) (*app.BaseResp, error) { func (s *AppServer) InitDatabase(ctx context.Context, in *app.Empty) (*app.BaseResp, error) {
l := base.NewInitDatabaseLogic(ctx, s.svcCtx) l := base.NewInitDatabaseLogic(ctx, s.svcCtx)
return l.InitDatabase(in) return l.InitDatabase(in)

View File

@ -1 +0,0 @@
package service

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.10
// protoc v3.21.11 // protoc v3.19.4
// source: app.proto // source: app.proto
package app package app
@ -821,6 +821,50 @@ func (x *LoginResponse) GetAuthToken() *AuthToken {
return nil return nil
} }
type LogoutUserRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LogoutUserRequest) Reset() {
*x = LogoutUserRequest{}
mi := &file_app_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LogoutUserRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LogoutUserRequest) ProtoMessage() {}
func (x *LogoutUserRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[13]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use LogoutUserRequest.ProtoReflect.Descriptor instead.
func (*LogoutUserRequest) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{13}
}
func (x *LogoutUserRequest) GetAccessToken() string {
if x != nil {
return x.AccessToken
}
return ""
}
// NumericDate 使用 timestamp 表示 JWT 中的时间字段 // NumericDate 使用 timestamp 表示 JWT 中的时间字段
type NumericDate struct { type NumericDate struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
@ -831,7 +875,7 @@ type NumericDate struct {
func (x *NumericDate) Reset() { func (x *NumericDate) Reset() {
*x = NumericDate{} *x = NumericDate{}
mi := &file_app_proto_msgTypes[13] mi := &file_app_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -843,7 +887,7 @@ func (x *NumericDate) String() string {
func (*NumericDate) ProtoMessage() {} func (*NumericDate) ProtoMessage() {}
func (x *NumericDate) ProtoReflect() protoreflect.Message { func (x *NumericDate) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[13] mi := &file_app_proto_msgTypes[14]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -856,7 +900,7 @@ func (x *NumericDate) ProtoReflect() protoreflect.Message {
// Deprecated: Use NumericDate.ProtoReflect.Descriptor instead. // Deprecated: Use NumericDate.ProtoReflect.Descriptor instead.
func (*NumericDate) Descriptor() ([]byte, []int) { func (*NumericDate) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{13} return file_app_proto_rawDescGZIP(), []int{14}
} }
func (x *NumericDate) GetTimestamp() *timestamppb.Timestamp { func (x *NumericDate) GetTimestamp() *timestamppb.Timestamp {
@ -876,7 +920,7 @@ type PageInfoReq struct {
func (x *PageInfoReq) Reset() { func (x *PageInfoReq) Reset() {
*x = PageInfoReq{} *x = PageInfoReq{}
mi := &file_app_proto_msgTypes[14] mi := &file_app_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -888,7 +932,7 @@ func (x *PageInfoReq) String() string {
func (*PageInfoReq) ProtoMessage() {} func (*PageInfoReq) ProtoMessage() {}
func (x *PageInfoReq) ProtoReflect() protoreflect.Message { func (x *PageInfoReq) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[14] mi := &file_app_proto_msgTypes[15]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -901,7 +945,7 @@ func (x *PageInfoReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use PageInfoReq.ProtoReflect.Descriptor instead. // Deprecated: Use PageInfoReq.ProtoReflect.Descriptor instead.
func (*PageInfoReq) Descriptor() ([]byte, []int) { func (*PageInfoReq) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{14} return file_app_proto_rawDescGZIP(), []int{15}
} }
func (x *PageInfoReq) GetPage() uint64 { func (x *PageInfoReq) GetPage() uint64 {
@ -933,7 +977,7 @@ type PageUserRequest struct {
func (x *PageUserRequest) Reset() { func (x *PageUserRequest) Reset() {
*x = PageUserRequest{} *x = PageUserRequest{}
mi := &file_app_proto_msgTypes[15] mi := &file_app_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -945,7 +989,7 @@ func (x *PageUserRequest) String() string {
func (*PageUserRequest) ProtoMessage() {} func (*PageUserRequest) ProtoMessage() {}
func (x *PageUserRequest) ProtoReflect() protoreflect.Message { func (x *PageUserRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[15] mi := &file_app_proto_msgTypes[16]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -958,7 +1002,7 @@ func (x *PageUserRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use PageUserRequest.ProtoReflect.Descriptor instead. // Deprecated: Use PageUserRequest.ProtoReflect.Descriptor instead.
func (*PageUserRequest) Descriptor() ([]byte, []int) { func (*PageUserRequest) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{15} return file_app_proto_rawDescGZIP(), []int{16}
} }
func (x *PageUserRequest) GetPage() uint64 { func (x *PageUserRequest) GetPage() uint64 {
@ -1020,7 +1064,7 @@ type PageUserResponse struct {
func (x *PageUserResponse) Reset() { func (x *PageUserResponse) Reset() {
*x = PageUserResponse{} *x = PageUserResponse{}
mi := &file_app_proto_msgTypes[16] mi := &file_app_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -1032,7 +1076,7 @@ func (x *PageUserResponse) String() string {
func (*PageUserResponse) ProtoMessage() {} func (*PageUserResponse) ProtoMessage() {}
func (x *PageUserResponse) ProtoReflect() protoreflect.Message { func (x *PageUserResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[16] mi := &file_app_proto_msgTypes[17]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -1045,7 +1089,7 @@ func (x *PageUserResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use PageUserResponse.ProtoReflect.Descriptor instead. // Deprecated: Use PageUserResponse.ProtoReflect.Descriptor instead.
func (*PageUserResponse) Descriptor() ([]byte, []int) { func (*PageUserResponse) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{16} return file_app_proto_rawDescGZIP(), []int{17}
} }
func (x *PageUserResponse) GetTotal() uint64 { func (x *PageUserResponse) GetTotal() uint64 {
@ -1080,7 +1124,7 @@ type RegisterUserRequest struct {
func (x *RegisterUserRequest) Reset() { func (x *RegisterUserRequest) Reset() {
*x = RegisterUserRequest{} *x = RegisterUserRequest{}
mi := &file_app_proto_msgTypes[17] mi := &file_app_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -1092,7 +1136,7 @@ func (x *RegisterUserRequest) String() string {
func (*RegisterUserRequest) ProtoMessage() {} func (*RegisterUserRequest) ProtoMessage() {}
func (x *RegisterUserRequest) ProtoReflect() protoreflect.Message { func (x *RegisterUserRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[17] mi := &file_app_proto_msgTypes[18]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -1105,7 +1149,7 @@ func (x *RegisterUserRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use RegisterUserRequest.ProtoReflect.Descriptor instead. // Deprecated: Use RegisterUserRequest.ProtoReflect.Descriptor instead.
func (*RegisterUserRequest) Descriptor() ([]byte, []int) { func (*RegisterUserRequest) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{17} return file_app_proto_rawDescGZIP(), []int{18}
} }
func (x *RegisterUserRequest) GetEmail() string { func (x *RegisterUserRequest) GetEmail() string {
@ -1190,7 +1234,7 @@ type RegisterUserResponse struct {
func (x *RegisterUserResponse) Reset() { func (x *RegisterUserResponse) Reset() {
*x = RegisterUserResponse{} *x = RegisterUserResponse{}
mi := &file_app_proto_msgTypes[18] mi := &file_app_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -1202,7 +1246,7 @@ func (x *RegisterUserResponse) String() string {
func (*RegisterUserResponse) ProtoMessage() {} func (*RegisterUserResponse) ProtoMessage() {}
func (x *RegisterUserResponse) ProtoReflect() protoreflect.Message { func (x *RegisterUserResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[18] mi := &file_app_proto_msgTypes[19]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -1215,7 +1259,7 @@ func (x *RegisterUserResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use RegisterUserResponse.ProtoReflect.Descriptor instead. // Deprecated: Use RegisterUserResponse.ProtoReflect.Descriptor instead.
func (*RegisterUserResponse) Descriptor() ([]byte, []int) { func (*RegisterUserResponse) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{18} return file_app_proto_rawDescGZIP(), []int{19}
} }
func (x *RegisterUserResponse) GetUser() *UserInfo { func (x *RegisterUserResponse) GetUser() *UserInfo {
@ -1269,7 +1313,7 @@ type RegisteredClaims struct {
func (x *RegisteredClaims) Reset() { func (x *RegisteredClaims) Reset() {
*x = RegisteredClaims{} *x = RegisteredClaims{}
mi := &file_app_proto_msgTypes[19] mi := &file_app_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -1281,7 +1325,7 @@ func (x *RegisteredClaims) String() string {
func (*RegisteredClaims) ProtoMessage() {} func (*RegisteredClaims) ProtoMessage() {}
func (x *RegisteredClaims) ProtoReflect() protoreflect.Message { func (x *RegisteredClaims) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[19] mi := &file_app_proto_msgTypes[20]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -1294,7 +1338,7 @@ func (x *RegisteredClaims) ProtoReflect() protoreflect.Message {
// Deprecated: Use RegisteredClaims.ProtoReflect.Descriptor instead. // Deprecated: Use RegisteredClaims.ProtoReflect.Descriptor instead.
func (*RegisteredClaims) Descriptor() ([]byte, []int) { func (*RegisteredClaims) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{19} return file_app_proto_rawDescGZIP(), []int{20}
} }
func (x *RegisteredClaims) GetIssuer() string { func (x *RegisteredClaims) GetIssuer() string {
@ -1355,7 +1399,7 @@ type UUIDReq struct {
func (x *UUIDReq) Reset() { func (x *UUIDReq) Reset() {
*x = UUIDReq{} *x = UUIDReq{}
mi := &file_app_proto_msgTypes[20] mi := &file_app_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -1367,7 +1411,7 @@ func (x *UUIDReq) String() string {
func (*UUIDReq) ProtoMessage() {} func (*UUIDReq) ProtoMessage() {}
func (x *UUIDReq) ProtoReflect() protoreflect.Message { func (x *UUIDReq) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[20] mi := &file_app_proto_msgTypes[21]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -1380,7 +1424,7 @@ func (x *UUIDReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use UUIDReq.ProtoReflect.Descriptor instead. // Deprecated: Use UUIDReq.ProtoReflect.Descriptor instead.
func (*UUIDReq) Descriptor() ([]byte, []int) { func (*UUIDReq) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{20} return file_app_proto_rawDescGZIP(), []int{21}
} }
func (x *UUIDReq) GetId() string { func (x *UUIDReq) GetId() string {
@ -1399,7 +1443,7 @@ type UUIDsReq struct {
func (x *UUIDsReq) Reset() { func (x *UUIDsReq) Reset() {
*x = UUIDsReq{} *x = UUIDsReq{}
mi := &file_app_proto_msgTypes[21] mi := &file_app_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -1411,7 +1455,7 @@ func (x *UUIDsReq) String() string {
func (*UUIDsReq) ProtoMessage() {} func (*UUIDsReq) ProtoMessage() {}
func (x *UUIDsReq) ProtoReflect() protoreflect.Message { func (x *UUIDsReq) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[21] mi := &file_app_proto_msgTypes[22]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -1424,7 +1468,7 @@ func (x *UUIDsReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use UUIDsReq.ProtoReflect.Descriptor instead. // Deprecated: Use UUIDsReq.ProtoReflect.Descriptor instead.
func (*UUIDsReq) Descriptor() ([]byte, []int) { func (*UUIDsReq) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{21} return file_app_proto_rawDescGZIP(), []int{22}
} }
func (x *UUIDsReq) GetIds() []string { func (x *UUIDsReq) GetIds() []string {
@ -1464,7 +1508,7 @@ type UserInfo struct {
func (x *UserInfo) Reset() { func (x *UserInfo) Reset() {
*x = UserInfo{} *x = UserInfo{}
mi := &file_app_proto_msgTypes[22] mi := &file_app_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -1476,7 +1520,7 @@ func (x *UserInfo) String() string {
func (*UserInfo) ProtoMessage() {} func (*UserInfo) ProtoMessage() {}
func (x *UserInfo) ProtoReflect() protoreflect.Message { func (x *UserInfo) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[22] mi := &file_app_proto_msgTypes[23]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -1489,7 +1533,7 @@ func (x *UserInfo) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserInfo.ProtoReflect.Descriptor instead. // Deprecated: Use UserInfo.ProtoReflect.Descriptor instead.
func (*UserInfo) Descriptor() ([]byte, []int) { func (*UserInfo) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{22} return file_app_proto_rawDescGZIP(), []int{23}
} }
func (x *UserInfo) GetId() uint64 { func (x *UserInfo) GetId() uint64 {
@ -1646,6 +1690,58 @@ func (x *UserInfo) GetUpdatedAt() int64 {
return 0 return 0
} }
type UserToken struct {
state protoimpl.MessageState `protogen:"open.v1"`
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token"`
UserId uint64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UserToken) Reset() {
*x = UserToken{}
mi := &file_app_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UserToken) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UserToken) ProtoMessage() {}
func (x *UserToken) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[24]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UserToken.ProtoReflect.Descriptor instead.
func (*UserToken) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{24}
}
func (x *UserToken) GetAccessToken() string {
if x != nil {
return x.AccessToken
}
return ""
}
func (x *UserToken) GetUserId() uint64 {
if x != nil {
return x.UserId
}
return 0
}
type VerifyCodeReq struct { type VerifyCodeReq struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Type VerifyCodeType `protobuf:"varint,1,opt,name=type,proto3,enum=app.VerifyCodeType" json:"type"` Type VerifyCodeType `protobuf:"varint,1,opt,name=type,proto3,enum=app.VerifyCodeType" json:"type"`
@ -1657,7 +1753,7 @@ type VerifyCodeReq struct {
func (x *VerifyCodeReq) Reset() { func (x *VerifyCodeReq) Reset() {
*x = VerifyCodeReq{} *x = VerifyCodeReq{}
mi := &file_app_proto_msgTypes[23] mi := &file_app_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -1669,7 +1765,7 @@ func (x *VerifyCodeReq) String() string {
func (*VerifyCodeReq) ProtoMessage() {} func (*VerifyCodeReq) ProtoMessage() {}
func (x *VerifyCodeReq) ProtoReflect() protoreflect.Message { func (x *VerifyCodeReq) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[23] mi := &file_app_proto_msgTypes[25]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -1682,7 +1778,7 @@ func (x *VerifyCodeReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use VerifyCodeReq.ProtoReflect.Descriptor instead. // Deprecated: Use VerifyCodeReq.ProtoReflect.Descriptor instead.
func (*VerifyCodeReq) Descriptor() ([]byte, []int) { func (*VerifyCodeReq) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{23} return file_app_proto_rawDescGZIP(), []int{25}
} }
func (x *VerifyCodeReq) GetType() VerifyCodeType { func (x *VerifyCodeReq) GetType() VerifyCodeType {
@ -1716,7 +1812,7 @@ type VerifyCodeResp struct {
func (x *VerifyCodeResp) Reset() { func (x *VerifyCodeResp) Reset() {
*x = VerifyCodeResp{} *x = VerifyCodeResp{}
mi := &file_app_proto_msgTypes[24] mi := &file_app_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -1728,7 +1824,7 @@ func (x *VerifyCodeResp) String() string {
func (*VerifyCodeResp) ProtoMessage() {} func (*VerifyCodeResp) ProtoMessage() {}
func (x *VerifyCodeResp) ProtoReflect() protoreflect.Message { func (x *VerifyCodeResp) ProtoReflect() protoreflect.Message {
mi := &file_app_proto_msgTypes[24] mi := &file_app_proto_msgTypes[26]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -1741,7 +1837,7 @@ func (x *VerifyCodeResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use VerifyCodeResp.ProtoReflect.Descriptor instead. // Deprecated: Use VerifyCodeResp.ProtoReflect.Descriptor instead.
func (*VerifyCodeResp) Descriptor() ([]byte, []int) { func (*VerifyCodeResp) Descriptor() ([]byte, []int) {
return file_app_proto_rawDescGZIP(), []int{24} return file_app_proto_rawDescGZIP(), []int{26}
} }
func (x *VerifyCodeResp) GetCaptchaCode() string { func (x *VerifyCodeResp) GetCaptchaCode() string {
@ -1809,7 +1905,9 @@ const file_app_proto_rawDesc = "" +
"\rLoginResponse\x12!\n" + "\rLoginResponse\x12!\n" +
"\x04user\x18\x01 \x01(\v2\r.app.UserInfoR\x04user\x12-\n" + "\x04user\x18\x01 \x01(\v2\r.app.UserInfoR\x04user\x12-\n" +
"\n" + "\n" +
"auth_token\x18\x02 \x01(\v2\x0e.app.AuthTokenR\tauthToken\"G\n" + "auth_token\x18\x02 \x01(\v2\x0e.app.AuthTokenR\tauthToken\"6\n" +
"\x11LogoutUserRequest\x12!\n" +
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\"G\n" +
"\vNumericDate\x128\n" + "\vNumericDate\x128\n" +
"\ttimestamp\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\">\n" + "\ttimestamp\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\">\n" +
"\vPageInfoReq\x12\x12\n" + "\vPageInfoReq\x12\x12\n" +
@ -1923,7 +2021,10 @@ const file_app_proto_rawDesc = "" +
"\x14_registration_sourceB\x0e\n" + "\x14_registration_sourceB\x0e\n" +
"\f_birthday_atB\r\n" + "\f_birthday_atB\r\n" +
"\v_created_atB\r\n" + "\v_created_atB\r\n" +
"\v_updated_at\"\x83\x01\n" + "\v_updated_at\"G\n" +
"\tUserToken\x12!\n" +
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\x12\x17\n" +
"\auser_id\x18\x02 \x01(\x04R\x06userId\"\x83\x01\n" +
"\rVerifyCodeReq\x12'\n" + "\rVerifyCodeReq\x12'\n" +
"\x04type\x18\x01 \x01(\x0e2\x13.app.VerifyCodeTypeR\x04type\x123\n" + "\x04type\x18\x01 \x01(\x0e2\x13.app.VerifyCodeTypeR\x04type\x123\n" +
"\faccount_type\x18\x02 \x01(\x0e2\x10.app.AccountTypeR\vaccountType\x12\x14\n" + "\faccount_type\x18\x02 \x01(\x0e2\x10.app.AccountTypeR\vaccountType\x12\x14\n" +
@ -1947,13 +2048,15 @@ const file_app_proto_rawDesc = "" +
"\fUPDATE_PHONE\x10\x05\x12\x10\n" + "\fUPDATE_PHONE\x10\x05\x12\x10\n" +
"\fUPDATE_EMAIL\x10\x06\x12\f\n" + "\fUPDATE_EMAIL\x10\x06\x12\f\n" +
"\bWITHDRAW\x10\a\x12\x17\n" + "\bWITHDRAW\x10\a\x12\x17\n" +
"\x13CHANGE_PAY_PASSWORD\x10\b2\xcb\x02\n" + "\x13CHANGE_PAY_PASSWORD\x10\b2\x81\x03\n" +
"\x03App\x12,\n" + "\x03App\x12,\n" +
"\tAuthToken\x12\f.app.AuthReq\x1a\x11.app.AuthInfoResp\x128\n" + "\tAuthToken\x12\f.app.AuthReq\x1a\x11.app.AuthInfoResp\x128\n" +
"\rGetVerifyCode\x12\x12.app.VerifyCodeReq\x1a\x13.app.VerifyCodeResp\x12C\n" + "\rGetVerifyCode\x12\x12.app.VerifyCodeReq\x1a\x13.app.VerifyCodeResp\x12C\n" +
"\fRegisterUser\x12\x18.app.RegisterUserRequest\x1a\x19.app.RegisterUserResponse\x122\n" + "\fRegisterUser\x12\x18.app.RegisterUserRequest\x1a\x19.app.RegisterUserResponse\x122\n" +
"\tLoginUser\x12\x11.app.LoginRequest\x1a\x12.app.LoginResponse\x128\n" + "\tLoginUser\x12\x11.app.LoginRequest\x1a\x12.app.LoginResponse\x128\n" +
"\tListUsers\x12\x14.app.PageUserRequest\x1a\x15.app.PageUserResponse\x12)\n" + "\tListUsers\x12\x14.app.PageUserRequest\x1a\x15.app.PageUserResponse\x124\n" +
"\n" +
"LogoutUser\x12\x0e.app.UserToken\x1a\x16.app.LogoutUserRequest\x12)\n" +
"\fInitDatabase\x12\n" + "\fInitDatabase\x12\n" +
".app.Empty\x1a\r.app.BaseRespB\aZ\x05./appb\x06proto3" ".app.Empty\x1a\r.app.BaseRespB\aZ\x05./appb\x06proto3"
@ -1970,7 +2073,7 @@ func file_app_proto_rawDescGZIP() []byte {
} }
var file_app_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_app_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_app_proto_msgTypes = make([]protoimpl.MessageInfo, 25) var file_app_proto_msgTypes = make([]protoimpl.MessageInfo, 27)
var file_app_proto_goTypes = []any{ var file_app_proto_goTypes = []any{
(AccountType)(0), // 0: app.AccountType (AccountType)(0), // 0: app.AccountType
(VerifyCodeType)(0), // 1: app.VerifyCodeType (VerifyCodeType)(0), // 1: app.VerifyCodeType
@ -1987,53 +2090,57 @@ var file_app_proto_goTypes = []any{
(*IDsReq)(nil), // 12: app.IDsReq (*IDsReq)(nil), // 12: app.IDsReq
(*LoginRequest)(nil), // 13: app.LoginRequest (*LoginRequest)(nil), // 13: app.LoginRequest
(*LoginResponse)(nil), // 14: app.LoginResponse (*LoginResponse)(nil), // 14: app.LoginResponse
(*NumericDate)(nil), // 15: app.NumericDate (*LogoutUserRequest)(nil), // 15: app.LogoutUserRequest
(*PageInfoReq)(nil), // 16: app.PageInfoReq (*NumericDate)(nil), // 16: app.NumericDate
(*PageUserRequest)(nil), // 17: app.PageUserRequest (*PageInfoReq)(nil), // 17: app.PageInfoReq
(*PageUserResponse)(nil), // 18: app.PageUserResponse (*PageUserRequest)(nil), // 18: app.PageUserRequest
(*RegisterUserRequest)(nil), // 19: app.RegisterUserRequest (*PageUserResponse)(nil), // 19: app.PageUserResponse
(*RegisterUserResponse)(nil), // 20: app.RegisterUserResponse (*RegisterUserRequest)(nil), // 20: app.RegisterUserRequest
(*RegisteredClaims)(nil), // 21: app.RegisteredClaims (*RegisterUserResponse)(nil), // 21: app.RegisterUserResponse
(*UUIDReq)(nil), // 22: app.UUIDReq (*RegisteredClaims)(nil), // 22: app.RegisteredClaims
(*UUIDsReq)(nil), // 23: app.UUIDsReq (*UUIDReq)(nil), // 23: app.UUIDReq
(*UserInfo)(nil), // 24: app.UserInfo (*UUIDsReq)(nil), // 24: app.UUIDsReq
(*VerifyCodeReq)(nil), // 25: app.VerifyCodeReq (*UserInfo)(nil), // 25: app.UserInfo
(*VerifyCodeResp)(nil), // 26: app.VerifyCodeResp (*UserToken)(nil), // 26: app.UserToken
(*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp (*VerifyCodeReq)(nil), // 27: app.VerifyCodeReq
(*structpb.Struct)(nil), // 28: google.protobuf.Struct (*VerifyCodeResp)(nil), // 28: app.VerifyCodeResp
(*timestamppb.Timestamp)(nil), // 29: google.protobuf.Timestamp
(*structpb.Struct)(nil), // 30: google.protobuf.Struct
} }
var file_app_proto_depIdxs = []int32{ var file_app_proto_depIdxs = []int32{
21, // 0: app.AuthInfoResp.claims:type_name -> app.RegisteredClaims 22, // 0: app.AuthInfoResp.claims:type_name -> app.RegisteredClaims
27, // 1: app.AuthReq.time:type_name -> google.protobuf.Timestamp 29, // 1: app.AuthReq.time:type_name -> google.protobuf.Timestamp
27, // 2: app.AuthToken.access_token_expires:type_name -> google.protobuf.Timestamp 29, // 2: app.AuthToken.access_token_expires:type_name -> google.protobuf.Timestamp
27, // 3: app.AuthToken.refresh_token_expires:type_name -> google.protobuf.Timestamp 29, // 3: app.AuthToken.refresh_token_expires:type_name -> google.protobuf.Timestamp
24, // 4: app.LoginResponse.user:type_name -> app.UserInfo 25, // 4: app.LoginResponse.user:type_name -> app.UserInfo
4, // 5: app.LoginResponse.auth_token:type_name -> app.AuthToken 4, // 5: app.LoginResponse.auth_token:type_name -> app.AuthToken
27, // 6: app.NumericDate.timestamp:type_name -> google.protobuf.Timestamp 29, // 6: app.NumericDate.timestamp:type_name -> google.protobuf.Timestamp
24, // 7: app.PageUserResponse.data:type_name -> app.UserInfo 25, // 7: app.PageUserResponse.data:type_name -> app.UserInfo
24, // 8: app.RegisterUserResponse.user:type_name -> app.UserInfo 25, // 8: app.RegisterUserResponse.user:type_name -> app.UserInfo
4, // 9: app.RegisterUserResponse.auth_token:type_name -> app.AuthToken 4, // 9: app.RegisterUserResponse.auth_token:type_name -> app.AuthToken
9, // 10: app.RegisteredClaims.audience:type_name -> app.ClaimStrings 9, // 10: app.RegisteredClaims.audience:type_name -> app.ClaimStrings
15, // 11: app.RegisteredClaims.expires_at:type_name -> app.NumericDate 16, // 11: app.RegisteredClaims.expires_at:type_name -> app.NumericDate
15, // 12: app.RegisteredClaims.not_before:type_name -> app.NumericDate 16, // 12: app.RegisteredClaims.not_before:type_name -> app.NumericDate
15, // 13: app.RegisteredClaims.issued_at:type_name -> app.NumericDate 16, // 13: app.RegisteredClaims.issued_at:type_name -> app.NumericDate
28, // 14: app.UserInfo.metadata:type_name -> google.protobuf.Struct 30, // 14: app.UserInfo.metadata:type_name -> google.protobuf.Struct
1, // 15: app.VerifyCodeReq.type:type_name -> app.VerifyCodeType 1, // 15: app.VerifyCodeReq.type:type_name -> app.VerifyCodeType
0, // 16: app.VerifyCodeReq.account_type:type_name -> app.AccountType 0, // 16: app.VerifyCodeReq.account_type:type_name -> app.AccountType
3, // 17: app.App.AuthToken:input_type -> app.AuthReq 3, // 17: app.App.AuthToken:input_type -> app.AuthReq
25, // 18: app.App.GetVerifyCode:input_type -> app.VerifyCodeReq 27, // 18: app.App.GetVerifyCode:input_type -> app.VerifyCodeReq
19, // 19: app.App.RegisterUser:input_type -> app.RegisterUserRequest 20, // 19: app.App.RegisterUser:input_type -> app.RegisterUserRequest
13, // 20: app.App.LoginUser:input_type -> app.LoginRequest 13, // 20: app.App.LoginUser:input_type -> app.LoginRequest
17, // 21: app.App.ListUsers:input_type -> app.PageUserRequest 18, // 21: app.App.ListUsers:input_type -> app.PageUserRequest
10, // 22: app.App.InitDatabase:input_type -> app.Empty 26, // 22: app.App.LogoutUser:input_type -> app.UserToken
2, // 23: app.App.AuthToken:output_type -> app.AuthInfoResp 10, // 23: app.App.InitDatabase:input_type -> app.Empty
26, // 24: app.App.GetVerifyCode:output_type -> app.VerifyCodeResp 2, // 24: app.App.AuthToken:output_type -> app.AuthInfoResp
20, // 25: app.App.RegisterUser:output_type -> app.RegisterUserResponse 28, // 25: app.App.GetVerifyCode:output_type -> app.VerifyCodeResp
14, // 26: app.App.LoginUser:output_type -> app.LoginResponse 21, // 26: app.App.RegisterUser:output_type -> app.RegisterUserResponse
18, // 27: app.App.ListUsers:output_type -> app.PageUserResponse 14, // 27: app.App.LoginUser:output_type -> app.LoginResponse
7, // 28: app.App.InitDatabase:output_type -> app.BaseResp 19, // 28: app.App.ListUsers:output_type -> app.PageUserResponse
23, // [23:29] is the sub-list for method output_type 15, // 29: app.App.LogoutUser:output_type -> app.LogoutUserRequest
17, // [17:23] is the sub-list for method input_type 7, // 30: app.App.InitDatabase:output_type -> app.BaseResp
24, // [24:31] is the sub-list for method output_type
17, // [17:24] is the sub-list for method input_type
17, // [17:17] is the sub-list for extension type_name 17, // [17:17] is the sub-list for extension type_name
17, // [17:17] is the sub-list for extension extendee 17, // [17:17] is the sub-list for extension extendee
0, // [0:17] is the sub-list for field type_name 0, // [0:17] is the sub-list for field type_name
@ -2045,16 +2152,16 @@ func file_app_proto_init() {
return return
} }
file_app_proto_msgTypes[11].OneofWrappers = []any{} file_app_proto_msgTypes[11].OneofWrappers = []any{}
file_app_proto_msgTypes[15].OneofWrappers = []any{} file_app_proto_msgTypes[16].OneofWrappers = []any{}
file_app_proto_msgTypes[17].OneofWrappers = []any{} file_app_proto_msgTypes[18].OneofWrappers = []any{}
file_app_proto_msgTypes[22].OneofWrappers = []any{} file_app_proto_msgTypes[23].OneofWrappers = []any{}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_proto_rawDesc), len(file_app_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_proto_rawDesc), len(file_app_proto_rawDesc)),
NumEnums: 2, NumEnums: 2,
NumMessages: 25, NumMessages: 27,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.6.0 // - protoc-gen-go-grpc v1.5.1
// - protoc v3.21.11 // - protoc v3.19.4
// source: app.proto // source: app.proto
package app package app
@ -24,6 +24,7 @@ const (
App_RegisterUser_FullMethodName = "/app.App/RegisterUser" App_RegisterUser_FullMethodName = "/app.App/RegisterUser"
App_LoginUser_FullMethodName = "/app.App/LoginUser" App_LoginUser_FullMethodName = "/app.App/LoginUser"
App_ListUsers_FullMethodName = "/app.App/ListUsers" App_ListUsers_FullMethodName = "/app.App/ListUsers"
App_LogoutUser_FullMethodName = "/app.App/LogoutUser"
App_InitDatabase_FullMethodName = "/app.App/InitDatabase" App_InitDatabase_FullMethodName = "/app.App/InitDatabase"
) )
@ -48,6 +49,9 @@ type AppClient interface {
// 分页查询用户信息 // 分页查询用户信息
// group: user // group: user
ListUsers(ctx context.Context, in *PageUserRequest, opts ...grpc.CallOption) (*PageUserResponse, error) ListUsers(ctx context.Context, in *PageUserRequest, opts ...grpc.CallOption) (*PageUserResponse, error)
// 用户退出登录
// group: user
LogoutUser(ctx context.Context, in *UserToken, opts ...grpc.CallOption) (*LogoutUserRequest, error)
// group: base // group: base
InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error) InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error)
} }
@ -110,6 +114,16 @@ func (c *appClient) ListUsers(ctx context.Context, in *PageUserRequest, opts ...
return out, nil return out, nil
} }
func (c *appClient) LogoutUser(ctx context.Context, in *UserToken, opts ...grpc.CallOption) (*LogoutUserRequest, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LogoutUserRequest)
err := c.cc.Invoke(ctx, App_LogoutUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *appClient) InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error) { func (c *appClient) InitDatabase(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BaseResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(BaseResp) out := new(BaseResp)
@ -141,6 +155,9 @@ type AppServer interface {
// 分页查询用户信息 // 分页查询用户信息
// group: user // group: user
ListUsers(context.Context, *PageUserRequest) (*PageUserResponse, error) ListUsers(context.Context, *PageUserRequest) (*PageUserResponse, error)
// 用户退出登录
// group: user
LogoutUser(context.Context, *UserToken) (*LogoutUserRequest, error)
// group: base // group: base
InitDatabase(context.Context, *Empty) (*BaseResp, error) InitDatabase(context.Context, *Empty) (*BaseResp, error)
mustEmbedUnimplementedAppServer() mustEmbedUnimplementedAppServer()
@ -154,22 +171,25 @@ type AppServer interface {
type UnimplementedAppServer struct{} type UnimplementedAppServer struct{}
func (UnimplementedAppServer) AuthToken(context.Context, *AuthReq) (*AuthInfoResp, error) { func (UnimplementedAppServer) AuthToken(context.Context, *AuthReq) (*AuthInfoResp, error) {
return nil, status.Error(codes.Unimplemented, "method AuthToken not implemented") return nil, status.Errorf(codes.Unimplemented, "method AuthToken not implemented")
} }
func (UnimplementedAppServer) GetVerifyCode(context.Context, *VerifyCodeReq) (*VerifyCodeResp, error) { func (UnimplementedAppServer) GetVerifyCode(context.Context, *VerifyCodeReq) (*VerifyCodeResp, error) {
return nil, status.Error(codes.Unimplemented, "method GetVerifyCode not implemented") return nil, status.Errorf(codes.Unimplemented, "method GetVerifyCode not implemented")
} }
func (UnimplementedAppServer) RegisterUser(context.Context, *RegisterUserRequest) (*RegisterUserResponse, error) { func (UnimplementedAppServer) RegisterUser(context.Context, *RegisterUserRequest) (*RegisterUserResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RegisterUser not implemented") return nil, status.Errorf(codes.Unimplemented, "method RegisterUser not implemented")
} }
func (UnimplementedAppServer) LoginUser(context.Context, *LoginRequest) (*LoginResponse, error) { func (UnimplementedAppServer) LoginUser(context.Context, *LoginRequest) (*LoginResponse, error) {
return nil, status.Error(codes.Unimplemented, "method LoginUser not implemented") return nil, status.Errorf(codes.Unimplemented, "method LoginUser not implemented")
} }
func (UnimplementedAppServer) ListUsers(context.Context, *PageUserRequest) (*PageUserResponse, error) { func (UnimplementedAppServer) ListUsers(context.Context, *PageUserRequest) (*PageUserResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListUsers not implemented") return nil, status.Errorf(codes.Unimplemented, "method ListUsers not implemented")
}
func (UnimplementedAppServer) LogoutUser(context.Context, *UserToken) (*LogoutUserRequest, error) {
return nil, status.Errorf(codes.Unimplemented, "method LogoutUser not implemented")
} }
func (UnimplementedAppServer) InitDatabase(context.Context, *Empty) (*BaseResp, error) { func (UnimplementedAppServer) InitDatabase(context.Context, *Empty) (*BaseResp, error) {
return nil, status.Error(codes.Unimplemented, "method InitDatabase not implemented") return nil, status.Errorf(codes.Unimplemented, "method InitDatabase not implemented")
} }
func (UnimplementedAppServer) mustEmbedUnimplementedAppServer() {} func (UnimplementedAppServer) mustEmbedUnimplementedAppServer() {}
func (UnimplementedAppServer) testEmbeddedByValue() {} func (UnimplementedAppServer) testEmbeddedByValue() {}
@ -182,7 +202,7 @@ type UnsafeAppServer interface {
} }
func RegisterAppServer(s grpc.ServiceRegistrar, srv AppServer) { func RegisterAppServer(s grpc.ServiceRegistrar, srv AppServer) {
// If the following call panics, it indicates UnimplementedAppServer was // If the following call pancis, it indicates UnimplementedAppServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.
@ -282,6 +302,24 @@ func _App_ListUsers_Handler(srv interface{}, ctx context.Context, dec func(inter
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _App_LogoutUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UserToken)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AppServer).LogoutUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: App_LogoutUser_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AppServer).LogoutUser(ctx, req.(*UserToken))
}
return interceptor(ctx, in, info, handler)
}
func _App_InitDatabase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _App_InitDatabase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty) in := new(Empty)
if err := dec(in); err != nil { if err := dec(in); err != nil {
@ -327,6 +365,10 @@ var App_ServiceDesc = grpc.ServiceDesc{
MethodName: "ListUsers", MethodName: "ListUsers",
Handler: _App_ListUsers_Handler, Handler: _App_ListUsers_Handler,
}, },
{
MethodName: "LogoutUser",
Handler: _App_LogoutUser_Handler,
},
{ {
MethodName: "InitDatabase", MethodName: "InitDatabase",
Handler: _App_InitDatabase_Handler, Handler: _App_InitDatabase_Handler,