This commit is contained in:
新亮
2021-05-15 13:01:30 +08:00
parent ec3c9f0ccf
commit a6947b59ac
60 changed files with 1278 additions and 283 deletions

View File

@@ -17,6 +17,7 @@ const (
ResubmitMsg = 10107
HashIdsDecodeError = 10108
SignatureError = 10109
RBACError = 10110
// 业务模块级错误码
// 用户模块
@@ -47,6 +48,8 @@ const (
AdminModifyPersonalInfoError = 20309
AdminMenuListError = 20310
AdminMenuCreateError = 20311
AdminOfflineError = 20312
AdminDetailError = 20313
// 配置
ConfigEmailError = 20401
@@ -83,6 +86,7 @@ var codeText = map[int]string{
ResubmitMsg: "请勿重复提交",
HashIdsDecodeError: "ID 参数有误",
SignatureError: "Signature Error",
RBACError: "暂无权限,请联系管理开通权限",
IllegalUserName: "非法用户名",
UserCreateError: "创建用户失败",
@@ -109,6 +113,8 @@ var codeText = map[int]string{
AdminModifyPersonalInfoError: "修改个人信息失败",
AdminMenuListError: "获取管理员菜单授权列表失败",
AdminMenuCreateError: "管理员菜单授权失败",
AdminOfflineError: "下线管理员失败",
AdminDetailError: "获取个人信息失败",
ConfigEmailError: "修改邮箱配置失败",
ConfigSaveError: "写入配置文件失败",

View File

@@ -1,20 +1,25 @@
package admin_handler
import (
"encoding/json"
"net/http"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/api/service/admin_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/password"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast"
)
type detailResponse struct {
Username string `json:"username"` // 用户名
Nickname string `json:"nickname"` // 昵称
Mobile string `json:"mobile"` // 手机号
Username string `json:"username"` // 用户名
Nickname string `json:"nickname"` // 昵称
Mobile string `json:"mobile"` // 手机号
Menu []admin_service.ListMyMenuData `json:"menu"` // 菜单栏
}
// Detail 管理员详情
@@ -38,15 +43,29 @@ func (h *handler) Detail() core.HandlerFunc {
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err),
code.AdminDetailError,
code.Text(code.AdminDetailError)).WithErr(err),
)
return
}
menuCacheData, err := h.cache.Get(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(searchOneData.Id)+":menu", cache.WithTrace(c.Trace()))
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AdminDetailError,
code.Text(code.AdminDetailError)).WithErr(err),
)
return
}
var menuData []admin_service.ListMyMenuData
_ = json.Unmarshal([]byte(menuCacheData), &menuData)
res.Username = info.Username
res.Nickname = info.Nickname
res.Mobile = info.Mobile
res.Menu = menuData
c.Payload(res)
}
}

View File

@@ -3,9 +3,11 @@ package admin_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/api/service/admin_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/password"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/xinliangnote/go-gin-api/pkg/time_parse"
@@ -28,6 +30,7 @@ type listData struct {
Nickname string `json:"nickname"` // 昵称
Mobile string `json:"mobile"` // 手机号
IsUsed int `json:"is_used"` // 是否启用 1:是 -1:否
IsOnline int `json:"is_online"` // 是否在线 1:是 -1:否
CreatedAt string `json:"created_at"` // 创建时间
CreatedUser string `json:"created_user"` // 创建人
UpdatedAt string `json:"updated_at"` // 更新时间
@@ -117,6 +120,11 @@ func (h *handler) List() core.HandlerFunc {
h.logger.Info("hashids err", zap.Error(err))
}
isOnline := -1
if h.cache.Exists(configs.RedisKeyPrefixLoginUser + password.GenerateLoginToken(v.Id)) {
isOnline = 1
}
data := listData{
Id: cast.ToInt(v.Id),
HashID: hashId,
@@ -124,6 +132,7 @@ func (h *handler) List() core.HandlerFunc {
Nickname: v.Nickname,
Mobile: v.Mobile,
IsUsed: cast.ToInt(v.IsUsed),
IsOnline: isOnline,
CreatedAt: v.CreatedAt.Format(time_parse.CSTLayout),
CreatedUser: v.CreatedUser,
UpdatedAt: v.UpdatedAt.Format(time_parse.CSTLayout),

View File

@@ -5,14 +5,14 @@ import (
"net/http"
"time"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/api/service/admin_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/password"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/pkg/errors"
"github.com/xinliangnote/go-gin-api/pkg/errors"
)
type loginRequest struct {
@@ -77,8 +77,60 @@ func (h *handler) Login() core.HandlerFunc {
// 用户信息
adminJsonInfo, _ := json.Marshal(info)
// 记录 Redis 中
err = h.cache.Set(h.adminService.CacheKeyPrefix()+token, string(adminJsonInfo), time.Hour*24, cache.WithTrace(c.Trace()))
// 将用户信息记录 Redis 中
err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token, string(adminJsonInfo), time.Hour*24, cache.WithTrace(c.Trace()))
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err),
)
return
}
searchMenuData := new(admin_service.SearchMyMenuData)
searchMenuData.AdminId = info.Id
menu, err := h.adminService.MyMenu(c, searchMenuData)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err),
)
return
}
// 菜单栏信息
menuJsonInfo, _ := json.Marshal(menu)
// 将菜单栏信息记录到 Redis 中
err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token+":menu", string(menuJsonInfo), time.Hour*24, cache.WithTrace(c.Trace()))
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err),
)
return
}
searchActionData := new(admin_service.SearchMyActionData)
searchActionData.AdminId = info.Id
action, err := h.adminService.MyAction(c, searchActionData)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err),
)
return
}
// 可访问接口信息
actionJsonInfo, _ := json.Marshal(action)
// 将可访问接口信息记录到 Redis 中
err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token+":action", string(actionJsonInfo), time.Hour*24, cache.WithTrace(c.Trace()))
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,

