diff --git a/corelib/page.go b/corelib/page.go index 77ac912..6fe1db5 100644 --- a/corelib/page.go +++ b/corelib/page.go @@ -7,13 +7,14 @@ type PaginationData struct { NumPages int `json:"num_pages"` // 总页数 LeftHasMore bool `json:"left_has_more"` // 左边显示更多 RightHasMore bool `json:"right_has_more"` // 右边显示更多 + Total int64 `json:"total"` // 数据总量 } // GetPaginationData 分页数据 // numPages 总页数 // currentPage 当前页 // aroundCount 当前页左右两边显示多少个分页按钮 -func GetPaginationData(numPages int, currentPage int, aroundCount int) PaginationData { +func GetPaginationData(numPages int, currentPage int, aroundCount int, total int64) PaginationData { // 边缘条件判断 if currentPage > numPages { currentPage = numPages @@ -49,7 +50,7 @@ func GetPaginationData(numPages int, currentPage int, aroundCount int) Paginatio } return PaginationData{ LeftPages: leftPages, RightPages: rightPages, CurrentPage: currentPage, - NumPages: numPages, LeftHasMore: leftHasMore, RightHasMore: rightHasMore} + NumPages: numPages, LeftHasMore: leftHasMore, RightHasMore: rightHasMore, Total: total} } // LeftPages RightPages diff --git a/corelib/redis.go b/corelib/redis.go index f8452bd..fb2c5a5 100644 --- a/corelib/redis.go +++ b/corelib/redis.go @@ -8,9 +8,9 @@ import ( ) var ( - RdbClient *redis.Client - CaptchaKey = "picgo:captcha:" // 验证码存储key - UserKey = "picgo:user:" + RdbClient *redis.Client + CaptchaKey = "picgo:captcha:" // 验证码存储key + UserNameKey = "picgo:user:username:" ) func NewRedis() { diff --git a/data/user.go b/data/user.go index 7a2cd76..1c4ab7b 100644 --- a/data/user.go +++ b/data/user.go @@ -26,15 +26,15 @@ func SysUserSelectByUsername(userName string) (model.SysUser, error) { corelib.Logger.Infoln("SysUserSelectByUsername 从缓存获取用户信息成功: ", user.Username) return user, nil } else { + corelib.Logger.Error("SysUserSelectByUsername 从缓存获取用户信息失败: ", userName, " error: ", err) err = nil - corelib.Logger.Infoln("SysUserSelectByUsername 从缓存获取用户信息失败: ", user.Username) } - if err = corelib.DbMysql.Model(model.SysUser{Username: userName}).First(&user).Error; err != nil { - corelib.Logger.Infoln("SysUserSelectByUsername 数据库中查询用户信息失败: ", err) + if err = corelib.DbMysql.Where("username = ?", userName).First(&user).Error; err != nil { + corelib.Logger.Error("SysUserSelectByUsername 数据库中查询用户信息失败: ", userName, " error: ", err) return user, err } if err = SysUserSetCacheByUsername(user); err != nil { - corelib.Logger.Infoln("SysUserSelectByUsername 缓存用户信息失败: ", err) + corelib.Logger.Error("SysUserSelectByUsername 缓存用户信息失败: ", err) return user, nil } return user, nil @@ -48,7 +48,7 @@ func SysUserSetCacheByUsername(user model.SysUser) error { if jsonData, err = corelib.JsonMarshal(user); err != nil { return err } - key := corelib.UserKey + user.Username + key := corelib.UserNameKey + user.Username if err = corelib.RdbClient.Set(context.Background(), key, string(jsonData), 5*time.Minute).Err(); err != nil { return err } @@ -62,7 +62,7 @@ func SysUserGetCacheByUsername(userName string) (model.SysUser, error) { user model.SysUser userCache string ) - if userCache, err = corelib.RdbClient.Get(context.Background(), corelib.UserKey+userName).Result(); err != nil { + if userCache, err = corelib.RdbClient.Get(context.Background(), corelib.UserNameKey+userName).Result(); err != nil { return model.SysUser{}, err } if err = corelib.JsonUnmarshal([]byte(userCache), &user); err != nil { diff --git a/handler/login.go b/handler/login.go index 776288c..fb42a96 100644 --- a/handler/login.go +++ b/handler/login.go @@ -31,7 +31,6 @@ func LoginApiHandler(w http.ResponseWriter, r *http.Request) { res model.LoginRequest user model.SysUser ) - if err = json.NewDecoder(r.Body).Decode(&res); err != nil { corelib.Logger.Error("LoginApiHandler, 参数获取失败") corelib.WriteJsonResponse(w, 400, "参数错误", nil) @@ -44,23 +43,29 @@ func LoginApiHandler(w http.ResponseWriter, r *http.Request) { } cid := getCaptchaId(r) if ok := captcha.Verify(cid, res.Captcha); !ok { - corelib.WriteJsonResponse(w, 1040, "验证码错误", nil) + corelib.Logger.Error("LoginApiHandler, 验证码错误") + corelib.WriteJsonResponse(w, 400, "验证码错误", nil) return } + if user, err = data.SysUserSelectByUsername(res.Username); err != nil { - corelib.WriteJsonResponse(w, 1041, "用户不存在", nil) + corelib.Logger.Error("LoginApiHandler, 用户不存在") + corelib.WriteJsonResponse(w, 1040, "用户不存在", nil) return } + // 验证用户名密码 if !corelib.ComparePasswords(user.Password, res.Password, user.Salt) { - corelib.WriteJsonResponse(w, 1042, "用户名或密码错误", nil) + corelib.Logger.Error("LoginApiHandler, 用户名或密码错误") + corelib.WriteJsonResponse(w, 1041, "用户名或密码错误", nil) return } session, _ := corelib.SessionStore.Get(r, configs.Settings.Server.SessionName) session.Values["username"] = user.Username + session.Values["id"] = user.ID if err = session.Save(r, w); err != nil { - corelib.Logger.Infoln("session save err:", err) - corelib.WriteJsonResponse(w, 1043, "会话保存失败", nil) + corelib.Logger.Error("LoginApiHandler, 会话保存失败:", err) + corelib.WriteJsonResponse(w, 1042, "会话保存失败", nil) return } w.Header().Set("Content-Type", "application/json") diff --git a/handler/user.go b/handler/user.go index 1eda055..c3e3146 100644 --- a/handler/user.go +++ b/handler/user.go @@ -2,6 +2,8 @@ package handler import ( "encoding/json" + "errors" + "fmt" "github.com/gorilla/csrf" "math" "net/http" @@ -16,18 +18,25 @@ func UserPageHandler(w http.ResponseWriter, r *http.Request) { var ( err error user model.SysUser + res model.UserpageResponse ) username := r.Context().Value("username").(string) if user, err = data.SysUserGetCacheByUsername(username); err != nil { http.Error(w, "IndexHandler SysUserGetCacheByUsername Error", http.StatusInternalServerError) return } + tmpData := map[string]interface{}{ csrf.TemplateTag: csrf.TemplateField(r), } tmpData["Title"] = "用户管理" tmpData["Active"] = r.URL.Path tmpData["IsSuper"] = user.IsSuper + if res, err = pagination(1, 10, ""); err != nil { + http.Error(w, fmt.Sprintf("%v", err), http.StatusInternalServerError) + return + } + tmpData["Data"] = res w.Header().Add("X-CSRF-Token", csrf.Token(r)) corelib.TemplateHandler(w, tmpData, "layout.html", "view/layout.html", "view/user.html") } else { @@ -45,21 +54,18 @@ func UserCreateApiHandler(w http.ResponseWriter, r *http.Request) { password string salt string ) - if err = json.NewDecoder(r.Body).Decode(&res); err != nil { corelib.Logger.Error("UserCreateApiHandler, 参数获取失败") corelib.WriteJsonResponse(w, 400, "参数错误", nil) return } - if (res.Username == "") || (res.Password == "") { corelib.Logger.Error("UserCreateApiHandler, 用户名或者密码为空") corelib.WriteJsonResponse(w, 400, "请输入用户名密码", nil) return } - - if isExists := data.SysUserExists(res.Username); !isExists { - corelib.Logger.Error("UserCreateApiHandler, 用户已经存在") + if isExists := data.SysUserExists(res.Username); isExists { + corelib.Logger.Error("UserCreateApiHandler, 用户: " + res.Username + "已经存在") corelib.WriteJsonResponse(w, 10050, "用户已经存在", nil) return } @@ -92,16 +98,17 @@ func UserCreateApiHandler(w http.ResponseWriter, r *http.Request) { func UserPageApiHandler(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodPost { var ( - err error - page int - onePageCount int - count int + err error + page int + pageSize int + total int64 + user []model.SysUser ) query := r.URL.Query() // 搜索条件 search := query.Get("search") // 每页显示多少跳 - if onePageCount, err = strconv.Atoi(query.Get("count")); err != nil { + if pageSize, err = strconv.Atoi(query.Get("pageSize")); err != nil { corelib.Logger.Error("UserPageApiHandler, 获取count参数失败") corelib.WriteJsonResponse(w, 400, "参数错误", nil) return @@ -112,15 +119,49 @@ func UserPageApiHandler(w http.ResponseWriter, r *http.Request) { corelib.WriteJsonResponse(w, 400, "参数错误", nil) return } - numPage := math.Ceil(float64(count) / float64(onePageCount)) - paginationData := corelib.GetPaginationData(int(numPage), page, 2) + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize if search != "" { - - } else { - + if err = corelib.DbMysql.Where("name like ?", "%"+search+"%").Find(&model.SysUser{}).Count(&total).Error; err != nil { + corelib.Logger.Error("UserPageApiHandler, search, 查询total失败") + corelib.WriteJsonResponse(w, 500, "数据查询失败", nil) + } + if err = corelib.DbMysql.Limit(pageSize).Offset(offset).Where("name like ?", "%"+search+"%").Find(&user).Error; err != nil { + corelib.Logger.Error("UserPageApiHandler, search, 查询分页数据失败") + corelib.WriteJsonResponse(w, 500, "数据查询失败", nil) + } + } + if err = corelib.DbMysql.Find(&model.SysUser{}).Count(&total).Error; err != nil { + corelib.Logger.Error("UserPageApiHandler, all, 查询total失败") + corelib.WriteJsonResponse(w, 500, "参数错误", nil) + } + if err = corelib.DbMysql.Limit(pageSize).Offset(offset).Find(&user).Error; err != nil { + corelib.Logger.Error("UserPageApiHandler, all, 查询分页数据失败") + corelib.WriteJsonResponse(w, 500, "参数错误", nil) } - corelib.Logger.Infoln("paginationData: ", paginationData) + numPage := math.Ceil(float64(total) / float64(pageSize)) + if page > int(numPage) { + page = int(numPage) + } + paginationData := corelib.GetPaginationData(int(numPage), page, 2, total) + res := model.UserpageResponse{} + res.PaginationData = paginationData + if total > 0 { + for _, v := range user { + res.Data = append(res.Data, model.UserResponse{ + ID: v.ID, + Username: v.Username, + IsSuper: v.IsSuper, + Remark: v.Remark, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + }) + } + } + corelib.WriteJsonResponse(w, http.StatusOK, "", res) } else { corelib.WriteJsonResponse(w, 405, "方法错误", nil) @@ -128,6 +169,54 @@ func UserPageApiHandler(w http.ResponseWriter, r *http.Request) { } } -func pagination(page, onePageCount int, search string) { +func pagination(page, pageSize int, search string) (model.UserpageResponse, error) { + var ( + err error + user []model.SysUser + total int64 + ) + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + if search != "" { + if err = corelib.DbMysql.Where("name like ?", "%"+search+"%").Find(&model.SysUser{}).Count(&total).Error; err != nil { + corelib.Logger.Error("UserPageApiHandler, search, 查询total失败", err) + return model.UserpageResponse{}, errors.New("数据查询失败") + } + if err = corelib.DbMysql.Limit(pageSize).Offset(offset).Where("name like ?", "%"+search+"%").Find(&user).Error; err != nil { + corelib.Logger.Error("UserPageApiHandler, search, 查询分页数据失败", err) + return model.UserpageResponse{}, errors.New("数据查询失败") + } + } else { + if err = corelib.DbMysql.Find(&model.SysUser{}).Count(&total).Error; err != nil { + corelib.Logger.Error("UserPageApiHandler, all, 查询total失败", err) + return model.UserpageResponse{}, errors.New("数据查询失败") + } + if err = corelib.DbMysql.Limit(pageSize).Offset(offset).Find(&user).Error; err != nil { + corelib.Logger.Error("UserPageApiHandler, all, 查询分页数据失败", err) + return model.UserpageResponse{}, errors.New("数据查询失败") + } + } + numPage := math.Ceil(float64(total) / float64(pageSize)) + if page > int(numPage) { + page = int(numPage) + } + paginationData := corelib.GetPaginationData(int(numPage), page, 2, total) + res := model.UserpageResponse{} + res.PaginationData = paginationData + if total > 0 { + for _, v := range user { + res.Data = append(res.Data, model.UserResponse{ + ID: v.ID, + Username: v.Username, + IsSuper: v.IsSuper, + Remark: v.Remark, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + }) + } + } + return res, nil } diff --git a/middleware/auth.go b/middleware/auth.go index fc0cc47..6b15d9b 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -2,6 +2,7 @@ package middleware import ( "context" + "github.com/gorilla/sessions" "net/http" "picgo/configs" "picgo/corelib" @@ -13,24 +14,32 @@ import ( // LoginMiddleware 登录 // 添加日志中间件到路由器 使用:r.Handle("/", LoginMiddleware(http.HandlerFunc(handler))) func LoginMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var ( + user model.SysUser + err error + session *sessions.Session + ) resPath := r.URL.Path if resPath == "/login" || resPath == "/captcha" || strings.HasPrefix(resPath, "/static") { next.ServeHTTP(w, r) } - session, _ := corelib.SessionStore.Get(r, configs.Settings.Server.SessionName) + if session, err = corelib.SessionStore.Get(r, configs.Settings.Server.SessionName); err == nil { + + } username, ok := session.Values["username"].(string) if !ok || username == "" { http.Redirect(w, r, "/login", http.StatusFound) return } - var ( - user model.SysUser - err error - ) if user, err = data.SysUserSelectByUsername(username); err != nil { http.Redirect(w, r, "/login", http.StatusFound) return } + // 权限判断 + if user.IsSuper != 1 && (resPath != "/" && resPath != "/api/v1/upload") { + http.Error(w, "没有权限访问", 403) + return + } ctx := context.WithValue(r.Context(), "username", user.Username) next.ServeHTTP(w, r.WithContext(ctx)) }) diff --git a/model/user_response.go b/model/user_response.go new file mode 100644 index 0000000..ad630c4 --- /dev/null +++ b/model/user_response.go @@ -0,0 +1,20 @@ +package model + +import ( + "picgo/corelib" + "time" +) + +type UserpageResponse struct { + corelib.PaginationData + Data []UserResponse +} + +type UserResponse struct { + ID int64 `gorm:"primary_key;unique;not null;comment:'主键'" json:"id"` + CreatedAt time.Time `gorm:"not null;type:timestamp;default:CURRENT_TIMESTAMP;comment:'创建时间'"` + UpdatedAt time.Time `gorm:"not null;type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:'更新时间'"` + Username string `gorm:"size:32;unique;not null;column:username;comment:'用户名'" json:"username"` + IsSuper int `gorm:"type:TINYINT(1);default:0;column:is_super;comment:'是否是超级管理员-0:否,1:是'" json:"is_super"` + Remark string `gorm:"size:64;column:remark;comment:'备注'" json:"remark"` +} diff --git a/router/router.go b/router/router.go index 0e2e40f..9a476a0 100644 --- a/router/router.go +++ b/router/router.go @@ -38,6 +38,7 @@ func InitRouter() *mux.Router { // 需要鉴权接口路由 r.Handle("/api/v1/upload", middleware.LoginMiddleware(http.HandlerFunc(handler.UploadFileApiHandler))).Methods(http.MethodPost) // 图片上传接口 r.Handle("/api/v1/user/create", middleware.LoginMiddleware(http.HandlerFunc(handler.UserCreateApiHandler))).Methods(http.MethodPost) // 新增用户 + r.Handle("/api/v1/user/page", middleware.LoginMiddleware(http.HandlerFunc(handler.UserPageApiHandler))).Methods(http.MethodPost) // 分页查询用户 // 应用 CORS CSRF 中间件 http.Handle("/", middleware.CorsMiddleware(CSRF(r))) diff --git a/static/js/lib/common.js b/static/js/lib/common.js index a76ff39..100663c 100644 --- a/static/js/lib/common.js +++ b/static/js/lib/common.js @@ -42,6 +42,18 @@ Common.prototype.AlertConfirm = function (params) { }) } +Common.prototype.AlertSimpleWarn = function (msg) { + if (!msg) { + msg = '失败!' + } + let $warn = $('