mirror of
https://github.com/xinliangnote/go-gin-api.git
synced 2024-04-21 12:31:46 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fd9a005d1 | ||
|
|
4530dab87b | ||
|
|
bf4a413bd0 | ||
|
|
79aa86f278 | ||
|
|
866bed5522 | ||
|
|
61cc349cb4 | ||
|
|
6825b4e5b1 | ||
|
|
62d7ac5cd9 | ||
|
|
b630456b90 | ||
|
|
bbe181817c | ||
|
|
15e99398cb | ||
|
|
dd19eab5d5 | ||
|
|
7cdb8ee6b4 | ||
|
|
400e0bb3f2 | ||
|
|
14f6853c2b | ||
|
|
4c37a7e6b5 | ||
|
|
fc7e58db04 | ||
|
|
4e3c2afafd | ||
|
|
63499a432e | ||
|
|
498033753e | ||
|
|
b05ed0b59d | ||
|
|
8ed27cdce1 | ||
|
|
9aa0067e07 | ||
|
|
e2cecae7b1 | ||
|
|
e22593b708 | ||
|
|
05f337088e | ||
|
|
17be120c06 | ||
|
|
77e8d58298 | ||
|
|
f47f57ef3c | ||
|
|
f17f6a74d2 | ||
|
|
382c7d3c8f | ||
|
|
b382919cb5 | ||
|
|
22bfb9ad10 | ||
|
|
4254184098 | ||
|
|
bb09c70a60 | ||
|
|
65873cbfaa | ||
|
|
6af343d0b1 | ||
|
|
62367a041a | ||
|
|
1b013e2023 | ||
|
|
eba8929b4d | ||
|
|
44ca2698cd | ||
|
|
2c5683cd14 | ||
|
|
8ca47f743d | ||
|
|
a3607106d3 | ||
|
|
6d9a07f5dd | ||
|
|
a6947b59ac |
+19
-5
@@ -1,5 +1,19 @@
|
||||
/.vscode
|
||||
.vscode
|
||||
/.idea
|
||||
.idea
|
||||
mfmt
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.idea
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Project runtime log
|
||||
logs/*.log
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# FROM 基于 golang:1.16-alpine
|
||||
FROM golang:1.16-alpine AS builder
|
||||
|
||||
# ENV 设置环境变量
|
||||
ENV GOPATH=/opt/repo
|
||||
ENV GO111MODULE=on
|
||||
ENV GOPROXY=https://goproxy.io,direct
|
||||
|
||||
# COPY 源路径 目标路径
|
||||
COPY . $GOPATH/src/github.com/xinliangnote/go-gin-api
|
||||
|
||||
# RUN 执行 go build .
|
||||
RUN cd $GOPATH/src/github.com/xinliangnote/go-gin-api && go build .
|
||||
|
||||
# FROM 基于 alpine:latest
|
||||
FROM alpine:latest
|
||||
|
||||
# RUN 设置代理镜像
|
||||
RUN echo -e http://mirrors.ustc.edu.cn/alpine/v3.13/main/ > /etc/apk/repositories
|
||||
|
||||
# RUN 设置 Asia/Shanghai 时区
|
||||
RUN apk --no-cache add tzdata && \
|
||||
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
||||
echo "Asia/Shanghai" > /etc/timezone
|
||||
|
||||
# COPY 源路径 目标路径 从镜像中 COPY
|
||||
COPY --from=builder /opt/repo/src/github.com/xinliangnote/go-gin-api /opt
|
||||
|
||||
# EXPOSE 设置端口映射
|
||||
EXPOSE 9999/tcp
|
||||
|
||||
# WORKDIR 设置工作目录
|
||||
WORKDIR /opt
|
||||
|
||||
# CMD 设置启动命令
|
||||
CMD ["./go-gin-api", "-env", "fat"]
|
||||
@@ -13,8 +13,7 @@
|
||||
1. 支持 [Swagger](https://github.com/swaggo/gin-swagger) 接口文档生成
|
||||
1. 支持 [GraphQL](https://github.com/99designs/gqlgen) 查询语言
|
||||
1. 支持 trace 项目内部链路追踪
|
||||
1. 支持 [pprof](https://github.com/gin-contrib/pprof) 性能剖析
|
||||
1. 支持 [jwt](https://github.com/dgrijalva/jwt-go) 接口鉴权
|
||||
1. 支持 [pprof](https://github.com/gin-contrib/pprof) 性能剖析
|
||||
1. 支持 errno 统一定义错误码
|
||||
1. 支持 [zap](https://go.uber.org/zap) 日志收集
|
||||
1. 支持 [viper](https://github.com/spf13/viper) 配置文件解析
|
||||
@@ -22,27 +21,31 @@
|
||||
1. 支持 [go-redis](https://github.com/go-redis/redis/v7) 组件
|
||||
1. 支持 RESTful API 返回值规范
|
||||
1. 支持 生成数据表 CURD、控制器方法 等代码生成器
|
||||
1. 支持 [cron](https://github.com/jakecoffman/cron) 定时任务,在后台可界面配置
|
||||
1. 支持 [websocket](https://github.com/gorilla/websocket) 实时通讯,在后台有界面演示
|
||||
1. 支持 web 界面,使用的 [Light Year Admin 模板](https://gitee.com/yinqi/Light-Year-Admin-Using-Iframe)
|
||||
|
||||
|
||||
## 文档索引
|
||||
## 文档索引(可加入交流群)
|
||||
|
||||
go-gin-api 文档由以下几个主要部分组成:
|
||||
- 中文文档:[go-gin-api - 语雀](https://www.yuque.com/xinliangnote/go-gin-api/ngc3x5)
|
||||
- English Document:[en.md](https://github.com/xinliangnote/go-gin-api/blob/master/en.md)
|
||||
|
||||
- 准备工作
|
||||
- 快速开始
|
||||
- 目录接口
|
||||
- 核心封装
|
||||
- 组件指南
|
||||
- 工具包
|
||||
## 轻量版
|
||||
|
||||
**详细文档:[https://www.yuque.com/xinliangnote/go-gin-api/ngc3x5](https://www.yuque.com/xinliangnote/go-gin-api/ngc3x5)**
|
||||
为了满足开发者对于简单、轻量级 API 框架的需求,开发了 gin-api-mono,旨在提供更便捷的业务开发体验。
|
||||
|
||||
相比于 go-gin-api,首先 gin-api-mono 去掉了一些集成的功能和界面,使得整个框架更加简洁、轻量。其次 gin-api-mono 对框架代码进行了升级,以确保其在性能和稳定性方面的优势。这样,开发者就可以更灵活地选择所需的功能,并获得更好的性能和稳定性。
|
||||
|
||||
详见链接:https://xiaobot.net/post/e9f7ef4c-81b1-4ffc-9053-bec55c3abb12
|
||||
|
||||
## 其他
|
||||
|
||||
查看 Jaeger 链路追踪代码,请查看 [v1.0版](https://github.com/xinliangnote/go-gin-api/releases/tag/v1.0),文档点这里 [jaeger.md](https://github.com/xinliangnote/go-gin-api/blob/master/docs/jaeger.md) 。
|
||||
查看 Jaeger 链路追踪 Demo 代码,请查看 [v1.0 版](https://github.com/xinliangnote/go-gin-api/releases/tag/v1.0) ,链接地址:http://127.0.0.1:9999/jaeger_test
|
||||
|
||||
## Learning together
|
||||
调用的其他服务端 Demo 代码为 [https://github.com/xinliangnote/go-jaeger-demo](https://github.com/xinliangnote/go-jaeger-demo)
|
||||
|
||||

|
||||
## 联系作者
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package assets
|
||||
|
||||
import "embed"
|
||||
|
||||
var (
|
||||
//go:embed bootstrap
|
||||
Bootstrap embed.FS
|
||||
|
||||
//go:embed templates
|
||||
Templates embed.FS
|
||||
)
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,32 @@
|
||||
document.write('<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-notify/bootstrap-notify.min.js"></script>');
|
||||
|
||||
function SuccessNotify(content) {
|
||||
$.notify({
|
||||
icon: "mdi mdi-alert",
|
||||
title: "",
|
||||
message: content,
|
||||
url: "",
|
||||
target: ""
|
||||
}, {
|
||||
type: "success",
|
||||
allow_dismiss: true,
|
||||
newest_on_top: false,
|
||||
placement: {
|
||||
from: "top",
|
||||
align: "right",
|
||||
},
|
||||
offset: {
|
||||
x: "20",
|
||||
y: "20"
|
||||
},
|
||||
spacing: "10",
|
||||
z_index: "1031",
|
||||
delay: "3000",
|
||||
animate: {
|
||||
enter: "animated fadeInDown",
|
||||
exit: "animated fadeOutUp"
|
||||
},
|
||||
onClosed: null,
|
||||
mouse_over: null
|
||||
});
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/authorization/ksort.js"></script>');
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/authorization/crypto-js.min.js"></script>');
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/authorization/hmac-sha256.js"></script>');
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/authorization/enc-base64.min.js"></script>');
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/jquery.cookie.min.js"></script>');
|
||||
document.write('<script type="text/javascript" src="/assets/bootstrap/js/authorization/ksort.js"></script>');
|
||||
document.write('<script type="text/javascript" src="/assets/bootstrap/js/authorization/crypto-js.min.js"></script>');
|
||||
document.write('<script type="text/javascript" src="/assets/bootstrap/js/authorization/hmac-sha256.js"></script>');
|
||||
document.write('<script type="text/javascript" src="/assets/bootstrap/js/authorization/enc-base64.min.js"></script>');
|
||||
document.write('<script type="text/javascript" src="/assets/bootstrap/js/jquery.cookie.min.js"></script>');
|
||||
document.write('<div style="display:none"><script type="text/javascript">document.write(unescape("%3Cspan id=\'cnzz_stat_icon_1279911342\'%3E%3C/span%3E%3Cscript src=\'https://v1.cnzz.com/z_stat.php%3Fid%3D1279911342%26\' type=\'text/javascript\'%3E%3C/script%3E"));</script></div>');
|
||||
|
||||
function GenerateAuthorization(path, method, params) {
|
||||
@@ -11,7 +11,7 @@ function GenerateAuthorization(path, method, params) {
|
||||
|
||||
let date = new Date();
|
||||
let datetime = date.getFullYear() + "-" // "年"
|
||||
+ ((date.getMonth() + 1) > 10 ? (date.getMonth() + 1) : "0" + (date.getMonth() + 1)) + "-" // "月"
|
||||
+ ((date.getMonth() + 1) >= 10 ? (date.getMonth() + 1) : "0" + (date.getMonth() + 1)) + "-" // "月"
|
||||
+ (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " " // "日"
|
||||
+ (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":" // "小时"
|
||||
+ (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) + ":" // "分钟"
|
||||
@@ -44,10 +44,13 @@ function AjaxError(response) {
|
||||
let errCode = response.status;
|
||||
let errMsg = response.responseText;
|
||||
|
||||
if (errCode === 401) { // 跳转到登录页
|
||||
// 关闭当前界面
|
||||
parent.window.close();
|
||||
window.open("/login");
|
||||
if (errCode === 401) {
|
||||
// 跳转到登录页
|
||||
if (window.frames.length !== parent.frames.length) {
|
||||
parent.window.open("/login",'_self');
|
||||
}else{
|
||||
window.open("/login",'_self');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -51,13 +51,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/authorization/md5.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/authorization/md5.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -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>
|
||||
@@ -52,11 +53,11 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.pagination.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.pagination.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
// 加载列表页数据
|
||||
@@ -80,7 +81,7 @@
|
||||
function (data) {
|
||||
if (data.list.length > 0) {
|
||||
var totalNum = data.pagination.total; //总条数
|
||||
var pageNum = Math.ceil(totalNum / data.pagination.pre_page_count); //分页的总页数
|
||||
var pageNum = Math.ceil(totalNum / data.pagination.per_page_count); //分页的总页数
|
||||
|
||||
$("#paginationDiv").pagination({
|
||||
current: data.pagination.current_page,
|
||||
@@ -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');
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
|
||||
<title>登录页面</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="bootstrap/favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/assets/bootstrap/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link rel="stylesheet" type="text/css" href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="../../bootstrap/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="../../bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="../../bootstrap/css/style.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bootstrap/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bootstrap/css/style.min.css">
|
||||
|
||||
<style>
|
||||
.login-form .has-feedback {
|
||||
@@ -47,7 +47,7 @@
|
||||
<body class="center-vh">
|
||||
<div class="card card-shadowed p-5 w-420 mb-0 mr-2 ml-2">
|
||||
<div class="text-center mb-3">
|
||||
<img src="../../bootstrap/images/logo-sidebar.png">
|
||||
<img src="/assets/bootstrap/images/logo-sidebar.png">
|
||||
</div>
|
||||
<form class="login-form">
|
||||
<div class="form-group has-feedback">
|
||||
@@ -71,10 +71,10 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/authorization/md5.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/authorization/md5.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -53,12 +53,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
const hash_id = {{ .HashID }}
|
||||
|
||||
+11
-11
@@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -44,12 +44,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
+17
-14
@@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -46,13 +46,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/authorization/md5.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/authorization/md5.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
@@ -132,8 +132,11 @@
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
parent.window.close();
|
||||
window.open("/login");
|
||||
if (window.frames.length !== parent.frames.length) {
|
||||
parent.window.open("/login",'_self');
|
||||
}else{
|
||||
window.open("/login",'_self');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -21,17 +21,17 @@
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="formGroupExampleInput">调用方</label>
|
||||
<label for="business_key">调用方</label>
|
||||
<input type="text" class="form-control" maxlength="25" id="business_key"
|
||||
placeholder="请输入调用方标识">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="formGroupExampleInput2">调用方对接人</label>
|
||||
<label for="business_developer">调用方对接人</label>
|
||||
<input type="text" class="form-control" maxlength="50" id="business_developer"
|
||||
placeholder="请输入调用方对接人">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleFormControlTextarea1">备注</label>
|
||||
<label for="remark">备注</label>
|
||||
<textarea class="form-control" maxlength="225" rows="3" id="remark"
|
||||
placeholder="备注"></textarea>
|
||||
</div>
|
||||
@@ -46,12 +46,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -67,13 +67,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -206,10 +206,10 @@ console.log({authorization: key + " " + digest, date: datetime});
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/main.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/main.min.js"></script>
|
||||
<script type="text/javascript">document.write(unescape("%3Cspan id='cnzz_stat_icon_1279911342'%3E%3C/span%3E%3Cscript src='https://v1.cnzz.com/z_stat.php%3Fid%3D1279911342%26' type='text/javascript'%3E%3C/script%3E"));</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -51,11 +51,11 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.pagination.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.pagination.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
// 加载列表页数据
|
||||
@@ -79,7 +79,7 @@
|
||||
function (data) {
|
||||
if (data.list.length > 0) {
|
||||
var totalNum = data.pagination.total; //总条数
|
||||
var pageNum = Math.ceil(totalNum / data.pagination.pre_page_count); //分页的总页数
|
||||
var pageNum = Math.ceil(totalNum / data.pagination.per_page_count); //分页的总页数
|
||||
|
||||
$("#paginationDiv").pagination({
|
||||
current: data.pagination.current_page,
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -56,10 +56,10 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">新增后台任务</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="name">任务名称</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="60" id="name"
|
||||
placeholder="请输入任务名称">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="protocol">选择任务执行方式</label>
|
||||
<select class="form-control" id="protocol">
|
||||
<option value="1">SHELL</option>
|
||||
<option value="2">HTTP</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group http_method">
|
||||
<label for="http_method">选择 HTTP 请求方式</label>
|
||||
<select class="form-control" id="http_method">
|
||||
<option value="0">请选择</option>
|
||||
<option value="1">GET</option>
|
||||
<option value="2">POST</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="spec">任务表达式</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="60" id="spec"
|
||||
placeholder="请输入任务表达式,例如:*/5 * * * *">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="command">命令</label>
|
||||
<textarea class="form-control maxlength" maxlength="250" rows="2" id="command"
|
||||
placeholder="请输入任务表达式,例如:/usr/local/bin/php /data/web/artisan consoleCommand"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="timeout">超时时间(单位:秒)</label>
|
||||
<input type="text" class="form-control" id="timeout"
|
||||
placeholder="请输入超时时间,例如:180">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="retry_times">重试次数</label>
|
||||
<input type="text" class="form-control" id="retry_times"
|
||||
placeholder="请输入重试次数,例如:3">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="retry_interval">重试间隔(单位:秒)</label>
|
||||
<input type="text" class="form-control" id="retry_interval"
|
||||
placeholder="请输入重试间隔,例如:60">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="notify_status">执行结束是否通知</label>
|
||||
<select class="form-control" id="notify_status">
|
||||
<option value="1">不通知</option>
|
||||
<option value="2">失败通知</option>
|
||||
<option value="3">结束通知</option>
|
||||
<option value="4">结果关键字匹配通知</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_type">
|
||||
<label for="notify_type">通知方式</label>
|
||||
<select class="form-control" id="notify_type">
|
||||
<option value="0">请选择</option>
|
||||
<option value="1">邮件</option>
|
||||
<option value="2">webhook</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_receiver_email">
|
||||
<label for="notify_receiver_email">通知者邮箱地址(多个用,分割)</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="255" id="notify_receiver_email"
|
||||
placeholder="请输入邮箱地址">
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_keyword">
|
||||
<label for="notify_keyword">匹配关键字(多个用,分割)</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="255" id="notify_keyword"
|
||||
placeholder="请输入匹配关键字">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>状态</label>
|
||||
<div class="clearfix">
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="statusOne" value="1" name="is_used"
|
||||
class="custom-control-input"
|
||||
checked="">
|
||||
<label class="custom-control-label" for="statusOne">启用</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="statusTwo" value="-1" name="is_used"
|
||||
class="custom-control-input">
|
||||
<label class="custom-control-label" for="statusTwo">禁用</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="remark">备注</label>
|
||||
<textarea class="form-control maxlength" maxlength="100" rows="3" id="remark"
|
||||
placeholder="备注"></textarea>
|
||||
</div>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
$(".maxlength").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$(".http_method").hide();
|
||||
|
||||
$("#protocol").change(function () {
|
||||
if ($(this).val() === "2") {
|
||||
$(".http_method").show();
|
||||
} else {
|
||||
$("#http_method").val(0);
|
||||
$(".http_method").hide();
|
||||
}
|
||||
})
|
||||
|
||||
$(".notify_type").hide();
|
||||
$(".notify_receiver_email").hide();
|
||||
$(".notify_keyword").hide();
|
||||
|
||||
$("#notify_status").change(function () {
|
||||
if ($(this).val() === "1") {
|
||||
$(".notify_type").hide();
|
||||
$("#notify_type").val(0)
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
$(".notify_keyword").hide();
|
||||
$("#notify_keyword").val("");
|
||||
} else if($(this).val() === "2" || $(this).val() === "3") {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").hide();
|
||||
$("#notify_keyword").val("");
|
||||
|
||||
if (("#notify_type").val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
}
|
||||
} else if($(this).val() === "4") {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").show();
|
||||
|
||||
if (("#notify_type").val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#notify_type").change(function (){
|
||||
if ($(this).val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
}
|
||||
});
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const name = $("#name").val();
|
||||
if (name === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入任务名称。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const spec = $("#spec").val();
|
||||
if (spec === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入任务表达式。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const command = $("#command").val();
|
||||
if (spec === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入命令。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
name: name,
|
||||
spec: spec,
|
||||
command: command,
|
||||
protocol: $("#protocol").val(),
|
||||
http_method: $("#http_method").val(),
|
||||
timeout: $("#timeout").val(),
|
||||
retry_times: $("#retry_times").val(),
|
||||
retry_interval: $("#retry_interval").val(),
|
||||
notify_status: $("#notify_status").val(),
|
||||
notify_type: $("#notify_type").val(),
|
||||
notify_receiver_email: $("#notify_receiver_email").val(),
|
||||
notify_keyword: $("#notify_keyword").val(),
|
||||
is_used: $("input[name='is_used']:checked").val(),
|
||||
remark: $("#remark").val(),
|
||||
};
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/cron",
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function (data) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 创建完成。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.href = "/cron/list";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,349 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">编辑后台任务</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="name">任务名称</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="60" id="name"
|
||||
placeholder="请输入任务名称">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="protocol">选择任务执行方式</label>
|
||||
<select class="form-control" id="protocol">
|
||||
<option value="1">SHELL</option>
|
||||
<option value="2">HTTP</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group http_method">
|
||||
<label for="http_method">选择 HTTP 请求方式</label>
|
||||
<select class="form-control" id="http_method">
|
||||
<option value="0">请选择</option>
|
||||
<option value="1">GET</option>
|
||||
<option value="2">POST</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="spec">任务表达式</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="60" id="spec"
|
||||
placeholder="请输入任务表达式,例如:*/5 * * * *">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="command">命令</label>
|
||||
<textarea class="form-control maxlength" maxlength="250" rows="2" id="command"
|
||||
placeholder="请输入任务表达式,例如:/usr/local/bin/php /data/web/artisan consoleCommand"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="timeout">超时时间(单位:秒)</label>
|
||||
<input type="text" class="form-control" id="timeout"
|
||||
placeholder="请输入超时时间,例如:180">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="retry_times">重试次数</label>
|
||||
<input type="text" class="form-control" id="retry_times"
|
||||
placeholder="请输入重试次数,例如:3">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="retry_interval">重试间隔(单位:秒)</label>
|
||||
<input type="text" class="form-control" id="retry_interval"
|
||||
placeholder="请输入重试间隔,例如:60">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="notify_status">执行结束是否通知</label>
|
||||
<select class="form-control" id="notify_status">
|
||||
<option value="1">不通知</option>
|
||||
<option value="2">失败通知</option>
|
||||
<option value="3">结束通知</option>
|
||||
<option value="4">结果关键字匹配通知</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_type">
|
||||
<label for="notify_type">通知方式</label>
|
||||
<select class="form-control" id="notify_type">
|
||||
<option value="0">请选择</option>
|
||||
<option value="1">邮件</option>
|
||||
<option value="2">webhook</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_receiver_email">
|
||||
<label for="notify_receiver_email">通知者邮箱地址(多个用,分割)</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="255" id="notify_receiver_email"
|
||||
placeholder="请输入邮箱地址">
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_keyword">
|
||||
<label for="notify_keyword">匹配关键字(多个用,分割)</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="255" id="notify_keyword"
|
||||
placeholder="请输入匹配关键字">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>状态</label>
|
||||
<div class="clearfix">
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="statusOne" value="1" name="is_used"
|
||||
class="custom-control-input">
|
||||
<label class="custom-control-label" for="statusOne">启用</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="statusTwo" value="-1" name="is_used"
|
||||
class="custom-control-input">
|
||||
<label class="custom-control-label" for="statusTwo">禁用</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="remark">备注</label>
|
||||
<textarea class="form-control maxlength" maxlength="100" rows="3" id="remark"
|
||||
placeholder="备注"></textarea>
|
||||
</div>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
const hash_id = {{ .HashID }}
|
||||
|
||||
$(".maxlength").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$("#protocol").change(function () {
|
||||
if ($(this).val() === "2") {
|
||||
$(".http_method").show();
|
||||
} else {
|
||||
$("#http_method").val(0);
|
||||
$(".http_method").hide();
|
||||
}
|
||||
})
|
||||
|
||||
$("#notify_status").change(function () {
|
||||
if ($(this).val() === "1") {
|
||||
$(".notify_type").hide();
|
||||
$("#notify_type").val(0)
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
$(".notify_keyword").hide();
|
||||
$("#notify_keyword").val("");
|
||||
} else if($(this).val() === "2" || $(this).val() === "3") {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").hide();
|
||||
$("#notify_keyword").val("");
|
||||
|
||||
if (("#notify_type").val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
}
|
||||
} else if($(this).val() === "4") {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").show();
|
||||
|
||||
if (("#notify_type").val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#notify_type").change(function (){
|
||||
if ($(this).val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
}
|
||||
});
|
||||
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/cron/" + hash_id,
|
||||
"",
|
||||
function () {},
|
||||
function (data) {
|
||||
$("#name").val(data.name);
|
||||
$("#protocol").val(data.protocol);
|
||||
if (data.protocol === 1) {
|
||||
$(".http_method").hide();
|
||||
}
|
||||
|
||||
$("#http_method").val(data.http_method);
|
||||
$("#spec").val(data.spec);
|
||||
$("#command").val(data.command);
|
||||
$("#timeout").val(data.timeout);
|
||||
$("#retry_times").val(data.retry_times);
|
||||
$("#retry_interval").val(data.retry_interval);
|
||||
$("#notify_status").val(data.notify_status);
|
||||
if (data.notify_status === 1) {
|
||||
$(".notify_type").hide();
|
||||
$(".notify_receiver_email").hide();
|
||||
$(".notify_keyword").hide();
|
||||
} else if (data.notify_status === 2 || data.notify_status === 3) {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").hide();
|
||||
|
||||
if (data.notify_type === 1) {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
}
|
||||
} else if (data.notify_status === 4) {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").show();
|
||||
|
||||
if (data.notify_type === 1) {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
}
|
||||
}
|
||||
|
||||
$("#notify_type").val(data.notify_type);
|
||||
$("#notify_receiver_email").val(data.notify_receiver_email);
|
||||
$("#notify_keyword").val(data.notify_keyword);
|
||||
$("input[name='is_used'][value='"+data.is_used+"']").attr("checked", true);
|
||||
$("#remark").val(data.remark);
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const name = $("#name").val();
|
||||
if (name === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入任务名称。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const spec = $("#spec").val();
|
||||
if (spec === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入任务表达式。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const command = $("#command").val();
|
||||
if (spec === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入命令。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
id:hash_id,
|
||||
name: name,
|
||||
spec: spec,
|
||||
command: command,
|
||||
protocol: $("#protocol").val(),
|
||||
http_method: $("#http_method").val(),
|
||||
timeout: $("#timeout").val(),
|
||||
retry_times: $("#retry_times").val(),
|
||||
retry_interval: $("#retry_interval").val(),
|
||||
notify_status: $("#notify_status").val(),
|
||||
notify_type: $("#notify_type").val(),
|
||||
notify_receiver_email: $("#notify_receiver_email").val(),
|
||||
notify_keyword: $("#notify_keyword").val(),
|
||||
is_used: $("input[name='is_used']:checked").val(),
|
||||
remark: $("#remark").val(),
|
||||
};
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/cron/" + hash_id,
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function (data) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 信息修改成功。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.href = "/cron/list";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,325 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-toolbar d-flex flex-column flex-md-row">
|
||||
<div class="toolbar-btn-action">
|
||||
<a class="btn btn-primary m-r-5" href="/cron/add"><i class="mdi mdi-plus"></i> 新增</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>编号</th>
|
||||
<th>任务名称</th>
|
||||
<th>cron 表达式</th>
|
||||
<th>执行方式</th>
|
||||
<th>超时限制(秒)</th>
|
||||
<th>重试次数</th>
|
||||
<th>重试间隔(秒)</th>
|
||||
<th>通知方式</th>
|
||||
<th>创建人</th>
|
||||
<th>创建日期</th>
|
||||
<th style="text-align: center; ">可用状态</th>
|
||||
<th style="text-align: center; ">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="tbody">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<ul class="pagination">
|
||||
<ul class="pagination" id="paginationDiv">
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.pagination.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
// 加载列表页数据
|
||||
getPageListData();
|
||||
|
||||
// 提示工具
|
||||
$('[data-toggle="popover"]').popover();
|
||||
|
||||
//$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
function getPageListData(page = 0, page_size = 0) {
|
||||
|
||||
if (parseInt(page) < 1) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (parseInt(page_size) < 1) {
|
||||
page_size = 10;
|
||||
}
|
||||
|
||||
AjaxFormNoAsync(
|
||||
"GET",
|
||||
"/api/cron",
|
||||
{page: page, page_size: page_size},
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
if (data.list.length > 0) {
|
||||
var totalNum = data.pagination.total; //总条数
|
||||
var pageNum = Math.ceil(totalNum / data.pagination.per_page_count); //分页的总页数
|
||||
|
||||
$("#paginationDiv").pagination({
|
||||
current: data.pagination.current_page,
|
||||
pageCount: pageNum,
|
||||
coping: true,
|
||||
homePage: '首页',
|
||||
endPage: '末页',
|
||||
mode: 'fixed',
|
||||
prevContent: '上一页',
|
||||
nextContent: '下一页',
|
||||
activeCls: 'pageActive',
|
||||
prevCls: 'pagePrev',
|
||||
nextCls: 'pageNext',
|
||||
callback: function (api) {
|
||||
$(".tbody").html("");
|
||||
getPageListData(api.getCurrent());
|
||||
}
|
||||
});
|
||||
|
||||
$.each(data.list, function (index, value) {
|
||||
var showUsedBadge = "";
|
||||
var showProtocolBadge = "";
|
||||
var optionUsedName = "";
|
||||
var notifyBadge = "";
|
||||
|
||||
if (value.is_used === 1) {
|
||||
optionUsedName = '禁用';
|
||||
showUsedBadge = '<span class="badge badge-success">启用</span>';
|
||||
}
|
||||
|
||||
if (value.is_used === -1) {
|
||||
optionUsedName = '启用';
|
||||
showUsedBadge = '<span class="badge badge-danger">禁用</span>';
|
||||
}
|
||||
|
||||
if (value.protocol === 1) {
|
||||
showProtocolBadge = '<span class="badge btn-brown">SHELL</span>';
|
||||
}
|
||||
|
||||
if (value.protocol === 2) {
|
||||
showProtocolBadge = '<span class="badge btn-warning">HTTP</span> ';
|
||||
showProtocolBadge += '<span class="badge btn-secondary">' + value.http_method_text + '</span>';
|
||||
}
|
||||
|
||||
notifyBadge = '<span class="badge btn-cyan">' + value.notify_status_text + '</span>';
|
||||
|
||||
const tr = '<tr>\n' +
|
||||
'<td>' + value.id + '</td>\n' +
|
||||
'<td>' + value.name + '</td>\n' +
|
||||
'<td>' + value.spec + ' <a tabindex="' + value.id + '" role="button" class="badge btn-secondary" data-toggle="popover" data-trigger="focus" title="' + value.spec + '" data-content="' + value.command + '">命令</a></td>\n' +
|
||||
'<td>' + showProtocolBadge + '</td>\n' +
|
||||
'<td>' + value.timeout + '</td>\n' +
|
||||
'<td>' + value.retry_times + '</td>\n' +
|
||||
'<td>' + value.retry_interval + '</td>\n' +
|
||||
'<td>' + notifyBadge + '</td>\n' +
|
||||
'<td>' + value.created_user + '</td>\n' +
|
||||
'<td>' + value.created_at + '</td>\n' +
|
||||
'<td style="text-align: center; ">' + showUsedBadge + '</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' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-is-used="' + value.is_used + '"' +
|
||||
' data-toggle="tooltip" data-original-title="' + optionUsedName + '">' + optionUsedName + '</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-edit" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="编辑">编辑</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-log" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="日志">日志</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-exec" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="手动执行">手动执行</a>\n' +
|
||||
'</div>\n' +
|
||||
'</td>\n' +
|
||||
'</tr>';
|
||||
|
||||
$(".tbody").append(tr);
|
||||
})
|
||||
} else {
|
||||
// 数据为空
|
||||
const tr = '<tr><td colspan="12" style="text-align: center">暂无数据</td></tr>';
|
||||
$(".tbody").append(tr);
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 启用/禁用
|
||||
$(document).on('click', '.btn-option', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
const isUsed = $(this).attr('data-is-used');
|
||||
|
||||
var tipMessage = "";
|
||||
var wantUsed = 0;
|
||||
if (isUsed === "1") { // 1=当前为启用状态,需要改成禁用
|
||||
tipMessage = "禁用";
|
||||
wantUsed = -1;
|
||||
}
|
||||
if (isUsed === "-1") { // -1=当前为禁用状态,需要改成启用
|
||||
tipMessage = "启用";
|
||||
wantUsed = 1;
|
||||
}
|
||||
|
||||
const patchData = {
|
||||
id: id,
|
||||
used: wantUsed,
|
||||
};
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">' + tipMessage + '</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/cron/used",
|
||||
patchData,
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 已' + tipMessage + '。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 编辑
|
||||
$(document).on('click', '.btn-edit', function () {
|
||||
location.href = "/cron/edit/" + $(this).attr('data-id');
|
||||
});
|
||||
|
||||
// 日志
|
||||
$(document).on('click', '.btn-log', function () {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '功能开发中...',
|
||||
});
|
||||
})
|
||||
|
||||
// 手动执行
|
||||
$(document).on('click', '.btn-exec', 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/cron/exec/" + 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'],
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,8 +3,8 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -15,10 +15,15 @@
|
||||
<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>
|
||||
<p>操作系统:{{ .GoOS }}
|
||||
<span class="badge badge-brown"> {{ .GoArch }} </span>
|
||||
<span class="badge badge-info"> {{ .GoVersion }} </span>
|
||||
<span class="badge badge-danger"> mysql{{ .MySQLVersion }} </span>
|
||||
<span class="badge badge-danger"> redis{{ .RedisVersion }} </span>
|
||||
</p>
|
||||
<p>项目地址:{{ .ProjectPath }}</p>
|
||||
<p>项目域名:{{ .Host }} <span class="badge badge-secondary"> {{ .Env }} </span></p>
|
||||
</div>
|
||||
@@ -82,7 +87,7 @@
|
||||
<div class="card-title">开源信息</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p>GitHub 地址:<a target="_blank" href="https://github.com/xinliangnote/go-gin-api">xinliangnote/go-gin-api</a></p>
|
||||
<p>GitHub 地址:<a target="_blank" href="https://github.com/xinliangnote/go-gin-api">[go-gin-api 源码地址]</a> <a target="_blank" href="https://www.yuque.com/xinliangnote/go-gin-api/ngc3x5">[中文文档]</a></p>
|
||||
<p>GitHub Stars:<a href="https://github.com/xinliangnote/go-gin-api/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/xinliangnote/go-gin-api?style=flat-square"></a></p>
|
||||
<p>GitHub Forks:<a href="https://github.com/xinliangnote/go-gin-api/network"><img alt="GitHub forks" src="https://img.shields.io/github/forks/xinliangnote/go-gin-api?style=flat-square"></a></p>
|
||||
</div>
|
||||
@@ -96,7 +101,7 @@
|
||||
</header>
|
||||
<div class="card-body text-center">
|
||||
<p style="height: 105px;">
|
||||
<img style="height: 100px;" src="../../bootstrap/images/qr-code.png"/>
|
||||
<img style="height: 100px;" src="/assets/bootstrap/images/qr-code.png"/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -48,9 +48,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("#btnOk").click(function(){
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -16,7 +16,7 @@
|
||||
<header class="card-header"><div class="card-title"> 生成控制器方法 </div></header>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label for="exampleFormControlInput1">handler 名称,例如:<code>user_handler</code></label>
|
||||
<label for="handlerName">API handler 名称,例如:<code>user</code>,<small>user 为 internal/api 目录下的文件夹名称</small></label>
|
||||
<input type="text" class="form-control" id="handlerName" placeholder="请输入 handler 名称">
|
||||
</div>
|
||||
|
||||
@@ -44,9 +44,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("#btnOk").click(function(){
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
|
||||
<title>管理面板</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="bootstrap/favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/assets/bootstrap/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/js/jquery-confirm/jquery-confirm.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/js/bootstrap-multitabs/multitabs.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/css/animate.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/css/style.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bootstrap/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bootstrap/css/animate.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/assets/bootstrap/css/style.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -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"/>
|
||||
<img src="/assets/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>
|
||||
@@ -223,7 +170,7 @@
|
||||
|
||||
<li class="dropdown dropdown-profile">
|
||||
<a href="javascript:void(0)" data-toggle="dropdown" class="dropdown-toggle">
|
||||
<img class="img-avatar img-avatar-48 m-r-10" src="bootstrap/images/users/avatar.png">
|
||||
<img class="img-avatar img-avatar-48 m-r-10" src="/assets/bootstrap/images/users/avatar.png">
|
||||
<span id="nickname"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
@@ -247,31 +194,31 @@
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
<!--End 头部信息-->
|
||||
|
||||
|
||||
<!--页面主要内容-->
|
||||
<main class="lyear-layout-content">
|
||||
|
||||
|
||||
<div id="iframe-content"></div>
|
||||
|
||||
|
||||
</main>
|
||||
<!--End 页面主要内容-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/perfect-scrollbar.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/jquery.cookie.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/index.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/perfect-scrollbar.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.cookie.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/index.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
AjaxForm(
|
||||
@@ -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);
|
||||
@@ -299,8 +281,11 @@
|
||||
$.cookie('_nav_title_', '');
|
||||
$.cookie('_login_token_', '');
|
||||
|
||||
parent.window.close();
|
||||
window.open("/login");
|
||||
if (window.frames.length !== parent.frames.length) {
|
||||
parent.window.open("/login",'_self');
|
||||
}else{
|
||||
window.open("/login",'_self');
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
|
||||
<title>服务初始化</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../bootstrap/favicon.ico">
|
||||
<title>项目初始化</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/assets/bootstrap/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-select/bootstrap-select.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -21,17 +22,27 @@
|
||||
<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 text-center">
|
||||
<h4 class="card-title">检测 · 环境</h4>
|
||||
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><span class="badge badge-info">检测 · 环境</span></h5>
|
||||
<div class="input-group mb-3">
|
||||
<p class="text-left">所需版本: Go Version <code>go1.15+</code>,目前版本:<code>{{.GoVersion}}</code>。</p>
|
||||
<p class="text-left">所需版本:<code>go{{.MinGoVersion}}+</code> (<small>当前版本 </small><code>{{.GoVersion}}</code>)</p>
|
||||
</div>
|
||||
|
||||
<h4 class="card-title">配置 · Redis</h4>
|
||||
<h5 class="card-title"><span class="badge badge-info">配置 · 语言</span></h5>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">语言</span>
|
||||
</div>
|
||||
<select class="form-control select-picker col-lg-3" data-width="auto"
|
||||
data-live-search="true" id="language">
|
||||
<option value="zh-cn">简体中文</option>
|
||||
<option value="en-us">English</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title"><span class="badge badge-info">配置 · Redis</span></h5>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">地址</span>
|
||||
@@ -56,8 +67,7 @@
|
||||
placeholder="请输入 DB ,序号从 0 开始,默认是 0">
|
||||
</div>
|
||||
|
||||
<h4 class="card-title">配置 · MySQL</h4>
|
||||
|
||||
<h5 class="card-title"><span class="badge badge-info">配置 · MySQL</span></h5>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">地址</span>
|
||||
@@ -88,21 +98,12 @@
|
||||
</div>
|
||||
<input type="text" class="form-control" id="mysql_name" value="{{ .Config.MySQL.Write.Name }}"
|
||||
placeholder="请输入数据库名">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><code>请确保此数据库已存在!</code></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<small>
|
||||
<i class="mdi mdi-checkbox-marked-circle"></i>
|
||||
初始化 MySQL 数据表:<code>authorized</code>、
|
||||
<code>authorized_api</code> 、
|
||||
<code>admin</code>、
|
||||
<code>menu</code>、
|
||||
<code>menu_action</code>、
|
||||
<code>admin_menu</code>;
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<button type="button" id="btnOk" class="btn btn-primary">初始化</button>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">初始化项目</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
@@ -124,12 +125,17 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-select/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-select/i18n/defaults-zh_CN.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.select-picker').selectpicker();
|
||||
|
||||
$("#btnOk").click(function () {
|
||||
|
||||
const redis_addr = $("#redis_addr").val();
|
||||
@@ -199,6 +205,8 @@
|
||||
}
|
||||
|
||||
const postData = {
|
||||
language: $("#language").val(),
|
||||
|
||||
redis_addr: redis_addr,
|
||||
redis_pass: $("#redis_pass").val(),
|
||||
redis_db: redis_db,
|
||||
|
||||
@@ -5,56 +5,120 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
|
||||
<title>服务升级</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../bootstrap/favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/assets/bootstrap/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<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.2.6</span>
|
||||
->
|
||||
<span class="badge badge-pill badge-warning">v1.2.7</span>
|
||||
</p>
|
||||
|
||||
<p>1、源代码升级:
|
||||
<mark>拉取最新代码,覆盖旧版本代码即可。</mark>
|
||||
</p>
|
||||
<p>2、数据表升级:
|
||||
<mark class="btn btn-xs btn-info upgrade" data-op="table" data-table="cron_task">创建 cron_task 表结构</mark>
|
||||
</p>
|
||||
<p>3、系统管理员->菜单管理,新增侧边栏:后台任务模块,同时添加菜单栏的功能权限。</p>
|
||||
<p>4、系统管理员->管理员,对管理员进行菜单授权。</p>
|
||||
<p>5、退出重新登录,即可以看到自己新增的模块。</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<p class="h6">
|
||||
<span class="badge badge-pill badge-warning">v1.2.7</span>
|
||||
->
|
||||
<span class="badge badge-pill badge-warning">v1.2.8</span>
|
||||
</p>
|
||||
|
||||
<p>1、源代码升级:
|
||||
<mark>拉取最新代码,覆盖旧版本代码即可。</mark>
|
||||
</p>
|
||||
<p>2、系统管理员->菜单管理,新增侧边栏:实用工具箱 -> WebSocket,同时添加菜单栏的功能权限。</p>
|
||||
<p>3、系统管理员->管理员,对管理员进行菜单授权。</p>
|
||||
<p>4、退出重新登录,即可以看到自己新增的模块。</p>
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$(document).on('click', '.upgrade', function () {
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -69,13 +69,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-treegrid/jquery.treegrid.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-select/bootstrap-select.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-treegrid/jquery.treegrid.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-select/bootstrap-select.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<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>
|
||||
@@ -76,18 +76,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-table/bootstrap-table.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-treegrid/jquery.treegrid.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-table/bootstrap-table.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-treegrid/jquery.treegrid.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="../../bootstrap/js/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-select/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-select/i18n/defaults-zh_CN.min.js"></script>
|
||||
src="/assets/bootstrap/js/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-select/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-select/i18n/defaults-zh_CN.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.select-picker').selectpicker();
|
||||
@@ -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");
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -61,11 +61,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-select/bootstrap-select.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/bootstrap-select/bootstrap-select.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -96,15 +96,15 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/vkbeautify.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/main.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-select/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-select/i18n/defaults-zh_CN.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/vkbeautify.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/main.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-select/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-select/i18n/defaults-zh_CN.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.select-picker').selectpicker();
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -75,12 +75,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -97,9 +97,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<div style="display:none">
|
||||
<script type="text/javascript">document.write(unescape("%3Cspan id='cnzz_stat_icon_1279911342'%3E%3C/span%3E%3Cscript src='https://v1.cnzz.com/z_stat.php%3Fid%3D1279911342%26' type='text/javascript'%3E%3C/script%3E"));</script>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/animate.min.css" rel="stylesheet">
|
||||
<link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">WebSocket 示例</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
|
||||
<ul class="lyear-timeline lyear-timeline-left timeline-content">
|
||||
<li class="lyear-timeline-item">
|
||||
<div class="lyear-timeline-item-dot">
|
||||
<span class="badge"></span>
|
||||
</div>
|
||||
<div class="lyear-timeline-item-content">
|
||||
<p class="mb-1"><strong class="connect-content"></strong></p>
|
||||
<p class="mb-0 connect-url"></p>
|
||||
<p>
|
||||
<time class="mb-3 connect-time"></time>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">WebSocket 示例</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#send_message"
|
||||
aria-selected="true">发送消息</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade active show" id="send_message">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">消息内容</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="message_content" placeholder="请输入消息内容">
|
||||
</div>
|
||||
|
||||
<button type="button" id="btnSend" class="btn btn-primary">发送</button>
|
||||
<button type="button" id="btnSending" class="btn btn-primary" disabled
|
||||
style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
发送中...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap-notify/notify.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
const ws = new WebSocket("ws://127.0.0.1:9999/socket/system/message");
|
||||
|
||||
//连接打开时触发
|
||||
ws.onopen = function (evt) {
|
||||
if (evt.isTrusted) {
|
||||
$(".badge").addClass("badge-success");
|
||||
$(".connect-content").html("Socket 连接成功");
|
||||
$(".connect-url").html("连接地址:" + evt.currentTarget.url);
|
||||
$(".connect-time").html(getCSTTime());
|
||||
} else {
|
||||
$(".badge").addClass("badge-danger");
|
||||
$(".connect-content").html("Socket 连接失败");
|
||||
$(".connect-url").html("请确认连接地址是否正确");
|
||||
$(".connect-time").html(getCSTTime());
|
||||
}
|
||||
};
|
||||
|
||||
//接收到消息时触发
|
||||
ws.onmessage = function (evt) {
|
||||
const messageData = JSON.parse(evt.data);
|
||||
|
||||
let html = '<li class="lyear-timeline-item">';
|
||||
html += '<div class="lyear-timeline-item-dot"><span class="badge badge-success"></span></div>';
|
||||
html += '<div class="lyear-timeline-item-content">';
|
||||
html += '<p class="mb-1"><strong>' + messageData.username + '</strong></p>';
|
||||
html += '<p class="mb-0">' + messageData.message + '</p>';
|
||||
html += '<p><time class="mb-3">' + messageData.time + '</time></p>';
|
||||
html += '</div></li>';
|
||||
|
||||
$(".timeline-content").prepend(html);
|
||||
};
|
||||
|
||||
//连接关闭时触发
|
||||
ws.onclose = function (evt) {
|
||||
|
||||
let html = '<li class="lyear-timeline-item">';
|
||||
html += '<div class="lyear-timeline-item-dot"><span class="badge badge-muted"></span></div>';
|
||||
html += '<div class="lyear-timeline-item-content">';
|
||||
html += '<p class="mb-1"><strong>Socket 连接关闭</strong></p>';
|
||||
html += '<p class="mb-0">连接已关闭</p>';
|
||||
html += '<p><time class="mb-3">' + getCSTTime() + '</time></p>';
|
||||
html += '</div></li>';
|
||||
|
||||
$(".timeline-content").prepend(html);
|
||||
|
||||
};
|
||||
|
||||
function getCSTTime() {
|
||||
let date = new Date();
|
||||
let datetime = date.getFullYear() + "-" // "年"
|
||||
+ ((date.getMonth() + 1) > 10 ? (date.getMonth() + 1) : "0" + (date.getMonth() + 1)) + "-" // "月"
|
||||
+ (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " " // "日"
|
||||
+ (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":" // "小时"
|
||||
+ (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) + ":" // "分钟"
|
||||
+ (date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds()); // "秒"
|
||||
|
||||
return datetime;
|
||||
}
|
||||
|
||||
$('#btnSend').on('click', function () {
|
||||
const messageContent = $("#message_content").val();
|
||||
if (messageContent === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入消息内容。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/tool/send_message",
|
||||
{message: messageContent},
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnSendLoading").show();
|
||||
},
|
||||
function (data) {
|
||||
$("#btnSendLoading").hide();
|
||||
$("#btnSend").show();
|
||||
|
||||
if (data.status === 'OK') {
|
||||
$("#message_content").val("");
|
||||
SuccessNotify("消息发送成功");
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
$("#btnSendLoading").hide();
|
||||
$("#btnSend").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/mysql"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
@@ -135,7 +135,7 @@ func (qb *{{.QueryBuilderName}}) Offset(offset int) *{{.QueryBuilderName}} {
|
||||
|
||||
{{$queryBuilderName := .QueryBuilderName}}
|
||||
{{range .OptionFields}}
|
||||
func (qb *{{$queryBuilderName}}) Where{{call $.Helpers.Titelize .FieldName}}(p db_repo.Predicate, value {{.FieldType}}) *{{$queryBuilderName}} {
|
||||
func (qb *{{$queryBuilderName}}) Where{{call $.Helpers.Titelize .FieldName}}(p mysql.Predicate, value {{.FieldType}}) *{{$queryBuilderName}} {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
## 执行命令
|
||||
|
||||
```$xslt
|
||||
// test_handler 为 ./internal/api/controller/ 中的包名
|
||||
./scripts/handlergen.sh test_handler
|
||||
```
|
||||
|
||||
## 模板文件参考
|
||||
|
||||
```go
|
||||
package test_handler
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_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/db"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ Handler = (*handler)(nil)
|
||||
|
||||
type Handler interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
|
||||
// Create 创建用户
|
||||
// @Tags Test
|
||||
// @Router /test/create [post]
|
||||
Create() core.HandlerFunc
|
||||
|
||||
// Update 编辑用户
|
||||
// @Tags Test
|
||||
// @Router /test/update [post]
|
||||
Update() core.HandlerFunc
|
||||
|
||||
// Delete 删除用户
|
||||
// @Tags Test
|
||||
// @Router /test/delete [post]
|
||||
Delete() core.HandlerFunc
|
||||
|
||||
// Detail 用户详情
|
||||
// @Tags Test
|
||||
// @Router /test/detail [post]
|
||||
Detail() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
userService user_service.UserService
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
userService: user_service.NewUserService(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) i() {}
|
||||
|
||||
```
|
||||
|
||||
以上会生成 4 个文件
|
||||
- func_create.go
|
||||
- func_update.go
|
||||
- func_delete.go
|
||||
- func_detail.go
|
||||
|
||||
## func_create.go 参考
|
||||
|
||||
```go
|
||||
package test_handler
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
type createRequest struct{}
|
||||
|
||||
type createResponse struct{}
|
||||
|
||||
// Create 创建用户
|
||||
// @Summary 创建用户
|
||||
// @Description 创建用户
|
||||
// @Tags Test
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Request body createRequest true "请求信息"
|
||||
// @Success 200 {object} createResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /test/create [post]
|
||||
func (h *handler) Create() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
+11
-4
@@ -25,7 +25,7 @@ func init() {
|
||||
|
||||
func main() {
|
||||
fs := token.NewFileSet()
|
||||
filePath := fmt.Sprintf("./internal/api/controller/%s", handlerName)
|
||||
filePath := fmt.Sprintf("./internal/api/%s", handlerName)
|
||||
parsedFile, err := decorator.ParseFile(fs, filePath+"/handler.go", nil, 0)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing package: %s: %s\n", filePath, err)
|
||||
@@ -59,12 +59,19 @@ func main() {
|
||||
continue
|
||||
}
|
||||
|
||||
filepath := "./internal/api/controller/" + handlerName
|
||||
filepath := "./internal/api/" + handlerName
|
||||
filename := fmt.Sprintf("%s/func_%s.go", filepath, strings.ToLower(v.Names[0].String()))
|
||||
funcFile, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0766)
|
||||
if err != nil {
|
||||
fmt.Printf("create and open func file error %v\n", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
if funcFile == nil {
|
||||
fmt.Printf("func file is nil \n")
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Println(" └── file : ", filename)
|
||||
|
||||
funcContent := fmt.Sprintf("package %s\n\n", handlerName)
|
||||
@@ -82,14 +89,14 @@ func main() {
|
||||
funcContent += fmt.Sprintf("// @Description%s \n", nameArr[1])
|
||||
// Tags
|
||||
funcContent += fmt.Sprintf("%s \n", v.Decorations().Start.All()[1])
|
||||
funcContent += fmt.Sprintf("// @Accept json \n")
|
||||
funcContent += fmt.Sprintf("// @Accept application/x-www-form-urlencoded \n")
|
||||
funcContent += fmt.Sprintf("// @Produce json \n")
|
||||
funcContent += fmt.Sprintf("// @Param Request body %sRequest true \"请求信息\" \n", Lcfirst(v.Names[0].String()))
|
||||
funcContent += fmt.Sprintf("// @Success 200 {object} %sResponse \n", Lcfirst(v.Names[0].String()))
|
||||
funcContent += fmt.Sprintf("// @Failure 400 {object} code.Failure \n")
|
||||
// Router
|
||||
funcContent += fmt.Sprintf("%s \n", v.Decorations().Start.All()[2])
|
||||
funcContent += fmt.Sprintf("func (h *handler) %s() core.HandlerFunc { \n return func(c core.Context) {\n\n}}", v.Names[0].String())
|
||||
funcContent += fmt.Sprintf("func (h *handler) %s() core.HandlerFunc { \n return func(ctx core.Context) {\n\n}}", v.Names[0].String())
|
||||
|
||||
funcFile.WriteString(funcContent)
|
||||
funcFile.Close()
|
||||
|
||||
+7
-8
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/cmd/mysqlmd/mysql"
|
||||
@@ -76,7 +75,7 @@ func main() {
|
||||
|
||||
for _, table := range tables {
|
||||
|
||||
filepath := "./internal/api/repository/db_repo/" + table.Name + "_repo"
|
||||
filepath := "./internal/repository/mysql/" + table.Name
|
||||
_ = os.Mkdir(filepath, 0766)
|
||||
fmt.Println("create dir : ", filepath)
|
||||
|
||||
@@ -86,7 +85,7 @@ func main() {
|
||||
fmt.Printf("markdown file error %v\n", err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println(" └── file : ", table.Name+"_repo/gen_table.md")
|
||||
fmt.Println(" └── file : ", table.Name+"/gen_table.md")
|
||||
|
||||
modelName := fmt.Sprintf("%s/gen_model.go", filepath)
|
||||
modelFile, err := os.OpenFile(modelName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0766)
|
||||
@@ -94,11 +93,11 @@ func main() {
|
||||
fmt.Printf("create and open model file error %v\n", err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println(" └── file : ", table.Name+"_repo/gen_model.go")
|
||||
fmt.Println(" └── file : ", table.Name+"/gen_model.go")
|
||||
|
||||
modelContent := fmt.Sprintf("package %s%s\n", table.Name, "_repo")
|
||||
modelContent := fmt.Sprintf("package %s\n", table.Name)
|
||||
modelContent += fmt.Sprintf(`import "time"`)
|
||||
modelContent += fmt.Sprintf("\n\n// %s \n", table.Comment.String)
|
||||
modelContent += fmt.Sprintf("\n\n// %s %s \n", capitalize(table.Name), table.Comment.String)
|
||||
modelContent += fmt.Sprintf("//go:generate gormgen -structs %s -input . \n", capitalize(table.Name))
|
||||
modelContent += fmt.Sprintf("type %s struct {\n", capitalize(table.Name))
|
||||
|
||||
@@ -237,7 +236,7 @@ func queryTableColumn(db *gorm.DB, dbName string, tableName string) ([]tableColu
|
||||
func getTargetIndexMap(tableNameArr []string, item string) map[int]int {
|
||||
indexMap := make(map[int]int)
|
||||
for i := 0; i < len(tableNameArr); i++ {
|
||||
if match, _ := regexp.MatchString(item, tableNameArr[i]); match {
|
||||
if tableNameArr[i] == item {
|
||||
if _, ok := indexMap[i]; ok {
|
||||
continue
|
||||
}
|
||||
@@ -256,8 +255,8 @@ func capitalize(s string) string {
|
||||
if i == 0 {
|
||||
if vv[i] >= 97 && vv[i] <= 122 {
|
||||
vv[i] -= 32
|
||||
upperStr += string(vv[i])
|
||||
}
|
||||
upperStr += string(vv[i])
|
||||
} else {
|
||||
upperStr += string(vv[i])
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ package mysql
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errors"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
+63
-32
@@ -1,10 +1,15 @@
|
||||
package configs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/env"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/file"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/viper"
|
||||
@@ -50,28 +55,49 @@ type Config struct {
|
||||
To string `toml:"to"`
|
||||
} `toml:"mail"`
|
||||
|
||||
JWT struct {
|
||||
Secret string `toml:"secret"`
|
||||
ExpireDuration time.Duration `toml:"expireDuration"`
|
||||
} `toml:"jwt"`
|
||||
|
||||
URLToken struct {
|
||||
Secret string `toml:"secret"`
|
||||
ExpireDuration time.Duration `toml:"expireDuration"`
|
||||
} `toml:"urlToken"`
|
||||
|
||||
HashIds struct {
|
||||
Secret string `toml:"secret"`
|
||||
Length int `toml:"length"`
|
||||
} `toml:"hashids"`
|
||||
|
||||
Language struct {
|
||||
Local string `toml:"local"`
|
||||
} `toml:"language"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
viper.SetConfigName(env.Active().Value() + "_configs")
|
||||
viper.SetConfigType("toml")
|
||||
viper.AddConfigPath("./configs")
|
||||
var (
|
||||
//go:embed dev_configs.toml
|
||||
devConfigs []byte
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
//go:embed fat_configs.toml
|
||||
fatConfigs []byte
|
||||
|
||||
//go:embed uat_configs.toml
|
||||
uatConfigs []byte
|
||||
|
||||
//go:embed pro_configs.toml
|
||||
proConfigs []byte
|
||||
)
|
||||
|
||||
func init() {
|
||||
var r io.Reader
|
||||
|
||||
switch env.Active().Value() {
|
||||
case "dev":
|
||||
r = bytes.NewReader(devConfigs)
|
||||
case "fat":
|
||||
r = bytes.NewReader(fatConfigs)
|
||||
case "uat":
|
||||
r = bytes.NewReader(uatConfigs)
|
||||
case "pro":
|
||||
r = bytes.NewReader(proConfigs)
|
||||
default:
|
||||
r = bytes.NewReader(fatConfigs)
|
||||
}
|
||||
|
||||
viper.SetConfigType("toml")
|
||||
|
||||
if err := viper.ReadConfig(r); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -79,6 +105,27 @@ func init() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
viper.SetConfigName(env.Active().Value() + "_configs")
|
||||
viper.AddConfigPath("./configs")
|
||||
|
||||
configFile := "./configs/" + env.Active().Value() + "_configs.toml"
|
||||
_, ok := file.IsExists(configFile)
|
||||
if !ok {
|
||||
if err := os.MkdirAll(filepath.Dir(configFile), 0766); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
f, err := os.Create(configFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
viper.WatchConfig()
|
||||
viper.OnConfigChange(func(e fsnotify.Event) {
|
||||
if err := viper.Unmarshal(config); err != nil {
|
||||
@@ -90,19 +137,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"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package configs
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
// MinGoVersion 最小 Go 版本
|
||||
MinGoVersion = 1.16
|
||||
|
||||
// ProjectVersion 项目版本
|
||||
ProjectVersion = "v1.2.8"
|
||||
|
||||
// ProjectName 项目名称
|
||||
ProjectName = "go-gin-api"
|
||||
|
||||
// ProjectDomain 项目域名
|
||||
ProjectDomain = "http://127.0.0.1"
|
||||
|
||||
// ProjectPort 项目端口
|
||||
ProjectPort = ":9999"
|
||||
|
||||
// ProjectAccessLogFile 项目访问日志存放文件
|
||||
ProjectAccessLogFile = "./logs/" + ProjectName + "-access.log"
|
||||
|
||||
// ProjectCronLogFile 项目后台任务日志存放文件
|
||||
ProjectCronLogFile = "./logs/" + ProjectName + "-cron.log"
|
||||
|
||||
// ProjectInstallMark 项目安装完成标识
|
||||
ProjectInstallMark = "INSTALL.lock"
|
||||
|
||||
// HeaderLoginToken 登录验证 Token,Header 中传递的参数
|
||||
HeaderLoginToken = "Token"
|
||||
|
||||
// HeaderSignToken 签名验证 Authorization,Header 中传递的参数
|
||||
HeaderSignToken = "Authorization"
|
||||
|
||||
// HeaderSignTokenDate 签名验证 Date,Header 中传递的参数
|
||||
HeaderSignTokenDate = "Authorization-Date"
|
||||
|
||||
// HeaderSignTokenTimeout 签名有效期为 2 分钟
|
||||
HeaderSignTokenTimeout = time.Minute * 2
|
||||
|
||||
// RedisKeyPrefixLoginUser Redis Key 前缀 - 登录用户信息
|
||||
RedisKeyPrefixLoginUser = ProjectName + ":login-user:"
|
||||
|
||||
// RedisKeyPrefixSignature Redis Key 前缀 - 签名验证信息
|
||||
RedisKeyPrefixSignature = ProjectName + ":signature:"
|
||||
|
||||
// ZhCN 简体中文 - 中国
|
||||
ZhCN = "zh-cn"
|
||||
|
||||
// EnUS 英文 - 美国
|
||||
EnUS = "en-us"
|
||||
|
||||
// MaxRequestsPerSecond 每秒最大请求量
|
||||
MaxRequestsPerSecond = 10000
|
||||
|
||||
// LoginSessionTTL 登录有效期为 24 小时
|
||||
LoginSessionTTL = time.Hour * 24
|
||||
)
|
||||
@@ -3,9 +3,8 @@
|
||||
length = 12
|
||||
secret = "6ab6122836cfef95f8db"
|
||||
|
||||
[jwt]
|
||||
expireduration = 24
|
||||
secret = "i1ydX9RtHyuJTrw7frcu"
|
||||
[language]
|
||||
local = "zh-cn"
|
||||
|
||||
[mail]
|
||||
host = "smtp.163.com"
|
||||
@@ -24,13 +23,13 @@
|
||||
[mysql.read]
|
||||
addr = "127.0.0.1:3306"
|
||||
name = "go_gin_api"
|
||||
pass = "root"
|
||||
pass = "123456789"
|
||||
user = "root"
|
||||
|
||||
[mysql.write]
|
||||
addr = "127.0.0.1:3306"
|
||||
name = "go_gin_api"
|
||||
pass = "root"
|
||||
pass = "123456789"
|
||||
user = "root"
|
||||
|
||||
[redis]
|
||||
@@ -40,7 +39,3 @@
|
||||
minidleconns = 5
|
||||
pass = ""
|
||||
poolsize = 10
|
||||
|
||||
[urltoken]
|
||||
expireduration = 10
|
||||
secret = "i1ydX9RtHyuJTrw7frcu"
|
||||
|
||||
+1444
-187
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
## Jaeger Demo 代码
|
||||
|
||||
- 代码在 [v1.0](https://github.com/xinliangnote/go-gin-api/releases/tag/v1.0) 版本。
|
||||
|
||||
#### Jaeger Demo 文档
|
||||
|
||||
访问:
|
||||
|
||||
```
|
||||
http://127.0.0.1:9999/jaeger_test
|
||||
```
|
||||
|
||||
服务端测试代码:
|
||||
|
||||
- [https://github.com/xinliangnote/go-jaeger-demo](https://github.com/xinliangnote/go-jaeger-demo)
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
+1432
-181
File diff suppressed because it is too large
Load Diff
+950
-154
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
## What is go-gin-api?
|
||||
|
||||
`go-gin-api` is a modular design API framework based on [Gin](https://github.com/gin-gonic/gin), which encapsulates commonly used functions, is simple to use, and is committed to fast business Research and development, while adding more restrictions, restrict the development members of the project team, avoid chaos, disorder and free and arbitrary coding.
|
||||
|
||||
For reference and learning at this stage, please be cautious when using it in a production environment!
|
||||
|
||||
Features:
|
||||
1. Rate-limitd APIs supported by [rate](https://golang.org/x/time/rate)
|
||||
1. Email notification when panic occurs
|
||||
1. Cross-Origin Resource Sharing supported by [cors](https://github.com/rs/cors)
|
||||
1. Metrics monitoring and alerting by [Prometheus](https://github.com/prometheus/client_golang)
|
||||
1. Automatically generate RESTful API documentation supported by [Swagger](https://github.com/swaggo/gin-swagger)
|
||||
1. GraphQL supported by [GraphQL](https://github.com/99designs/gqlgen)
|
||||
1. Trace project internal invoking
|
||||
1. Visualized performance analysis supported by [pprof](https://github.com/gin-contrib/pprof)
|
||||
1. Uniformly defined error codes supported by errno
|
||||
1. Blazing fast, structured, leveled logging system by [zap](https://go.uber.org/zap)
|
||||
1. Complete configuration solution supported by [viper](https://github.com/spf13/viper)
|
||||
1. Developer Friendly ORM library supported by [gorm](https://gorm.io/gorm)
|
||||
1. Redis supported by [go-redis](https://github.com/go-redis/redis)
|
||||
1. Standard RESTful API return value
|
||||
1. CURD code generator , controller generator, etc.
|
||||
1. Web interface, supported by [Light Year Admin template](https://gitee.com/yinqi/Light-Year-Admin-Using-Iframe)
|
||||
|
||||
|
||||
|
||||
## Index
|
||||
|
||||
**Detailed documentation:[https://www.yuque.com/xinliangnote/go-gin-api/ngc3x5](https://www.yuque.com/xinliangnote/go-gin-api/ngc3x5)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Help us with translating `go-gin-api` to your native language.
|
||||
|
||||
## Learning together
|
||||
|
||||

|
||||
@@ -1,53 +1,39 @@
|
||||
module github.com/xinliangnote/go-gin-api
|
||||
|
||||
go 1.15
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.13.0
|
||||
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
|
||||
github.com/99designs/gqlgen v0.14.0
|
||||
github.com/StackExchange/wmi v1.2.1 // indirect
|
||||
github.com/dave/dst v0.26.2
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/fsnotify/fsnotify v1.5.1
|
||||
github.com/gin-contrib/pprof v1.3.0
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||
github.com/go-openapi/spec v0.20.0 // indirect
|
||||
github.com/go-redis/redis/v7 v7.4.0
|
||||
github.com/golang/protobuf v1.5.0
|
||||
github.com/gin-gonic/gin v1.7.4
|
||||
github.com/go-playground/locales v0.14.0
|
||||
github.com/go-playground/universal-translator v0.18.0
|
||||
github.com/go-playground/validator/v10 v10.9.0
|
||||
github.com/go-redis/redis/v7 v7.4.1
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/jakecoffman/cron v0.0.0-20190106200828-7e2009c226a5
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/onsi/ginkgo v1.14.2 // indirect
|
||||
github.com/onsi/gomega v1.10.4 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.10.0
|
||||
github.com/rs/cors v1.7.0
|
||||
github.com/shirou/gopsutil v3.21.2+incompatible
|
||||
github.com/speps/go-hashids v2.0.0+incompatible
|
||||
github.com/spf13/cast v1.3.0
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/swaggo/gin-swagger v1.3.0
|
||||
github.com/swaggo/swag v1.7.0
|
||||
github.com/tklauser/go-sysconf v0.3.4 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.1.0
|
||||
go.uber.org/multierr v1.6.0
|
||||
go.uber.org/zap v1.16.0
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
|
||||
golang.org/x/mod v0.4.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
|
||||
golang.org/x/text v0.3.5 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
||||
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a
|
||||
google.golang.org/genproto v0.0.0-20210224155714-063164c882e6 // indirect
|
||||
google.golang.org/grpc v1.37.0
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/rs/cors v1.8.0
|
||||
github.com/shirou/gopsutil v3.21.10+incompatible
|
||||
github.com/speps/go-hashids v1.0.0
|
||||
github.com/spf13/cast v1.4.1
|
||||
github.com/spf13/viper v1.9.0
|
||||
github.com/swaggo/gin-swagger v1.3.3
|
||||
github.com/swaggo/swag v1.7.4
|
||||
github.com/tklauser/go-sysconf v0.3.9 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.2.0
|
||||
go.uber.org/multierr v1.7.0
|
||||
go.uber.org/zap v1.19.1
|
||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11
|
||||
golang.org/x/tools v0.1.7
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gorm.io/driver/mysql v1.0.3
|
||||
gorm.io/gorm v1.20.9
|
||||
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
||||
gorm.io/driver/mysql v1.2.0
|
||||
gorm.io/gorm v1.22.3
|
||||
)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package alert
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/proposal"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errors"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/mail"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// NotifyHandler 告警通知
|
||||
func NotifyHandler(logger *zap.Logger) func(msg *proposal.AlertMessage) {
|
||||
if logger == nil {
|
||||
panic("logger required")
|
||||
}
|
||||
|
||||
return func(msg *proposal.AlertMessage) {
|
||||
cfg := configs.Get().Mail
|
||||
if cfg.Host == "" || cfg.Port == 0 || cfg.User == "" || cfg.Pass == "" || cfg.To == "" {
|
||||
logger.Error("Mail config error")
|
||||
return
|
||||
}
|
||||
|
||||
subject, body, err := newHTMLEmail(
|
||||
msg.Method,
|
||||
msg.HOST,
|
||||
msg.URI,
|
||||
msg.TraceID,
|
||||
msg.ErrorMessage,
|
||||
msg.ErrorStack,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error("email template error", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
options := &mail.Options{
|
||||
MailHost: cfg.Host,
|
||||
MailPort: cfg.Port,
|
||||
MailUser: cfg.User,
|
||||
MailPass: cfg.Pass,
|
||||
MailTo: cfg.To,
|
||||
Subject: subject,
|
||||
Body: body,
|
||||
}
|
||||
if err := mail.Send(options); err != nil {
|
||||
logger.Error("发送告警通知邮件失败", zap.Error(errors.WithStack(err)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,54 @@
|
||||
package templates
|
||||
package alert
|
||||
|
||||
const PanicMail = `
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewHTMLEmail 告警邮件模板
|
||||
func newHTMLEmail(method, host, uri, id string, msg interface{}, stack string) (subject string, body string, err error) {
|
||||
mailData := &struct {
|
||||
URL string
|
||||
ID string
|
||||
Msg string
|
||||
Stack string
|
||||
Year int
|
||||
}{
|
||||
URL: fmt.Sprintf("%s %s%s", method, host, uri),
|
||||
ID: id,
|
||||
Msg: fmt.Sprintf("%+v", msg),
|
||||
Stack: stack,
|
||||
Year: time.Now().Year(),
|
||||
}
|
||||
|
||||
// subject 邮件主题
|
||||
subject = fmt.Sprintf("[系统告警]-%s", uri)
|
||||
|
||||
// body 邮件内容
|
||||
body, err = getEmailHTMLContent(mailTemplate, mailData)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getEmailHTMLContent 获取邮件模板
|
||||
func getEmailHTMLContent(mailTpl string, mailData interface{}) (string, error) {
|
||||
tpl, err := template.New("email tpl").Parse(mailTpl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
err = tpl.Execute(buffer, mailData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
const mailTemplate = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
@@ -27,7 +75,7 @@ const PanicMail = `
|
||||
<!-- Logo -->
|
||||
<tr>
|
||||
<td style="padding: 25px 0; text-align: center;">
|
||||
系统异常
|
||||
系统告警
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -84,7 +132,7 @@ const PanicMail = `
|
||||
Stack:
|
||||
</td>
|
||||
<td style="width: 90%;">
|
||||
{{.Stack}}}
|
||||
{{.Stack}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
+16
-15
@@ -1,19 +1,19 @@
|
||||
package admin_handler
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"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/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/admin"
|
||||
)
|
||||
|
||||
type createRequest struct {
|
||||
Username string `form:"username"` // 用户名
|
||||
Nickname string `form:"nickname"` // 昵称
|
||||
Mobile string `form:"mobile"` // 手机号
|
||||
Password string `form:"password"` // 密码
|
||||
Username string `form:"username" binding:"required"` // 用户名
|
||||
Nickname string `form:"nickname" binding:"required"` // 昵称
|
||||
Mobile string `form:"mobile" binding:"required"` // 手机号
|
||||
Password string `form:"password" binding:"required"` // MD5后的密码
|
||||
}
|
||||
|
||||
type createResponse struct {
|
||||
@@ -24,29 +24,30 @@ type createResponse struct {
|
||||
// @Summary 新增管理员
|
||||
// @Description 新增管理员
|
||||
// @Tags API.admin
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param username formData string true "用户名"
|
||||
// @Param nickname formData string true "昵称"
|
||||
// @Param mobile formData string true "手机号"
|
||||
// @Param password formData string true "密码"
|
||||
// @Param password formData string true "MD5后的密码"
|
||||
// @Success 200 {object} createResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin [post]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Create() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(createRequest)
|
||||
res := new(createResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
validation.Error(err)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
createData := new(admin_service.CreateAdminData)
|
||||
createData := new(admin.CreateAdminData)
|
||||
createData.Nickname = req.Nickname
|
||||
createData.Username = req.Username
|
||||
createData.Mobile = req.Mobile
|
||||
@@ -54,10 +55,10 @@ func (h *handler) Create() core.HandlerFunc {
|
||||
|
||||
id, err := h.adminService.Create(c, createData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminCreateError,
|
||||
code.Text(code.AdminCreateError)).WithErr(err),
|
||||
code.Text(code.AdminCreateError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+12
-12
@@ -1,12 +1,11 @@
|
||||
package admin_handler
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"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/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/admin"
|
||||
)
|
||||
|
||||
type createAdminMenuRequest struct {
|
||||
@@ -22,46 +21,47 @@ type createAdminMenuResponse struct {
|
||||
// @Summary 提交菜单授权
|
||||
// @Description 提交菜单授权
|
||||
// @Tags API.admin
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id formData string true "Hashid"
|
||||
// @Param actions formData string true "功能权限ID,多个用,分割"
|
||||
// @Success 200 {object} createResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/menu [post]
|
||||
// @Security LoginToken
|
||||
func (h *handler) CreateAdminMenu() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(createAdminMenuRequest)
|
||||
res := new(createAdminMenuResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
createData := new(admin_service.CreateMenuData)
|
||||
createData := new(admin.CreateMenuData)
|
||||
createData.AdminId = int32(ids[0])
|
||||
createData.Actions = req.Actions
|
||||
|
||||
err = h.adminService.CreateMenu(c, createData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminMenuCreateError,
|
||||
code.Text(code.AdminMenuCreateError)).WithErr(err),
|
||||
code.Text(code.AdminMenuCreateError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+9
-9
@@ -1,11 +1,10 @@
|
||||
package admin_handler
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type deleteRequest struct {
|
||||
@@ -26,25 +25,26 @@ type deleteResponse struct {
|
||||
// @Success 200 {object} deleteResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/{id} [delete]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Delete() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(deleteRequest)
|
||||
res := new(deleteResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -53,10 +53,10 @@ func (h *handler) Delete() core.HandlerFunc {
|
||||
|
||||
err = h.adminService.Delete(c, id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminDeleteError,
|
||||
code.Text(code.AdminDeleteError)).WithErr(err),
|
||||
code.Text(code.AdminDeleteError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/password"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/redis"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/admin"
|
||||
)
|
||||
|
||||
type detailResponse struct {
|
||||
Username string `json:"username"` // 用户名
|
||||
Nickname string `json:"nickname"` // 昵称
|
||||
Mobile string `json:"mobile"` // 手机号
|
||||
Menu []admin.ListMyMenuData `json:"menu"` // 菜单栏
|
||||
}
|
||||
|
||||
// Detail 管理员详情
|
||||
// @Summary 管理员详情
|
||||
// @Description 管理员详情
|
||||
// @Tags API.admin
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Success 200 {object} detailResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/info [get]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Detail() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
res := new(detailResponse)
|
||||
|
||||
searchOneData := new(admin.SearchOneData)
|
||||
searchOneData.Id = ctx.SessionUserInfo().UserID
|
||||
searchOneData.IsUsed = 1
|
||||
|
||||
info, err := h.adminService.Detail(ctx, searchOneData)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminDetailError,
|
||||
code.Text(code.AdminDetailError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
menuCacheData, err := h.cache.Get(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(searchOneData.Id)+":menu", redis.WithTrace(ctx.Trace()))
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminDetailError,
|
||||
code.Text(code.AdminDetailError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var menuData []admin.ListMyMenuData
|
||||
err = json.Unmarshal([]byte(menuCacheData), &menuData)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminDetailError,
|
||||
code.Text(code.AdminDetailError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Username = info.Username
|
||||
res.Nickname = info.Nickname
|
||||
res.Mobile = info.Mobile
|
||||
res.Menu = menuData
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
+34
-21
@@ -1,16 +1,16 @@
|
||||
package admin_handler
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"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/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/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/time_parse"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/password"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/admin"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/timeutil"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type listRequest struct {
|
||||
@@ -28,6 +28,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"` // 更新时间
|
||||
@@ -39,7 +40,7 @@ type listResponse struct {
|
||||
Pagination struct {
|
||||
Total int `json:"total"`
|
||||
CurrentPage int `json:"current_page"`
|
||||
PrePageCount int `json:"pre_page_count"`
|
||||
PerPageCount int `json:"per_page_count"`
|
||||
} `json:"pagination"`
|
||||
}
|
||||
|
||||
@@ -47,25 +48,26 @@ type listResponse struct {
|
||||
// @Summary 管理员列表
|
||||
// @Description 管理员列表
|
||||
// @Tags API.admin
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param page query int false "第几页"
|
||||
// @Param page_size query string false "每页显示条数"
|
||||
// @Param page query int true "第几页" default(1)
|
||||
// @Param page_size query int true "每页显示条数" default(10)
|
||||
// @Param username query string false "用户名"
|
||||
// @Param nickname query string false "昵称"
|
||||
// @Param mobile query string false "手机号"
|
||||
// @Success 200 {object} listResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin [get]
|
||||
// @Security LoginToken
|
||||
func (h *handler) List() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(listRequest)
|
||||
res := new(listResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -80,7 +82,7 @@ func (h *handler) List() core.HandlerFunc {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
searchData := new(admin_service.SearchData)
|
||||
searchData := new(admin.SearchData)
|
||||
searchData.Page = page
|
||||
searchData.PageSize = pageSize
|
||||
searchData.Username = req.Username
|
||||
@@ -89,32 +91,42 @@ func (h *handler) List() core.HandlerFunc {
|
||||
|
||||
resListData, err := h.adminService.PageList(c, searchData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminListError,
|
||||
code.Text(code.AdminListError)).WithErr(err),
|
||||
code.Text(code.AdminListError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resCountData, err := h.adminService.PageListCount(c, searchData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminListError,
|
||||
code.Text(code.AdminListError)).WithErr(err),
|
||||
code.Text(code.AdminListError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
res.Pagination.Total = cast.ToInt(resCountData)
|
||||
res.Pagination.PrePageCount = pageSize
|
||||
res.Pagination.PerPageCount = pageSize
|
||||
res.Pagination.CurrentPage = page
|
||||
res.List = make([]listData, len(resListData))
|
||||
|
||||
for k, v := range resListData {
|
||||
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
|
||||
if err != nil {
|
||||
h.logger.Info("hashids err", zap.Error(err))
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsEncodeError,
|
||||
code.Text(code.HashIdsEncodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
isOnline := -1
|
||||
if h.cache.Exists(configs.RedisKeyPrefixLoginUser + password.GenerateLoginToken(v.Id)) {
|
||||
isOnline = 1
|
||||
}
|
||||
|
||||
data := listData{
|
||||
@@ -124,9 +136,10 @@ func (h *handler) List() core.HandlerFunc {
|
||||
Nickname: v.Nickname,
|
||||
Mobile: v.Mobile,
|
||||
IsUsed: cast.ToInt(v.IsUsed),
|
||||
CreatedAt: v.CreatedAt.Format(time_parse.CSTLayout),
|
||||
IsOnline: isOnline,
|
||||
CreatedAt: v.CreatedAt.Format(timeutil.CSTLayout),
|
||||
CreatedUser: v.CreatedUser,
|
||||
UpdatedAt: v.UpdatedAt.Format(time_parse.CSTLayout),
|
||||
UpdatedAt: v.UpdatedAt.Format(timeutil.CSTLayout),
|
||||
UpdatedUser: v.UpdatedUser,
|
||||
}
|
||||
|
||||
+18
-18
@@ -1,12 +1,11 @@
|
||||
package admin_handler
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"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/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/admin"
|
||||
)
|
||||
|
||||
type listAdminMenuRequest struct {
|
||||
@@ -14,68 +13,69 @@ type listAdminMenuRequest struct {
|
||||
}
|
||||
|
||||
type listAdminMenuResponse struct {
|
||||
List []admin_service.ListMenuData `json:"list"`
|
||||
UserName string `json:"username"`
|
||||
List []admin.ListMenuData `json:"list"`
|
||||
UserName string `json:"username"`
|
||||
}
|
||||
|
||||
// ListAdminMenu 菜单授权列表
|
||||
// @Summary 菜单授权列表
|
||||
// @Description 菜单授权列表
|
||||
// @Tags API.admin
|
||||
// @Accept json
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id path string true "hashId"
|
||||
// @Success 200 {object} listAdminMenuResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/menu/:id [get]
|
||||
// @Router /api/admin/menu/{id} [get]
|
||||
// @Security LoginToken
|
||||
func (h *handler) ListAdminMenu() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(listAdminMenuRequest)
|
||||
res := new(listAdminMenuResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
searchOneData := new(admin_service.SearchOneData)
|
||||
searchOneData := new(admin.SearchOneData)
|
||||
searchOneData.Id = int32(ids[0])
|
||||
searchOneData.IsUsed = 1
|
||||
|
||||
info, err := h.adminService.Detail(c, searchOneData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminMenuListError,
|
||||
code.Text(code.AdminMenuListError)).WithErr(err),
|
||||
code.Text(code.AdminMenuListError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.UserName = info.Username
|
||||
|
||||
searchData := new(admin_service.SearchListMenuData)
|
||||
searchData := new(admin.SearchListMenuData)
|
||||
searchData.AdminId = int32(ids[0])
|
||||
|
||||
listData, err := h.adminService.ListMenu(c, searchData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminMenuListError,
|
||||
code.Text(code.AdminMenuListError)).WithErr(err),
|
||||
code.Text(code.AdminMenuListError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/password"
|
||||
"github.com/xinliangnote/go-gin-api/internal/proposal"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/redis"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/admin"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errors"
|
||||
)
|
||||
|
||||
type loginRequest struct {
|
||||
Username string `form:"username"` // 用户名
|
||||
Password string `form:"password"` // 密码
|
||||
}
|
||||
|
||||
type loginResponse struct {
|
||||
Token string `json:"token"` // 用户身份标识
|
||||
}
|
||||
|
||||
// Login 管理员登录
|
||||
// @Summary 管理员登录
|
||||
// @Description 管理员登录
|
||||
// @Tags API.admin
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param username formData string true "用户名"
|
||||
// @Param password formData string true "MD5后的密码"
|
||||
// @Success 200 {object} loginResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/login [post]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Login() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(loginRequest)
|
||||
res := new(loginResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
searchOneData := new(admin.SearchOneData)
|
||||
searchOneData.Username = req.Username
|
||||
searchOneData.Password = password.GeneratePassword(req.Password)
|
||||
searchOneData.IsUsed = 1
|
||||
|
||||
info, err := h.adminService.Detail(c, searchOneData)
|
||||
if err != nil {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLoginError,
|
||||
code.Text(code.AdminLoginError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if info == nil {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLoginError,
|
||||
code.Text(code.AdminLoginError)).WithError(errors.New("未查询出符合条件的用户")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
token := password.GenerateLoginToken(info.Id)
|
||||
|
||||
// 用户信息
|
||||
sessionUserInfo := &proposal.SessionUserInfo{
|
||||
UserID: info.Id,
|
||||
UserName: info.Username,
|
||||
}
|
||||
|
||||
// 将用户信息记录到 Redis 中
|
||||
err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token, string(sessionUserInfo.Marshal()), configs.LoginSessionTTL, redis.WithTrace(c.Trace()))
|
||||
if err != nil {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLoginError,
|
||||
code.Text(code.AdminLoginError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
searchMenuData := new(admin.SearchMyMenuData)
|
||||
searchMenuData.AdminId = info.Id
|
||||
menu, err := h.adminService.MyMenu(c, searchMenuData)
|
||||
if err != nil {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLoginError,
|
||||
code.Text(code.AdminLoginError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// 菜单栏信息
|
||||
menuJsonInfo, _ := json.Marshal(menu)
|
||||
|
||||
// 将菜单栏信息记录到 Redis 中
|
||||
err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token+":menu", string(menuJsonInfo), configs.LoginSessionTTL, redis.WithTrace(c.Trace()))
|
||||
if err != nil {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLoginError,
|
||||
code.Text(code.AdminLoginError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
searchActionData := new(admin.SearchMyActionData)
|
||||
searchActionData.AdminId = info.Id
|
||||
action, err := h.adminService.MyAction(c, searchActionData)
|
||||
if err != nil {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLoginError,
|
||||
code.Text(code.AdminLoginError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// 可访问接口信息
|
||||
actionJsonInfo, _ := json.Marshal(action)
|
||||
|
||||
// 将可访问接口信息记录到 Redis 中
|
||||
err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token+":action", string(actionJsonInfo), configs.LoginSessionTTL, redis.WithTrace(c.Trace()))
|
||||
if err != nil {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLoginError,
|
||||
code.Text(code.AdminLoginError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Token = token
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/redis"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errors"
|
||||
)
|
||||
|
||||
type logoutResponse struct {
|
||||
Username string `json:"username"` // 用户账号
|
||||
}
|
||||
|
||||
// Logout 管理员登出
|
||||
// @Summary 管理员登出
|
||||
// @Description 管理员登出
|
||||
// @Tags API.admin
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Success 200 {object} logoutResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/logout [post]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Logout() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
res := new(logoutResponse)
|
||||
res.Username = c.SessionUserInfo().UserName
|
||||
|
||||
if !h.cache.Del(configs.RedisKeyPrefixLoginUser+c.GetHeader(configs.HeaderLoginToken), redis.WithTrace(c.Trace())) {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLogOutError,
|
||||
code.Text(code.AdminLogOutError)).WithError(errors.New("cache del err")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
+19
-23
@@ -1,15 +1,12 @@
|
||||
package admin_handler
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"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/code"
|
||||
"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"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/admin"
|
||||
)
|
||||
|
||||
type modifyPasswordRequest struct {
|
||||
@@ -25,53 +22,52 @@ type modifyPasswordResponse struct {
|
||||
// @Summary 修改密码
|
||||
// @Description 修改密码
|
||||
// @Tags API.admin
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param old_password formData string true "旧密码"
|
||||
// @Param new_password formData string true "新密码"
|
||||
// @Success 200 {object} modifyPasswordResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/modify_password [patch]
|
||||
// @Security LoginToken
|
||||
func (h *handler) ModifyPassword() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
return func(ctx core.Context) {
|
||||
req := new(modifyPasswordRequest)
|
||||
res := new(modifyPasswordResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
if err := ctx.ShouldBindForm(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
userId := cast.ToInt32(c.UserID())
|
||||
|
||||
searchOneData := new(admin_service.SearchOneData)
|
||||
searchOneData.Id = userId
|
||||
searchOneData := new(admin.SearchOneData)
|
||||
searchOneData.Id = ctx.SessionUserInfo().UserID
|
||||
searchOneData.Password = password.GeneratePassword(req.OldPassword)
|
||||
searchOneData.IsUsed = 1
|
||||
|
||||
info, err := h.adminService.Detail(c, searchOneData)
|
||||
info, err := h.adminService.Detail(ctx, searchOneData)
|
||||
if err != nil || info == nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminModifyPasswordError,
|
||||
code.Text(code.AdminModifyPasswordError)).WithErr(err),
|
||||
code.Text(code.AdminModifyPasswordError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.adminService.ModifyPassword(c, userId, req.NewPassword); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
if err := h.adminService.ModifyPassword(ctx, ctx.SessionUserInfo().UserID, req.NewPassword); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminModifyPasswordError,
|
||||
code.Text(code.AdminModifyPasswordError)).WithErr(err),
|
||||
code.Text(code.AdminModifyPasswordError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Username = c.UserName()
|
||||
c.Payload(res)
|
||||
res.Username = ctx.SessionUserInfo().UserName
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
+16
-20
@@ -1,14 +1,11 @@
|
||||
package admin_handler
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"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/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/admin"
|
||||
)
|
||||
|
||||
type modifyPersonalInfoRequest struct {
|
||||
@@ -24,42 +21,41 @@ type modifyPersonalInfoResponse struct {
|
||||
// @Summary 修改个人信息
|
||||
// @Description 修改个人信息
|
||||
// @Tags API.admin
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param nickname formData string true "昵称"
|
||||
// @Param mobile formData string true "手机号"
|
||||
// @Success 200 {object} modifyPersonalInfoResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/modify_password [patch]
|
||||
// @Router /api/admin/modify_personal_info [patch]
|
||||
// @Security LoginToken
|
||||
func (h *handler) ModifyPersonalInfo() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
return func(ctx core.Context) {
|
||||
req := new(modifyPersonalInfoRequest)
|
||||
res := new(modifyPersonalInfoResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
if err := ctx.ShouldBindForm(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
userId := cast.ToInt32(c.UserID())
|
||||
|
||||
modifyData := new(admin_service.ModifyData)
|
||||
modifyData := new(admin.ModifyData)
|
||||
modifyData.Nickname = req.Nickname
|
||||
modifyData.Mobile = req.Mobile
|
||||
|
||||
if err := h.adminService.ModifyPersonalInfo(c, userId, modifyData); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
if err := h.adminService.ModifyPersonalInfo(ctx, ctx.SessionUserInfo().UserID, modifyData); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminModifyPersonalInfoError,
|
||||
code.Text(code.AdminModifyPersonalInfoError)).WithErr(err),
|
||||
code.Text(code.AdminModifyPersonalInfoError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Username = c.UserName()
|
||||
c.Payload(res)
|
||||
res.Username = ctx.SessionUserInfo().UserName
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/password"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/redis"
|
||||
)
|
||||
|
||||
type offlineRequest struct {
|
||||
Id string `form:"id"` // 主键ID
|
||||
}
|
||||
|
||||
type offlineResponse struct {
|
||||
Id int32 `json:"id"` // 主键ID
|
||||
}
|
||||
|
||||
// Offline 下线管理员
|
||||
// @Summary 下线管理员
|
||||
// @Description 下线管理员
|
||||
// @Tags API.admin
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id formData string true "Hashid"
|
||||
// @Success 200 {object} offlineResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/offline [patch]
|
||||
// @Security LoginToken
|
||||
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(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
id := int32(ids[0])
|
||||
|
||||
b := h.cache.Del(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(id), redis.WithTrace(c.Trace()))
|
||||
if !b {
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminOfflineError,
|
||||
code.Text(code.AdminOfflineError)),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = id
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
+9
-9
@@ -1,11 +1,10 @@
|
||||
package admin_handler
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type resetPasswordRequest struct {
|
||||
@@ -26,25 +25,26 @@ type resetPasswordResponse struct {
|
||||
// @Success 200 {object} resetPasswordResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/reset_password/{id} [patch]
|
||||
// @Security LoginToken
|
||||
func (h *handler) ResetPassword() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(resetPasswordRequest)
|
||||
res := new(resetPasswordResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -53,10 +53,10 @@ func (h *handler) ResetPassword() core.HandlerFunc {
|
||||
|
||||
err = h.adminService.ResetPassword(c, id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminResetPasswordError,
|
||||
code.Text(code.AdminResetPasswordError)).WithErr(err),
|
||||
code.Text(code.AdminResetPasswordError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+10
-10
@@ -1,11 +1,10 @@
|
||||
package admin_handler
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type updateUsedRequest struct {
|
||||
@@ -21,32 +20,33 @@ type updateUsedResponse struct {
|
||||
// @Summary 更新管理员为启用/禁用
|
||||
// @Description 更新管理员为启用/禁用
|
||||
// @Tags API.admin
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id formData string true "Hashid"
|
||||
// @Param used formData int true "是否启用 1:是 -1:否"
|
||||
// @Success 200 {object} updateUsedResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/used [patch]
|
||||
// @Security LoginToken
|
||||
func (h *handler) UpdateUsed() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(updateUsedRequest)
|
||||
res := new(updateUsedResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -55,10 +55,10 @@ func (h *handler) UpdateUsed() core.HandlerFunc {
|
||||
|
||||
err = h.adminService.UpdateUsed(c, id, req.Used)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminUpdateError,
|
||||
code.Text(code.AdminUpdateError)).WithErr(err),
|
||||
code.Text(code.AdminUpdateError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package admin_handler
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"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/db"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/mysql"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/redis"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/admin"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/hash"
|
||||
|
||||
"go.uber.org/zap"
|
||||
@@ -18,7 +18,7 @@ type Handler interface {
|
||||
|
||||
// Login 管理员登录
|
||||
// @Tags API.admin
|
||||
// @Router /api/admin/login [post]
|
||||
// @Router /api/login [post]
|
||||
Login() core.HandlerFunc
|
||||
|
||||
// Logout 管理员登出
|
||||
@@ -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]
|
||||
@@ -73,23 +78,23 @@ type Handler interface {
|
||||
|
||||
// ListAdminMenu 菜单授权列表
|
||||
// @Tags API.admin
|
||||
// @Router /api/admin/menu/:id [get]
|
||||
// @Router /api/admin/menu/{id} [get]
|
||||
ListAdminMenu() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
cache redis.Repo
|
||||
hashids hash.Hash
|
||||
adminService admin_service.Service
|
||||
adminService admin.Service
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
|
||||
func New(logger *zap.Logger, db mysql.Repo, cache redis.Repo) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
hashids: hash.New(configs.Get().HashIds.Secret, configs.Get().HashIds.Length),
|
||||
adminService: admin_service.New(db, cache),
|
||||
adminService: admin.New(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,12 +1,11 @@
|
||||
package authorized_handler
|
||||
package authorized
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/authorized_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/authorized"
|
||||
)
|
||||
|
||||
type createRequest struct {
|
||||
@@ -23,7 +22,7 @@ type createResponse struct {
|
||||
// @Summary 新增调用方
|
||||
// @Description 新增调用方
|
||||
// @Tags API.authorized
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param business_key formData string true "调用方key"
|
||||
// @Param business_developer formData string true "调用方对接人"
|
||||
@@ -31,30 +30,31 @@ type createResponse struct {
|
||||
// @Success 200 {object} createResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/authorized [post]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Create() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(createRequest)
|
||||
res := new(createResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
createData := new(authorized_service.CreateAuthorizedData)
|
||||
createData := new(authorized.CreateAuthorizedData)
|
||||
createData.BusinessKey = req.BusinessKey
|
||||
createData.BusinessDeveloper = req.BusinessDeveloper
|
||||
createData.Remark = req.Remark
|
||||
|
||||
id, err := h.authorizedService.Create(c, createData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizedCreateError,
|
||||
code.Text(code.AuthorizedCreateError)).WithErr(err),
|
||||
code.Text(code.AuthorizedCreateError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+14
-14
@@ -1,12 +1,11 @@
|
||||
package authorized_handler
|
||||
package authorized
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/authorized_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/authorized"
|
||||
)
|
||||
|
||||
type createAPIRequest struct {
|
||||
@@ -23,7 +22,7 @@ type createAPIResponse struct {
|
||||
// @Summary 授权调用方接口地址
|
||||
// @Description 授权调用方接口地址
|
||||
// @Tags API.authorized
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id formData string true "HashID"
|
||||
// @Param method formData string true "请求方法"
|
||||
@@ -31,25 +30,26 @@ type createAPIResponse struct {
|
||||
// @Success 200 {object} createAPIResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/authorized_api [post]
|
||||
// @Security LoginToken
|
||||
func (h *handler) CreateAPI() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(createAPIRequest)
|
||||
res := new(createAPIResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -59,25 +59,25 @@ func (h *handler) CreateAPI() core.HandlerFunc {
|
||||
// 通过 id 查询出 business_key
|
||||
authorizedInfo, err := h.authorizedService.Detail(c, id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizedDetailError,
|
||||
code.Text(code.AuthorizedDetailError)).WithErr(err),
|
||||
code.Text(code.AuthorizedDetailError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
createAPIData := new(authorized_service.CreateAuthorizedAPIData)
|
||||
createAPIData := new(authorized.CreateAuthorizedAPIData)
|
||||
createAPIData.BusinessKey = authorizedInfo.BusinessKey
|
||||
createAPIData.Method = req.Method
|
||||
createAPIData.API = req.API
|
||||
|
||||
createId, err := h.authorizedService.CreateAPI(c, createAPIData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizedCreateAPIError,
|
||||
code.Text(code.AuthorizedCreateAPIError)).WithErr(err),
|
||||
code.Text(code.AuthorizedCreateAPIError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+9
-9
@@ -1,11 +1,10 @@
|
||||
package authorized_handler
|
||||
package authorized
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type deleteRequest struct {
|
||||
@@ -26,25 +25,26 @@ type deleteResponse struct {
|
||||
// @Success 200 {object} deleteResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/authorized/{id} [delete]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Delete() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(deleteRequest)
|
||||
res := new(deleteResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -53,10 +53,10 @@ func (h *handler) Delete() core.HandlerFunc {
|
||||
|
||||
err = h.authorizedService.Delete(c, id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizedDeleteError,
|
||||
code.Text(code.AuthorizedDeleteError)).WithErr(err),
|
||||
code.Text(code.AuthorizedDeleteError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+9
-9
@@ -1,11 +1,10 @@
|
||||
package authorized_handler
|
||||
package authorized
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type deleteAPIRequest struct {
|
||||
@@ -26,25 +25,26 @@ type deleteAPIResponse struct {
|
||||
// @Success 200 {object} deleteAPIResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/authorized_api/{id} [delete]
|
||||
// @Security LoginToken
|
||||
func (h *handler) DeleteAPI() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(deleteAPIRequest)
|
||||
res := new(deleteAPIResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -53,10 +53,10 @@ func (h *handler) DeleteAPI() core.HandlerFunc {
|
||||
|
||||
err = h.authorizedService.DeleteAPI(c, id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizedDeleteAPIError,
|
||||
code.Text(code.AuthorizedDeleteAPIError)).WithErr(err),
|
||||
code.Text(code.AuthorizedDeleteAPIError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+25
-21
@@ -1,16 +1,14 @@
|
||||
package authorized_handler
|
||||
package authorized
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/authorized_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/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/time_parse"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/authorized"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/timeutil"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type listRequest struct {
|
||||
@@ -41,7 +39,7 @@ type listResponse struct {
|
||||
Pagination struct {
|
||||
Total int `json:"total"`
|
||||
CurrentPage int `json:"current_page"`
|
||||
PrePageCount int `json:"pre_page_count"`
|
||||
PerPageCount int `json:"per_page_count"`
|
||||
} `json:"pagination"`
|
||||
}
|
||||
|
||||
@@ -49,10 +47,10 @@ type listResponse struct {
|
||||
// @Summary 调用方列表
|
||||
// @Description 调用方列表
|
||||
// @Tags API.authorized
|
||||
// @Accept json
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param page query int false "第几页"
|
||||
// @Param page_size query string false "每页显示条数"
|
||||
// @Param page query int true "第几页" default(1)
|
||||
// @Param page_size query int true "每页显示条数" default(10)
|
||||
// @Param business_key query string false "调用方key"
|
||||
// @Param business_secret query string false "调用方secret"
|
||||
// @Param business_developer query string false "调用方对接人"
|
||||
@@ -60,15 +58,16 @@ type listResponse struct {
|
||||
// @Success 200 {object} listResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/authorized [get]
|
||||
// @Security LoginToken
|
||||
func (h *handler) List() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(listRequest)
|
||||
res := new(listResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -83,7 +82,7 @@ func (h *handler) List() core.HandlerFunc {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
searchData := new(authorized_service.SearchData)
|
||||
searchData := new(authorized.SearchData)
|
||||
searchData.Page = page
|
||||
searchData.PageSize = pageSize
|
||||
searchData.BusinessKey = req.BusinessKey
|
||||
@@ -92,32 +91,37 @@ func (h *handler) List() core.HandlerFunc {
|
||||
|
||||
resListData, err := h.authorizedService.PageList(c, searchData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizedListError,
|
||||
code.Text(code.AuthorizedListError)).WithErr(err),
|
||||
code.Text(code.AuthorizedListError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resCountData, err := h.authorizedService.PageListCount(c, searchData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizedListError,
|
||||
code.Text(code.AuthorizedListError)).WithErr(err),
|
||||
code.Text(code.AuthorizedListError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
res.Pagination.Total = cast.ToInt(resCountData)
|
||||
res.Pagination.PrePageCount = pageSize
|
||||
res.Pagination.PerPageCount = pageSize
|
||||
res.Pagination.CurrentPage = page
|
||||
res.List = make([]listData, len(resListData))
|
||||
|
||||
for k, v := range resListData {
|
||||
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
|
||||
if err != nil {
|
||||
h.logger.Info("hashids err", zap.Error(err))
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsEncodeError,
|
||||
code.Text(code.HashIdsEncodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
data := listData{
|
||||
@@ -128,9 +132,9 @@ func (h *handler) List() core.HandlerFunc {
|
||||
BusinessDeveloper: v.BusinessDeveloper,
|
||||
Remark: v.Remark,
|
||||
IsUsed: cast.ToInt(v.IsUsed),
|
||||
CreatedAt: v.CreatedAt.Format(time_parse.CSTLayout),
|
||||
CreatedAt: v.CreatedAt.Format(timeutil.CSTLayout),
|
||||
CreatedUser: v.CreatedUser,
|
||||
UpdatedAt: v.UpdatedAt.Format(time_parse.CSTLayout),
|
||||
UpdatedAt: v.UpdatedAt.Format(timeutil.CSTLayout),
|
||||
UpdatedUser: v.UpdatedUser,
|
||||
}
|
||||
|
||||
+20
-16
@@ -1,15 +1,13 @@
|
||||
package authorized_handler
|
||||
package authorized
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/authorized_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/authorized"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type listAPIRequest struct {
|
||||
@@ -32,31 +30,32 @@ type listAPIResponse struct {
|
||||
// @Summary 调用方接口地址列表
|
||||
// @Description 调用方接口地址列表
|
||||
// @Tags API.authorized
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id query string true "hashID"
|
||||
// @Success 200 {object} listAPIResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/authorized_api [get]
|
||||
// @Security LoginToken
|
||||
func (h *handler) ListAPI() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(listAPIRequest)
|
||||
res := new(listAPIResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -66,25 +65,25 @@ func (h *handler) ListAPI() core.HandlerFunc {
|
||||
// 通过 id 查询出 business_key
|
||||
authorizedInfo, err := h.authorizedService.Detail(c, id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizedDetailError,
|
||||
code.Text(code.AuthorizedDetailError)).WithErr(err),
|
||||
code.Text(code.AuthorizedDetailError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.BusinessKey = authorizedInfo.BusinessKey
|
||||
|
||||
searchAPIData := new(authorized_service.SearchAPIData)
|
||||
searchAPIData := new(authorized.SearchAPIData)
|
||||
searchAPIData.BusinessKey = authorizedInfo.BusinessKey
|
||||
|
||||
resListData, err := h.authorizedService.ListAPI(c, searchAPIData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizedListAPIError,
|
||||
code.Text(code.AuthorizedListAPIError)).WithErr(err),
|
||||
code.Text(code.AuthorizedListAPIError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -94,7 +93,12 @@ func (h *handler) ListAPI() core.HandlerFunc {
|
||||
for k, v := range resListData {
|
||||
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
|
||||
if err != nil {
|
||||
h.logger.Info("hashids err", zap.Error(err))
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsEncodeError,
|
||||
code.Text(code.HashIdsEncodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
data := listAPIData{
|
||||
+11
-11
@@ -1,11 +1,10 @@
|
||||
package authorized_handler
|
||||
package authorized
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type updateUsedRequest struct {
|
||||
@@ -21,32 +20,33 @@ type updateUsedResponse struct {
|
||||
// @Summary 更新调用方为启用/禁用
|
||||
// @Description 更新调用方为启用/禁用
|
||||
// @Tags API.authorized
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id formData string true "Hashid"
|
||||
// @Param id formData string true "hashID"
|
||||
// @Param used formData int true "是否启用 1:是 -1:否"
|
||||
// @Success 200 {object} updateUsedResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/authorized/used [patch]
|
||||
// @Security LoginToken
|
||||
func (h *handler) UpdateUsed() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(updateUsedRequest)
|
||||
res := new(updateUsedResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -55,10 +55,10 @@ func (h *handler) UpdateUsed() core.HandlerFunc {
|
||||
|
||||
err = h.authorizedService.UpdateUsed(c, id, req.Used)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizedUpdateError,
|
||||
code.Text(code.AuthorizedUpdateError)).WithErr(err),
|
||||
code.Text(code.AuthorizedUpdateError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+8
-8
@@ -1,11 +1,11 @@
|
||||
package authorized_handler
|
||||
package authorized
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/authorized_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/db"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/mysql"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/redis"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/authorized"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/hash"
|
||||
|
||||
"go.uber.org/zap"
|
||||
@@ -54,16 +54,16 @@ type Handler interface {
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
authorizedService authorized_service.Service
|
||||
cache redis.Repo
|
||||
authorizedService authorized.Service
|
||||
hashids hash.Hash
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
|
||||
func New(logger *zap.Logger, db mysql.Repo, cache redis.Repo) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
authorizedService: authorized_service.New(db, cache),
|
||||
authorizedService: authorized.New(db, cache),
|
||||
hashids: hash.New(configs.Get().HashIds.Secret, configs.Get().HashIds.Length),
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package code
|
||||
|
||||
// 错误时返回结构
|
||||
type Failure struct {
|
||||
Code int `json:"code"` // 业务码
|
||||
Message string `json:"message"` // 描述信息
|
||||
}
|
||||
|
||||
const (
|
||||
// 服务级错误码
|
||||
ServerError = 10101
|
||||
TooManyRequests = 10102
|
||||
ParamBindError = 10103
|
||||
AuthorizationError = 10104
|
||||
CallHTTPError = 10105
|
||||
ResubmitError = 10106
|
||||
ResubmitMsg = 10107
|
||||
HashIdsDecodeError = 10108
|
||||
SignatureError = 10109
|
||||
|
||||
// 业务模块级错误码
|
||||
// 用户模块
|
||||
IllegalUserName = 20101
|
||||
UserCreateError = 20102
|
||||
UserUpdateError = 20103
|
||||
UserSearchError = 20104
|
||||
|
||||
// 授权调用方
|
||||
AuthorizedCreateError = 20201
|
||||
AuthorizedListError = 20202
|
||||
AuthorizedDeleteError = 20203
|
||||
AuthorizedUpdateError = 20204
|
||||
AuthorizedDetailError = 20205
|
||||
AuthorizedCreateAPIError = 20206
|
||||
AuthorizedListAPIError = 20207
|
||||
AuthorizedDeleteAPIError = 20208
|
||||
|
||||
// 管理员
|
||||
AdminCreateError = 20301
|
||||
AdminListError = 20302
|
||||
AdminDeleteError = 20303
|
||||
AdminUpdateError = 20304
|
||||
AdminResetPasswordError = 20305
|
||||
AdminLoginError = 20306
|
||||
AdminLogOutError = 20307
|
||||
AdminModifyPasswordError = 20308
|
||||
AdminModifyPersonalInfoError = 20309
|
||||
AdminMenuListError = 20310
|
||||
AdminMenuCreateError = 20311
|
||||
|
||||
// 配置
|
||||
ConfigEmailError = 20401
|
||||
ConfigSaveError = 20402
|
||||
ConfigRedisConnectError = 20403
|
||||
ConfigMySQLConnectError = 20404
|
||||
ConfigMySQLInstallError = 20405
|
||||
ConfigGoVersionError = 20406
|
||||
|
||||
// 实用工具箱
|
||||
SearchRedisError = 20501
|
||||
ClearRedisError = 20502
|
||||
SearchRedisEmpty = 20503
|
||||
SearchMySQLError = 20504
|
||||
|
||||
// 菜单栏
|
||||
MenuCreateError = 20601
|
||||
MenuUpdateError = 20602
|
||||
MenuListError = 20603
|
||||
MenuDeleteError = 20604
|
||||
MenuDetailError = 20605
|
||||
MenuCreateActionError = 20606
|
||||
MenuListActionError = 20607
|
||||
MenuDeleteActionError = 20608
|
||||
)
|
||||
|
||||
var codeText = map[int]string{
|
||||
ServerError: "Internal Server Error",
|
||||
TooManyRequests: "Too Many Requests",
|
||||
ParamBindError: "参数信息有误",
|
||||
AuthorizationError: "签名信息有误",
|
||||
CallHTTPError: "调用第三方 HTTP 接口失败",
|
||||
ResubmitError: "Resubmit Error",
|
||||
ResubmitMsg: "请勿重复提交",
|
||||
HashIdsDecodeError: "ID 参数有误",
|
||||
SignatureError: "Signature Error",
|
||||
|
||||
IllegalUserName: "非法用户名",
|
||||
UserCreateError: "创建用户失败",
|
||||
UserUpdateError: "更新用户失败",
|
||||
UserSearchError: "查询用户失败",
|
||||
|
||||
AuthorizedCreateError: "创建调用方失败",
|
||||
AuthorizedListError: "获取调用方列表页失败",
|
||||
AuthorizedDeleteError: "删除调用方失败",
|
||||
AuthorizedUpdateError: "更新调用方失败",
|
||||
AuthorizedDetailError: "获取调用方详情失败",
|
||||
AuthorizedCreateAPIError: "创建调用方API地址失败",
|
||||
AuthorizedListAPIError: "获取调用方API地址列表失败",
|
||||
AuthorizedDeleteAPIError: "删除调用方API地址失败",
|
||||
|
||||
AdminCreateError: "创建管理员失败",
|
||||
AdminListError: "获取管理员列表页失败",
|
||||
AdminDeleteError: "删除管理员失败",
|
||||
AdminUpdateError: "更新管理员失败",
|
||||
AdminResetPasswordError: "重置密码失败",
|
||||
AdminLoginError: "登录失败",
|
||||
AdminLogOutError: "退出失败",
|
||||
AdminModifyPasswordError: "修改密码失败",
|
||||
AdminModifyPersonalInfoError: "修改个人信息失败",
|
||||
AdminMenuListError: "获取管理员菜单授权列表失败",
|
||||
AdminMenuCreateError: "管理员菜单授权失败",
|
||||
|
||||
ConfigEmailError: "修改邮箱配置失败",
|
||||
ConfigSaveError: "写入配置文件失败",
|
||||
ConfigRedisConnectError: "Redis 连接失败",
|
||||
ConfigMySQLConnectError: "MySQL 连接失败",
|
||||
ConfigMySQLInstallError: "MySQL 初始化数据失败",
|
||||
ConfigGoVersionError: "Go Version 不满足要求",
|
||||
|
||||
SearchRedisError: "查询 Redis Key 失败",
|
||||
ClearRedisError: "清空 Redis Key 失败",
|
||||
SearchRedisEmpty: "查询的 Redis Key 不存在",
|
||||
SearchMySQLError: "查询 MySQL 失败",
|
||||
|
||||
MenuCreateError: "创建菜单失败",
|
||||
MenuUpdateError: "更新菜单失败",
|
||||
MenuDeleteError: "删除菜单失败",
|
||||
MenuListError: "获取菜单列表页失败",
|
||||
MenuDetailError: "获取菜单详情失败",
|
||||
MenuCreateActionError: "创建菜单栏功能权限失败",
|
||||
MenuListActionError: "获取菜单栏功能权限列表失败",
|
||||
MenuDeleteActionError: "删除菜单栏功能权限失败",
|
||||
}
|
||||
|
||||
func Text(code int) string {
|
||||
return codeText[code]
|
||||
}
|
||||
+14
-18
@@ -1,14 +1,13 @@
|
||||
package config_handler
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/env"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/mail"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
@@ -31,7 +30,7 @@ type emailResponse struct {
|
||||
// @Summary 修改邮件配置
|
||||
// @Description 修改邮件配置
|
||||
// @Tags API.config
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param host formData string true "邮箱服务器"
|
||||
// @Param port formData string true "端口"
|
||||
@@ -41,15 +40,16 @@ type emailResponse struct {
|
||||
// @Success 200 {object} emailResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/config/email [patch]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Email() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(emailRequest)
|
||||
res := new(emailResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -60,22 +60,18 @@ 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(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ConfigEmailError,
|
||||
"Mail Send error: "+err.Error()).WithErr(err),
|
||||
code.SendEmailError,
|
||||
code.Text(code.SendEmailError)+err.Error()).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
viper.SetConfigName(env.Active().Value() + "_configs")
|
||||
viper.SetConfigType("toml")
|
||||
viper.AddConfigPath("./configs")
|
||||
|
||||
viper.Set("mail.host", req.Host)
|
||||
viper.Set("mail.port", cast.ToInt(req.Port))
|
||||
viper.Set("mail.user", req.User)
|
||||
@@ -84,10 +80,10 @@ func (h *handler) Email() core.HandlerFunc {
|
||||
|
||||
err := viper.WriteConfig()
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ConfigEmailError,
|
||||
code.Text(code.ConfigEmailError)).WithErr(err),
|
||||
code.WriteConfigError,
|
||||
code.Text(code.WriteConfigError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package config_handler
|
||||
package config
|
||||
|
||||
import (
|
||||
"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"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/mysql"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/redis"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -21,10 +21,10 @@ type Handler interface {
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
cache redis.Repo
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
|
||||
func New(logger *zap.Logger, db mysql.Repo, cache redis.Repo) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
@@ -1,9 +0,0 @@
|
||||
## controller
|
||||
|
||||
控制器层。
|
||||
|
||||
主要接收参数、验证参数、调用 `service` 层的业务逻辑处理,最后返回数据。
|
||||
|
||||
命名规范:
|
||||
|
||||
- 包名以 `_handler` 结尾。
|
||||
@@ -1,52 +0,0 @@
|
||||
package admin_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"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/pkg/errno"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type detailResponse struct {
|
||||
Username string `json:"username"` // 用户名
|
||||
Nickname string `json:"nickname"` // 昵称
|
||||
Mobile string `json:"mobile"` // 手机号
|
||||
}
|
||||
|
||||
// Detail 管理员详情
|
||||
// @Summary 管理员详情
|
||||
// @Description 管理员详情
|
||||
// @Tags API.admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} detailResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/info [get]
|
||||
func (h *handler) Detail() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
res := new(detailResponse)
|
||||
|
||||
searchOneData := new(admin_service.SearchOneData)
|
||||
searchOneData.Id = cast.ToInt32(c.UserID())
|
||||
searchOneData.IsUsed = 1
|
||||
|
||||
info, err := h.adminService.Detail(c, searchOneData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLoginError,
|
||||
code.Text(code.AdminLoginError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Username = info.Username
|
||||
res.Nickname = info.Nickname
|
||||
res.Mobile = info.Mobile
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package admin_handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type loginRequest struct {
|
||||
Username string `form:"username"` // 用户名
|
||||
Password string `form:"password"` // 密码
|
||||
}
|
||||
|
||||
type loginResponse struct {
|
||||
Token string `json:"token"` // 用户身份标识
|
||||
}
|
||||
|
||||
// Login 管理员登录
|
||||
// @Summary 管理员登录
|
||||
// @Description 管理员登录
|
||||
// @Tags API.admin
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Param username formData string true "用户名"
|
||||
// @Param password formData string true "密码"
|
||||
// @Success 200 {object} loginResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/login [post]
|
||||
func (h *handler) Login() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(loginRequest)
|
||||
res := new(loginResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
searchOneData := new(admin_service.SearchOneData)
|
||||
searchOneData.Username = req.Username
|
||||
searchOneData.Password = password.GeneratePassword(req.Password)
|
||||
searchOneData.IsUsed = 1
|
||||
|
||||
info, err := h.adminService.Detail(c, searchOneData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLoginError,
|
||||
code.Text(code.AdminLoginError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if info == nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLoginError,
|
||||
code.Text(code.AdminLoginError)).WithErr(errors.New("未查询出符合条件的用户")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
token := password.GenerateLoginToken(info.Id)
|
||||
|
||||
// 用户信息
|
||||
adminJsonInfo, _ := json.Marshal(info)
|
||||
|
||||
// 记录 Redis 中
|
||||
err = h.cache.Set(h.adminService.CacheKeyPrefix()+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
|
||||
}
|
||||
|
||||
res.Token = token
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package admin_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type logoutResponse struct {
|
||||
Username string `json:"username"` // 用户账号
|
||||
}
|
||||
|
||||
// Logout 管理员登出
|
||||
// @Summary 管理员登出
|
||||
// @Description 管理员登出
|
||||
// @Tags API.admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} logoutResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/admin/login [post]
|
||||
func (h *handler) Logout() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
res := new(logoutResponse)
|
||||
res.Username = c.UserName()
|
||||
|
||||
if !h.cache.Del(h.adminService.CacheKeyPrefix()+password.GenerateLoginToken(cast.ToInt32(c.UserID())), cache.WithTrace(c.Trace())) {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.AdminLogOutError,
|
||||
code.Text(code.AdminLogOutError)).WithErr(errors.New("cache del err")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/cron"
|
||||
)
|
||||
|
||||
type createRequest struct {
|
||||
Name string `form:"name" binding:"required"` // 任务名称
|
||||
Spec string `form:"spec" binding:"required"` // crontab 表达式
|
||||
Command string `form:"command" binding:"required"` // 执行命令
|
||||
Protocol int32 `form:"protocol" binding:"required"` // 执行方式 1:shell 2:http
|
||||
HttpMethod int32 `form:"http_method"` // http 请求方式 1:get 2:post
|
||||
Timeout int32 `form:"timeout" binding:"required"` // 超时时间(单位:秒)
|
||||
RetryTimes int32 `form:"retry_times" binding:"required"` // 重试次数
|
||||
RetryInterval int32 `form:"retry_interval" binding:"required"` // 重试间隔(单位:秒)
|
||||
NotifyStatus int32 `form:"notify_status" binding:"required"` // 执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知
|
||||
NotifyType int32 `form:"notify_type"` // 通知类型 1:邮件 2:webhook
|
||||
NotifyReceiverEmail string `form:"notify_receiver_email"` // 通知者邮箱地址(多个用,分割)
|
||||
NotifyKeyword string `form:"notify_keyword"` // 通知匹配关键字(多个用,分割)
|
||||
Remark string `form:"remark"` // 备注
|
||||
IsUsed int32 `form:"is_used" binding:"required"` // 是否启用 1:是 -1:否
|
||||
}
|
||||
|
||||
type createResponse struct {
|
||||
Id int32 `json:"id"` // 主键ID
|
||||
}
|
||||
|
||||
// Create 创建任务
|
||||
// @Summary 创建任务
|
||||
// @Description 创建任务
|
||||
// @Tags API.cron
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param name formData string true "任务名称"
|
||||
// @Param spec formData string true "crontab 表达式"
|
||||
// @Param command formData string true "执行命令"
|
||||
// @Param protocol formData int true "执行方式 1:shell 2:http"
|
||||
// @Param http_method formData int false "http 请求方式 1:get 2:post"
|
||||
// @Param timeout formData int true "超时时间(单位:秒)"
|
||||
// @Param retry_times formData int true "重试次数"
|
||||
// @Param retry_interval formData int true "重试间隔(单位:秒)"
|
||||
// @Param notify_status formData int true "执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知"
|
||||
// @Param notify_type formData int false "通知类型 1:邮件 2:webhook"
|
||||
// @Param notify_receiver_email formData string false "通知者邮箱地址(多个用,分割)"
|
||||
// @Param notify_keyword formData string false "通知匹配关键字(多个用,分割)"
|
||||
// @Param remark formData string false "备注"
|
||||
// @Param is_used formData int true "是否启用 1:是 -1:否"
|
||||
// @Success 200 {object} createResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/cron [post]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Create() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(createRequest)
|
||||
res := new(createResponse)
|
||||
if err := ctx.ShouldBindForm(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
validation.Error(err)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
createData := new(cron.CreateCronTaskData)
|
||||
createData.Name = req.Name
|
||||
createData.Spec = req.Spec
|
||||
createData.Command = req.Command
|
||||
createData.Protocol = req.Protocol
|
||||
createData.HttpMethod = req.HttpMethod
|
||||
createData.Timeout = req.Timeout
|
||||
createData.RetryTimes = req.RetryTimes
|
||||
createData.RetryInterval = req.RetryInterval
|
||||
createData.NotifyStatus = req.NotifyStatus
|
||||
createData.NotifyType = req.NotifyType
|
||||
createData.NotifyReceiverEmail = req.NotifyReceiverEmail
|
||||
createData.NotifyKeyword = req.NotifyKeyword
|
||||
createData.Remark = req.Remark
|
||||
createData.IsUsed = req.IsUsed
|
||||
|
||||
id, err := h.cronService.Create(ctx, createData)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.CronCreateError,
|
||||
code.Text(code.CronCreateError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = id
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/cron"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type detailRequest struct {
|
||||
Id string `uri:"id"` // HashID
|
||||
}
|
||||
|
||||
type detailResponse struct {
|
||||
Name string `json:"name"` // 任务名称
|
||||
Spec string `json:"spec"` // crontab 表达式
|
||||
Command string `json:"command"` // 执行命令
|
||||
Protocol int32 `json:"protocol"` // 执行方式 1:shell 2:http
|
||||
HttpMethod int32 `json:"http_method"` // http 请求方式 1:get 2:post
|
||||
Timeout int32 `json:"timeout"` // 超时时间(单位:秒)
|
||||
RetryTimes int32 `json:"retry_times"` // 重试次数
|
||||
RetryInterval int32 `json:"retry_interval"` // 重试间隔(单位:秒)
|
||||
NotifyStatus int32 `json:"notify_status"` // 执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知
|
||||
NotifyType int32 `json:"notify_type"` // 通知类型 1:邮件 2:webhook
|
||||
NotifyReceiverEmail string `json:"notify_receiver_email"` // 通知者邮箱地址(多个用,分割)
|
||||
NotifyKeyword string `json:"notify_keyword"` // 通知匹配关键字(多个用,分割)
|
||||
Remark string `json:"remark"` // 备注
|
||||
IsUsed int32 `json:"is_used"` // 是否启用 1:是 -1:否
|
||||
}
|
||||
|
||||
// Detail 获取单条任务详情
|
||||
// @Summary 获取单条任务详情
|
||||
// @Description 获取单条任务详情
|
||||
// @Tags API.cron
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "hashId"
|
||||
// @Success 200 {object} detailResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/cron/{id} [get]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Detail() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(detailRequest)
|
||||
res := new(detailResponse)
|
||||
if err := ctx.ShouldBindURI(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
validation.Error(err)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
searchOneData := new(cron.SearchOneData)
|
||||
searchOneData.Id = cast.ToInt32(ids[0])
|
||||
|
||||
info, err := h.cronService.Detail(ctx, searchOneData)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.CronDetailError,
|
||||
code.Text(code.CronDetailError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Name = info.Name
|
||||
res.Spec = info.Spec
|
||||
res.Command = info.Command
|
||||
res.Protocol = info.Protocol
|
||||
res.HttpMethod = info.HttpMethod
|
||||
res.Timeout = info.Timeout
|
||||
res.RetryTimes = info.RetryTimes
|
||||
res.RetryInterval = info.RetryInterval
|
||||
res.NotifyStatus = info.NotifyStatus
|
||||
res.NotifyType = info.NotifyType
|
||||
res.NotifyReceiverEmail = info.NotifyReceiverEmail
|
||||
res.NotifyKeyword = info.NotifyKeyword
|
||||
res.Remark = info.Remark
|
||||
res.IsUsed = info.IsUsed
|
||||
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type executeRequest struct {
|
||||
Id string `uri:"id"` // HashID
|
||||
}
|
||||
|
||||
type executeResponse struct {
|
||||
Id int `json:"id"` // ID
|
||||
}
|
||||
|
||||
// Execute 手动执行单条任务
|
||||
// @Summary 手动执行单条任务
|
||||
// @Description 手动执行单条任务
|
||||
// @Tags API.cron
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "hashId"
|
||||
// @Success 200 {object} detailResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/cron/exec/{id} [patch]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Execute() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(executeRequest)
|
||||
res := new(executeResponse)
|
||||
if err := ctx.ShouldBindURI(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
validation.Error(err)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.cronService.Execute(ctx, cast.ToInt32(ids[0]))
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.CronExecuteError,
|
||||
code.Text(code.CronExecuteError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = ids[0]
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
Executable
+165
@@ -0,0 +1,165 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/mysql/cron_task"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/cron"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/timeutil"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type listRequest struct {
|
||||
Page int `form:"page"` // 第几页
|
||||
PageSize int `form:"page_size"` // 每页显示条数
|
||||
Name string `form:"name"` // 任务名称
|
||||
Protocol int `form:"protocol"` // 执行方式 1:shell 2:http
|
||||
IsUsed int `form:"is_used"` // 是否启用 1:是 -1:否
|
||||
}
|
||||
|
||||
type listData struct {
|
||||
Id int `json:"id"` // ID
|
||||
HashID string `json:"hashid"` // hashid
|
||||
Name string `json:"name"` // 任务名称
|
||||
Protocol int `json:"protocol"` // 执行方式 1:shell 2:http
|
||||
ProtocolText string `json:"protocol_text"` // 执行方式
|
||||
Spec string `json:"spec"` // crontab 表达式
|
||||
Command string `json:"command"` // 执行命令
|
||||
HttpMethod int `json:"http_method"` // http 请求方式 1:get 2:post
|
||||
HttpMethodText string `json:"http_method_text"` // http 请求方式
|
||||
Timeout int `json:"timeout"` // 超时时间(单位:秒)
|
||||
RetryTimes int `json:"retry_times"` // 重试次数
|
||||
RetryInterval int `json:"retry_interval"` // 重试间隔(单位:秒)
|
||||
NotifyStatus int `json:"notify_status"` // 执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知
|
||||
NotifyStatusText string `json:"notify_status_text"` // 执行结束是否通知
|
||||
IsUsed int `json:"is_used"` // 是否启用 1=启用 2=禁用
|
||||
IsUsedText string `json:"is_used_text"` // 是否启用
|
||||
CreatedAt string `json:"created_at"` // 创建时间
|
||||
CreatedUser string `json:"created_user"` // 创建人
|
||||
UpdatedAt string `json:"updated_at"` // 更新时间
|
||||
UpdatedUser string `json:"updated_user"` // 更新人
|
||||
}
|
||||
|
||||
type listResponse struct {
|
||||
List []listData `json:"list"`
|
||||
Pagination struct {
|
||||
Total int `json:"total"`
|
||||
CurrentPage int `json:"current_page"`
|
||||
PerPageCount int `json:"per_page_count"`
|
||||
} `json:"pagination"`
|
||||
}
|
||||
|
||||
// List 任务列表
|
||||
// @Summary 任务列表
|
||||
// @Description 任务列表
|
||||
// @Tags API.cron
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param page query int true "第几页" default(1)
|
||||
// @Param page_size query int true "每页显示条数" default(10)
|
||||
// @Param name query string false "任务名称"
|
||||
// @Param protocol query int false "执行方式 1:shell 2:http"
|
||||
// @Param is_used query int false "是否启用 1:是 -1:否"
|
||||
// @Success 200 {object} listResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/cron [get]
|
||||
// @Security LoginToken
|
||||
func (h *handler) List() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(listRequest)
|
||||
res := new(listResponse)
|
||||
if err := ctx.ShouldBindForm(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
validation.Error(err)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
page := req.Page
|
||||
if page == 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
pageSize := req.PageSize
|
||||
if pageSize == 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
searchData := new(cron.SearchData)
|
||||
searchData.Page = req.Page
|
||||
searchData.PageSize = req.PageSize
|
||||
searchData.Name = req.Name
|
||||
searchData.Protocol = cast.ToInt32(req.Protocol)
|
||||
searchData.IsUsed = cast.ToInt32(req.IsUsed)
|
||||
|
||||
resListData, err := h.cronService.PageList(ctx, searchData)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.CronListError,
|
||||
code.Text(code.CronListError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resCountData, err := h.cronService.PageListCount(ctx, searchData)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.CronListError,
|
||||
code.Text(code.CronListError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Pagination.Total = cast.ToInt(resCountData)
|
||||
res.Pagination.PerPageCount = pageSize
|
||||
res.Pagination.CurrentPage = page
|
||||
res.List = make([]listData, len(resListData))
|
||||
|
||||
for k, v := range resListData {
|
||||
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsEncodeError,
|
||||
code.Text(code.HashIdsEncodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
data := listData{
|
||||
Id: cast.ToInt(v.Id),
|
||||
HashID: hashId,
|
||||
Name: v.Name,
|
||||
Protocol: cast.ToInt(v.Protocol),
|
||||
ProtocolText: cron_task.ProtocolText[v.Protocol],
|
||||
Spec: v.Spec,
|
||||
Command: v.Command,
|
||||
HttpMethod: cast.ToInt(v.HttpMethod),
|
||||
HttpMethodText: cron_task.HttpMethodText[v.HttpMethod],
|
||||
Timeout: cast.ToInt(v.Timeout),
|
||||
RetryTimes: cast.ToInt(v.RetryTimes),
|
||||
RetryInterval: cast.ToInt(v.RetryInterval),
|
||||
NotifyStatus: cast.ToInt(v.NotifyStatus),
|
||||
NotifyStatusText: cron_task.NotifyStatusText[v.NotifyStatus],
|
||||
IsUsed: cast.ToInt(v.IsUsed),
|
||||
IsUsedText: cron_task.IsUsedText[v.IsUsed],
|
||||
CreatedAt: v.CreatedAt.Format(timeutil.CSTLayout),
|
||||
CreatedUser: v.CreatedUser,
|
||||
UpdatedAt: v.UpdatedAt.Format(timeutil.CSTLayout),
|
||||
UpdatedUser: v.UpdatedUser,
|
||||
}
|
||||
|
||||
res.List[k] = data
|
||||
}
|
||||
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/cron"
|
||||
)
|
||||
|
||||
type modifyRequest struct {
|
||||
Id string `form:"id" binding:"required"` // 任务ID
|
||||
Name string `form:"name" binding:"required"` // 任务名称
|
||||
Spec string `form:"spec" binding:"required"` // crontab 表达式
|
||||
Command string `form:"command" binding:"required"` // 执行命令
|
||||
Protocol int32 `form:"protocol" binding:"required"` // 执行方式 1:shell 2:http
|
||||
HttpMethod int32 `form:"http_method"` // http 请求方式 1:get 2:post
|
||||
Timeout int32 `form:"timeout" binding:"required"` // 超时时间(单位:秒)
|
||||
RetryTimes int32 `form:"retry_times" binding:"required"` // 重试次数
|
||||
RetryInterval int32 `form:"retry_interval" binding:"required"` // 重试间隔(单位:秒)
|
||||
NotifyStatus int32 `form:"notify_status" binding:"required"` // 执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知
|
||||
NotifyType int32 `form:"notify_type"` // 通知类型 1:邮件 2:webhook
|
||||
NotifyReceiverEmail string `form:"notify_receiver_email"` // 通知者邮箱地址(多个用,分割)
|
||||
NotifyKeyword string `form:"notify_keyword"` // 通知匹配关键字(多个用,分割)
|
||||
Remark string `form:"remark"` // 备注
|
||||
IsUsed int32 `form:"is_used" binding:"required"` // 是否启用 1:是 -1:否
|
||||
}
|
||||
|
||||
type modifyResponse struct {
|
||||
Id int32 `json:"id"` // 主键ID
|
||||
}
|
||||
|
||||
// Modify 编辑任务
|
||||
// @Summary 编辑任务
|
||||
// @Description 编辑任务
|
||||
// @Tags API.cron
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id formData string true "hashID"
|
||||
// @Param name formData string true "任务名称"
|
||||
// @Param spec formData string true "crontab 表达式"
|
||||
// @Param command formData string true "执行命令"
|
||||
// @Param protocol formData int true "执行方式 1:shell 2:http"
|
||||
// @Param http_method formData int false "http 请求方式 1:get 2:post"
|
||||
// @Param timeout formData int true "超时时间(单位:秒)"
|
||||
// @Param retry_times formData int true "重试次数"
|
||||
// @Param retry_interval formData int true "重试间隔(单位:秒)"
|
||||
// @Param notify_status formData int true "执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知"
|
||||
// @Param notify_type formData int false "通知类型 1:邮件 2:webhook"
|
||||
// @Param notify_receiver_email formData string false "通知者邮箱地址(多个用,分割)"
|
||||
// @Param notify_keyword formData string false "通知匹配关键字(多个用,分割)"
|
||||
// @Param remark formData string false "备注"
|
||||
// @Param is_used formData int true "是否启用 1:是 -1:否"
|
||||
// @Success 200 {object} modifyResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/cron/{id} [post]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Modify() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(modifyRequest)
|
||||
res := new(modifyResponse)
|
||||
if err := ctx.ShouldBindForm(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
validation.Error(err)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
id := int32(ids[0])
|
||||
|
||||
modifyData := new(cron.ModifyCronTaskData)
|
||||
modifyData.Name = req.Name
|
||||
modifyData.Spec = req.Spec
|
||||
modifyData.Command = req.Command
|
||||
modifyData.Protocol = req.Protocol
|
||||
modifyData.HttpMethod = req.HttpMethod
|
||||
modifyData.Timeout = req.Timeout
|
||||
modifyData.RetryTimes = req.RetryTimes
|
||||
modifyData.RetryInterval = req.RetryInterval
|
||||
modifyData.NotifyStatus = req.NotifyStatus
|
||||
modifyData.NotifyType = req.NotifyType
|
||||
modifyData.NotifyReceiverEmail = req.NotifyReceiverEmail
|
||||
modifyData.NotifyKeyword = req.NotifyKeyword
|
||||
modifyData.Remark = req.Remark
|
||||
modifyData.IsUsed = req.IsUsed
|
||||
|
||||
if err := h.cronService.Modify(ctx, id, modifyData); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.CronUpdateError,
|
||||
code.Text(code.CronUpdateError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = id
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
|
||||
)
|
||||
|
||||
type updateUsedRequest struct {
|
||||
Id string `form:"id"` // 主键ID
|
||||
Used int32 `form:"used"` // 是否启用 1:是 -1:否
|
||||
}
|
||||
|
||||
type updateUsedResponse struct {
|
||||
Id int32 `json:"id"` // 主键ID
|
||||
}
|
||||
|
||||
// UpdateUsed 更新任务为启用/禁用
|
||||
// @Summary 更新任务为启用/禁用
|
||||
// @Description 更新任务为启用/禁用
|
||||
// @Tags API.cron
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id formData string true "hashID"
|
||||
// @Param used formData int true "是否启用 1:是 -1:否"
|
||||
// @Success 200 {object} updateUsedResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/cron/used [patch]
|
||||
// @Security LoginToken
|
||||
func (h *handler) UpdateUsed() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(updateUsedRequest)
|
||||
res := new(updateUsedResponse)
|
||||
if err := ctx.ShouldBindForm(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
validation.Error(err)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
id := int32(ids[0])
|
||||
|
||||
err = h.cronService.UpdateUsed(ctx, id, req.Used)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AdminUpdateError,
|
||||
code.Text(code.AdminUpdateError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = id
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
cronRepo "github.com/xinliangnote/go-gin-api/internal/repository/cron"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/mysql"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/redis"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/cron"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/hash"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ Handler = (*handler)(nil)
|
||||
|
||||
type Handler interface {
|
||||
i()
|
||||
|
||||
// Create 创建任务
|
||||
// @Tags API.cron
|
||||
// @Router /api/cron [post]
|
||||
Create() core.HandlerFunc
|
||||
|
||||
// Modify 编辑任务
|
||||
// @Tags API.cron
|
||||
// @Router /api/cron/{id} [post]
|
||||
Modify() core.HandlerFunc
|
||||
|
||||
// List 任务列表
|
||||
// @Tags API.cron
|
||||
// @Router /api/cron [get]
|
||||
List() core.HandlerFunc
|
||||
|
||||
// UpdateUsed 更新任务为启用/禁用
|
||||
// @Tags API.cron
|
||||
// @Router /api/cron/used [patch]
|
||||
UpdateUsed() core.HandlerFunc
|
||||
|
||||
// Detail 获取单条任务详情
|
||||
// @Tags API.cron
|
||||
// @Router /api/cron/{id} [get]
|
||||
Detail() core.HandlerFunc
|
||||
|
||||
// Execute 手动执行任务
|
||||
// @Tags API.cron
|
||||
// @Router /api/cron/exec/{id} [patch]
|
||||
Execute() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache redis.Repo
|
||||
hashids hash.Hash
|
||||
cronService cron.Service
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db mysql.Repo, cache redis.Repo, cronServer cronRepo.Server) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
hashids: hash.New(configs.Get().HashIds.Secret, configs.Get().HashIds.Length),
|
||||
cronService: cron.New(db, cache, cronServer),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) i() {}
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
type md5Request struct {
|
||||
Str string `uri:"str" binding:"required"` // 需要加密的字符串
|
||||
}
|
||||
|
||||
type md5Response struct {
|
||||
Md5Str string `json:"md5_str"` // MD5后的字符串
|
||||
}
|
||||
|
||||
// Md5 加密
|
||||
// @Summary 加密
|
||||
// @Description 加密
|
||||
// @Tags Helper
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param str path string true "需要加密的字符串"
|
||||
// @Success 200 {object} md5Response
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /helper/md5/{str} [get]
|
||||
func (h *handler) Md5() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(md5Request)
|
||||
res := new(md5Response)
|
||||
|
||||
if err := ctx.ShouldBindURI(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
m := md5.New()
|
||||
m.Write([]byte(req.Str))
|
||||
res.Md5Str = hex.EncodeToString(m.Sum(nil))
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
Executable
+100
@@ -0,0 +1,100 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errors"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/signature"
|
||||
)
|
||||
|
||||
type signRequest struct {
|
||||
Key string `form:"key" binding:"required"` // 调用方 KEY
|
||||
Path string `form:"path" binding:"required"` // 请求路径 (不附带 querystring),例如:/api/login
|
||||
Method string `form:"method" binding:"required"` // 请求方式,例如:POST
|
||||
Params string `form:"params" binding:"required"` // 请求参数,例如:username=tom&password=123456
|
||||
}
|
||||
|
||||
type signResponse struct {
|
||||
Authorization string `json:"authorization"` // 签名信息-Authorization
|
||||
AuthorizationDate string `json:"authorization_date"` // 签名信息-Authorization-Date
|
||||
}
|
||||
|
||||
// Sign 签名
|
||||
// @Summary 签名
|
||||
// @Description 签名
|
||||
// @Tags Helper
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param key formData string true "调用方 KEY"
|
||||
// @Param path formData string true "请求路径 (不附带 querystring),例如:/api/login"
|
||||
// @Param method formData string true "请求方式,例如:POST"
|
||||
// @Param params formData string true "请求参数,例如:username=tom&password=123456"
|
||||
// @Success 200 {object} signResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /helper/sign [post]
|
||||
func (h *handler) Sign() core.HandlerFunc {
|
||||
return func(ctx core.Context) {
|
||||
req := new(signRequest)
|
||||
res := new(signResponse)
|
||||
|
||||
if err := ctx.ShouldBindForm(req); err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
authorizedInfo, err := h.authorizedService.DetailByKey(ctx, req.Key)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizationError,
|
||||
code.Text(code.AuthorizationError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if authorizedInfo.IsUsed == -1 {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizationError,
|
||||
code.Text(code.AuthorizationError)).WithError(errors.New(req.Key + " 已被禁止调用")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(req.Params)
|
||||
|
||||
params, err := url.ParseQuery(req.Params)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizationError,
|
||||
"params 传递格式不正确"),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
sign := signature.New(req.Key, authorizedInfo.Secret, configs.HeaderSignTokenTimeout)
|
||||
authorized, date, err := sign.Generate(req.Path, req.Method, params)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizationError,
|
||||
"sign 生成失败"),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Authorization = authorized
|
||||
res.AuthorizationDate = date
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/mysql"
|
||||
"github.com/xinliangnote/go-gin-api/internal/repository/redis"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/authorized"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ Handler = (*handler)(nil)
|
||||
|
||||
type Handler interface {
|
||||
i()
|
||||
|
||||
// Md5 加密
|
||||
// @Tags Helper
|
||||
// @Router /helper/md5/{str} [get]
|
||||
Md5() core.HandlerFunc
|
||||
|
||||
// Sign 签名
|
||||
// @Tags Helper
|
||||
// @Router /helper/sign [post]
|
||||
Sign() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
db mysql.Repo
|
||||
authorizedService authorized.Service
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db mysql.Repo, cache redis.Repo) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
db: db,
|
||||
authorizedService: authorized.New(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) i() {}
|
||||
+15
-15
@@ -1,12 +1,11 @@
|
||||
package menu_handler
|
||||
package menu
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/menu_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/menu"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
@@ -28,21 +27,22 @@ type createResponse struct {
|
||||
// @Summary 创建/编辑菜单
|
||||
// @Description 创建/编辑菜单
|
||||
// @Tags API.menu
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param Request body createRequest true "请求信息"
|
||||
// @Success 200 {object} createResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/menu [post]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Create() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(createRequest)
|
||||
res := new(createResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -50,27 +50,27 @@ func (h *handler) Create() core.HandlerFunc {
|
||||
if req.Id != "" { // 编辑功能
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
id := int32(ids[0])
|
||||
|
||||
updateData := new(menu_service.UpdateMenuData)
|
||||
updateData := new(menu.UpdateMenuData)
|
||||
updateData.Name = req.Name
|
||||
updateData.Icon = req.Icon
|
||||
updateData.Link = req.Link
|
||||
|
||||
err = h.menuService.Modify(c, id, updateData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.MenuUpdateError,
|
||||
code.Text(code.MenuUpdateError)).WithErr(err),
|
||||
code.Text(code.MenuUpdateError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -88,7 +88,7 @@ func (h *handler) Create() core.HandlerFunc {
|
||||
level = 1
|
||||
}
|
||||
|
||||
createData := new(menu_service.CreateMenuData)
|
||||
createData := new(menu.CreateMenuData)
|
||||
createData.Pid = pid
|
||||
createData.Name = req.Name
|
||||
createData.Icon = req.Icon
|
||||
@@ -97,10 +97,10 @@ func (h *handler) Create() core.HandlerFunc {
|
||||
|
||||
id, err := h.menuService.Create(c, createData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.MenuCreateError,
|
||||
code.Text(code.MenuCreateError)).WithErr(err),
|
||||
code.Text(code.MenuCreateError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+15
-15
@@ -1,12 +1,11 @@
|
||||
package menu_handler
|
||||
package menu
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/menu_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/menu"
|
||||
)
|
||||
|
||||
type createActionRequest struct {
|
||||
@@ -23,7 +22,7 @@ type createActionResponse struct {
|
||||
// @Summary 创建功能权限
|
||||
// @Description 创建功能权限
|
||||
// @Tags API.menu
|
||||
// @Accept multipart/form-data
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id formData string true "HashID"
|
||||
// @Param method formData string true "请求方法"
|
||||
@@ -31,54 +30,55 @@ type createActionResponse struct {
|
||||
// @Success 200 {object} createActionResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/menu_action [post]
|
||||
// @Security LoginToken
|
||||
func (h *handler) CreateAction() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(createActionRequest)
|
||||
res := new(createActionResponse)
|
||||
if err := c.ShouldBindForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
id := int32(ids[0])
|
||||
|
||||
searchOneData := new(menu_service.SearchOneData)
|
||||
searchOneData := new(menu.SearchOneData)
|
||||
searchOneData.Id = id
|
||||
menuInfo, err := h.menuService.Detail(c, searchOneData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.MenuDetailError,
|
||||
code.Text(code.MenuDetailError)).WithErr(err),
|
||||
code.Text(code.MenuDetailError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
createActionData := new(menu_service.CreateMenuActionData)
|
||||
createActionData := new(menu.CreateMenuActionData)
|
||||
createActionData.MenuId = menuInfo.Id
|
||||
createActionData.Method = req.Method
|
||||
createActionData.API = req.API
|
||||
|
||||
createId, err := h.menuService.CreateAction(c, createActionData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.MenuCreateActionError,
|
||||
code.Text(code.MenuCreateActionError)).WithErr(err),
|
||||
code.Text(code.MenuCreateActionError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+9
-9
@@ -1,11 +1,10 @@
|
||||
package menu_handler
|
||||
package menu
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type deleteRequest struct {
|
||||
@@ -26,25 +25,26 @@ type deleteResponse struct {
|
||||
// @Success 200 {object} deleteResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/menu/{id} [delete]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Delete() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(deleteRequest)
|
||||
res := new(deleteResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -53,10 +53,10 @@ func (h *handler) Delete() core.HandlerFunc {
|
||||
|
||||
err = h.menuService.Delete(c, id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.MenuDeleteError,
|
||||
code.Text(code.MenuDeleteError)).WithErr(err),
|
||||
code.Text(code.MenuDeleteError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+9
-9
@@ -1,11 +1,10 @@
|
||||
package menu_handler
|
||||
package menu
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type deleteActionRequest struct {
|
||||
@@ -26,25 +25,26 @@ type deleteActionResponse struct {
|
||||
// @Success 200 {object} deleteActionResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/menu_action/{id} [delete]
|
||||
// @Security LoginToken
|
||||
func (h *handler) DeleteAction() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(deleteActionRequest)
|
||||
res := new(deleteActionResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -53,10 +53,10 @@ func (h *handler) DeleteAction() core.HandlerFunc {
|
||||
|
||||
err = h.menuService.DeleteAction(c, id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.MenuDeleteActionError,
|
||||
code.Text(code.MenuDeleteActionError)).WithErr(err),
|
||||
code.Text(code.MenuDeleteActionError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+12
-12
@@ -1,12 +1,11 @@
|
||||
package menu_handler
|
||||
package menu
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/menu_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/menu"
|
||||
)
|
||||
|
||||
type detailRequest struct {
|
||||
@@ -25,46 +24,47 @@ type detailResponse struct {
|
||||
// @Summary 菜单详情
|
||||
// @Description 菜单详情
|
||||
// @Tags API.menu
|
||||
// @Accept json
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id path string true "hashId"
|
||||
// @Success 200 {object} detailResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/menu/{id} [get]
|
||||
// @Security LoginToken
|
||||
func (h *handler) Detail() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(detailRequest)
|
||||
res := new(detailResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
code.Text(code.ParamBindError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ids, err := h.hashids.HashidsDecode(req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsDecodeError,
|
||||
code.Text(code.HashIdsDecodeError)).WithErr(err),
|
||||
code.Text(code.HashIdsDecodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
id := int32(ids[0])
|
||||
|
||||
searchOneData := new(menu_service.SearchOneData)
|
||||
searchOneData := new(menu.SearchOneData)
|
||||
searchOneData.Id = id
|
||||
|
||||
info, err := h.menuService.Detail(c, searchOneData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.MenuDetailError,
|
||||
code.Text(code.MenuDetailError)).WithErr(err),
|
||||
code.Text(code.MenuDetailError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
+16
-10
@@ -1,15 +1,13 @@
|
||||
package menu_handler
|
||||
package menu
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/menu_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/internal/services/menu"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type listData struct {
|
||||
@@ -20,6 +18,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 {
|
||||
@@ -30,20 +29,21 @@ type listResponse struct {
|
||||
// @Summary 菜单列表
|
||||
// @Description 菜单列表
|
||||
// @Tags API.menu
|
||||
// @Accept json
|
||||
// @Accept application/x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Success 200 {object} listResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/menu [get]
|
||||
// @Security LoginToken
|
||||
func (h *handler) List() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
res := new(listResponse)
|
||||
resListData, err := h.menuService.List(c, new(menu_service.SearchData))
|
||||
resListData, err := h.menuService.List(c, new(menu.SearchData))
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.MenuListError,
|
||||
code.Text(code.MenuListError)).WithErr(err),
|
||||
code.Text(code.MenuListError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -53,7 +53,12 @@ func (h *handler) List() core.HandlerFunc {
|
||||
for k, v := range resListData {
|
||||
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
|
||||
if err != nil {
|
||||
h.logger.Info("hashids err", zap.Error(err))
|
||||
c.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.HashIdsEncodeError,
|
||||
code.Text(code.HashIdsEncodeError)).WithError(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
data := listData{
|
||||
@@ -64,6 +69,7 @@ func (h *handler) List() core.HandlerFunc {
|
||||
Link: v.Link,
|
||||
Icon: v.Icon,
|
||||
IsUsed: v.IsUsed,
|
||||
Sort: v.Sort,
|
||||
}
|
||||
|
||||
res.List[k] = data
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user