View File

@@ -3,14 +3,12 @@ package admin_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/password"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/pkg/errors"
"github.com/spf13/cast"
"github.com/xinliangnote/go-gin-api/pkg/errors"
)
type logoutResponse struct {
@@ -31,7 +29,7 @@ func (h *handler) Logout() core.HandlerFunc {
res := new(logoutResponse)
res.Username = c.UserName()
if !h.cache.Del(h.adminService.CacheKeyPrefix()+password.GenerateLoginToken(cast.ToInt32(c.UserID())), cache.WithTrace(c.Trace())) {
if !h.cache.Del(configs.RedisKeyPrefixLoginUser+c.GetHeader(configs.LoginToken), cache.WithTrace(c.Trace())) {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AdminLogOutError,

View File

@@ -0,0 +1,70 @@
package admin_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/password"
"github.com/xinliangnote/go-gin-api/pkg/errno"
)
type offlineRequest struct {
Id string `form:"id"` // 主键ID
}
type offlineResponse struct {
Id int32 `json:"id"` // 主键ID
}
// Offline 下线管理员
// @Summary 下线管理员
// @Description 下线管理员
// @Tags API.admin
// @Accept multipart/form-data
// @Produce json
// @Param id formData string true "Hashid"
// @Success 200 {object} offlineResponse
// @Failure 400 {object} code.Failure
// @Router /api/admin/offline [patch]
func (h *handler) Offline() core.HandlerFunc {
return func(c core.Context) {
req := new(offlineRequest)
res := new(offlineResponse)
if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err),
)
return
}
id := int32(ids[0])
b := h.cache.Del(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(id), cache.WithTrace(c.Trace()))
if !b {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AdminOfflineError,
code.Text(code.AdminOfflineError)),
)
return
}
res.Id = id
c.Payload(res)
}
}

View File

@@ -56,6 +56,11 @@ type Handler interface {
// @Router /api/admin/{id} [delete]
Delete() core.HandlerFunc
// Offline 下线管理员
// @Tags API.admin
// @Router /api/admin/offline [patch]
Offline() core.HandlerFunc
// UpdateUsed 更新管理员为启用/禁用
// @Tags API.admin
// @Router /api/admin/used [patch]

View File

@@ -60,8 +60,8 @@ func (h *handler) Email() core.HandlerFunc {
MailUser: req.User,
MailPass: req.Pass,
MailTo: req.To,
Subject: fmt.Sprintf("%s[%s] 邮箱告警人调整通知。", configs.ProjectName(), env.Active().Value()),
Body: fmt.Sprintf("%s[%s] 已添加您为系统告警通知人。", configs.ProjectName(), env.Active().Value()),
Subject: fmt.Sprintf("%s[%s] 邮箱告警人调整通知。", configs.ProjectName, env.Active().Value()),
Body: fmt.Sprintf("%s[%s] 已添加您为系统告警通知人。", configs.ProjectName, env.Active().Value()),
}
if err := mail.Send(options); err != nil {
c.AbortWithError(errno.NewError(

View File

@@ -20,6 +20,7 @@ type listData struct {
Link string `json:"link"` // 链接地址
Icon string `json:"icon"` // 图标
IsUsed int32 `json:"is_used"` // 是否启用 1=启用 -1=禁用
Sort int32 `json:"sort"` // 排序
}
type listResponse struct {
@@ -64,6 +65,7 @@ func (h *handler) List() core.HandlerFunc {
Link: v.Link,
Icon: v.Icon,
IsUsed: v.IsUsed,
Sort: v.Sort,
}
res.List[k] = data

View File

@@ -0,0 +1,69 @@
package menu_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
)
type updateSortRequest struct {
Id string `form:"id"` // HashId
Sort int32 `form:"sort"` // 排序
}
type updateSortResponse struct {
Id int32 `json:"id"` // 主键ID
}
// UpdateSort 更新菜单排序
// @Summary 更新菜单排序
// @Description 更新菜单排序
// @Tags API.menu
// @Accept multipart/form-data
// @Produce json
// @Param id formData string true "Hashid"
// @Param sort formData int true "排序"
// @Success 200 {object} updateSortResponse
// @Failure 400 {object} code.Failure
// @Router /api/menu/sort [patch]
func (h *handler) UpdateSort() core.HandlerFunc {
return func(c core.Context) {
req := new(updateSortRequest)
res := new(updateSortResponse)
if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err),
)
return
}
id := int32(ids[0])
err = h.menuService.UpdateSort(c, id, req.Sort)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MenuUpdateError,
code.Text(code.MenuUpdateError)).WithErr(err),
)
return
}
res.Id = id
c.Payload(res)
}
}

View File

