feature/member #1

Merged
daniel.w merged 4 commits from feature/member into main 2024-08-27 07:40:29 +00:00
18 changed files with 423 additions and 73 deletions
Showing only changes of commit 66c85dd650 - Show all commits

View File

@ -1,10 +1,19 @@
#goctl api plugin -plugin goctl-swagger="swagger -filename gateway.json -host dev-api.30cm.net" -api ./generate/api/gateway.api -dir . #goctl api plugin -plugin goctl-swagger="swagger -filename gateway.json -host dev-api.30cm.net" -api ./generate/api/gateway.api -dir .
#goctl api go -api ./generate/api/gateway.api -dir . -style go_zero #goctl api go -api ./generate/api/gateway.api -dir . -style go_zero
GOFMT ?= gofmt "-s" GOFMT ?= gofmt
GOFILES := $(shell find . -name "*.go") GOFILES := $(shell find . -name "*.go")
.PHONY: fmt .PHONY: fmt
fmt: # 格式優化 fmt: # 格式優化
$(GOFMT) -w $(GOFILES) $(GOFMT) -s -w $(GOFILES)
goimports -w ./ goimports -w ./
.PHONY: gen-doc
gen-doc: # 格式優化
goctl api plugin -plugin goctl-swagger="swagger -filename gateway.json -host dev-api.30cm.net" -api ./generate/api/gateway.api -dir .
.PHONY: gen-api
gen-api: # 格式優化
goctl api go -api ./generate/api/gateway.api -dir . -style go_zero

View File

@ -17,6 +17,7 @@ PermissionRpc:
- 127.0.0.1:2379 - 127.0.0.1:2379
Key: permission.rpc Key: permission.rpc
Token: Token:
Secret: kupiHowBonBon
Expired: 300s Expired: 300s
RedisCluster: RedisCluster:

View File

