diff --git a/assets/templates/admin/admin_list.html b/assets/templates/admin/admin_list.html index 61cad852..3cb71a15 100644 --- a/assets/templates/admin/admin_list.html +++ b/assets/templates/admin/admin_list.html @@ -32,7 +32,8 @@ 手机号 创建日期 更新日期 - 状态 + 可用状态 + 在线状态 操作 @@ -102,6 +103,7 @@ $.each(data.list, function (index, value) { var showUsedBadge = ""; + var showOnlineBadge = ""; var optionUsedName = ""; if (value.is_used === 1) { @@ -114,6 +116,14 @@ showUsedBadge = '禁用' } + if (value.is_online === 1) { + showOnlineBadge = '在线' + } + + if (value.is_online === -1) { + showOnlineBadge = '离线' + } + const tr = '\n' + '' + value.id + '\n' + '' + value.username + '\n' + @@ -122,6 +132,7 @@ '' + value.created_at + '\n' + '' + value.updated_at + '\n' + '' + showUsedBadge + '\n' + + '' + showOnlineBadge + '\n' + '\n' + '
\n' + ' 菜单授权\n' + + ' 下线\n' + ' 删除\n' + @@ -280,6 +294,57 @@ location.href = "/admin/action/" + $(this).attr('data-id'); }); + // 下线 + $(document).on('click', '.btn-offline', function () { + const id = $(this).attr('data-id'); + + $.confirm({ + title: '谨慎操作', + content: '确认要 下线 吗?', + icon: 'mdi mdi-alert', + animation: 'scale', + closeAnimation: 'zoom', + buttons: { + okay: { + text: '确认', + keys: ['enter'], + btnClass: 'btn-orange', + action: function () { + AjaxForm( + "PATCH", + '/api/admin/offline', + {id: id}, + function () {}, + function (data) { + $.alert({ + title: '操作成功', + icon: 'mdi mdi-check-decagram', + type: 'green', + content: '编号:' + data.id + ' 已下线。', + buttons: { + okay: { + text: '关闭', + action: function () { + location.reload(); + } + } + } + }); + }, + function (response) { + AjaxError(response); + } + ); + } + }, + cancel: { + text: '取消', + keys: ['ctrl', 'shift'], + } + } + }); + }); + // 删除 $(document).on('click', '.btn-confirm', function () { const id = $(this).attr('data-id'); diff --git a/assets/templates/dashboard/dashboard.html b/assets/templates/dashboard/dashboard.html index f76ebc39..b348d519 100644 --- a/assets/templates/dashboard/dashboard.html +++ b/assets/templates/dashboard/dashboard.html @@ -15,7 +15,7 @@
-
项目信息
+
项目信息 {{ .ProjectVersion }}

操作系统:{{ .GoOS }} {{ .GoArch }} {{ .GoVersion }}

diff --git a/assets/templates/index/index.html b/assets/templates/index/index.html index d224bbb0..fc72cfac 100644 --- a/assets/templates/index/index.html +++ b/assets/templates/index/index.html @@ -22,82 +22,29 @@
- +
- + - +
- +
- +
- +
@@ -281,6 +228,41 @@ function () {}, function (data) { $("#nickname").html(data.nickname); + $(".nav-drawer").html(""); + let li = ''; + + if (data.menu.length > 0) { + let newArr = []; + data.menu.forEach(function (v) { + if (v.pid === 0) { + v.children = []; + newArr.push(v) + } + }); + + data.menu.forEach(function (v) { + newArr.forEach(function (item) { + if (v.pid === item.id) { + item.children.push(v) + } + }) + }); + + $.each(newArr, function (index, value) { + li += ''; + }); + + $(".nav-drawer").html(li); + } }, function (response) { AjaxError(response); diff --git a/assets/templates/install/upgrade_view.html b/assets/templates/install/upgrade_view.html index 504e4ca0..fa24f5be 100644 --- a/assets/templates/install/upgrade_view.html +++ b/assets/templates/install/upgrade_view.html @@ -18,34 +18,74 @@
-
+
-
服务升级
+
无版本号情况,服务升级
+

发现代码运行报错或缺少表字段,如何进行服务初始化?

+

1、删除根目录文件: + {{ .LockFile }} + 。 +

+

2、重新启动服务。

+ +

发现 GitHub 仓库更新了新代码,如何进行服务升级?

1、源代码升级: 拉取最新代码,覆盖旧版本代码即可。

2、数据表升级:

- {{range $key, $value := .List}} -

- MySQL 数据表:{{$value.TableName}} - {{if eq $value.IsHave 1}} 已存在 - {{else}} 不存在 - {{end}} + + + + + + + + + + {{range $key, $value := .List}} + + + + + + {{end}} + +
数据表是否存在操作
{{$value.TableName}} + {{if eq $value.IsHave 1}} 已存在 + {{else}} 不存在 + {{end}} + + + +
+ +

+
+
- - -

- {{end}} +
+
+
+
有版本号情况,服务升级
+
+
+

+ v1.26 + -> + v1.27 +

+

......

+
diff --git a/assets/templates/menu/menu_view.html b/assets/templates/menu/menu_view.html index 6788bf68..04d1da96 100644 --- a/assets/templates/menu/menu_view.html +++ b/assets/templates/menu/menu_view.html @@ -15,7 +15,7 @@
-
+
配置菜单栏
@@ -64,7 +64,7 @@
-
+
菜单栏列表
@@ -127,7 +127,14 @@ field: 'name', title: '名称', formatter: function (value, row, index) { - return '' + row.name ; + return '' + row.name; + } + }, + { + field: 'sort', + title: '排序', + formatter: function (value, row, index) { + return ''; } }, { @@ -302,6 +309,59 @@ ); } + $(document).on('blur', '.sort', function () { + const hashid = $(this).attr("data-id"); + const name = $(this).attr("data-name"); + const val = $(this).val(); + + $.confirm({ + title: '谨慎操作', + content: '确认要将 ' + name + ' 的排序设置为 ' + val + ' 吗?', + icon: 'mdi mdi-alert', + animation: 'scale', + closeAnimation: 'zoom', + buttons: { + okay: { + text: '确认', + keys: ['enter'], + btnClass: 'btn-orange', + action: function () { + AjaxForm( + "PATCH", + "/api/menu/sort", + {id: hashid, sort: val}, + function () { + }, + function () { + $.alert({ + title: '操作成功', + icon: 'mdi mdi-check-decagram', + type: 'green', + content: '菜单:' + name + '排序成功。', + buttons: { + okay: { + text: '关闭', + action: function () { + location.reload(); + } + } + } + }); + }, + function (response) { + AjaxError(response); + } + ); + } + }, + cancel: { + text: '取消', + keys: ['ctrl', 'shift'], + } + } + }); + }); + $(document).on('click', '.customSwitch', function () { let state = $(this).attr("state"); const hashid = $(this).attr("hashid"); diff --git a/configs/configs.go b/configs/configs.go index 1c34bf15..7155926d 100644 --- a/configs/configs.go +++ b/configs/configs.go @@ -1,7 +1,6 @@ package configs import ( - "fmt" "time" "github.com/xinliangnote/go-gin-api/pkg/env" @@ -90,19 +89,3 @@ func init() { func Get() Config { return *config } - -func ProjectName() string { - return "go-gin-api" -} - -func ProjectPort() string { - return ":9999" -} - -func ProjectLogFile() string { - return fmt.Sprintf("./logs/%s-access.log", ProjectName()) -} - -func ProjectInstallFile() string { - return "INSTALL.lock" -} diff --git a/configs/constants.go b/configs/constants.go new file mode 100644 index 00000000..184850a5 --- /dev/null +++ b/configs/constants.go @@ -0,0 +1,36 @@ +package configs + +const ( + // 项目版本 + ProjectVersion = "v1.2.6" + + // 项目名称 + ProjectName = "go-gin-api" + + // 项目端口 + ProjectPort = ":9999" + + // 项目日志存放文件 + ProjectLogFile = "./logs/" + ProjectName + "-access.log" + + // 项目安装完成标识 + ProjectInstallMark = "INSTALL.lock" + + // 登录验证 Token,Header 中传递的参数 + LoginToken = "Token" + + // 签名验证 Token,Header 中传递的参数 + SignToken = "Authorization" + + // 签名验证 Date,Header 中传递的参数 + SignTokenDate = "Authorization-Date" + + // Redis Key 前缀 - 防止重复提交 + RedisKeyPrefixRequestID = ProjectName + ":request-id:" + + // Redis Key 前缀 - 登录用户信息 + RedisKeyPrefixLoginUser = ProjectName + ":login-user:" + + // Redis Key 前缀 - 签名验证信息 + RedisKeyPrefixSignature = ProjectName + ":signature:" +) diff --git a/docs/docs.go b/docs/docs.go index cfd6078d..69d92851 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -177,9 +177,9 @@ var doc = `{ }, "/api/admin/login": { "post": { - "description": "管理员登录", + "description": "管理员登出", "consumes": [ - "multipart/form-data" + "application/json" ], "produces": [ "application/json" @@ -187,28 +187,12 @@ var doc = `{ "tags": [ "API.admin" ], - "summary": "管理员登录", - "parameters": [ - { - "type": "string", - "description": "用户名", - "name": "username", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "密码", - "name": "password", - "in": "formData", - "required": true - } - ], + "summary": "管理员登出", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/admin_handler.loginResponse" + "$ref": "#/definitions/admin_handler.logoutResponse" } }, "400": { @@ -348,6 +332,44 @@ var doc = `{ } } }, + "/api/admin/offline": { + "patch": { + "description": "下线管理员", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.admin" + ], + "summary": "下线管理员", + "parameters": [ + { + "type": "string", + "description": "Hashid", + "name": "id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin_handler.offlineResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/api/admin/reset_password/{id}": { "patch": { "description": "重置密码", @@ -928,11 +950,56 @@ var doc = `{ } } }, + "/api/menu/sort": { + "patch": { + "description": "更新菜单排序", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.menu" + ], + "summary": "更新菜单排序", + "parameters": [ + { + "type": "string", + "description": "Hashid", + "name": "id", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "排序", + "name": "sort", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/menu_handler.updateSortResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/api/menu/used": { "patch": { "description": "更新菜单为启用/禁用", "consumes": [ - "application/json" + "multipart/form-data" ], "produces": [ "application/json" @@ -1467,6 +1534,13 @@ var doc = `{ "admin_handler.detailResponse": { "type": "object", "properties": { + "menu": { + "description": "菜单栏", + "type": "array", + "items": { + "$ref": "#/definitions/admin_service.ListMyMenuData" + } + }, "mobile": { "description": "手机号", "type": "string" @@ -1514,6 +1588,10 @@ var doc = `{ "description": "ID", "type": "integer" }, + "is_online": { + "description": "是否在线 1:是 -1:否", + "type": "integer" + }, "is_used": { "description": "是否启用 1:是 -1:否", "type": "integer" @@ -1601,6 +1679,15 @@ var doc = `{ } } }, + "admin_handler.offlineResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, "admin_handler.resetPasswordResponse": { "type": "object", "properties": { @@ -1640,6 +1727,31 @@ var doc = `{ } } }, + "admin_service.ListMyMenuData": { + "type": "object", + "properties": { + "icon": { + "description": "图标", + "type": "string" + }, + "id": { + "description": "ID", + "type": "integer" + }, + "link": { + "description": "链接地址", + "type": "string" + }, + "name": { + "description": "菜单名称", + "type": "string" + }, + "pid": { + "description": "父类ID", + "type": "integer" + } + } + }, "authorized_handler.createAPIResponse": { "type": "object", "properties": { @@ -1972,6 +2084,10 @@ var doc = `{ "pid": { "description": "父类ID", "type": "integer" + }, + "sort": { + "description": "排序", + "type": "integer" } } }, @@ -1986,6 +2102,15 @@ var doc = `{ } } }, + "menu_handler.updateSortResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, "menu_handler.updateUsedResponse": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index d8c76140..d5d30d85 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -160,9 +160,9 @@ }, "/api/admin/login": { "post": { - "description": "管理员登录", + "description": "管理员登出", "consumes": [ - "multipart/form-data" + "application/json" ], "produces": [ "application/json" @@ -170,28 +170,12 @@ "tags": [ "API.admin" ], - "summary": "管理员登录", - "parameters": [ - { - "type": "string", - "description": "用户名", - "name": "username", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "密码", - "name": "password", - "in": "formData", - "required": true - } - ], + "summary": "管理员登出", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/admin_handler.loginResponse" + "$ref": "#/definitions/admin_handler.logoutResponse" } }, "400": { @@ -331,6 +315,44 @@ } } }, + "/api/admin/offline": { + "patch": { + "description": "下线管理员", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.admin" + ], + "summary": "下线管理员", + "parameters": [ + { + "type": "string", + "description": "Hashid", + "name": "id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin_handler.offlineResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/api/admin/reset_password/{id}": { "patch": { "description": "重置密码", @@ -911,11 +933,56 @@ } } }, + "/api/menu/sort": { + "patch": { + "description": "更新菜单排序", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.menu" + ], + "summary": "更新菜单排序", + "parameters": [ + { + "type": "string", + "description": "Hashid", + "name": "id", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "排序", + "name": "sort", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/menu_handler.updateSortResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/api/menu/used": { "patch": { "description": "更新菜单为启用/禁用", "consumes": [ - "application/json" + "multipart/form-data" ], "produces": [ "application/json" @@ -1450,6 +1517,13 @@ "admin_handler.detailResponse": { "type": "object", "properties": { + "menu": { + "description": "菜单栏", + "type": "array", + "items": { + "$ref": "#/definitions/admin_service.ListMyMenuData" + } + }, "mobile": { "description": "手机号", "type": "string" @@ -1497,6 +1571,10 @@ "description": "ID", "type": "integer" }, + "is_online": { + "description": "是否在线 1:是 -1:否", + "type": "integer" + }, "is_used": { "description": "是否启用 1:是 -1:否", "type": "integer" @@ -1584,6 +1662,15 @@ } } }, + "admin_handler.offlineResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, "admin_handler.resetPasswordResponse": { "type": "object", "properties": { @@ -1623,6 +1710,31 @@ } } }, + "admin_service.ListMyMenuData": { + "type": "object", + "properties": { + "icon": { + "description": "图标", + "type": "string" + }, + "id": { + "description": "ID", + "type": "integer" + }, + "link": { + "description": "链接地址", + "type": "string" + }, + "name": { + "description": "菜单名称", + "type": "string" + }, + "pid": { + "description": "父类ID", + "type": "integer" + } + } + }, "authorized_handler.createAPIResponse": { "type": "object", "properties": { @@ -1955,6 +2067,10 @@ "pid": { "description": "父类ID", "type": "integer" + }, + "sort": { + "description": "排序", + "type": "integer" } } }, @@ -1969,6 +2085,15 @@ } } }, + "menu_handler.updateSortResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, "menu_handler.updateUsedResponse": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index f6f8aad7..bece00b6 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -13,6 +13,11 @@ definitions: type: object admin_handler.detailResponse: properties: + menu: + description: 菜单栏 + items: + $ref: '#/definitions/admin_service.ListMyMenuData' + type: array mobile: description: 手机号 type: string @@ -46,6 +51,9 @@ definitions: id: description: ID type: integer + is_online: + description: 是否在线 1:是 -1:否 + type: integer is_used: description: 是否启用 1:是 -1:否 type: integer @@ -105,6 +113,12 @@ definitions: description: 用户账号 type: string type: object + admin_handler.offlineResponse: + properties: + id: + description: 主键ID + type: integer + type: object admin_handler.resetPasswordResponse: properties: id: @@ -132,6 +146,24 @@ definitions: description: 父类ID type: integer type: object + admin_service.ListMyMenuData: + properties: + icon: + description: 图标 + type: string + id: + description: ID + type: integer + link: + description: 链接地址 + type: string + name: + description: 菜单名称 + type: string + pid: + description: 父类ID + type: integer + type: object authorized_handler.createAPIResponse: properties: id: @@ -364,6 +396,9 @@ definitions: pid: description: 父类ID type: integer + sort: + description: 排序 + type: integer type: object menu_handler.listResponse: properties: @@ -372,6 +407,12 @@ definitions: $ref: '#/definitions/menu_handler.listData' type: array type: object + menu_handler.updateSortResponse: + properties: + id: + description: 主键ID + type: integer + type: object menu_handler.updateUsedResponse: properties: id: @@ -593,31 +634,20 @@ paths: /api/admin/login: post: consumes: - - multipart/form-data - description: 管理员登录 - parameters: - - description: 用户名 - in: formData - name: username - required: true - type: string - - description: 密码 - in: formData - name: password - required: true - type: string + - application/json + description: 管理员登出 produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/admin_handler.loginResponse' + $ref: '#/definitions/admin_handler.logoutResponse' "400": description: Bad Request schema: $ref: '#/definitions/code.Failure' - summary: 管理员登录 + summary: 管理员登出 tags: - API.admin /api/admin/menu: @@ -705,6 +735,31 @@ paths: summary: 修改个人信息 tags: - API.admin + /api/admin/offline: + patch: + consumes: + - multipart/form-data + description: 下线管理员 + parameters: + - description: Hashid + in: formData + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/admin_handler.offlineResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 下线管理员 + tags: + - API.admin /api/admin/reset_password/{id}: patch: consumes: @@ -1115,10 +1170,40 @@ paths: summary: 菜单详情 tags: - API.menu - /api/menu/used: + /api/menu/sort: patch: consumes: + - multipart/form-data + description: 更新菜单排序 + parameters: + - description: Hashid + in: formData + name: id + required: true + type: string + - description: 排序 + in: formData + name: sort + required: true + type: integer + produces: - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/menu_handler.updateSortResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 更新菜单排序 + tags: + - API.menu + /api/menu/used: + patch: + consumes: + - multipart/form-data description: 更新菜单为启用/禁用 parameters: - description: Hashid diff --git a/internal/api/code/code.go b/internal/api/code/code.go index e820fa91..e3062731 100644 --- a/internal/api/code/code.go +++ b/internal/api/code/code.go @@ -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: "写入配置文件失败", diff --git a/internal/api/controller/admin_handler/func_detail.go b/internal/api/controller/admin_handler/func_detail.go index 83182148..c7ed001e 100644 --- a/internal/api/controller/admin_handler/func_detail.go +++ b/internal/api/controller/admin_handler/func_detail.go @@ -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) } } diff --git a/internal/api/controller/admin_handler/func_list.go b/internal/api/controller/admin_handler/func_list.go index af5748b2..38df74c2 100755 --- a/internal/api/controller/admin_handler/func_list.go +++ b/internal/api/controller/admin_handler/func_list.go @@ -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), diff --git a/internal/api/controller/admin_handler/func_login.go b/internal/api/controller/admin_handler/func_login.go index decac582..27664065 100644 --- a/internal/api/controller/admin_handler/func_login.go +++ b/internal/api/controller/admin_handler/func_login.go @@ -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, diff --git a/internal/api/controller/admin_handler/func_logout.go b/internal/api/controller/admin_handler/func_logout.go index 73e7f904..93dae299 100644 --- a/internal/api/controller/admin_handler/func_logout.go +++ b/internal/api/controller/admin_handler/func_logout.go @@ -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, diff --git a/internal/api/controller/admin_handler/func_offline.go b/internal/api/controller/admin_handler/func_offline.go new file mode 100755 index 00000000..85e8d4bd --- /dev/null +++ b/internal/api/controller/admin_handler/func_offline.go @@ -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) + } +} diff --git a/internal/api/controller/admin_handler/handler.go b/internal/api/controller/admin_handler/handler.go index f21c42d8..e839ee6f 100644 --- a/internal/api/controller/admin_handler/handler.go +++ b/internal/api/controller/admin_handler/handler.go @@ -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] diff --git a/internal/api/controller/config_handler/func_email.go b/internal/api/controller/config_handler/func_email.go index 7d2ed13a..6070e3b7 100755 --- a/internal/api/controller/config_handler/func_email.go +++ b/internal/api/controller/config_handler/func_email.go @@ -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( diff --git a/internal/api/controller/menu_handler/func_list.go b/internal/api/controller/menu_handler/func_list.go index ab1fd3bf..47f31c57 100755 --- a/internal/api/controller/menu_handler/func_list.go +++ b/internal/api/controller/menu_handler/func_list.go @@ -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 diff --git a/internal/api/controller/menu_handler/func_updatesort.go b/internal/api/controller/menu_handler/func_updatesort.go new file mode 100755 index 00000000..ece2a778 --- /dev/null +++ b/internal/api/controller/menu_handler/func_updatesort.go @@ -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) + } +} diff --git a/internal/api/controller/menu_handler/func_updateused.go b/internal/api/controller/menu_handler/func_updateused.go index 99cdd436..5aba603d 100755 --- a/internal/api/controller/menu_handler/func_updateused.go +++ b/internal/api/controller/menu_handler/func_updateused.go @@ -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:否" diff --git a/internal/api/controller/menu_handler/handler.go b/internal/api/controller/menu_handler/handler.go index 12c2e2ff..9473cc36 100644 --- a/internal/api/controller/menu_handler/handler.go +++ b/internal/api/controller/menu_handler/handler.go @@ -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] diff --git a/internal/api/repository/db_repo/menu_repo/gen_menu.go b/internal/api/repository/db_repo/menu_repo/gen_menu.go index 437d8644..2e675698 100755 --- a/internal/api/repository/db_repo/menu_repo/gen_menu.go +++ b/internal/api/repository/db_repo/menu_repo/gen_menu.go @@ -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 diff --git a/internal/api/repository/db_repo/menu_repo/gen_model.go b/internal/api/repository/db_repo/menu_repo/gen_model.go index 044ac0a4..42e977e5 100755 --- a/internal/api/repository/db_repo/menu_repo/gen_model.go +++ b/internal/api/repository/db_repo/menu_repo/gen_model.go @@ -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"` // 创建时间 diff --git a/internal/api/repository/db_repo/menu_repo/gen_table.md b/internal/api/repository/db_repo/menu_repo/gen_table.md index 90ba154c..bb4d8e59 100755 --- a/internal/api/repository/db_repo/menu_repo/gen_table.md +++ b/internal/api/repository/db_repo/menu_repo/gen_table.md @@ -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 | | | diff --git a/internal/api/service/admin_service/service.go b/internal/api/service/admin_service/service.go index 2d22a7ba..55cd8b54 100644 --- a/internal/api/service/admin_service/service.go +++ b/internal/api/service/admin_service/service.go @@ -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 -} diff --git a/internal/api/service/admin_service/service_delete.go b/internal/api/service/admin_service/service_delete.go index 570837dd..9a2a83e7 100644 --- a/internal/api/service/admin_service/service_delete.go +++ b/internal/api/service/admin_service/service_delete.go @@ -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 } diff --git a/internal/api/service/admin_service/service_listmenu.go b/internal/api/service/admin_service/service_listmenu.go index f4b3f23e..d54971cd 100644 --- a/internal/api/service/admin_service/service_listmenu.go +++ b/internal/api/service/admin_service/service_listmenu.go @@ -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 diff --git a/internal/api/service/admin_service/service_modifypassword.go b/internal/api/service/admin_service/service_modifypassword.go index d428fc61..9256eea6 100644 --- a/internal/api/service/admin_service/service_modifypassword.go +++ b/internal/api/service/admin_service/service_modifypassword.go @@ -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 } diff --git a/internal/api/service/admin_service/service_myaction.go b/internal/api/service/admin_service/service_myaction.go new file mode 100644 index 00000000..234935c2 --- /dev/null +++ b/internal/api/service/admin_service/service_myaction.go @@ -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 +} diff --git a/internal/api/service/admin_service/service_mymenu.go b/internal/api/service/admin_service/service_mymenu.go new file mode 100644 index 00000000..0c622c54 --- /dev/null +++ b/internal/api/service/admin_service/service_mymenu.go @@ -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 +} diff --git a/internal/api/service/admin_service/service_resetpassword.go b/internal/api/service/admin_service/service_resetpassword.go index 321edb52..245c7581 100644 --- a/internal/api/service/admin_service/service_resetpassword.go +++ b/internal/api/service/admin_service/service_resetpassword.go @@ -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 } diff --git a/internal/api/service/admin_service/service_updateused.go b/internal/api/service/admin_service/service_updateused.go index fbf2e602..366247ea 100644 --- a/internal/api/service/admin_service/service_updateused.go +++ b/internal/api/service/admin_service/service_updateused.go @@ -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 } diff --git a/internal/api/service/authorized_service/service.go b/internal/api/service/authorized_service/service.go index 43d6171b..2e13da07 100644 --- a/internal/api/service/authorized_service/service.go +++ b/internal/api/service/authorized_service/service.go @@ -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() diff --git a/internal/api/service/authorized_service/service_createapi.go b/internal/api/service/authorized_service/service_createapi.go index 6bba57f6..320e5bbe 100644 --- a/internal/api/service/authorized_service/service_createapi.go +++ b/internal/api/service/authorized_service/service_createapi.go @@ -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 } diff --git a/internal/api/service/authorized_service/service_delete.go b/internal/api/service/authorized_service/service_delete.go index 456d6d4d..65c404c6 100644 --- a/internal/api/service/authorized_service/service_delete.go +++ b/internal/api/service/authorized_service/service_delete.go @@ -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 } diff --git a/internal/api/service/authorized_service/service_deleteapi.go b/internal/api/service/authorized_service/service_deleteapi.go index 4b14b3c4..6bb8e316 100644 --- a/internal/api/service/authorized_service/service_deleteapi.go +++ b/internal/api/service/authorized_service/service_deleteapi.go @@ -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 } diff --git a/internal/api/service/authorized_service/service_detailbykey.go b/internal/api/service/authorized_service/service_detailbykey.go index dc6a1d0e..9b2acb44 100644 --- a/internal/api/service/authorized_service/service_detailbykey.go +++ b/internal/api/service/authorized_service/service_detailbykey.go @@ -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) diff --git a/internal/api/service/authorized_service/service_updateused.go b/internal/api/service/authorized_service/service_updateused.go index 7749d124..19d98587 100644 --- a/internal/api/service/authorized_service/service_updateused.go +++ b/internal/api/service/authorized_service/service_updateused.go @@ -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 } diff --git a/internal/api/service/menu_service/service.go b/internal/api/service/menu_service/service.go index ce1ac5bb..f81579f7 100644 --- a/internal/api/service/menu_service/service.go +++ b/internal/api/service/menu_service/service.go @@ -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 -} diff --git a/internal/api/service/menu_service/service_list.go b/internal/api/service/menu_service/service_list.go index 59a64513..b2c670dc 100644 --- a/internal/api/service/menu_service/service_list.go +++ b/internal/api/service/menu_service/service_list.go @@ -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 diff --git a/internal/api/service/menu_service/service_updatesort.go b/internal/api/service/menu_service/service_updatesort.go new file mode 100644 index 00000000..88e3ad4d --- /dev/null +++ b/internal/api/service/menu_service/service_updatesort.go @@ -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 +} diff --git a/internal/pkg/cache/redis.go b/internal/pkg/cache/redis.go index cadec928..7d010a9d 100644 --- a/internal/pkg/cache/redis.go +++ b/internal/pkg/cache/redis.go @@ -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) diff --git a/internal/pkg/core/context.go b/internal/pkg/core/context.go index 0cc59bfe..63b05cbf 100644 --- a/internal/pkg/core/context.go +++ b/internal/pkg/core/context.go @@ -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) diff --git a/internal/pkg/core/core.go b/internal/pkg/core/core.go index 12b6f146..e1342d31 100644 --- a/internal/pkg/core/core.go +++ b/internal/pkg/core/core.go @@ -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")) diff --git a/internal/router/middleware/middle_jwt.go b/internal/router/middleware/middle_jwt.go index 162f3274..7c6e8d83 100644 --- a/internal/router/middleware/middle_jwt.go +++ b/internal/router/middleware/middle_jwt.go @@ -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) { diff --git a/internal/router/middleware/middle_rbac.go b/internal/router/middleware/middle_rbac.go new file mode 100644 index 00000000..5e668014 --- /dev/null +++ b/internal/router/middleware/middle_rbac.go @@ -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 + } + } + + } +} diff --git a/internal/router/middleware/middle_resubmit.go b/internal/router/middleware/middle_resubmit.go index ec3b4b81..7b985ff9 100644 --- a/internal/router/middleware/middle_resubmit.go +++ b/internal/router/middleware/middle_resubmit.go @@ -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 { diff --git a/internal/router/middleware/middle_signature.go b/internal/router/middleware/middle_signature.go index 2c9bb5b5..88318da1 100644 --- a/internal/router/middleware/middle_signature.go +++ b/internal/router/middleware/middle_signature.go @@ -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, diff --git a/internal/router/middleware/middle_token.go b/internal/router/middleware/middle_token.go index fcaa6ead..d98cd124 100644 --- a/internal/router/middleware/middle_token.go +++ b/internal/router/middleware/middle_token.go @@ -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, diff --git a/internal/router/middleware/middlerware.go b/internal/router/middleware/middlerware.go index dddd6bb0..59c4447b 100644 --- a/internal/router/middleware/middlerware.go +++ b/internal/router/middleware/middlerware.go @@ -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 { diff --git a/internal/router/router.go b/internal/router/router.go index 75cd8996..97a2084d 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -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 { // 已安装 diff --git a/internal/router/router_api.go b/internal/router/router_api.go index af4104da..5a2532f1 100644 --- a/internal/router/router_api.go +++ b/internal/router/router_api.go @@ -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()) diff --git a/internal/web/controller/dashboard_handler/func_view.go b/internal/web/controller/dashboard_handler/func_view.go index b782b3d6..92de3ca9 100644 --- a/internal/web/controller/dashboard_handler/func_view.go +++ b/internal/web/controller/dashboard_handler/func_view.go @@ -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) } diff --git a/internal/web/controller/install_handler/func_execute.go b/internal/web/controller/install_handler/func_execute.go index 49ad603c..1b834ef3 100644 --- a/internal/web/controller/install_handler/func_execute.go +++ b/internal/web/controller/install_handler/func_execute.go @@ -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, diff --git a/internal/web/controller/install_handler/mysql_table/table_menu.go b/internal/web/controller/install_handler/mysql_table/table_menu.go index b5426a25..18bc03aa 100644 --- a/internal/web/controller/install_handler/mysql_table/table_menu.go +++ b/internal/web/controller/install_handler/mysql_table/table_menu.go @@ -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 } diff --git a/internal/web/controller/install_handler/mysql_table/table_menu_action.go b/internal/web/controller/install_handler/mysql_table/table_menu_action.go index 1ebaf402..1ae911b9 100644 --- a/internal/web/controller/install_handler/mysql_table/table_menu_action.go +++ b/internal/web/controller/install_handler/mysql_table/table_menu_action.go @@ -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 } diff --git a/internal/web/controller/tool_handler/func_logsview.go b/internal/web/controller/tool_handler/func_logsview.go index 7724a424..ae57d0ee 100644 --- a/internal/web/controller/tool_handler/func_logsview.go +++ b/internal/web/controller/tool_handler/func_logsview.go @@ -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)) } diff --git a/internal/web/controller/upgrade_handler/func_upgradeview.go b/internal/web/controller/upgrade_handler/func_upgradeview.go index 49d6ed3d..28e0afe0 100644 --- a/internal/web/controller/upgrade_handler/func_upgradeview.go +++ b/internal/web/controller/upgrade_handler/func_upgradeview.go @@ -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) } } diff --git a/main.go b/main.go index df6ed209..aa461c04 100644 --- a/main.go +++ b/main.go @@ -31,9 +31,9 @@ import ( func main() { // 初始化 logger loggers, err := logger.NewJSONLogger( - logger.WithField("domain", fmt.Sprintf("%s[%s]", configs.ProjectName(), env.Active().Value())), + logger.WithField("domain", fmt.Sprintf("%s[%s]", configs.ProjectName, env.Active().Value())), logger.WithTimeLayout("2006-01-02 15:04:05"), - logger.WithFileP(configs.ProjectLogFile()), + logger.WithFileP(configs.ProjectLogFile), ) if err != nil { panic(err) @@ -47,7 +47,7 @@ func main() { } server := &http.Server{ - Addr: configs.ProjectPort(), + Addr: configs.ProjectPort, Handler: s.Mux, }