@@ -21,7 +21,7 @@ type updateUsedResponse struct {
// @Summary 更新菜单为启用/禁用
// @Description 更新菜单为启用/禁用
// @Tags API.menu
// @Accept json
// @Accept multipart/form-data
// @Produce json
// @Param id formData string true "Hashid"
// @Param used formData int true "是否启用 1:是 -1:否"

View File

@@ -36,6 +36,11 @@ type Handler interface {
// @Router /api/menu/used [patch]
UpdateUsed() core.HandlerFunc
// UpdateSort 更新菜单排序
// @Tags API.menu
// @Router /api/menu/sort [patch]
UpdateSort() core.HandlerFunc
// List 菜单列表
// @Tags API.menu
// @Router /api/menu [get]

View File

@@ -377,6 +377,49 @@ func (qb *menuRepoQueryBuilder) OrderByLevel(asc bool) *menuRepoQueryBuilder {
return qb
}
func (qb *menuRepoQueryBuilder) WhereSort(p db_repo.Predicate, value int32) *menuRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "sort", p),
value,
})
return qb
}
func (qb *menuRepoQueryBuilder) WhereSortIn(value []int32) *menuRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "sort", "IN"),
value,
})
return qb
}
func (qb *menuRepoQueryBuilder) WhereSortNotIn(value []int32) *menuRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "sort", "NOT IN"),
value,
})
return qb
}
func (qb *menuRepoQueryBuilder) OrderBySort(asc bool) *menuRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "sort "+order)
return qb
}
func (qb *menuRepoQueryBuilder) WhereIsUsed(p db_repo.Predicate, value int32) *menuRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string

View File

