upgrade
This commit is contained in:
@@ -32,7 +32,8 @@
|
||||
<th>手机号</th>
|
||||
<th>创建日期</th>
|
||||
<th>更新日期</th>
|
||||
<th style="text-align: center; ">状态</th>
|
||||
<th style="text-align: center; ">可用状态</th>
|
||||
<th style="text-align: center; ">在线状态</th>
|
||||
<th style="text-align: center; ">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -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 = '<span class="badge badge-danger">禁用</span></td>'
|
||||
}
|
||||
|
||||
if (value.is_online === 1) {
|
||||
showOnlineBadge = '<span class="badge btn-primary">在线</span></td>'
|
||||
}
|
||||
|
||||
if (value.is_online === -1) {
|
||||
showOnlineBadge = '<span class="badge btn-secondary">离线</span></td>'
|
||||
}
|
||||
|
||||
const tr = '<tr>\n' +
|
||||
'<td>' + value.id + '</td>\n' +
|
||||
'<td>' + value.username + '</td>\n' +
|
||||
@@ -122,6 +132,7 @@
|
||||
'<td>' + value.created_at + '</td>\n' +
|
||||
'<td>' + value.updated_at + '</td>\n' +
|
||||
'<td style="text-align: center; ">' + showUsedBadge + '</td>\n' +
|
||||
'<td style="text-align: center; ">' + showOnlineBadge + '</td>\n' +
|
||||
'<td style="text-align: center; ">\n' +
|
||||
'<div class="btn-group">\n' +
|
||||
' <a class="btn btn-xs btn-default btn-option" href="#!" title=""\n' +
|
||||
@@ -134,6 +145,9 @@
|
||||
' <a class="btn btn-xs btn-default btn-menu" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="菜单授权">菜单授权</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-offline" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="下线">下线</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-confirm" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="删除">删除</a>\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: '确认要 <strong style="color: red">下线</strong> 吗?',
|
||||
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');
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-secondary">
|
||||
<header class="card-header">
|
||||
<div class="card-title">项目信息</div>
|
||||
<div class="card-title">项目信息 <span class="badge badge-pill badge-warning">{{ .ProjectVersion }}</span> </div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p>操作系统:{{ .GoOS }} <span class="badge badge-brown"> {{ .GoArch }} </span> <span class="badge badge-info"> {{ .GoVersion }} </span></p>
|
||||
|
||||
@@ -22,82 +22,29 @@
|
||||
<div class="lyear-layout-container">
|
||||
<!--左侧导航-->
|
||||
<aside class="lyear-layout-sidebar">
|
||||
|
||||
|
||||
<!-- logo -->
|
||||
<div id="logo" class="sidebar-header">
|
||||
<a href="/">
|
||||
<img src="bootstrap/images/logo-sidebar.png"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="lyear-layout-sidebar-info lyear-scroll">
|
||||
|
||||
<div class="lyear-layout-sidebar-info lyear-scroll">
|
||||
|
||||
<nav class="sidebar-main">
|
||||
<ul class="nav-drawer">
|
||||
<li class="nav-item active"> <a class="multitabs" href="/dashboard"><i class="mdi mdi-home"></i> <span>仪表盘</span></a> </li>
|
||||
<li class="nav-item nav-item-has-subnav">
|
||||
<a href="javascript:void(0)"><i class="mdi mdi-settings-box"></i> <span>配置信息</span></a>
|
||||
<ul class="nav nav-subnav">
|
||||
<li> <a class="multitabs" href="/config/email">告警邮箱</a> </li>
|
||||
<li> <a class="multitabs" href="/config/code">错误码</a> </li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item nav-item-has-subnav">
|
||||
<a href="javascript:void(0)"><i class="mdi mdi-code-not-equal-variant"></i> <span>代码生成器</span></a>
|
||||
<ul class="nav nav-subnav">
|
||||
<li> <a class="multitabs" href="/generator/gorm">生成数据表 CURD</a> </li>
|
||||
<li> <a class="multitabs" href="/generator/handler">生成控制器方法</a> </li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item nav-item-has-subnav">
|
||||
<a href="javascript:void(0)"><i class="mdi mdi-playlist-check"></i> <span>授权调用方</span></a>
|
||||
<ul class="nav nav-subnav">
|
||||
<li> <a class="multitabs" href="/authorized/list">调用方</a> </li>
|
||||
<li> <a class="multitabs" href="/authorized/demo">使用说明</a> </li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item nav-item-has-subnav">
|
||||
<a href="javascript:void(0)"><i class="mdi mdi-account"></i> <span>系统管理员</span></a>
|
||||
<ul class="nav nav-subnav">
|
||||
<li> <a class="multitabs" href="/admin/list">管理员</a> </li>
|
||||
<li> <a class="multitabs" href="/admin/menu">菜单管理</a> </li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item nav-item-has-subnav">
|
||||
<a href="javascript:void(0)"><i class="mdi mdi-database-search"></i> <span>查询小助手</span></a>
|
||||
<ul class="nav nav-subnav">
|
||||
<li> <a class="multitabs" href="/tool/cache">查询缓存</a> </li>
|
||||
<li> <a class="multitabs" href="/tool/data">查询数据</a> </li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item nav-item-has-subnav">
|
||||
<a href="javascript:void(0)"><i class="mdi mdi-tools"></i> <span>实用工具箱</span></a>
|
||||
<ul class="nav nav-subnav">
|
||||
<li> <a class="multitabs" href="/upgrade">服务升级</a> </li>
|
||||
<li> <a class="multitabs" href="/tool/hashids">Hashids</a> </li>
|
||||
<li> <a class="multitabs" href="/tool/logs">调用日志</a> </li>
|
||||
<li> <a target="_blank" href="/swagger/index.html">接口文档</a> </li>
|
||||
<li> <a target="_blank" href="/graphql">GraphQL</a> </li>
|
||||
<li> <a target="_blank" href="/metrics">接口指标</a> </li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
</aside>
|
||||
<!--End 左侧导航-->
|
||||
|
||||
|
||||
<!--头部信息-->
|
||||
<header class="lyear-layout-header">
|
||||
|
||||
|
||||
<nav class="navbar">
|
||||
|
||||
|
||||
<div class="navbar-left">
|
||||
<div class="lyear-aside-toggler">
|
||||
<span class="lyear-toggler-bar"></span>
|
||||
@@ -105,7 +52,7 @@
|
||||
<span class="lyear-toggler-bar"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<ul class="navbar-right d-flex align-items-center">
|
||||
|
||||
<!--切换主题配色-->
|
||||
@@ -152,28 +99,28 @@
|
||||
<span class="inverse">
|
||||
<input type="radio" name="header_bg" value="default" id="header_bg_1" checked>
|
||||
<label for="header_bg_1"></label>
|
||||
</span>
|
||||
<span>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_2" id="header_bg_2">
|
||||
<label for="header_bg_2"></label>
|
||||
</span>
|
||||
<span>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_3" id="header_bg_3">
|
||||
<label for="header_bg_3"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_4" id="header_bg_4">
|
||||
<label for="header_bg_4"></label>
|
||||
</span>
|
||||
<span>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_5" id="header_bg_5">
|
||||
<label for="header_bg_5"></label>
|
||||
</span>
|
||||
<span>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_6" id="header_bg_6">
|
||||
<label for="header_bg_6"></label>
|
||||
</span>
|
||||
<span>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_7" id="header_bg_7">
|
||||
<label for="header_bg_7"></label>
|
||||
</span>
|
||||
@@ -247,17 +194,17 @@
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
<!--End 头部信息-->
|
||||
|
||||
|
||||
<!--页面主要内容-->
|
||||
<main class="lyear-layout-content">
|
||||
|
||||
|
||||
<div id="iframe-content"></div>
|
||||
|
||||
|
||||
</main>
|
||||
<!--End 页面主要内容-->
|
||||
</div>
|
||||
@@ -281,6 +228,41 @@
|
||||
function () {},
|
||||
function (data) {
|
||||
$("#nickname").html(data.nickname);
|
||||
$(".nav-drawer").html("");
|
||||
let li = '<li class="nav-item active"><a class="multitabs" href="/dashboard"><i class="mdi mdi-home"></i> <span>仪表盘</span></a></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 += '<li class="nav-item nav-item-has-subnav">';
|
||||
li += '<a href="javascript:void(0)"><i class="mdi '+ value.icon +'"></i> <span>'+ value.name +'</span></a>';
|
||||
li += '<ul class="nav nav-subnav">';
|
||||
|
||||
|
||||
value.children.forEach(function (item) {
|
||||
li += '<li> <a class="multitabs" href="'+ item.link +'"> '+ item.name +' </a> </li>';
|
||||
});
|
||||
|
||||
li += '</ul></li>';
|
||||
});
|
||||
|
||||
$(".nav-drawer").html(li);
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
|
||||
@@ -18,34 +18,74 @@
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">服务升级</div>
|
||||
<div class="card-title">无版本号情况,服务升级</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p class="h6">发现代码运行报错或缺少表字段,如何进行服务初始化?</p>
|
||||
<p>1、删除根目录文件:
|
||||
<mark>{{ .LockFile }}</mark>
|
||||
。
|
||||
</p>
|
||||
<p>2、重新启动服务。</p>
|
||||
|
||||
<hr/>
|
||||
<p class="h6">发现 GitHub 仓库更新了新代码,如何进行服务升级?</p>
|
||||
<p>1、源代码升级:
|
||||
<mark>拉取最新代码,覆盖旧版本代码即可。</mark>
|
||||
</p>
|
||||
<p>2、数据表升级:</p>
|
||||
|
||||
{{range $key, $value := .List}}
|
||||
<p style="margin-left: 24px;">
|
||||
<i class="mdi mdi-checkbox-marked-circle"></i> MySQL 数据表:{{$value.TableName}}
|
||||
{{if eq $value.IsHave 1}} <font class="text-success">已存在</font>
|
||||
{{else}} <font class="text-danger">不存在</font>
|
||||
{{end}}
|
||||
<table style="margin-left: 24px;" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>数据表</th>
|
||||
<th>是否存在</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $key, $value := .List}}
|
||||
<tr>
|
||||
<td>{{$value.TableName}}</td>
|
||||
<td>
|
||||
{{if eq $value.IsHave 1}} <font class="text-success">已存在</font>
|
||||
{{else}} <font class="text-danger">不存在</font>
|
||||
{{end}}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-info upgrade" data-op="table"
|
||||
data-table="{{$value.TableName}}">创建表结构
|
||||
</button>
|
||||
<button class="btn btn-xs btn-cyan upgrade" data-op="table_data"
|
||||
data-table="{{$value.TableName}}">初始化数据
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<button class="btn btn-xs btn-info upgrade" data-op="table"
|
||||
data-table="{{$value.TableName}}">创建表结构
|
||||
</button>
|
||||
<button class="btn btn-xs btn-cyan upgrade" data-op="table_data"
|
||||
data-table="{{$value.TableName}}">初始化数据
|
||||
</button>
|
||||
</p>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">有版本号情况,服务升级</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p class="h6">
|
||||
<span class="badge badge-pill badge-warning">v1.26</span>
|
||||
->
|
||||
<span class="badge badge-pill badge-warning">v1.27</span>
|
||||
</p>
|
||||
<p>......</p>
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="col-lg-5">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">配置菜单栏</div>
|
||||
@@ -64,7 +64,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="col-lg-7">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">菜单栏列表</div>
|
||||
@@ -127,7 +127,14 @@
|
||||
field: 'name',
|
||||
title: '名称',
|
||||
formatter: function (value, row, index) {
|
||||
return '<i class="mdi ' + row.icon + '"></i>' + row.name ;
|
||||
return '<i class="mdi ' + row.icon + '"></i>' + row.name;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'sort',
|
||||
title: '排序',
|
||||
formatter: function (value, row, index) {
|
||||
return '<input type="text" value="'+ row.sort +'" data-id="' + row.hashid + '" data-name="' + row.name + '" class="form-control sort" style="width:60px;">';
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -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 + ' 的排序设置为 <strong style="color: red">' + val + '</strong> 吗?',
|
||||
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");
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
36
configs/constants.go
Normal file
36
configs/constants.go
Normal file
@@ -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:"
|
||||
)
|
||||
167
docs/docs.go
167
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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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/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:
|
||||
- application/json
|
||||
- multipart/form-data
|
||||
description: 更新菜单为启用/禁用
|
||||
parameters:
|
||||
- description: Hashid
|
||||
|
||||
@@ -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: "写入配置文件失败",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
70
internal/api/controller/admin_handler/func_offline.go
Executable file
70
internal/api/controller/admin_handler/func_offline.go
Executable 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)
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
69
internal/api/controller/menu_handler/func_updatesort.go
Executable file
69
internal/api/controller/menu_handler/func_updatesort.go
Executable 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)
|
||||
}
|
||||
}
|
||||
@@ -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:否"
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"` // 创建时间
|
||||
|
||||
@@ -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 | | |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
45
internal/api/service/admin_service/service_myaction.go
Normal file
45
internal/api/service/admin_service/service_myaction.go
Normal 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
|
||||
}
|
||||
69
internal/api/service/admin_service/service_mymenu.go
Normal file
69
internal/api/service/admin_service/service_mymenu.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
23
internal/api/service/menu_service/service_updatesort.go
Normal file
23
internal/api/service/menu_service/service_updatesort.go
Normal 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
|
||||
}
|
||||
2
internal/pkg/cache/redis.go
vendored
2
internal/pkg/cache/redis.go
vendored
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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) {
|
||||
|
||||
77
internal/router/middleware/middle_rbac.go
Normal file
77
internal/router/middleware/middle_rbac.go
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 { // 已安装
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
6
main.go
6
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,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user