Skip to content

Commit

Permalink
feat(middleware,controller): finish bizctx, user data in ctx, get use…
Browse files Browse the repository at this point in the history
…r info
  • Loading branch information
greenhat616 committed Aug 5, 2023
1 parent fc395a8 commit 92229f5
Show file tree
Hide file tree
Showing 25 changed files with 386 additions and 69 deletions.
2 changes: 1 addition & 1 deletion api/admin/admin.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/admin/v1/grant_authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type GrantUserAuthorityReq struct {
}

type GrantUserAuthorityRes struct {
ID int64 `json:"id" dc:"用户 ID"`
ID uint `json:"id" dc:"用户 ID"`
Name string `json:"name" dc:"用户名"`
Email string `json:"email" dc:"邮箱"`
Token string `json:"token" dc:"Token"`
Expand Down
2 changes: 1 addition & 1 deletion api/poll/poll.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/poll/v1/detail.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type GetPollDetailReq struct {
}

type PollRecord struct {
UserID int64 `json:"user_id" dc:"用户 ID"`
UserID uint `json:"user_id" dc:"用户 ID"`
Point int `json:"point" dc:"投票点数"`
Type consts.PollMethod `json:"type" dc:"投票类型"`
Comment string `json:"comment" dc:"理由"`
Expand Down
2 changes: 1 addition & 1 deletion api/poll/v1/mark.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type GetPollMarksReq struct {
}

type PollMark struct {
ID int64 `json:"id" dc:"标记 ID"`
ID uint `json:"id" dc:"标记 ID"`
Text string `json:"text" dc:"标记文本"`
Level consts.PollMarkLevel `json:"level" dc:"标记等级"`
Property consts.PollMarkProperty `json:"property" dc:"标记属性"`
Expand Down
2 changes: 1 addition & 1 deletion api/poll/v1/poll.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type PollReq struct {
g.Meta `path:"/poll" tags:"Poll" method:"put" summary:"投票"`
Method consts.PollMethod `json:"method" dc:"投票方式" v:"required|enums"`
SentenceUUID string `json:"sentence_uuid" dc:"句子 UUID" v:"required|length:36"`
MarkID int64 `json:"mark_id" dc:"标记" v:"required-unless:method,1"`
MarkID uint `json:"mark_id" dc:"标记" v:"required-unless:method,1"`
Comment string `json:"comment" dc:"理由" v:"required-if:method,2,3|length:1,255"`
}

Expand Down
2 changes: 1 addition & 1 deletion api/user/user.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions api/user/v1/poll_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ type GetUserPollResultReq struct {
type UserPollElement struct {
UserPollLog
PollInfo v1.PollSchema `json:"poll_info" dc:"投票信息"`
Marks []int64 `json:"marks" dc:"投票标记"`
Marks []uint `json:"marks" dc:"投票标记"`
}

type UserPollResult struct {
Total int64 `json:"total" dc:"总数"`
Total uint `json:"total" dc:"总数"`
Collection []UserPollElement `json:"collection" dc:"数据"`
}

Expand Down
11 changes: 6 additions & 5 deletions api/user/v1/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/hitokoto-osc/reviewer/internal/consts"
)

Expand All @@ -21,16 +22,16 @@ type UserPoll struct {
Points UserPollPoints `json:"points" dc:"投票点数"`
Count int `json:"count" dc:"投票次数"`
Score int `json:"score" dc:"投票得分"`
CreatedAt string `json:"created_at" dc:"创建时间"`
UpdatedAt string `json:"updated_at" dc:"更新时间"`
CreatedAt *gtime.Time `json:"created_at" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updated_at" dc:"更新时间"`
}

type GetUserRes struct {
ID int64 `json:"id" dc:"用户 ID"`
ID uint `json:"id" dc:"用户 ID"`
Name string `json:"name" dc:"用户名"`
Email string `json:"email" dc:"邮箱"`
Role consts.UserRole `json:"role" dc:"角色"`
Poll UserPoll `json:"poll" dc:"投票信息"`
CreatedAt string `json:"created_at" dc:"创建时间"`
UpdatedAt string `json:"updated_at" dc:"更新时间"`
CreatedAt *gtime.Time `json:"created_at" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updated_at" dc:"更新时间"`
}
2 changes: 1 addition & 1 deletion internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var (
s.Group("/api/v1", func(group *ghttp.RouterGroup) {
group.Bind(index.NewV1())
group.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware().AuthorizationV1)
group.Middleware(service.Middleware().Ctx, service.Middleware().AuthorizationV1)
group.Bind(user.NewV1(), poll.NewV1())
group.Group("/admin", func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware().AuthorizationAdminV1)
Expand Down
16 changes: 13 additions & 3 deletions internal/consts/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ const (
type UserRole string

const (
RoleAdmin UserRole = "管理员"
RoleUser UserRole = "普通用户"
RoleReviewer UserRole = "审核员"
UserRoleGuest UserRole = "游客"
UserRoleAdmin UserRole = "管理员"
UserRoleUser UserRole = "普通用户"
UserRoleReviewer UserRole = "审核员"
)

type UserStatus int
Expand All @@ -28,3 +29,12 @@ const (
UserLoginTypeToken UserLoginType = "token" // Token 尝试
UserLoginTypeUsername UserLoginType = "username" // Username 尝试
)

// UserPollPoints 用户可以的投票点数
type UserPollPoints int

const (
UserPollPointsReviewer UserPollPoints = 1 // 审核员
UserPollPointsNormal UserPollPoints = 1 // 普通用户
UserPollPointsAdmin UserPollPoints = 2 // 管理员
)
34 changes: 33 additions & 1 deletion internal/controller/user/user_v1_get_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,44 @@ package user
import (
"context"

"github.com/gogf/gf/v2/frame/g"

"github.com/gogf/gf/v2/util/gconv"

"github.com/hitokoto-osc/reviewer/internal/service"

"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"

v1 "github.com/hitokoto-osc/reviewer/api/user/v1"
)

func (c *ControllerV1) GetUser(ctx context.Context, req *v1.GetUserReq) (res *v1.GetUserRes, err error) {
return nil, gerror.NewCode(gcode.CodeNotImplemented)
bizctx := service.BizCtx().Get(ctx)
if bizctx == nil || bizctx.User == nil { // 正常情况下不会出现
g.Log().Error(ctx, service.BizCtx().Get(ctx))
err = gerror.NewCode(gcode.CodeUnknown, "bizctx or bizctx.User is nil")
}
user := bizctx.User
res = &v1.GetUserRes{
ID: user.Id,
Name: user.Name,
Email: user.Email,
Role: user.Role,
Poll: v1.UserPoll{
Points: v1.UserPollPoints{
Total: user.Poll.Points,
Approve: user.Poll.Accept,
Reject: user.Poll.Reject,
NeedModify: user.Poll.NeedEdited,
},
Count: user.Poll.Points / gconv.Int(service.User().GetUserPollPointsByUserRole(ctx, user.Role)), // TODO: 稍后换成数据库 Count
Score: user.Poll.Score,
CreatedAt: user.Poll.CreatedAt,
UpdatedAt: user.Poll.UpdatedAt,
},
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}
return
}
44 changes: 44 additions & 0 deletions internal/logic/bizctx/bizctx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package bizctx

import (
"context"

"github.com/hitokoto-osc/reviewer/internal/service"

"github.com/gogf/gf/v2/net/ghttp"
"github.com/hitokoto-osc/reviewer/internal/consts"
"github.com/hitokoto-osc/reviewer/internal/model"
)

type sBizCtx struct{}

func init() {
service.RegisterBizCtx(New())
}

func New() service.IBizCtx {
return &sBizCtx{}
}

// Init initializes and injects custom business context object into request context.
func (s *sBizCtx) Init(r *ghttp.Request, customCtx *model.Context) {
r.SetCtxVar(consts.ContextKey, customCtx)
}

// Get retrieves and returns the user object from context.
// It returns nil if nothing found in given context.
func (s *sBizCtx) Get(ctx context.Context) *model.Context {
value := ctx.Value(consts.ContextKey)
if value == nil {
return nil
}
if localCtx, ok := value.(*model.Context); ok {
return localCtx
}
return nil
}

// SetUser injects business user object into context.
func (s *sBizCtx) SetUser(ctx context.Context, ctxUser *model.ContextUser) {
s.Get(ctx).User = ctxUser
}
1 change: 1 addition & 0 deletions internal/logic/logic.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions internal/logic/middleware/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package middleware

import (
"net/http"
"strings"

"github.com/gogf/gf/v2/errors/gerror"

"github.com/hitokoto-osc/reviewer/internal/model"

"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/hitokoto-osc/reviewer/internal/consts"
"github.com/hitokoto-osc/reviewer/internal/service"
)

// AuthorizationV1 用于 v1 接口校验用户是否登录
// 尝试顺序 Authorization: Bearer Token -> param -> form -> body -> query -> Router
func (s *sMiddleware) AuthorizationV1(r *ghttp.Request) {
g.Log("AuthorizationV1").Debugf(r.GetCtx(), "AuthorizationV1: %s", r.GetHeader("Authorization"))
authStr := r.GetHeader("Authorization")
if authStr == "" || !strings.HasPrefix(authStr, "Bearer ") {
if v := r.Get("token"); v != nil && !v.IsNil() && strings.HasPrefix(v.String(), "Bearer ") {
authStr = v.String()
} else {
r.Response.Status = http.StatusUnauthorized
return
}
}
token := strings.Trim(strings.TrimPrefix(authStr, "Bearer "), " ")
if len(token) != consts.UserAccessTokenV1Length {
r.Response.Status = http.StatusUnauthorized
return
}
flag, err := service.User().VerifyAPIV1Token(r.GetCtx(), token)
if err != nil {
g.Log().Error(r.GetCtx(), gerror.Wrap(err, "校验用户 Token 时发生错误"))
r.Response.Status = http.StatusInternalServerError
return
}
if !flag {
r.Response.Status = http.StatusUnauthorized
return
}
// 注入用户信息到上下文
user, err := service.User().GetUserByToken(r.GetCtx(), token)
if err != nil {
g.Log().Error(r.GetCtx(), gerror.Wrap(err, "获取用户信息时发生错误"))
} else if user == nil {
r.Response.Status = http.StatusUnauthorized
return
}
userPoll, err := service.User().GetPollUserByUserID(r.GetCtx(), user.Id)
if err != nil {
g.Log().Error(r.GetCtx(), gerror.Wrap(err, "获取用户投票信息时发生错误"))
} else if userPoll == nil {
g.Log().Error(r.GetCtx(), gerror.New("获取用户投票信息时发生错误,用户投票信息为空"))
r.Response.Status = http.StatusInternalServerError
return
}
userPattern := &model.UserPattern{
Users: *user,
Poll: *userPoll,
Role: service.User().MustGetRoleByUser(r.GetCtx(), user),
Status: service.User().MustGetUserStatusByUser(r.GetCtx(), user),
}
service.BizCtx().SetUser(r.GetCtx(), userPattern)
r.Middleware.Next()
}

// AuthorizationAdminV1 用于 v1 接口校验用户是否登录且是否具有管理员权限
func (s *sMiddleware) AuthorizationAdminV1(r *ghttp.Request) {
r.Middleware.Next()
}
14 changes: 14 additions & 0 deletions internal/logic/middleware/ctx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package middleware

import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/hitokoto-osc/reviewer/internal/model"
"github.com/hitokoto-osc/reviewer/internal/service"
)

// Ctx 用于注入业务上下文,实现单次请求中的业务数据共享
func (s *sMiddleware) Ctx(r *ghttp.Request) {
customCtx := &model.Context{}
service.BizCtx().Init(r, customCtx)
r.Middleware.Next()
}
39 changes: 0 additions & 39 deletions internal/logic/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ package middleware

import (
"net/http"
"strings"

"github.com/hitokoto-osc/reviewer/internal/consts"

"github.com/gogf/gf/v2/frame/g"

"github.com/hitokoto-osc/reviewer/internal/service"

Expand Down Expand Up @@ -80,37 +75,3 @@ func (s *sMiddleware) HandlerResponse(r *ghttp.Request) {
TS: gtime.TimestampMilli(),
})
}

// AuthorizationV1 用于 v1 接口校验用户是否登录
// 尝试顺序 Authorization: Bearer Token -> param -> form -> body -> query -> Router
func (s *sMiddleware) AuthorizationV1(r *ghttp.Request) {
g.Log("AuthorizationV1").Debugf(r.GetCtx(), "AuthorizationV1: %s", r.GetHeader("Authorization"))
authStr := r.GetHeader("Authorization")
if authStr == "" || !strings.HasPrefix(authStr, "Bearer ") {
if v := r.Get("token"); v != nil && !v.IsNil() && strings.HasPrefix(v.String(), "Bearer ") {
authStr = v.String()
} else {
r.Response.Status = http.StatusUnauthorized
return
}
}
token := strings.Trim(strings.TrimPrefix(authStr, "Bearer "), " ")
if len(token) != consts.UserAccessTokenV1Length {
r.Response.Status = http.StatusUnauthorized
return
}
flag, err := service.User().VerifyAPIV1Token(r.GetCtx(), token)
if err != nil {
g.Log().Panicf(r.GetCtx(), "校验用户 Token 时发生错误: %s", err.Error())
}
if !flag {
r.Response.Status = http.StatusUnauthorized
return
}
r.Middleware.Next()
}

// AuthorizationAdminV1 用于 v1 接口校验用户是否登录且是否具有管理员权限
func (s *sMiddleware) AuthorizationAdminV1(r *ghttp.Request) {
r.Middleware.Next()
}
Loading

0 comments on commit 92229f5

Please sign in to comment.