@@ -11,6 +11,7 @@ type Menu struct {
Link string // 链接地址
Icon string // 图标
Level int32 // 菜单类型 1:一级菜单 2:二级菜单
Sort int32 // 排序
IsUsed int32 // 是否启用 1:是 -1:否
IsDeleted int32 // 是否删除 1:是 -1:否
CreatedAt time.Time `gorm:"time"` // 创建时间

View File

@@ -9,9 +9,10 @@
| 4 | link | 链接地址 | varchar(100) | | NO | | |
| 5 | icon | 图标 | varchar(60) | | NO | | |
| 6 | level | 菜单类型 1:一级菜单 2:二级菜单 | tinyint(1) unsigned | | NO | | 1 |
| 7 | is_used | 是否启用 1:是 -1:否 | tinyint(1) | | NO | | 1 |
| 8 | is_deleted | 是否删除 1:是 -1:否 | tinyint(1) | | NO | | -1 |
| 9 | created_at | 创建时间 | timestamp | | NO | | CURRENT_TIMESTAMP |
| 10 | created_user | 创建 | varchar(60) | | NO | | |
| 11 | updated_at | 更新时间 | timestamp | | NO | on update CURRENT_TIMESTAMP | CURRENT_TIMESTAMP |
| 12 | updated_user | 更新 | varchar(60) | | NO | | |
| 7 | sort | 排序 | int(11) unsigned | | NO | | 0 |
| 8 | is_used | 是否启用 1:是 -1:否 | tinyint(1) | | NO | | 1 |
| 9 | is_deleted | 是否删除 1:是 -1:否 | tinyint(1) | | NO | | -1 |
| 10 | created_at | 创建时间 | timestamp | | NO | | CURRENT_TIMESTAMP |
| 11 | created_user | 创建人 | varchar(60) | | NO | | |
| 12 | updated_at | 更新时间 | timestamp | | NO | on update CURRENT_TIMESTAMP | CURRENT_TIMESTAMP |
| 13 | updated_user | 更新人 | varchar(60) | | NO | | |

View File

@@ -1,8 +1,8 @@
package admin_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/admin_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/menu_action_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
@@ -10,12 +10,8 @@ import (
var _ Service = (*service)(nil)
// 定义缓存前缀
var cacheKeyPrefix = configs.ProjectName() + ":admin:"
type Service interface {
i()
CacheKeyPrefix() (pre string)
Create(ctx core.Context, adminData *CreateAdminData) (id int32, err error)
PageList(ctx core.Context, searchData *SearchData) (listData []*admin_repo.Admin, err error)
@@ -29,6 +25,8 @@ type Service interface {
CreateMenu(ctx core.Context, menuData *CreateMenuData) (err error)
ListMenu(ctx core.Context, searchData *SearchListMenuData) (menuData []ListMenuData, err error)
MyMenu(ctx core.Context, searchData *SearchMyMenuData) (menuData []ListMyMenuData, err error)
MyAction(ctx core.Context, searchData *SearchMyActionData) (actionData []*menu_action_repo.MenuAction, err error)
}
type service struct {
@@ -44,8 +42,3 @@ func New(db db.Repo, cache cache.Repo) Service {
}
func (s *service) i() {}
func (s *service) CacheKeyPrefix() (pre string) {
pre = cacheKeyPrefix
return
}

View File

@@ -1,6 +1,7 @@
package admin_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/admin_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
@@ -21,6 +22,6 @@ func (s *service) Delete(ctx core.Context, id int32) (err error) {
return err
}
s.cache.Del(cacheKeyPrefix+password.GenerateLoginToken(id), cache.WithTrace(ctx.Trace()))
s.cache.Del(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(id), cache.WithTrace(ctx.Trace()))
return
}

View File

@@ -22,7 +22,7 @@ func (s *service) ListMenu(ctx core.Context, searchData *SearchListMenuData) (me
menuQb := menu_repo.NewQueryBuilder()
menuQb.WhereIsDeleted(db_repo.EqualPredicate, -1)
menuListData, err := menuQb.
OrderById(false).
OrderBySort(true).
QueryAll(s.db.GetDbR().WithContext(ctx.RequestContext()))
if err != nil {
return nil, err

View File

@@ -1,6 +1,7 @@
package admin_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/admin_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
@@ -21,6 +22,6 @@ func (s *service) ModifyPassword(ctx core.Context, id int32, newPassword string)
return err
}
s.cache.Del(cacheKeyPrefix+password.GenerateLoginToken(id), cache.WithTrace(ctx.Trace()))
s.cache.Del(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(id), cache.WithTrace(ctx.Trace()))
return
}

View File

@@ -0,0 +1,45 @@
package admin_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/admin_menu_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/menu_action_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
type SearchMyActionData struct {
AdminId int32 `json:"admin_id"` // 管理员ID
}
func (s *service) MyAction(ctx core.Context, searchData *SearchMyActionData) (actionData []*menu_action_repo.MenuAction, err error) {
adminMenuQb := admin_menu_repo.NewQueryBuilder()
if searchData.AdminId != 0 {
adminMenuQb.WhereAdminId(db_repo.EqualPredicate, searchData.AdminId)
}
adminMenuListData, err := adminMenuQb.
OrderById(false).
QueryAll(s.db.GetDbR().WithContext(ctx.RequestContext()))
if err != nil {
return nil, err
}
if len(adminMenuListData) <= 0 {
return
}
var menuIds []int32
for _, v := range adminMenuListData {
menuIds = append(menuIds, v.MenuId)
}
actionQb := menu_action_repo.NewQueryBuilder()
actionQb.WhereIsDeleted(db_repo.EqualPredicate, -1)
actionQb.WhereMenuIdIn(menuIds)
actionData, err = actionQb.QueryAll(s.db.GetDbR().WithContext(ctx.RequestContext()))
if err != nil {
return nil, err
}
return
}

View File

@@ -0,0 +1,69 @@
package admin_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/admin_menu_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/menu_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
type SearchMyMenuData struct {
AdminId int32 `json:"admin_id"` // 管理员ID
}
type ListMyMenuData struct {
Id int32 `json:"id"` // ID
Pid int32 `json:"pid"` // 父类ID
Name string `json:"name"` // 菜单名称
Link string `json:"link"` // 链接地址
Icon string `json:"icon"` // 图标
}
func (s *service) MyMenu(ctx core.Context, searchData *SearchMyMenuData) (menuData []ListMyMenuData, err error) {
adminMenuQb := admin_menu_repo.NewQueryBuilder()
if searchData.AdminId != 0 {
adminMenuQb.WhereAdminId(db_repo.EqualPredicate, searchData.AdminId)
}
adminMenuListData, err := adminMenuQb.
OrderById(false).
QueryAll(s.db.GetDbR().WithContext(ctx.RequestContext()))
if err != nil {
return nil, err
}
if len(adminMenuListData) <= 0 {
return
}
menuQb := menu_repo.NewQueryBuilder()
menuQb.WhereIsDeleted(db_repo.EqualPredicate, -1)
menuListData, err := menuQb.
OrderBySort(true).
QueryAll(s.db.GetDbR().WithContext(ctx.RequestContext()))
if err != nil {
return nil, err
}
if len(menuListData) <= 0 {
return
}
for _, menuAllV := range menuListData {
for _, v := range adminMenuListData {
if menuAllV.Id == v.MenuId {
data := ListMyMenuData{
Id: menuAllV.Id,
Pid: menuAllV.Pid,
Name: menuAllV.Name,
Link: menuAllV.Link,
Icon: menuAllV.Icon,
}
menuData = append(menuData, data)
}
}
}
return
}

View File

@@ -1,6 +1,7 @@
package admin_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/admin_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
@@ -21,6 +22,6 @@ func (s *service) ResetPassword(ctx core.Context, id int32) (err error) {
return err
}
s.cache.Del(cacheKeyPrefix+password.GenerateLoginToken(id), cache.WithTrace(ctx.Trace()))
s.cache.Del(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(id), cache.WithTrace(ctx.Trace()))
return
}

View File

@@ -1,6 +1,7 @@
package admin_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/admin_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
@@ -21,6 +22,6 @@ func (s *service) UpdateUsed(ctx core.Context, id int32, used int32) (err error)
return err
}
s.cache.Del(cacheKeyPrefix+password.GenerateLoginToken(id), cache.WithTrace(ctx.Trace()))
s.cache.Del(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(id), cache.WithTrace(ctx.Trace()))
return
}

View File

