feature(1.2.7): 新增 WEB 模块 - 后台任务

- 新增数据表:cron_task;
- 后台新增后台任务模块,支持(创建、编辑、启用/禁用、列表);
- 调整项目初始化,使其支持安装后台任务模块;
- 调整服务升级指引,1.2.6 -> 1.2.7;
This commit is contained in:
新亮
2021-08-21 21:04:42 +08:00
parent 22bfb9ad10
commit b382919cb5
38 changed files with 3212 additions and 291 deletions

View File

@@ -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="../../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">
</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="../../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">
$(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>

View File

@@ -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="../../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">
</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="../../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">
$(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>

View File

@@ -0,0 +1,284 @@
<!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="../../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">
</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="../../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/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.pre_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 () {
$.alert({
title: '温馨提示',
icon: 'mdi mdi-alert',
type: 'orange',
content: '功能开发中...',
});
})
})
</script>
</body>
</html>

View File

@@ -108,7 +108,8 @@
<code>admin</code>
<code>menu</code>
<code>menu_action</code>
<code>admin_menu</code>
<code>admin_menu</code>
<code>cron_task</code>
</small>
</div>

View File

@@ -83,7 +83,16 @@
->
<span class="badge badge-pill badge-warning">v1.2.7</span>
</p>
<p>......</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/>
</div>

View File

@@ -2,7 +2,7 @@ package configs
const (
// ProjectVersion 项目版本
ProjectVersion = "v1.2.6"
ProjectVersion = "v1.2.7"
// ProjectName 项目名称
ProjectName = "go-gin-api"

View File

@@ -0,0 +1,99 @@
package cron_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/service/cron_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/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/pkg/errno"
)
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 multipart/form-data
// @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]
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(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err)).WithErr(err),
)
return
}
createData := new(cron_service.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(errno.NewError(
http.StatusBadRequest,
code.CronCreateError,
code.Text(code.CronCreateError)).WithErr(err),
)
return
}
res.Id = id
ctx.Payload(res)
}
}

View File

@@ -0,0 +1,99 @@
package cron_handler
import (
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/service/cron_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"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]
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(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err)).WithErr(err),
)
return
}
ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err),
)
return
}
searchOneData := new(cron_service.SearchOneData)
searchOneData.Id = cast.ToInt32(ids[0])
info, err := h.cronService.Detail(ctx, searchOneData)
if err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.CronDetailError,
code.Text(code.CronDetailError)).WithErr(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)
}
}

View File