@ -42,6 +42,24 @@
} }
}, },
"parameters": [ "parameters": [
{
"name": "device_id",
"in": "header",
"required": true,
"type": "string"
},
{
"name": "ip_address",
"in": "header",
"required": true,
"type": "string"
},
{
"name": "brewser",
"in": "header",
"required": true,
"type": "string"
},
{ {
"name": "body", "name": "body",
"in": "body", "in": "body",
@ -112,7 +130,7 @@
"post": { "post": {
"summary": "發送忘記密碼驗證", "summary": "發送忘記密碼驗證",
"description": "發送忘記密碼驗證(三分鐘內只能發一次信)", "description": "發送忘記密碼驗證(三分鐘內只能發一次信)",
"operationId": "ForgetPassworCode", "operationId": "ForgetPasswordCode",
"responses": { "responses": {
"200": { "200": {
"description": "A successful response.", "description": "A successful response.",
@ -179,12 +197,6 @@
} }
}, },
"parameters": [ "parameters": [
{
"name": "uid",
"in": "header",
"required": true,
"type": "string"
},
{ {
"name": "token", "name": "token",
"in": "header", "in": "header",
@ -223,6 +235,24 @@
} }
}, },
"parameters": [ "parameters": [
{
"name": "device_id",
"in": "header",
"required": true,
"type": "string"
},
{
"name": "ip_address",
"in": "header",
"required": true,
"type": "string"
},
{
"name": "brewser",
"in": "header",
"required": true,
"type": "string"
},
{ {
"name": "body", "name": "body",
"in": "body", "in": "body",
@ -269,16 +299,74 @@
}, },
"parameters": [ "parameters": [
{ {
"name": "uid", "name": "token",
"in": "header",
"required": true,
"type": "string"
}
],
"tags": [
"gateway/member"
]
}
},
"/api/v1/member/refresh_access_token": {
"put": {
"summary": "更新Token",
"description": "用 RefreshToken 換取 AccessToken",
"operationId": "RefreshAccessToken",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/LoginResp"
}
},
"400": {
"description": "輸入的參數錯誤",
"schema": {
"$ref": "#/definitions/BaseResponse"
}
},
"403": {
"description": "無效的驗證碼",
"schema": {
"$ref": "#/definitions/BaseResponse"
}
},
"500": {
"description": "伺服器出錯",
"schema": {
"$ref": "#/definitions/BaseResponse"
}
}
},
"parameters": [
{
"name": "device_id",
"in": "header", "in": "header",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
{ {
"name": "token", "name": "ip_address",
"in": "header", "in": "header",
"required": true, "required": true,
"type": "string" "type": "string"
},
{
"name": "brewser",
"in": "header",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/UpdateTokenReq"
}
} }
], ],
"tags": [ "tags": [
@ -418,13 +506,14 @@
"description": "平台名稱 digimon, google, twitter" "description": "平台名稱 digimon, google, twitter"
}, },
"account_type": { "account_type": {
"type": "string", "type": "integer",
"format": "int64",
"enum": [ "enum": [
"1", "1",
"2", "2",
"3" "3"
], ],
"description": "帳號類型 1 Email 2. 台灣手機 3. 任意" "description": "帳號類型 1 手機 2 信箱 3 自定義帳號"
} }
}, },
"title": "CreateAccountRequest", "title": "CreateAccountRequest",
@ -461,13 +550,14 @@
"description": "帳號名稱" "description": "帳號名稱"
}, },
"account_type": { "account_type": {
"type": "string", "type": "integer",
"format": "int32",
"enum": [ "enum": [
"1", "1",
"2", "2",
"3" "3"
], ],
"description": "帳號類型 1 Email 2. 台灣手機 3. 任意" "description": "帳號類型 1 手機 2 信箱 3 自定義帳號"
} }
}, },
"title": "ForgetPasswordCodeReq", "title": "ForgetPasswordCodeReq",
@ -476,6 +566,11 @@
"account_type" "account_type"
] ]
}, },
"GetMemberHeader": {
"type": "object",
"properties": {},
"title": "GetMemberHeader"
},
"Header": { "Header": {
"type": "object", "type": "object",
"properties": {}, "properties": {},
@ -484,6 +579,10 @@
"LoginItem": { "LoginItem": {
"type": "object", "type": "object",
"properties": { "properties": {
"uid": {
"type": "string",
"description": "Account"
},
"access_token": { "access_token": {
"type": "string", "type": "string",
"description": "訪問令牌 預設 5 分鐘過期" "description": "訪問令牌 預設 5 分鐘過期"
@ -499,6 +598,7 @@
}, },
"title": "LoginItem", "title": "LoginItem",
"required": [ "required": [
"uid",
"access_token", "access_token",
"refresh_token", "refresh_token",
"token_type" "token_type"
@ -525,13 +625,14 @@
"description": "平台名稱 digimon, google, twitter" "description": "平台名稱 digimon, google, twitter"
}, },
"account_type": { "account_type": {
"type": "string", "type": "integer",
"format": "int64",
"enum": [ "enum": [
"1", "1",
"2", "2",
"3" "3"
], ],
"description": "帳號類型 1 Email 2. 台灣手機 3. 任意" "description": "帳號類型 1 手機 2 信箱 3 自定義帳號"
} }
}, },
"title": "LoginReq", "title": "LoginReq",
@ -559,6 +660,11 @@
"data" "data"
] ]
}, },
"MemberLoginHeader": {
"type": "object",
"properties": {},
"title": "MemberLoginHeader"
},
"Status": { "Status": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -614,10 +720,70 @@
"token_check" "token_check"
] ]
}, },
"UpdateTokenReq": {
"type": "object",
"properties": {
"uid": {
"type": "string",
"description": "uid"
},
"token": {
"type": "string",
"description": "refresh token"
}
},
"title": "UpdateTokenReq",
"required": [
"uid",
"token"
]
},
"UserInfo": { "UserInfo": {
"type": "object", "type": "object",
"properties": {}, "properties": {
"title": "UserInfo" "uid": {
"type": "string"
},
"verify_type": {
"type": "string"
},
"alarm_type": {
"type": "string"
},
"status": {
"type": "string"
},
"language": {
"type": "string"
},
"currency": {
"type": "string"
},
"avatar": {
"type": "string"
},
"curreate_time": {
"type": "string"
},
"update_time": {
"type": "string"
},
"nick_name": {
"type": "string"
}
},
"title": "UserInfo",
"required": [
"uid",
"verify_type",
"alarm_type",
"status",
"language",
"currency",
"avatar",
"curreate_time",
"update_time"
]
}, },
"UserInfoResp": { "UserInfoResp": {
"type": "object", "type": "object",

View File

@ -166,12 +166,28 @@ type Header {
Token string `header:"token"` Token string `header:"token"`
} }
type GetMemberHeader {
Token string `header:"token"`
}
type UserInfoResp { type UserInfoResp {
Status Status `json:"status"` // 狀態 Status Status `json:"status"` // 狀態
Data UserInfo `json:"data"` Data UserInfo `json:"data"`
} }
type UserInfo {} type UserInfo {
UID string `json:"uid"`
VerifyType string `json:"verify_type"`
AlarmType string `json:"alarm_type"`
Status string `json:"status"`
Language string `json:"language"`
Currency string `json:"currency"`
Avatar string `json:"avatar"`
CreateTime string `json:"curreate_time"`
UpdateTime string `json:"update_time"`
NickName *string `json:"nick_name,omitempty"`
}
@server( @server(
group: member group: member
@ -189,7 +205,7 @@ service gateway {
summary: "會員登出" summary: "會員登出"
) )
@handler Logout @handler Logout
get /member/logout (Header) returns (BaseResponse) get /member/logout (GetMemberHeader) returns (BaseResponse)
/* @respdoc-400 (BaseResponse) // 輸入的參數錯誤 */ /* @respdoc-400 (BaseResponse) // 輸入的參數錯誤 */
/* @respdoc-403 (BaseResponse) // 無效的Token */ /* @respdoc-403 (BaseResponse) // 無效的Token */
@ -198,5 +214,5 @@ service gateway {
summary: "取得會員資訊" summary: "取得會員資訊"
) )
@handler Info @handler Info
get /member/info (Header) returns (UserInfoResp) get /member/info (GetMemberHeader) returns (UserInfoResp)
} }

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.22.3
require ( require (
code.30cm.net/digimon/library-go/errors v1.0.1 code.30cm.net/digimon/library-go/errors v1.0.1
code.30cm.net/digimon/library-go/jwt v1.0.0
code.30cm.net/digimon/proto-all v0.0.0-20240826070029-4a87e93fd2cf code.30cm.net/digimon/proto-all v0.0.0-20240826070029-4a87e93fd2cf
github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf v1.3.2
github.com/matcornic/hermes/v2 v2.1.0 github.com/matcornic/hermes/v2 v2.1.0

View File

@ -11,6 +11,7 @@ import (
type Config struct { type Config struct {
rest.RestConf rest.RestConf
Token struct { Token struct {
Secret string
Expired time.Duration Expired time.Duration
} }
// Redis Cluster // Redis Cluster

View File

@ -0,0 +1,14 @@
package domain
type ContextKey string
func (c ContextKey) ToString() string {
return string(c)
}
const (
RoleCode ContextKey = "role"
DeviceIDCode ContextKey = "device_id"
ScopeCode ContextKey = "scope"
UidCode ContextKey = "uid"
)

View File

@ -14,7 +14,7 @@ import (
func InfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { func InfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var req types.Header var req types.GetMemberHeader
if err := httpx.Parse(r, &req); err != nil { if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err) httpx.ErrorCtx(r.Context(), w, err)
return return

View File

@ -14,7 +14,7 @@ import (
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.Header var req types.GetMemberHeader
if err := httpx.Parse(r, &req); err != nil { if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err) httpx.ErrorCtx(r.Context(), w, err)
return return

View File

@ -4,11 +4,12 @@ import (
"app-cloudep-portal-api-gateway/internal/domain" "app-cloudep-portal-api-gateway/internal/domain"
"app-cloudep-portal-api-gateway/internal/svc" "app-cloudep-portal-api-gateway/internal/svc"
"app-cloudep-portal-api-gateway/internal/types" "app-cloudep-portal-api-gateway/internal/types"
ers "code.30cm.net/digimon/library-go/errors"
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
"context" "context"
"fmt" "fmt"
ers "code.30cm.net/digimon/library-go/errors"
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
) )

View File

@ -4,10 +4,13 @@ import (
"app-cloudep-portal-api-gateway/internal/domain" "app-cloudep-portal-api-gateway/internal/domain"
"app-cloudep-portal-api-gateway/internal/svc" "app-cloudep-portal-api-gateway/internal/svc"
"app-cloudep-portal-api-gateway/internal/types" "app-cloudep-portal-api-gateway/internal/types"
ers "code.30cm.net/digimon/library-go/errors"
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
"context" "context"
"fmt" "fmt"
notificationRpc "code.30cm.net/digimon/proto-all/pkg/notification"
ers "code.30cm.net/digimon/library-go/errors"
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
"github.com/matcornic/hermes/v2" "github.com/matcornic/hermes/v2"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
@ -62,28 +65,27 @@ func (l *ForgetPasswordCodeLogic) ForgetPasswordCode(req *types.ForgetPasswordCo
if err != nil { if err != nil {
return nil, err return nil, err
} }
fmt.Println(info)
// // 準備驗證碼郵件 // 準備驗證碼郵件
// nickName := info.Data.Uid nickName := info.Data.Uid
// if info.Data.NickName != nil { if info.Data.NickName != nil {
// nickName = *info.Data.NickName nickName = *info.Data.NickName
// } }
// mailContent, title, err := ForgerZHTW(nickName, code.Data.VerifyCode) mailContent, title, err := ForgerZHTW(nickName, code.Data.VerifyCode)
// if err != nil { if err != nil {
// return nil, ers.InvalidFormat("failed to generate mail content: ", err.Error()) return nil, ers.InvalidFormat("failed to generate mail content: ", err.Error())
// } }
// // 發送郵件 // 發送郵件
// _, err = l.svcCtx.NotificationRpc.SendMail(l.ctx, &notificationRpc.SendMailReq{ _, err = l.svcCtx.NotificationRpc.SendMail(l.ctx, &notificationRpc.SendMailReq{
// Body: mailContent, Body: mailContent,
// Subject: title, Subject: title,
// To: req.Account, To: req.Account,
// From: l.svcCtx.Config.MailSender, From: l.svcCtx.Config.MailSender,
// }) })
// if err != nil { if err != nil {
// return nil, err return nil, err
// } }
// 設置 Redis 鍵,並設置 3 分鐘的過期時間 // 設置 Redis 鍵,並設置 3 分鐘的過期時間
err = l.svcCtx.Redis.Set(rk, code.Data.VerifyCode) err = l.svcCtx.Redis.Set(rk, code.Data.VerifyCode)

View File

@ -1,11 +1,14 @@
package member package member
import ( import (
"context" "app-cloudep-portal-api-gateway/internal/domain"
"app-cloudep-portal-api-gateway/internal/payload"
"app-cloudep-portal-api-gateway/internal/svc" "app-cloudep-portal-api-gateway/internal/svc"
"app-cloudep-portal-api-gateway/internal/types" "app-cloudep-portal-api-gateway/internal/types"
"context"
"time"
accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
) )
@ -23,8 +26,30 @@ func NewInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *InfoLogic {
} }
} }
func (l *InfoLogic) Info(req *types.Header) (resp *types.UserInfoResp, err error) { func (l *InfoLogic) Info(_ *types.GetMemberHeader) (resp *types.UserInfoResp, err error) {
// todo: add your logic here and delete this line info, err := l.svcCtx.AccountRpc.GetUserInfo(l.ctx, &accountRpc.GetUserInfoReq{
Uid: payload.UID(l.ctx),
})
if err != nil {
return nil, err
}
return return &types.UserInfoResp{
Status: types.Status{
Code: domain.SuccessCode,
Message: domain.SuccessMsg,
},
Data: types.UserInfo{
UID: info.Data.Uid,
VerifyType: info.Data.VerifyType.String(),
AlarmType: info.Data.AlarmType.String(),
Status: info.Data.Status.String(),
Language: info.Data.Language,
Currency: info.Data.Currency,
Avatar: info.Data.Avatar,
CreateTime: time.Unix(info.Data.CreateTime, 0).UTC().Format(time.RFC3339),
UpdateTime: time.Unix(info.Data.UpdateTime, 0).UTC().Format(time.RFC3339),
NickName: info.Data.NickName,
},
}, nil
} }

View File

@ -1,8 +1,11 @@
package member package member
import ( import (
"app-cloudep-portal-api-gateway/internal/domain"
"context" "context"
"code.30cm.net/digimon/proto-all/pkg/permission"
"app-cloudep-portal-api-gateway/internal/svc" "app-cloudep-portal-api-gateway/internal/svc"
"app-cloudep-portal-api-gateway/internal/types" "app-cloudep-portal-api-gateway/internal/types"
@ -23,8 +26,18 @@ func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogi
} }
} }
func (l *LogoutLogic) Logout(req *types.Header) (resp *types.BaseResponse, err error) { func (l *LogoutLogic) Logout(req *types.GetMemberHeader) (resp *types.BaseResponse, err error) {
// todo: add your logic here and delete this line _, err = l.svcCtx.TokenRpc.CancelToken(l.ctx, &permission.CancelTokenReq{
Token: req.Token,
})
if err != nil {
return nil, err
}
return return &types.BaseResponse{
Status: types.Status{
Code: domain.SuccessCode,
Message: domain.SuccessMsg,
},
}, nil
} }