@@ -1,7 +1,6 @@
package authorized_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_api_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
@@ -11,9 +10,6 @@ import (
var _ Service = (*service)(nil)
// 定义缓存前缀
var cacheKeyPrefix = configs.ProjectName() + ":authorized:"
type Service interface {
i()

View File

@@ -1,6 +1,7 @@
package authorized_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_api_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
@@ -25,6 +26,6 @@ func (s *service) CreateAPI(ctx core.Context, authorizedAPIData *CreateAuthorize
return 0, err
}
s.cache.Del(cacheKeyPrefix+authorizedAPIData.BusinessKey, cache.WithTrace(ctx.Trace()))
s.cache.Del(configs.RedisKeyPrefixSignature+authorizedAPIData.BusinessKey, cache.WithTrace(ctx.Trace()))
return
}

View File

@@ -1,6 +1,7 @@
package authorized_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
@@ -32,6 +33,6 @@ func (s *service) Delete(ctx core.Context, id int32) (err error) {
return err
}
s.cache.Del(cacheKeyPrefix+authorizedInfo.BusinessKey, cache.WithTrace(ctx.Trace()))
s.cache.Del(configs.RedisKeyPrefixSignature+authorizedInfo.BusinessKey, cache.WithTrace(ctx.Trace()))
return
}

View File

@@ -1,6 +1,7 @@
package authorized_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_api_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
@@ -32,6 +33,6 @@ func (s *service) DeleteAPI(ctx core.Context, id int32) (err error) {
return err
}
s.cache.Del(cacheKeyPrefix+authorizedApiInfo.BusinessKey, cache.WithTrace(ctx.Trace()))
s.cache.Del(configs.RedisKeyPrefixSignature+authorizedApiInfo.BusinessKey, cache.WithTrace(ctx.Trace()))
return
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/json"
"time"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_api_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo"
@@ -26,7 +27,7 @@ type cacheApiData struct {
func (s *service) DetailByKey(ctx core.Context, key string) (cacheData *CacheAuthorizedData, err error) {
// 查询缓存
cacheKey := cacheKeyPrefix + key
cacheKey := configs.RedisKeyPrefixSignature + key
value, err := s.cache.Get(cacheKey, cache.WithTrace(ctx.RequestContext().Trace))
cacheData = new(CacheAuthorizedData)

View File

@@ -1,6 +1,7 @@
package authorized_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
@@ -31,6 +32,6 @@ func (s *service) UpdateUsed(ctx core.Context, id int32, used int32) (err error)
return err
}
s.cache.Del(cacheKeyPrefix+authorizedInfo.BusinessKey, cache.WithTrace(ctx.Trace()))
s.cache.Del(configs.RedisKeyPrefixSignature+authorizedInfo.BusinessKey, cache.WithTrace(ctx.Trace()))
return
}

View File