@@ -0,0 +1,165 @@
package cron_handler
import (
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/cron_task_repo"
"github.com/xinliangnote/go-gin-api/internal/api/service/cron_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/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/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"`
PrePageCount int `json:"pre_page_count"`
} `json:"pagination"`
}
// List 任务列表
// @Summary 任务列表
// @Description 任务列表
// @Tags API.cron
// @Accept multipart/form-data
// @Produce json
// @Param page query int false "第几页"
// @Param page_size query string false "每页显示条数"
// @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]
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(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err)).WithErr(err),
)
return
}
page := req.Page
if page == 0 {
page = 1
}
pageSize := req.PageSize
if pageSize == 0 {
pageSize = 10
}
searchData := new(cron_service.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(errno.NewError(
http.StatusBadRequest,
code.CronListError,
code.Text(code.CronListError)).WithErr(err),
)
return
}
resCountData, err := h.cronService.PageListCount(ctx, searchData)
if err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.CronListError,
code.Text(code.CronListError)).WithErr(err),
)
return
}
res.Pagination.Total = cast.ToInt(resCountData)
res.Pagination.PrePageCount = 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(errno.NewError(
http.StatusBadRequest,
code.HashIdsEncodeError,
code.Text(code.HashIdsEncodeError)).WithErr(err),
)
return
}
data := listData{
Id: cast.ToInt(v.Id),
HashID: hashId,
Name: v.Name,
Protocol: cast.ToInt(v.Protocol),
ProtocolText: cron_task_repo.ProtocolText[v.Protocol],
Spec: v.Spec,
Command: v.Command,
HttpMethod: cast.ToInt(v.HttpMethod),
HttpMethodText: cron_task_repo.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_repo.NotifyStatusText[v.NotifyStatus],
IsUsed: cast.ToInt(v.IsUsed),
IsUsedText: cron_task_repo.IsUsedText[v.IsUsed],
CreatedAt: v.CreatedAt.Format(time_parse.CSTLayout),
CreatedUser: v.CreatedUser,
UpdatedAt: v.UpdatedAt.Format(time_parse.CSTLayout),
UpdatedUser: v.UpdatedUser,
}
res.List[k] = data
}
ctx.Payload(res)
}
}

View File

@@ -0,0 +1,111 @@
package cron_handler
import (
"github.com/xinliangnote/go-gin-api/internal/api/service/cron_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/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/pkg/errno"
"net/http"
)
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 multipart/form-data
// @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]
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(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err)).WithErr(err),
)
return
}
ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err),
)
return
}
id := int32(ids[0])
modifyData := new(cron_service.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(errno.NewError(
http.StatusBadRequest,
code.CronUpdateError,
code.Text(code.CronUpdateError)).WithErr(err),
)
return
}
res.Id = id
ctx.Payload(res)
}
}

View File

@@ -0,0 +1,70 @@
package cron_handler
import (
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
"net/http"
"github.com/xinliangnote/go-gin-api/internal/pkg/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
)
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 multipart/form-data
// @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]
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(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err)).WithErr(err),
)
return
}
ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err),
)
return
}
id := int32(ids[0])
err = h.cronService.UpdateUsed(ctx, id, req.Used)
if err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AdminUpdateError,
code.Text(code.AdminUpdateError)).WithErr(err),
)
return
}
res.Id = id
ctx.Payload(res)
}
}

View File

@@ -0,0 +1,61 @@
package cron_handler
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/service/cron_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/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
}
type handler struct {
logger *zap.Logger
cache cache.Repo
hashids hash.Hash
cronService cron_service.Service
}
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
return &handler{
logger: logger,
cache: cache,
hashids: hash.New(configs.Get().HashIds.Secret, configs.Get().HashIds.Length),
cronService: cron_service.New(db, cache),
}
}
func (h *handler) i() {}

View File

@@ -0,0 +1,937 @@
///////////////////////////////////////////////////////////
// THIS FILE IS AUTO GENERATED by gormgen, DON'T EDIT IT //
// ANY CHANGES DONE HERE WILL BE LOST //
///////////////////////////////////////////////////////////
package cron_task_repo
import (
"fmt"
"time"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/pkg/errors"
"gorm.io/gorm"
)
func NewModel() *CronTask {
return new(CronTask)
}
func NewQueryBuilder() *cronTaskRepoQueryBuilder {
return new(cronTaskRepoQueryBuilder)
}
func (t *CronTask) Create(db *gorm.DB) (id int32, err error) {
if err = db.Create(t).Error; err != nil {
return 0, errors.Wrap(err, "create err")
}
return t.Id, nil
}
type cronTaskRepoQueryBuilder struct {
order []string
where []struct {
prefix string
value interface{}
}
limit int
offset int
}
func (qb *cronTaskRepoQueryBuilder) buildQuery(db *gorm.DB) *gorm.DB {
ret := db
for _, where := range qb.where {
ret = ret.Where(where.prefix, where.value)
}
for _, order := range qb.order {
ret = ret.Order(order)
}
ret = ret.Limit(qb.limit).Offset(qb.offset)
return ret
}
func (qb *cronTaskRepoQueryBuilder) Updates(db *gorm.DB, m map[string]interface{}) (err error) {
db = db.Model(&CronTask{})
for _, where := range qb.where {
db.Where(where.prefix, where.value)
}
if err = db.Updates(m).Error; err != nil {
return errors.Wrap(err, "updates err")
}
return nil
}
func (qb *cronTaskRepoQueryBuilder) Delete(db *gorm.DB) (err error) {
for _, where := range qb.where {
db = db.Where(where.prefix, where.value)
}
if err = db.Delete(&CronTask{}).Error; err != nil {
return errors.Wrap(err, "delete err")
}
return nil
}
func (qb *cronTaskRepoQueryBuilder) Count(db *gorm.DB) (int64, error) {
var c int64
res := qb.buildQuery(db).Model(&CronTask{}).Count(&c)
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
c = 0
}
return c, res.Error
}
func (qb *cronTaskRepoQueryBuilder) First(db *gorm.DB) (*CronTask, error) {
ret := &CronTask{}
res := qb.buildQuery(db).First(ret)
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
ret = nil
}
return ret, res.Error
}
func (qb *cronTaskRepoQueryBuilder) QueryOne(db *gorm.DB) (*CronTask, error) {
qb.limit = 1
ret, err := qb.QueryAll(db)
if len(ret) > 0 {
return ret[0], err
}
return nil, err
}
func (qb *cronTaskRepoQueryBuilder) QueryAll(db *gorm.DB) ([]*CronTask, error) {
var ret []*CronTask
err := qb.buildQuery(db).Find(&ret).Error
return ret, err
}
func (qb *cronTaskRepoQueryBuilder) Limit(limit int) *cronTaskRepoQueryBuilder {
qb.limit = limit
return qb
}
func (qb *cronTaskRepoQueryBuilder) Offset(offset int) *cronTaskRepoQueryBuilder {
qb.offset = offset
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereId(p db_repo.Predicate, value int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "id", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereIdIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "id", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereIdNotIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "id", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderById(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "id "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereName(p db_repo.Predicate, value string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "name", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNameIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "name", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNameNotIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "name", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByName(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "name "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereSpec(p db_repo.Predicate, value string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "spec", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereSpecIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "spec", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereSpecNotIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "spec", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderBySpec(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "spec "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereCommand(p db_repo.Predicate, value string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "command", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereCommandIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "command", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereCommandNotIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "command", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByCommand(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "command "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereProtocol(p db_repo.Predicate, value int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "protocol", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereProtocolIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "protocol", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereProtocolNotIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "protocol", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByProtocol(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "protocol "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereHttpMethod(p db_repo.Predicate, value int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "http_method", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereHttpMethodIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "http_method", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereHttpMethodNotIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "http_method", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByHttpMethod(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "http_method "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereTimeout(p db_repo.Predicate, value int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "timeout", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereTimeoutIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "timeout", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereTimeoutNotIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "timeout", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByTimeout(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "timeout "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereRetryTimes(p db_repo.Predicate, value int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "retry_times", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereRetryTimesIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "retry_times", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereRetryTimesNotIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "retry_times", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByRetryTimes(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "retry_times "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereRetryInterval(p db_repo.Predicate, value int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "retry_interval", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereRetryIntervalIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "retry_interval", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereRetryIntervalNotIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "retry_interval", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByRetryInterval(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "retry_interval "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyStatus(p db_repo.Predicate, value int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_status", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyStatusIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_status", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyStatusNotIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_status", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByNotifyStatus(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "notify_status "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyType(p db_repo.Predicate, value int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_type", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyTypeIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_type", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyTypeNotIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_type", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByNotifyType(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "notify_type "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyReceiverEmail(p db_repo.Predicate, value string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_receiver_email", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyReceiverEmailIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_receiver_email", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyReceiverEmailNotIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_receiver_email", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByNotifyReceiverEmail(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "notify_receiver_email "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyKeyword(p db_repo.Predicate, value string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_keyword", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyKeywordIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_keyword", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereNotifyKeywordNotIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "notify_keyword", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByNotifyKeyword(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "notify_keyword "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereRemark(p db_repo.Predicate, value string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "remark", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereRemarkIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "remark", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereRemarkNotIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "remark", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByRemark(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "remark "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereIsUsed(p db_repo.Predicate, value int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "is_used", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereIsUsedIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "is_used", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereIsUsedNotIn(value []int32) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "is_used", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByIsUsed(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "is_used "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereCreatedAt(p db_repo.Predicate, value time.Time) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "created_at", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereCreatedAtIn(value []time.Time) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "created_at", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereCreatedAtNotIn(value []time.Time) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "created_at", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByCreatedAt(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "created_at "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereCreatedUser(p db_repo.Predicate, value string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "created_user", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereCreatedUserIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "created_user", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereCreatedUserNotIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "created_user", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByCreatedUser(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "created_user "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereUpdatedAt(p db_repo.Predicate, value time.Time) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "updated_at", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereUpdatedAtIn(value []time.Time) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "updated_at", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereUpdatedAtNotIn(value []time.Time) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "updated_at", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByUpdatedAt(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "updated_at "+order)
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereUpdatedUser(p db_repo.Predicate, value string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "updated_user", p),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereUpdatedUserIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "updated_user", "IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) WhereUpdatedUserNotIn(value []string) *cronTaskRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "updated_user", "NOT IN"),
value,
})
return qb
}
func (qb *cronTaskRepoQueryBuilder) OrderByUpdatedUser(asc bool) *cronTaskRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "updated_user "+order)
return qb
}

View File

@@ -0,0 +1,27 @@
package cron_task_repo
import "time"
// CronTask 后台任务表
//go:generate gormgen -structs CronTask -input .
type CronTask struct {
Id int32 // 主键
Name string // 任务名称
Spec string // crontab 表达式
Command string // 执行命令
Protocol int32 // 执行方式 1:shell 2:http
HttpMethod int32 // http 请求方式 1:get 2:post
Timeout int32 // 超时时间(单位:秒)
RetryTimes int32 // 重试次数
RetryInterval int32 // 重试间隔(单位:秒)
NotifyStatus int32 // 执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知
NotifyType int32 // 通知类型 1:邮件 2:webhook
NotifyReceiverEmail string // 通知者邮箱地址(多个用,分割)
NotifyKeyword string // 通知匹配关键字(多个用,分割)
Remark string // 备注
IsUsed int32 // 是否启用 1:是 -1:否
CreatedAt time.Time `gorm:"time"` // 创建时间
CreatedUser string // 创建人
UpdatedAt time.Time `gorm:"time"` // 更新时间
UpdatedUser string // 更新人
}

View File

@@ -0,0 +1,24 @@
#### go_gin_api.cron_task
后台任务表
| 序号 | 名称 | 描述 | 类型 | 键 | 为空 | 额外 | 默认值 |
| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: |
| 1 | id | 主键 | int unsigned | PRI | NO | auto_increment | |
| 2 | name | 任务名称 | varchar(64) | MUL | NO | | |
| 3 | spec | crontab 表达式 | varchar(64) | | NO | | |
| 4 | command | 执行命令 | varchar(255) | | NO | | |
| 5 | protocol | 执行方式 1:shell 2:http | tinyint unsigned | | NO | | 1 |
| 6 | http_method | http 请求方式 1:get 2:post | tinyint unsigned | | NO | | 1 |
| 7 | timeout | 超时时间(单位:秒) | int unsigned | | NO | | 60 |
| 8 | retry_times | 重试次数 | tinyint(1) | | NO | | 3 |
| 9 | retry_interval | 重试间隔(单位:秒) | int | | NO | | 60 |
| 10 | notify_status | 执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知 | tinyint unsigned | | NO | | 0 |
| 11 | notify_type | 通知类型 1:邮件 2:webhook | tinyint unsigned | | NO | | 1 |
| 12 | notify_receiver_email | 通知者邮箱地址(多个用,分割) | varchar(255) | | NO | | |
| 13 | notify_keyword | 通知匹配关键字(多个用,分割) | varchar(255) | | NO | | |
| 14 | remark | 备注 | varchar(100) | | NO | | |
| 15 | is_used | 是否启用 1:是 -1:否 | tinyint(1) | | NO | | 1 |
| 16 | created_at | 创建时间 | timestamp | | NO | DEFAULT_GENERATED | CURRENT_TIMESTAMP |
| 17 | created_user | 创建人 | varchar(60) | | NO | | |
| 18 | updated_at | 更新时间 | timestamp | | NO | DEFAULT_GENERATED on update CURRENT_TIMESTAMP | CURRENT_TIMESTAMP |
| 19 | updated_user | 更新人 | varchar(60) | | NO | | |

View File

@@ -0,0 +1,47 @@
package cron_task_repo
const (
ProtocolShell = 1
ProtocolHTTP = 2
HttpMethodGet = 1
HttpMethodPost = 2
NotifyStatusNo = 1
NotifyStatusFailed = 2
NotifyStatusStopped = 3
NotifyStatusKeyword = 4
NotifyTypeEmail = 1
NotifyTypeWebhook = 2
IsUsedYES = 1
IsUsedNo = -1
)
var ProtocolText = map[int32]string{
ProtocolShell: "SHELL",
ProtocolHTTP: "HTTP",
}
var HttpMethodText = map[int32]string{
HttpMethodGet: "GET",
HttpMethodPost: "POST",
}
var NotifyStatusText = map[int32]string{
NotifyStatusNo: "不通知",
NotifyStatusFailed: "失败通知",
NotifyStatusStopped: "结束通知",
NotifyStatusKeyword: "结果关键字匹配通知",
}
var NotifyTypeText = map[int32]string{
NotifyTypeEmail: "邮件",
NotifyTypeWebhook: "Webhook",
}
var IsUsedText = map[int32]string{
IsUsedYES: "启用",
IsUsedNo: "禁用",
}

View File

@@ -0,0 +1,35 @@
package cron_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/cron_task_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
)
var _ Service = (*service)(nil)
type Service interface {
i()
Create(ctx core.Context, createData *CreateCronTaskData) (id int32, err error)
Modify(ctx core.Context, id int32, modifyData *ModifyCronTaskData) (err error)
PageList(ctx core.Context, searchData *SearchData) (listData []*cron_task_repo.CronTask, err error)
PageListCount(ctx core.Context, searchData *SearchData) (total int64, err error)
UpdateUsed(ctx core.Context, id int32, used int32) (err error)
Detail(ctx core.Context, searchOneData *SearchOneData) (info *cron_task_repo.CronTask, err error)
}
type service struct {
db db.Repo
cache cache.Repo
}
func New(db db.Repo, cache cache.Repo) Service {
return &service{
db: db,
cache: cache,
}
}
func (s *service) i() {}

View File

@@ -0,0 +1,49 @@
package cron_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/cron_task_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
type CreateCronTaskData struct {
Name string // 任务名称
Spec string // crontab 表达式
Command string // 执行命令
Protocol int32 // 执行方式 1:shell 2:http
HttpMethod int32 // http 请求方式 1:get 2:post
Timeout int32 // 超时时间(单位:秒)
RetryTimes int32 // 重试次数
RetryInterval int32 // 重试间隔(单位:秒)
NotifyStatus int32 // 执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知
NotifyType int32 // 通知类型 1:邮件 2:webhook
NotifyReceiverEmail string // 通知者邮箱地址(多个用,分割)
NotifyKeyword string // 通知匹配关键字(多个用,分割)
Remark string // 备注
IsUsed int32 // 是否启用 1:是 -1:否
}
func (s *service) Create(ctx core.Context, createData *CreateCronTaskData) (id int32, err error) {
model := cron_task_repo.NewModel()
model.Name = createData.Name
model.Spec = createData.Spec
model.Command = createData.Command
model.Protocol = createData.Protocol
model.HttpMethod = createData.HttpMethod
model.Timeout = createData.Timeout
model.RetryTimes = createData.RetryTimes
model.RetryInterval = createData.RetryInterval
model.NotifyStatus = createData.NotifyStatus
model.NotifyType = createData.NotifyType
model.NotifyReceiverEmail = createData.NotifyReceiverEmail
model.NotifyKeyword = createData.NotifyKeyword
model.Remark = createData.Remark
model.IsUsed = createData.IsUsed
model.CreatedUser = ctx.UserName()
id, err = model.Create(s.db.GetDbW().WithContext(ctx.RequestContext()))
if err != nil {
return 0, err
}
return
}

View File

@@ -0,0 +1,26 @@
package cron_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/cron_task_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
type SearchOneData struct {
Id int32 // 任务ID
}
func (s *service) Detail(ctx core.Context, searchOneData *SearchOneData) (info *cron_task_repo.CronTask, err error) {
qb := cron_task_repo.NewQueryBuilder()
if searchOneData.Id != 0 {
qb.WhereId(db_repo.EqualPredicate, searchOneData.Id)
}
info, err = qb.QueryOne(s.db.GetDbR().WithContext(ctx.RequestContext()))
if err != nil {
return nil, err
}
return
}

View File

@@ -0,0 +1,53 @@
package cron_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/cron_task_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
type ModifyCronTaskData struct {
Name string // 任务名称
Spec string // crontab 表达式
Command string // 执行命令
Protocol int32 // 执行方式 1:shell 2:http
HttpMethod int32 // http 请求方式 1:get 2:post
Timeout int32 // 超时时间(单位:秒)
RetryTimes int32 // 重试次数
RetryInterval int32 // 重试间隔(单位:秒)
NotifyStatus int32 // 执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知
NotifyType int32 // 通知类型 1:邮件 2:webhook
NotifyReceiverEmail string // 通知者邮箱地址(多个用,分割)
NotifyKeyword string // 通知匹配关键字(多个用,分割)
Remark string // 备注
IsUsed int32 // 是否启用 1:是 -1:否
}
func (s *service) Modify(ctx core.Context, id int32, modifyData *ModifyCronTaskData) (err error) {
data := map[string]interface{}{
"name": modifyData.Name,
"spec": modifyData.Spec,
"command": modifyData.Command,
"protocol": modifyData.Protocol,
"http_method": modifyData.HttpMethod,
"timeout": modifyData.Timeout,
"retry_times": modifyData.RetryTimes,
"retry_interval": modifyData.RetryInterval,
"notify_status": modifyData.NotifyStatus,
"notify_type": modifyData.NotifyType,
"notify_receiver_email": modifyData.NotifyReceiverEmail,
"notify_keyword": modifyData.NotifyKeyword,
"remark": modifyData.Remark,
"is_used": modifyData.IsUsed,
"updated_user": ctx.UserName(),
}
qb := cron_task_repo.NewQueryBuilder()
qb.WhereId(db_repo.EqualPredicate, id)
err = qb.Updates(s.db.GetDbW().WithContext(ctx.RequestContext()), data)
if err != nil {
return err
}
return
}

View File

@@ -0,0 +1,54 @@
package cron_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/cron_task_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
type SearchData struct {
Page int // 第几页
PageSize int // 每页显示条数
Name string // 任务名称
Protocol int32 // 执行方式
IsUsed int32 // 是否启用
}
func (s *service) PageList(ctx core.Context, searchData *SearchData) (listData []*cron_task_repo.CronTask, err error) {
page := searchData.Page
if page == 0 {
page = 1
}
pageSize := searchData.PageSize
if pageSize == 0 {
pageSize = 10
}
offset := (page - 1) * pageSize
qb := cron_task_repo.NewQueryBuilder()
if searchData.Name != "" {
qb.WhereName(db_repo.EqualPredicate, searchData.Name)
}
if searchData.Protocol != 0 {
qb.WhereProtocol(db_repo.EqualPredicate, searchData.Protocol)
}
if searchData.IsUsed != 0 {
qb.WhereIsUsed(db_repo.EqualPredicate, searchData.IsUsed)
}
listData, err = qb.
Limit(pageSize).
Offset(offset).
OrderById(false).
QueryAll(s.db.GetDbR().WithContext(ctx.RequestContext()))
if err != nil {
return nil, err
}
return
}

View File

@@ -0,0 +1,30 @@
package cron_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/cron_task_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
func (s *service) PageListCount(ctx core.Context, searchData *SearchData) (total int64, err error) {
qb := cron_task_repo.NewQueryBuilder()
if searchData.Name != "" {
qb.WhereName(db_repo.EqualPredicate, searchData.Name)
}
if searchData.Protocol != 0 {
qb.WhereProtocol(db_repo.EqualPredicate, searchData.Protocol)
}
if searchData.IsUsed != 0 {
qb.WhereIsUsed(db_repo.EqualPredicate, searchData.IsUsed)
}
total, err = qb.Count(s.db.GetDbR().WithContext(ctx.RequestContext()))
if err != nil {
return 0, err
}
return
}

View File

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

View File

@@ -60,6 +60,11 @@ const (
MenuCreateActionError = 20306
MenuListActionError = 20307
MenuDeleteActionError = 20308
CronCreateError = 20401
CronUpdateError = 20402
CronListError = 20403
CronDetailError = 20404
)
func Text(code int) string {

View File

@@ -52,4 +52,9 @@ var enUSText = map[int]string{
MenuCreateActionError: "Failed to create menu action",
MenuListActionError: "Failed to get menu action list",
MenuDeleteActionError: "Failed to delete menu action",
CronCreateError: "Failed to create cron",
CronUpdateError: "Failed to update menu",
CronListError: "Failed to get cron list",
CronDetailError: "Failed to get cron detail",
}

View File

@@ -52,4 +52,9 @@ var zhCNText = map[int]string{
MenuCreateActionError: "创建菜单栏功能权限失败",
MenuListActionError: "获取菜单栏功能权限列表失败",
MenuDeleteActionError: "删除菜单栏功能权限失败",
CronCreateError: "创建后台任务失败",
CronUpdateError: "更新后台任务失败",
CronListError: "获取定时任务列表失败",
CronDetailError: "获取定时任务详情失败",
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/xinliangnote/go-gin-api/internal/api/controller/admin_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/authorized_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/config_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/cron_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/menu_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/tool_handler"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
@@ -79,5 +80,13 @@ func setApiRouter(r *resource) {
configHandler := config_handler.New(r.logger, r.db, r.cache)
api.PATCH("/config/email", configHandler.Email())
// cron
cronHandler := cron_handler.New(r.logger, r.db, r.cache)
api.POST("/cron", cronHandler.Create())
api.GET("/cron", cronHandler.List())
api.GET("/cron/:id", cronHandler.Detail())
api.POST("/cron/:id", cronHandler.Modify())
api.PATCH("/cron/used", cronHandler.UpdateUsed())
}
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/xinliangnote/go-gin-api/internal/web/controller/admin_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/authorized_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/config_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/cron_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/dashboard_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/generator_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/index_handler"
@@ -23,6 +24,7 @@ func setWebRouter(r *resource) {
toolHandler := tool_handler.New(r.logger, r.db, r.cache)
adminHandler := admin_handler.New(r.logger, r.db, r.cache)
upgradeHandler := upgrade_handler.New(r.logger, r.db, r.cache)
cronTaskHandler := cron_handler.New(r.logger, r.db, r.cache)
// 无需记录日志,无需 RBAC 权限验证
notRBAC := r.mux.Group("", r.middles.DisableLog())
@@ -80,5 +82,9 @@ func setWebRouter(r *resource) {
web.GET("/tool/cache", toolHandler.CacheView())
web.GET("/tool/data", toolHandler.DataView())
// 后台任务
web.GET("/cron/list", cronTaskHandler.ListView())
web.GET("/cron/add", cronTaskHandler.AddView())
web.GET("/cron/edit/:id", cronTaskHandler.EditView())
}
}

View File

@@ -0,0 +1,9 @@
package cron_handler
import "github.com/xinliangnote/go-gin-api/internal/pkg/core"
func (h *handler) AddView() core.HandlerFunc {
return func(ctx core.Context) {
ctx.HTML("cron_task_add", nil)
}
}

View File

@@ -0,0 +1,35 @@
package cron_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/internal/pkg/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
)
type editViewRequest struct {
Id string `uri:"id"` // 主键ID
}
type editViewResponse struct {
HashID string `json:"hash_id"` // hashID
}
func (h *handler) EditView() core.HandlerFunc {
return func(ctx core.Context) {
req := new(editViewRequest)
if err := ctx.ShouldBindURI(req); err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
obj := new(editViewResponse)
obj.HashID = req.Id
ctx.HTML("cron_task_edit", obj)
}
}

View File

@@ -0,0 +1,9 @@
package cron_handler
import "github.com/xinliangnote/go-gin-api/internal/pkg/core"
func (h *handler) ListView() core.HandlerFunc {
return func(ctx core.Context) {
ctx.HTML("cron_task_list", nil)
}
}

View File

@@ -0,0 +1,33 @@
package cron_handler
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"
"go.uber.org/zap"
)
var _ Handler = (*handler)(nil)
type Handler interface {
i()
AddView() core.HandlerFunc
EditView() core.HandlerFunc
ListView() core.HandlerFunc
}
type handler struct {
logger *zap.Logger
cache cache.Repo
}
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
return &handler{
logger: logger,
cache: cache,
}
}
func (h *handler) i() {}

View File

@@ -34,10 +34,42 @@ type initExecuteRequest struct {
}
func (h *handler) Execute() core.HandlerFunc {
return func(c core.Context) {
installTableList := map[string]map[string]string{
"authorized": {
"table_sql": mysql_table.CreateAuthorizedTableSql(),
"table_data_sql": mysql_table.CreateAuthorizedTableDataSql(),
},
"authorized_api": {
"table_sql": mysql_table.CreateAuthorizedAPITableSql(),
"table_data_sql": mysql_table.CreateAuthorizedAPITableDataSql(),
},
"admin": {
"table_sql": mysql_table.CreateAdminTableSql(),
"table_data_sql": mysql_table.CreateAdminTableDataSql(),
},
"admin_menu": {
"table_sql": mysql_table.CreateAdminMenuTableSql(),
"table_data_sql": mysql_table.CreateAdminMenuTableDataSql(),
},
"menu": {
"table_sql": mysql_table.CreateMenuTableSql(),
"table_data_sql": mysql_table.CreateMenuTableDataSql(),
},
"menu_action": {
"table_sql": mysql_table.CreateMenuActionTableSql(),
"table_data_sql": mysql_table.CreateMenuActionTableDataSql(),
},
"cron_task": {
"table_sql": mysql_table.CreateCronTaskTableSql(),
"table_data_sql": "",
},
}
return func(ctx core.Context) {
req := new(initExecuteRequest)
if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError(
if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
@@ -45,19 +77,20 @@ func (h *handler) Execute() core.HandlerFunc {
return
}
// region 验证 version
versionStr := runtime.Version()
version := cast.ToFloat32(versionStr[2:6])
if version < 1.15 {
c.AbortWithError(errno.NewError(
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.GoVersionError,
code.Text(code.GoVersionError)),
)
return
}
// endregion
outPutString := ""
// region 验证 Redis 配置
cfg := configs.Get()
redisClient := redis.NewClient(&redis.Options{
Addr: req.RedisAddr,
@@ -69,7 +102,7 @@ func (h *handler) Execute() core.HandlerFunc {
})
if err := redisClient.Ping().Err(); err != nil {
c.AbortWithError(errno.NewError(
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.RedisConnectError,
code.Text(code.RedisConnectError)).WithErr(err),
@@ -79,8 +112,10 @@ func (h *handler) Execute() core.HandlerFunc {
defer redisClient.Close()
outPutString += "已检测 Redis 配置可用。\n"
outPutString := "已检测 Redis 配置可用。\n"
// endregion
// region 验证 MySQL 配置
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=%t&loc=%s",
req.MySQLUser,
req.MySQLPass,
@@ -97,7 +132,7 @@ func (h *handler) Execute() core.HandlerFunc {
})
if err != nil {
c.AbortWithError(errno.NewError(
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLConnectError,
code.Text(code.MySQLConnectError)).WithErr(err),
@@ -111,7 +146,9 @@ func (h *handler) Execute() core.HandlerFunc {
defer dbClient.Close()
outPutString += "已检测 MySQL 配置可用。\n"
// endregion
// region 写入配置文件
viper.SetConfigName(env.Active().Value() + "_configs")
viper.SetConfigType("toml")
viper.AddConfigPath("./configs")
@@ -133,7 +170,7 @@ func (h *handler) Execute() core.HandlerFunc {
viper.Set("mysql.write.name", req.MySQLName)
if viper.WriteConfig() != nil {
c.AbortWithError(errno.NewError(
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.WriteConfigError,
code.Text(code.WriteConfigError)).WithErr(err),
@@ -143,131 +180,46 @@ func (h *handler) Execute() core.HandlerFunc {
outPutString += "语言包 " + req.Language + " 配置成功。\n"
outPutString += "配置项 Redis、MySQL 配置成功。\n"
// endregion
if err = db.Exec(mysql_table.CreateAuthorizedTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
// region 初始化表结构 + 默认数据
for k, v := range installTableList {
if v["table_sql"] != "" {
// region 初始化表结构
if err = db.Exec(v["table_sql"]).Error; err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表:" + k + " 成功。\n"
// endregion
// region 初始化默认数据
if v["table_data_sql"] != "" {
if err = db.Exec(v["table_data_sql"]).Error; err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表:" + k + " 默认数据成功。\n"
}
// endregion
}
}
outPutString += "初始化 MySQL 数据表authorized 成功。\n"
// endregion
if err = db.Exec(mysql_table.CreateAuthorizedTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表authorized 默认数据成功。\n"
if err = db.Exec(mysql_table.CreateAuthorizedAPITableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表authorized_api 成功。\n"
if err = db.Exec(mysql_table.CreateAuthorizedAPITableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表authorized_api 默认数据成功。\n"
if err = db.Exec(mysql_table.CreateAdminTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表admin 成功。\n"
if err = db.Exec(mysql_table.CreateAdminTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表admin 默认数据成功。\n"
if err = db.Exec(mysql_table.CreateMenuTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表menu 成功。\n"
if err = db.Exec(mysql_table.CreateMenuTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表menu 默认数据成功。\n"
if err = db.Exec(mysql_table.CreateMenuActionTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表menu_action 成功。\n"
if err = db.Exec(mysql_table.CreateMenuActionTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表menu_action 默认数据成功。\n"
if err = db.Exec(mysql_table.CreateAdminMenuTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表admin_menu 成功。\n"
if err = db.Exec(mysql_table.CreateAdminMenuTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表admin_menu 默认数据成功。\n"
// 生成 install 完成标识
// region 生成 install 完成标识
f, err := os.Create(configs.ProjectInstallMark)
if err != nil {
c.AbortWithError(errno.NewError(
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
@@ -275,7 +227,8 @@ func (h *handler) Execute() core.HandlerFunc {
return
}
defer f.Close()
// endregion
c.Payload(outPutString)
ctx.Payload(outPutString)
}
}

View File

@@ -47,7 +47,9 @@ func CreateAdminMenuTableDataSql() (sql string) {
sql += "(19, 1, 1, 'init'),"
sql += "(20, 1, 3, 'init'),"
sql += "(21, 1, 2, 'init'),"
sql += "(22, 1, 22, 'init');"
sql += "(22, 1, 22, 'init'),"
sql += "(23, 1, 23, 'init'),"
sql += "(24, 1, 24, 'init');"
return
}

View File

@@ -0,0 +1,53 @@
package mysql_table
//CREATE TABLE `cron_task` (
//`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
//`name` varchar(64) NOT NULL DEFAULT '' COMMENT '任务名称',
//`spec` varchar(64) NOT NULL DEFAULT '' COMMENT 'crontab 表达式',
//`command` varchar(255) NOT NULL DEFAULT '' COMMENT '执行命令',
//`protocol` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '执行方式 1:shell 2:http',
//`http_method` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT 'http 请求方式 1:get 2:post',
//`timeout` int(11) unsigned NOT NULL DEFAULT '60' COMMENT '超时时间(单位:秒)',
//`retry_times` tinyint(1) NOT NULL DEFAULT '3' COMMENT '重试次数',
//`retry_interval` int(11) NOT NULL DEFAULT '60' COMMENT '重试间隔(单位:秒)',
//`notify_status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知',
//`notify_type` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '通知类型 1:邮件 2:webhook',
//`notify_receiver_email` varchar(255) NOT NULL DEFAULT '' COMMENT '通知者邮箱地址(多个用,分割)',
//`notify_keyword` varchar(255) NOT NULL DEFAULT '' COMMENT '通知匹配关键字(多个用,分割)',
//`remark` varchar(100) NOT NULL DEFAULT '' COMMENT '备注',
//`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用 1:是 -1:否',
//`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
//`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人',
//`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
//`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人',
//PRIMARY KEY (`id`),
//KEY `idx_name` (`name`)
//) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台任务表';
func CreateCronTaskTableSql() (sql string) {
sql = "CREATE TABLE `cron_task` ("
sql += "`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',"
sql += "`name` varchar(64) NOT NULL DEFAULT '' COMMENT '任务名称',"
sql += "`spec` varchar(64) NOT NULL DEFAULT '' COMMENT 'crontab 表达式',"
sql += "`command` varchar(255) NOT NULL DEFAULT '' COMMENT '执行命令',"
sql += "`protocol` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '执行方式 1:shell 2:http',"
sql += "`http_method` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT 'http 请求方式 1:get 2:post',"
sql += "`timeout` int(11) unsigned NOT NULL DEFAULT '60' COMMENT '超时时间(单位:秒)',"
sql += "`retry_times` tinyint(1) NOT NULL DEFAULT '3' COMMENT '重试次数',"
sql += "`retry_interval` int(11) NOT NULL DEFAULT '60' COMMENT '重试间隔(单位:秒)',"
sql += "`notify_status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '执行结束是否通知 1:不通知 2:失败通知 3:结束通知 4:结果关键字匹配通知',"
sql += "`notify_type` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '通知类型 1:邮件 2:webhook',"
sql += "`notify_receiver_email` varchar(255) NOT NULL DEFAULT '' COMMENT '通知者邮箱地址(多个用,分割)',"
sql += "`notify_keyword` varchar(255) NOT NULL DEFAULT '' COMMENT '通知匹配关键字(多个用,分割)',"
sql += "`remark` varchar(100) NOT NULL DEFAULT '' COMMENT '备注',"
sql += "`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用 1:是 -1:否',"
sql += "`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',"
sql += "`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人',"
sql += "`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',"
sql += "`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人',"
sql += "PRIMARY KEY (`id`),"
sql += "KEY `idx_name` (`name`)"
sql += ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='后台任务表';"
return
}

View File

@@ -40,28 +40,30 @@ func CreateMenuTableSql() (sql string) {
func CreateMenuTableDataSql() (sql string) {
sql = "INSERT INTO `menu` (`id`, `pid`, `name`, `link`, `icon`, `level`, `sort`, `created_user`) VALUES"
sql += "(1, 0, '配置信息', '', 'mdi-settings-box', 1, 1, 'init'),"
sql += "(2, 1, '告警邮箱', '/config/email', '', 2, 11, 'init'),"
sql += "(3, 1, '错误码', '/config/code', '', 2, 12, 'init'),"
sql += "(4, 0, '代码生成器', '', 'mdi-code-not-equal-variant', 1, 2, 'init'),"
sql += "(5, 4, '生成数据表 CURD', '/generator/gorm', '', 2, 21, 'init'),"
sql += "(6, 4, '生成控制器方法', '/generator/handler', '', 2, 22, 'init'),"
sql += "(7, 0, '授权调用方', '', 'mdi-playlist-check', 1, 3, 'init'),"
sql += "(8, 7, '调用方', '/authorized/list', '', 2, 31, 'init'),"
sql += "(9, 7, '使用说明', '/authorized/demo', '', 2, 32, 'init'),"
sql += "(10, 0, '系统管理员', '', 'mdi-account', 1, 4, 'init'),"
sql += "(11, 10, '管理员', '/admin/list', '', 2, 41, 'init'),"
sql += "(12, 10, '菜单管理', '/admin/menu', '', 2, 42, 'init'),"
sql += "(13, 0, '查询小助手', '', 'mdi-database-search', 1, 5, 'init'),"
sql += "(14, 13, '查询缓存', '/tool/cache', '', 2, 51, 'init'),"
sql += "(15, 13, '查询数据', '/tool/data', '', 2, 52, 'init'),"
sql += "(16, 0, '实用工具箱', '', 'mdi-tools', 1, 6, 'init'),"
sql += "(17, 16, 'Hashids', '/tool/hashids', '', 2, 62, 'init'),"
sql += "(18, 16, '调用日志', '/tool/logs', '', 2, 63, 'init'),"
sql += "(19, 16, '接口文档', '/swagger/index.html', '', 2, 64, 'init'),"
sql += "(20, 16, 'GraphQL', '/graphql', '', 2, 65, 'init'),"
sql += "(21, 16, '接口指标', '/metrics', '', 2, 66, 'init'),"
sql += "(22, 16, '服务升级', '/upgrade', '', 2, 61, 'init');"
sql += "(1, 0, '配置信息', '', 'mdi-settings-box', 1, 10, 'init'),"
sql += "(2, 1, '告警邮箱', '/config/email', '', 2, 101, 'init'),"
sql += "(3, 1, '错误码', '/config/code', '', 2, 102, 'init'),"
sql += "(4, 0, '代码生成器', '', 'mdi-code-not-equal-variant', 1, 20, 'init'),"
sql += "(5, 4, '生成数据表 CURD', '/generator/gorm', '', 2, 201, 'init'),"
sql += "(6, 4, '生成控制器方法', '/generator/handler', '', 2, 202, 'init'),"
sql += "(7, 0, '授权调用方', '', 'mdi-playlist-check', 1, 30, 'init'),"
sql += "(8, 7, '调用方', '/authorized/list', '', 2, 301, 'init'),"
sql += "(9, 7, '使用说明', '/authorized/demo', '', 2, 302, 'init'),"
sql += "(10, 0, '系统管理员', '', 'mdi-account', 1, 50, 'init'),"
sql += "(11, 10, '管理员', '/admin/list', '', 2, 501, 'init'),"
sql += "(12, 10, '菜单管理', '/admin/menu', '', 2, 502, 'init'),"
sql += "(13, 0, '查询小助手', '', 'mdi-database-search', 1, 60, 'init'),"
sql += "(14, 13, '查询缓存', '/tool/cache', '', 2, 601, 'init'),"
sql += "(15, 13, '查询数据', '/tool/data', '', 2, 602, 'init'),"
sql += "(16, 0, '实用工具箱', '', 'mdi-tools', 1, 70, 'init'),"
sql += "(17, 16, 'Hashids', '/tool/hashids', '', 2, 702, 'init'),"
sql += "(18, 16, '调用日志', '/tool/logs', '', 2, 703, 'init'),"
sql += "(19, 16, '接口文档', '/swagger/index.html', '', 2, 704, 'init'),"
sql += "(20, 16, 'GraphQL', '/graphql', '', 2, 705, 'init'),"
sql += "(21, 16, '接口指标', '/metrics', '', 2, 706, 'init'),"
sql += "(22, 16, '服务升级', '/upgrade', '', 2, 701, 'init'),"
sql += "(23, 0, '后台任务', '', 'mdi-av-timer', 1, 40, 'init'),"
sql += "(24, 23, '任务列表', '/cron/list', '', 2, 401, 'init');"
return
}

View File

@@ -72,7 +72,14 @@ func CreateMenuActionTableDataSql() (sql string) {
sql += "(36, 12, 'DELETE', '/api/menu_action/*', 'init'),"
sql += "(37, 22, 'POST', '/upgrade/execute', 'init'),"
sql += "(38, 11, 'PATCH', '/api/admin/offline', 'init'),"
sql += "(39, 12, 'PATCH', '/api/menu/sort', 'init');"
sql += "(39, 12, 'PATCH', '/api/menu/sort', 'init'),"
sql += "(40, 24, 'GET', '/cron/add', 'init'),"
sql += "(41, 24, 'GET', '/cron/edit/*', 'init'),"
sql += "(42, 24, 'POST', '/api/cron', 'init'),"
sql += "(43, 24, 'POST', '/api/cron/*', 'init'),"
sql += "(44, 24, 'GET', '/api/cron', 'init'),"
sql += "(45, 24, 'GET', '/api/cron/*', 'init'),"
sql += "(46, 24, 'PATCH', '/api/cron/used', 'init');"
return
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/web/controller/install_handler/mysql_table"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/xinliangnote/go-gin-api/pkg/errors"
)
type upgradeExecuteRequest struct {
@@ -15,10 +16,47 @@ type upgradeExecuteRequest struct {
}
func (h *handler) UpgradeExecute() core.HandlerFunc {
return func(c core.Context) {
upgradeTableList := map[string]map[string]string{
"authorized": {
"table_sql": mysql_table.CreateAuthorizedTableSql(),
"table_data_sql": mysql_table.CreateAuthorizedTableDataSql(),
},
"authorized_api": {
"table_sql": mysql_table.CreateAuthorizedAPITableSql(),
"table_data_sql": mysql_table.CreateAuthorizedAPITableDataSql(),
},
"admin": {
"table_sql": mysql_table.CreateAdminTableSql(),
"table_data_sql": mysql_table.CreateAdminTableDataSql(),
},
"admin_menu": {
"table_sql": mysql_table.CreateAdminMenuTableSql(),
"table_data_sql": mysql_table.CreateAdminMenuTableDataSql(),
},
"menu": {
"table_sql": mysql_table.CreateMenuTableSql(),
"table_data_sql": mysql_table.CreateMenuTableDataSql(),
},
"menu_action": {
"table_sql": mysql_table.CreateMenuActionTableSql(),
"table_data_sql": mysql_table.CreateMenuActionTableDataSql(),
},
"cron_task": {
"table_sql": mysql_table.CreateCronTaskTableSql(),
"table_data_sql": "",
},
}
upgradeTableOp := map[string]bool{
"table": true,
"table_data": true,
}
return func(ctx core.Context) {
req := new(upgradeExecuteRequest)
if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError(
if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
@@ -29,150 +67,48 @@ func (h *handler) UpgradeExecute() core.HandlerFunc {
outPutString := ""
db := h.db.GetDbW()
if req.TableName == "authorized" && req.Op == "table" {
if err := db.Exec(mysql_table.CreateAuthorizedTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
if upgradeTableList[req.TableName] == nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)).WithErr(errors.New("数据表不存在")),
)
return
}
if !upgradeTableOp[req.Op] {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)).WithErr(errors.New("非法操作")),
)
return
}
if req.Op == "table" {
if err := db.Exec(upgradeTableList[req.TableName]["table_sql"]).Error; err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表authorized 成功。"
}
if req.TableName == "authorized" && req.Op == "table_data" {
if err := db.Exec(mysql_table.CreateAuthorizedTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
outPutString = "初始化 MySQL 数据表:" + req.TableName + " 成功。"
} else if req.Op == "table_data" {
if err := db.Exec(upgradeTableList[req.TableName]["table_data_sql"]).Error; err != nil {
ctx.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表authorized 默认数据成功。"
outPutString = "初始化 MySQL 数据表:" + req.TableName + " 默认数据成功。"
}
if req.TableName == "authorized_api" && req.Op == "table" {
if err := db.Exec(mysql_table.CreateAuthorizedAPITableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表authorized_api 成功。"
}
if req.TableName == "authorized_api" && req.Op == "table_data" {
if err := db.Exec(mysql_table.CreateAuthorizedAPITableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表authorized_api 默认数据成功。"
}
if req.TableName == "admin" && req.Op == "table" {
if err := db.Exec(mysql_table.CreateAdminTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表admin 成功。"
}
if req.TableName == "admin" && req.Op == "table_data" {
if err := db.Exec(mysql_table.CreateAdminTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表admin 默认数据成功。"
}
if req.TableName == "menu" && req.Op == "table" {
if err := db.Exec(mysql_table.CreateMenuTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表menu 成功。"
}
if req.TableName == "menu" && req.Op == "table_data" {
if err := db.Exec(mysql_table.CreateMenuTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表menu 默认数据成功。"
}
if req.TableName == "menu_action" && req.Op == "table" {
if err := db.Exec(mysql_table.CreateMenuActionTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表menu_action 成功。"
}
if req.TableName == "menu_action" && req.Op == "table_data" {
if err := db.Exec(mysql_table.CreateMenuActionTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表menu_action 默认数据成功。"
}
if req.TableName == "admin_menu" && req.Op == "table" {
if err := db.Exec(mysql_table.CreateAdminMenuTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表admin_menu 成功。"
}
if req.TableName == "admin_menu" && req.Op == "table_data" {
if err := db.Exec(mysql_table.CreateAdminMenuTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err),
)
return
}
outPutString = "初始化 MySQL 数据表admin_menu 默认数据成功。"
}
c.Payload(outPutString)
ctx.Payload(outPutString)
}
}