View File

@ -2,11 +2,12 @@ package member
import ( import (
"app-cloudep-portal-api-gateway/internal/domain" "app-cloudep-portal-api-gateway/internal/domain"
"context"
"fmt"
ers "code.30cm.net/digimon/library-go/errors" ers "code.30cm.net/digimon/library-go/errors"
accountRpc "code.30cm.net/digimon/proto-all/pkg/member" accountRpc "code.30cm.net/digimon/proto-all/pkg/member"
permissionRpc "code.30cm.net/digimon/proto-all/pkg/permission" permissionRpc "code.30cm.net/digimon/proto-all/pkg/permission"
"context"
"fmt"
"app-cloudep-portal-api-gateway/internal/svc" "app-cloudep-portal-api-gateway/internal/svc"
"app-cloudep-portal-api-gateway/internal/types" "app-cloudep-portal-api-gateway/internal/types"

View File

@ -1,19 +1,79 @@
package middleware package middleware
import "net/http" import (
"app-cloudep-portal-api-gateway/internal/domain"
"app-cloudep-portal-api-gateway/internal/types"
"context"
"net/http"
ers "code.30cm.net/digimon/library-go/errors"
token "code.30cm.net/digimon/library-go/jwt"
permissionRpc "code.30cm.net/digimon/proto-all/pkg/permission"
"github.com/zeromicro/go-zero/rest/httpx"
)
type AuthMiddlewareParam struct {
TokenSec string
TokenClient permissionRpc.TokenServiceClient
}
type AuthMiddleware struct { type AuthMiddleware struct {
tokenSec string
tokenClient permissionRpc.TokenServiceClient
} }
func NewAuthMiddleware() *AuthMiddleware { func NewAuthMiddleware(param AuthMiddlewareParam) *AuthMiddleware {
return &AuthMiddleware{} return &AuthMiddleware{
} tokenSec: param.TokenSec,
tokenClient: param.TokenClient,
func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// TODO generate middleware implement function, delete after code implementation
// Passthrough to next handler if need
next(w, r)
} }
} }
// Handle 處理 Auth Middleware
func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 解析 Header
header := types.GetMemberHeader{}
if err := httpx.ParseHeaders(r, &header); err != nil {
m.writeErrorResponse(w, r, http.StatusBadRequest, "Failed to parse headers", int64(ers.InvalidFormat().FullCode()))
return
}
// 驗證 Token
claim, err := token.ParseClaims(header.Token, m.tokenSec, true)
if err != nil {
// 是否需要紀錄錯誤,是不是只要紀錄除了驗證失敗或過期之外的真錯誤
m.writeErrorResponse(w, r, http.StatusForbidden, err.Error(), int64(ers.Forbidden().FullCode()))
return
}
// 驗證 Token 是否在黑名單中
if _, err := m.tokenClient.ValidationToken(r.Context(), &permissionRpc.ValidationTokenReq{Token: header.Token}); err != nil {
m.writeErrorResponse(w, r, http.StatusForbidden, err.Error(), int64(ers.Forbidden().FullCode()))
return
}
// 設置 context 並傳遞給下一個處理器
ctx := SetContext(r, claim)
next(w, r.WithContext(ctx))
}
}
func SetContext(r *http.Request, claim token.DataClaims) context.Context {
ctx := context.WithValue(r.Context(), domain.RoleCode, claim.Role())
ctx = context.WithValue(ctx, domain.UidCode, claim.UID())
ctx = context.WithValue(ctx, domain.DeviceIDCode, claim.DeviceID())
ctx = context.WithValue(ctx, domain.ScopeCode, claim.Get(domain.ScopeCode.ToString()))
return ctx
}
// writeErrorResponse 用於處理錯誤回應
func (m *AuthMiddleware) writeErrorResponse(w http.ResponseWriter, r *http.Request, statusCode int, message string, code int64) {
httpx.WriteJsonCtx(r.Context(), w, statusCode, types.BaseResponse{
Status: types.Status{
Code: code,
Message: message,
},
})
}