@@ -1,7 +1,6 @@
package menu_service
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/menu_action_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/menu_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
@@ -11,17 +10,14 @@ import (
var _ Service = (*service)(nil)
// 定义缓存前缀
var cacheKeyPrefix = configs.ProjectName() + ":admin:"
type Service interface {
i()
CacheKeyPrefix() (pre string)
Create(ctx core.Context, menuData *CreateMenuData) (id int32, err error)
Modify(ctx core.Context, id int32, menuData *UpdateMenuData) (err error)
List(ctx core.Context, searchData *SearchData) (listData []*menu_repo.Menu, err error)
UpdateUsed(ctx core.Context, id int32, used int32) (err error)
UpdateSort(ctx core.Context, id int32, sort int32) (err error)
Delete(ctx core.Context, id int32) (err error)
Detail(ctx core.Context, searchOneData *SearchOneData) (info *menu_repo.Menu, err error)
@@ -43,8 +39,3 @@ func New(db db.Repo, cache cache.Repo) Service {
}
func (s *service) i() {}
func (s *service) CacheKeyPrefix() (pre string) {
pre = cacheKeyPrefix
return
}

View File

@@ -20,7 +20,7 @@ func (s *service) List(ctx core.Context, searchData *SearchData) (listData []*me
}
listData, err = qb.
OrderById(false).
OrderBySort(true).
QueryAll(s.db.GetDbR().WithContext(ctx.RequestContext()))
if err != nil {
return nil, err

View File

@@ -0,0 +1,23 @@
package menu_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/menu_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
func (s *service) UpdateSort(ctx core.Context, id int32, sort int32) (err error) {
data := map[string]interface{}{
"sort": sort,
"updated_user": ctx.UserName(),
}
qb := menu_repo.NewQueryBuilder()
qb.WhereId(db_repo.EqualPredicate, id)
err = qb.Updates(s.db.GetDbW().WithContext(ctx.RequestContext()), data)
if err != nil {
return err
}
return
}

View File

@@ -4,11 +4,11 @@ import (
"time"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/pkg/errors"
"github.com/xinliangnote/go-gin-api/pkg/time_parse"
"github.com/xinliangnote/go-gin-api/pkg/trace"
"github.com/go-redis/redis/v7"
"github.com/pkg/errors"
)
type Option func(*option)

View File

@@ -111,11 +111,11 @@ type Context interface {
// SetHeader 设置 Header
SetHeader(key, value string)
// UserID 获取 JWT 中 UserID
// UserID 获取 UserID
UserID() int64
setUserID(userID int64)
// UserName 获取 JWT 中 UserName
// UserName 获取 UserName
UserName() string
setUserName(userName string)

View File

@@ -252,7 +252,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
}
fmt.Println(color.Blue(_UI))
fmt.Println(color.Green(fmt.Sprintf("* [register port %s]", configs.ProjectPort())))
fmt.Println(color.Green(fmt.Sprintf("* [register port %s]", configs.ProjectPort)))
fmt.Println(color.Green(fmt.Sprintf("* [register env %s]", env.Active().Value())))
mux.engine.StaticFS("bootstrap", http.Dir("./assets/bootstrap"))

View File

@@ -7,9 +7,8 @@ import (
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/xinliangnote/go-gin-api/pkg/errors"
"github.com/xinliangnote/go-gin-api/pkg/token"
"github.com/pkg/errors"
)
func (m *middleware) Jwt(ctx core.Context) (userId int64, userName string, err errno.Error) {

View File

@@ -0,0 +1,77 @@
package middleware
import (
"encoding/json"
"net/http"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/menu_action_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/xinliangnote/go-gin-api/pkg/errors"
"github.com/xinliangnote/go-gin-api/pkg/urltable"
)
func (m *middleware) RBAC() core.HandlerFunc {
return func(c core.Context) {
token := c.GetHeader("Token")
if token == "" {
c.AbortWithError(errno.NewError(
http.StatusUnauthorized,
code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中缺少 Token 参数")),
)
return
}
if !m.cache.Exists(configs.RedisKeyPrefixLoginUser + token) {
c.AbortWithError(errno.NewError(
http.StatusUnauthorized,
code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errors.New("请先登录 1")),
)
return
}
if !m.cache.Exists(configs.RedisKeyPrefixLoginUser + token + ":action") {
c.AbortWithError(errno.NewError(
http.StatusUnauthorized,
code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errors.New("请先登录 2")),
)
return
}
actionData, err := m.cache.Get(configs.RedisKeyPrefixLoginUser+token+":action", cache.WithTrace(c.Trace()))
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusUnauthorized,
code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(err),
)
return
}
var actions []menu_action_repo.MenuAction
_ = json.Unmarshal([]byte(actionData), &actions)
if len(actions) > 0 {
table := urltable.NewTable()
for _, v := range actions {
_ = table.Append(v.Method + v.Api)
}
if pattern, _ := table.Mapping(c.Method() + c.Path()); pattern == "" {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.RBACError,
code.Text(code.RBACError)).WithErr(errors.New(c.Method() + c.Path() + " 未进行 RBAC 授权")),
)
return
}
}
}
}

View File

@@ -15,9 +15,6 @@ import (
)
func (m *middleware) Resubmit() core.HandlerFunc {
redisKeyPrefix := configs.ProjectName() + ":request-id:"
return func(c core.Context) {
cfg := configs.Get().URLToken
@@ -31,7 +28,7 @@ func (m *middleware) Resubmit() core.HandlerFunc {
return
}
redisKey := redisKeyPrefix + tokenString
redisKey := configs.RedisKeyPrefixRequestID + tokenString
if !m.cache.Exists(redisKey) {
err = m.cache.Set(redisKey, "1", time.Minute*cfg.ExpireDuration)
if err != nil {

View File

@@ -5,13 +5,13 @@ import (
"strings"
"time"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/xinliangnote/go-gin-api/pkg/errors"
"github.com/xinliangnote/go-gin-api/pkg/signature"
"github.com/xinliangnote/go-gin-api/pkg/urltable"
"github.com/pkg/errors"
)
const ttl = time.Minute * 2 // 签名超时时间 2 分钟
@@ -23,7 +23,7 @@ var whiteListPath = map[string]bool{
func (m *middleware) Signature() core.HandlerFunc {
return func(c core.Context) {
// 签名信息
authorization := c.GetHeader("Authorization")
authorization := c.GetHeader(configs.SignToken)
if authorization == "" {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
@@ -34,7 +34,7 @@ func (m *middleware) Signature() core.HandlerFunc {
}
// 时间信息
date := c.GetHeader("Authorization-Date")
date := c.GetHeader(configs.SignTokenDate)
if date == "" {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,

View File

@@ -4,16 +4,16 @@ import (
"encoding/json"
"net/http"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/pkg/errors"
"github.com/xinliangnote/go-gin-api/pkg/errors"
)
func (m *middleware) Token(ctx core.Context) (userId int64, userName string, err errno.Error) {
token := ctx.GetHeader("Token")
token := ctx.GetHeader(configs.LoginToken)
if token == "" {
err = errno.NewError(
http.StatusUnauthorized,
@@ -23,7 +23,7 @@ func (m *middleware) Token(ctx core.Context) (userId int64, userName string, err
return
}
if !m.cache.Exists(m.adminService.CacheKeyPrefix() + token) {
if !m.cache.Exists(configs.RedisKeyPrefixLoginUser + token) {
err = errno.NewError(
http.StatusUnauthorized,
code.AuthorizationError,
@@ -32,7 +32,7 @@ func (m *middleware) Token(ctx core.Context) (userId int64, userName string, err
return
}
cacheData, cacheErr := m.cache.Get(m.adminService.CacheKeyPrefix()+token, cache.WithTrace(ctx.Trace()))
cacheData, cacheErr := m.cache.Get(configs.RedisKeyPrefixLoginUser+token, cache.WithTrace(ctx.Trace()))
if cacheErr != nil {
err = errno.NewError(
http.StatusUnauthorized,

View File

@@ -31,6 +31,9 @@ type Middleware interface {
// Token 签名验证,对登录用户的验证
Token(ctx core.Context) (userId int64, userName string, err errno.Error)
// RBAC 权限验证
RBAC() core.HandlerFunc
}
type middleware struct {

View File

@@ -9,9 +9,9 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/metrics"
"github.com/xinliangnote/go-gin-api/internal/pkg/notify"
"github.com/xinliangnote/go-gin-api/internal/router/middleware"
"github.com/xinliangnote/go-gin-api/pkg/errors"
"github.com/xinliangnote/go-gin-api/pkg/file"
"github.com/pkg/errors"
"go.uber.org/zap"
)
@@ -39,9 +39,9 @@ func NewHTTPServer(logger *zap.Logger) (*Server, error) {
r := new(resource)
r.logger = logger
openBrowserUri := "http://127.0.0.1" + configs.ProjectPort()
openBrowserUri := "http://127.0.0.1" + configs.ProjectPort
_, ok := file.IsExists(configs.ProjectInstallFile())
_, ok := file.IsExists(configs.ProjectInstallMark)
if !ok { // 未安装
openBrowserUri += "/install"
} else { // 已安装

View File

@@ -30,7 +30,7 @@ func setApiRouter(r *resource) {
}
// 需要签名验证、登录验证、RBAC 权限验证
api := r.mux.Group("/api", core.WrapAuthHandler(r.middles.Token), r.middles.Signature())
api := r.mux.Group("/api", core.WrapAuthHandler(r.middles.Token), r.middles.Signature(), r.middles.RBAC())
{
// authorized
authorizedHandler := authorized_handler.New(r.logger, r.db, r.cache)
@@ -46,6 +46,7 @@ func setApiRouter(r *resource) {
api.POST("/admin", adminHandler.Create())
api.GET("/admin", adminHandler.List())
api.PATCH("/admin/used", adminHandler.UpdateUsed())
api.PATCH("/admin/offline", adminHandler.Offline())
api.PATCH("/admin/reset_password/:id", core.AliasForRecordMetrics("/api/admin/reset_password"), adminHandler.ResetPassword())
api.DELETE("/admin/:id", core.AliasForRecordMetrics("/api/admin"), adminHandler.Delete())
@@ -58,6 +59,7 @@ func setApiRouter(r *resource) {
api.GET("/menu", menuHandler.List())
api.GET("/menu/:id", core.AliasForRecordMetrics("/api/menu"), menuHandler.Detail())
api.PATCH("/menu/used", menuHandler.UpdateUsed())
api.PATCH("/menu/sort", menuHandler.UpdateSort())
api.DELETE("/menu/:id", core.AliasForRecordMetrics("/api/menu"), menuHandler.Delete())
api.POST("/menu_action", menuHandler.CreateAction())
api.GET("/menu_action", menuHandler.ListAction())

View File

@@ -8,6 +8,7 @@ import (
"strings"
"time"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/env"
@@ -48,6 +49,8 @@ type viewResponse struct {
Host string
GoOS string
GoArch string
ProjectVersion string
}
func (h *handler) View() core.HandlerFunc {
@@ -88,6 +91,7 @@ func (h *handler) View() core.HandlerFunc {
obj.Env = env.Active().Value()
obj.GoOS = runtime.GOOS
obj.GoArch = runtime.GOARCH
obj.ProjectVersion = configs.ProjectVersion
c.HTML("dashboard", obj)
}

View File

@@ -260,7 +260,7 @@ func (h *handler) Execute() core.HandlerFunc {
outPutString += "初始化 MySQL 数据表admin_menu 默认数据成功。\n"
// 生成 install 完成标识
f, err := os.Create(configs.ProjectInstallFile())
f, err := os.Create(configs.ProjectInstallMark)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,

View File

@@ -7,6 +7,7 @@ package mysql_table
//`link` varchar(100) NOT NULL DEFAULT '' COMMENT '链接地址',
//`icon` varchar(60) NOT NULL DEFAULT '' COMMENT '图标',
//`level` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '菜单类型 1:一级菜单 2:二级菜单',
//`sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序',
//`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用 1:是 -1:否',
//`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',
//`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -24,6 +25,7 @@ func CreateMenuTableSql() (sql string) {
sql += "`link` varchar(100) NOT NULL DEFAULT '' COMMENT '链接地址',"
sql += "`icon` varchar(60) NOT NULL DEFAULT '' COMMENT '图标',"
sql += "`level` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '菜单类型 1:一级菜单 2:二级菜单',"
sql += "`sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序',"
sql += "`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用 1:是 -1:否',"
sql += "`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',"
sql += "`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',"
@@ -37,29 +39,29 @@ func CreateMenuTableSql() (sql string) {
}
func CreateMenuTableDataSql() (sql string) {
sql = "INSERT INTO `menu` (`id`, `pid`, `name`, `link`, `icon`, `level`, `created_user`) VALUES"
sql += "(1, 0, '配置信息', '', 'mdi-settings-box', 1, 'init'),"
sql += "(2, 1, '告警邮箱', '/config/email', '', 2, 'init'),"
sql += "(3, 1, '错误码', '/config/code', '', 2, 'init'),"
sql += "(4, 0, '代码生成器', '', 'mdi-code-not-equal-variant', 1, 'init'),"
sql += "(5, 4, '生成数据表 CURD', '/generator/gorm', '', 2, 'init'),"
sql += "(6, 4, '生成控制器方法', '/generator/handler', '', 2, 'init'),"
sql += "(7, 0, '授权调用方', '', 'mdi-playlist-check', 1, 'init'),"
sql += "(8, 7, '调用方', '/authorized/list', '', 2, 'init'),"
sql += "(9, 7, '使用说明', '/authorized/demo', '', 2, 'init'),"
sql += "(10, 0, '系统管理员', '', 'mdi-account', 1, 'init'),"
sql += "(11, 10, '管理员', '/admin/list', '', 2, 'init'),"
sql += "(12, 10, '菜单管理', '/admin/menu', '', 2, 'init'),"
sql += "(13, 0, '查询小助手', '', 'mdi-database-search', 1, 'init'),"
sql += "(14, 13, '查询缓存', '/tool/cache', '', 2, 'init'),"
sql += "(15, 13, '查询数据', '/tool/data', '', 2, 'init'),"
sql += "(16, 0, '实用工具箱', '', 'mdi-tools', 1, 'init'),"
sql += "(17, 16, 'Hashids', '/tool/hashids', '', 2, 'init'),"
sql += "(18, 16, '调用日志', '/tool/logs', '', 2, 'init'),"
sql += "(19, 16, '接口文档', '/swagger/index.html', '', 2, 'init'),"
sql += "(20, 16, 'GraphQL', '/graphql', '', 2, 'init'),"
sql += "(21, 16, '接口指标', '/metrics', '', 2, 'init'),"
sql += "(22, 16, '服务升级', '/upgrade', '', 2, 'init');"
sql = "INSERT INTO `menu` (`id`, `pid`, `name`, `link`, `icon`, `level`, `sort`, `created_user`) VALUES"
sql += "(1, 0, '配置信息', '', 'mdi-settings-box', 1, 1, 'init'),"
sql += "(2, 1, '告警邮箱', '/config/email', '', 2, 11, 'init'),"
sql += "(3, 1, '错误码', '/config/code', '', 2, 12, 'init'),"
sql += "(4, 0, '代码生成器', '', 'mdi-code-not-equal-variant', 1, 2, 'init'),"
sql += "(5, 4, '生成数据表 CURD', '/generator/gorm', '', 2, 21, 'init'),"
sql += "(6, 4, '生成控制器方法', '/generator/handler', '', 2, 22, 'init'),"
sql += "(7, 0, '授权调用方', '', 'mdi-playlist-check', 1, 3, 'init'),"
sql += "(8, 7, '调用方', '/authorized/list', '', 2, 31, 'init'),"
sql += "(9, 7, '使用说明', '/authorized/demo', '', 2, 32, 'init'),"
sql += "(10, 0, '系统管理员', '', 'mdi-account', 1, 4, 'init'),"
sql += "(11, 10, '管理员', '/admin/list', '', 2, 41, 'init'),"
sql += "(12, 10, '菜单管理', '/admin/menu', '', 2, 42, 'init'),"
sql += "(13, 0, '查询小助手', '', 'mdi-database-search', 1, 5, 'init'),"
sql += "(14, 13, '查询缓存', '/tool/cache', '', 2, 51, 'init'),"
sql += "(15, 13, '查询数据', '/tool/data', '', 2, 52, 'init'),"
sql += "(16, 0, '实用工具箱', '', 'mdi-tools', 1, 6, 'init'),"
sql += "(17, 16, 'Hashids', '/tool/hashids', '', 2, 62, 'init'),"
sql += "(18, 16, '调用日志', '/tool/logs', '', 2, 63, 'init'),"
sql += "(19, 16, '接口文档', '/swagger/index.html', '', 2, 64, 'init'),"
sql += "(20, 16, 'GraphQL', '/graphql', '', 2, 65, 'init'),"
sql += "(21, 16, '接口指标', '/metrics', '', 2, 66, 'init'),"
sql += "(22, 16, '服务升级', '/upgrade', '', 2, 61, 'init');"
return
}

View File

@@ -70,7 +70,9 @@ func CreateMenuActionTableDataSql() (sql string) {
sql += "(34, 12, 'GET', '/api/menu_action', 'init'),"
sql += "(35, 12, 'POST', '/api/menu_action', 'init'),"
sql += "(36, 12, 'DELETE', '/api/menu_action/*', 'init'),"
sql += "(37, 22, 'POST', '/upgrade/execute', 'init');"
sql += "(37, 22, 'POST', '/upgrade/execute', 'init'),"
sql += "(38, 11, 'PATCH', '/api/admin/offline', 'init'),"
sql += "(39, 12, 'PATCH', '/api/menu/sort', 'init');"
return
}

View File

@@ -44,7 +44,7 @@ func (h *handler) LogsView() core.HandlerFunc {
}
return func(c core.Context) {
readLineFromEnd, err := file.NewReadLineFromEnd(configs.ProjectLogFile())
readLineFromEnd, err := file.NewReadLineFromEnd(configs.ProjectLogFile)
if err != nil {
h.logger.Error("NewReadLineFromEnd err", zap.Error(err))
}

View File

@@ -8,7 +8,8 @@ import (
)
type upgradeViewResponse struct {
List []upgradeViewData `json:"list"`
LockFile string `json:"lock_file"`
List []upgradeViewData `json:"list"`
}
type upgradeViewData struct {
@@ -68,6 +69,7 @@ func (h *handler) UpgradeView() core.HandlerFunc {
obj := new(upgradeViewResponse)
obj.List = tableData
obj.LockFile = configs.ProjectInstallMark
c.HTML("upgrade_view", obj)
}
}