22
internal/payload/token.go Normal file
View File

@ -0,0 +1,22 @@
package payload
import (
"app-cloudep-portal-api-gateway/internal/domain"
"context"
)
func UID(ctx context.Context) string {
return ctx.Value(domain.UidCode).(string)
}
func Scope(ctx context.Context) string {
return ctx.Value(domain.ScopeCode).(string)
}
func Role(ctx context.Context) string {
return ctx.Value(domain.RoleCode).(string)
}
func DeviceID(ctx context.Context) string {
return ctx.Value(domain.DeviceIDCode).(string)
}

View File

@ -32,11 +32,15 @@ func NewServiceContext(c config.Config) *ServiceContext {
panic(err) panic(err)
} }
tc := permissionRpc.NewTokenServiceClient(zrpc.MustNewClient(c.PermissionRpc).Conn())
return &ServiceContext{ return &ServiceContext{
Config: c, Config: c,
AuthMiddleware: middleware.NewAuthMiddleware(), AuthMiddleware: middleware.NewAuthMiddleware(middleware.AuthMiddlewareParam{
TokenSec: c.Token.Secret,
TokenClient: tc,
}),
AccountRpc: accountRpc.NewAccountClient(zrpc.MustNewClient(c.AccountRpc).Conn()), AccountRpc: accountRpc.NewAccountClient(zrpc.MustNewClient(c.AccountRpc).Conn()),
TokenRpc: permissionRpc.NewTokenServiceClient(zrpc.MustNewClient(c.PermissionRpc).Conn()), TokenRpc: tc,
NotificationRpc: notificationRpc.NewSenderServiceClient(zrpc.MustNewClient(c.NotificationRpc).Conn()), NotificationRpc: notificationRpc.NewSenderServiceClient(zrpc.MustNewClient(c.NotificationRpc).Conn()),
Redis: *newRedis, Redis: *newRedis,
} }

View File

@ -87,10 +87,24 @@ type Header struct {
Token string `header:"token"` Token string `header:"token"`
} }
type GetMemberHeader struct {
Token string `header:"token"`
}
type UserInfoResp struct { type UserInfoResp struct {
Status Status `json:"status"` // 狀態 Status Status `json:"status"` // 狀態
Data UserInfo `json:"data"` Data UserInfo `json:"data"`
} }
type UserInfo struct { type UserInfo struct {
UID string `json:"uid"`
VerifyType string `json:"verify_type"`
AlarmType string `json:"alarm_type"`
Status string `json:"status"`
Language string `json:"language"`
Currency string `json:"currency"`
Avatar string `json:"avatar"`
CreateTime string `json:"curreate_time"`
UpdateTime string `json:"update_time"`
NickName *string `json:"nick_name,omitempty"`
} }