mirror of
https://github.com/xinliangnote/go-gin-api.git
synced 2024-04-21 12:31:46 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e918fce59 | ||
|
|
36ef904211 | ||
|
|
49ccaf9bec | ||
|
|
4381a9a6f6 | ||
|
|
5e5db8917d | ||
|
|
f7b3dbae5e | ||
|
|
5d7975056c | ||
|
|
499e171d63 | ||
|
|
8d6cd2174e | ||
|
|
3823eee2ca | ||
|
|
0b6b9dd69f | ||
|
|
7327275b7a | ||
|
|
9b5028e9de | ||
|
|
cc8012b09d | ||
|
|
334bfbb4aa |
@@ -18,9 +18,11 @@
|
||||
1. 支持 errno 统一定义错误码
|
||||
1. 支持 [zap](https://go.uber.org/zap) 日志收集
|
||||
1. 支持 [viper](https://github.com/spf13/viper) 配置文件解析
|
||||
1. 支持 [grom](https://gorm.io/gorm) 数据库组件
|
||||
1. 支持 [gorm](https://gorm.io/gorm) 数据库组件
|
||||
1. 支持 [go-redis](https://github.com/go-redis/redis/v7) 组件
|
||||
|
||||
1. 支持 RESTful API 返回值规范
|
||||
1. 支持 gormgen、handlergen 代码生成工具
|
||||
1. 支持 web 界面,使用的 [Light Year Admin 模板](https://gitee.com/yinqi/Light-Year-Admin-Using-Iframe)
|
||||
|
||||
|
||||
## 文档索引
|
||||
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
+6
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+4303
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,114 @@
|
||||
.mt-wrapper{
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.mt-nav-bar {
|
||||
width : 100%;
|
||||
z-index: 200;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
}
|
||||
.mt-nav-bar .mt-nav{
|
||||
background-color: #fff;
|
||||
}
|
||||
.mt-nav-panel{
|
||||
overflow: hidden;
|
||||
}
|
||||
.mt-nav-panel ul{
|
||||
width: 10000px;
|
||||
}
|
||||
.mt-nav-panel ul li {
|
||||
position: relative;
|
||||
}
|
||||
.mt-tab-content{
|
||||
height: 100%;
|
||||
}
|
||||
.mt-close-tab {
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
top: 18px;
|
||||
right: 10px;
|
||||
color: #c2c2c2;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
vertical-align: 2px;
|
||||
-webkit-border-radius: 50%;
|
||||
-moz-border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
-webkit-transition: all .3s cubic-bezier(.645,.045,.355,1);
|
||||
transition: all .3s cubic-bezier(.645,.045,.355,1);
|
||||
-webkit-transform-origin: 100% 50%;
|
||||
transform-origin: 100% 50%;
|
||||
}
|
||||
.mt-close-tab:before {
|
||||
-webkit-transform: scale(.8);
|
||||
transform: scale(.8);
|
||||
display: inline-block;
|
||||
/*vertical-align: -1px;*/
|
||||
}
|
||||
li:hover .mt-close-tab {
|
||||
display: inline;
|
||||
}
|
||||
.mt-hidden-list .mt-close-tab {
|
||||
display: none !important;
|
||||
}
|
||||
.mt-nav-bar a {
|
||||
cursor: pointer !important;
|
||||
max-height: 48px;
|
||||
padding-top: 14px;
|
||||
padding-bottom: 13px;
|
||||
}
|
||||
@media (max-width: 767px){
|
||||
.mt-tab-content{
|
||||
/*padding-top: 0 !important;*/
|
||||
}
|
||||
}
|
||||
.mt-tab-content{
|
||||
-webkit-overflow-scrolling:touch;
|
||||
overflow:auto;
|
||||
}
|
||||
.mt-dragging-tab{
|
||||
left: auto;
|
||||
position: absolute !important;
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
.mt-dragging-tab > a{
|
||||
background: #FBFDFD !important;
|
||||
}
|
||||
|
||||
/*新增*/
|
||||
.mt-nav-bar .mt-nav .nav-tabs {
|
||||
margin-bottom: 0px;
|
||||
border-color: #eceeef;
|
||||
}
|
||||
.mt-close-tab:hover {
|
||||
color: #f96868;
|
||||
}
|
||||
.mt-dropdown .caret {
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
}
|
||||
.mt-dropdown .dropdown-menu {
|
||||
margin-top: 0px;
|
||||
}
|
||||
#contextify-menu {
|
||||
min-width: 80px!important;
|
||||
}
|
||||
.mt-close-tab:hover {
|
||||
background-color: #f96868;
|
||||
color: #fff;
|
||||
}
|
||||
.mt-nav .nav-tabs a:not([data-type="main"]) {
|
||||
padding-right: 40px;
|
||||
}
|
||||
.mt-nav-tools-left li a,
|
||||
.mt-nav-tools-right li a {
|
||||
padding-right: 15px!important;
|
||||
}
|
||||
.mt-nav-bar .mt-nav .nav-tabs .mt-dragging {
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
Vendored
+6
File diff suppressed because one or more lines are too long
Vendored
+134
@@ -0,0 +1,134 @@
|
||||
;jQuery( function() {
|
||||
// 停止
|
||||
$("body").on('click','[data-stopPropagation]',function (e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
// 滚动条
|
||||
if($('.lyear-scroll')[0]) {
|
||||
$('.lyear-scroll').each(function(){
|
||||
new PerfectScrollbar(this, {
|
||||
swipeEasing: false,
|
||||
suppressScrollX: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 侧边栏
|
||||
$(document).on('click', '.lyear-aside-toggler', function() {
|
||||
$('.lyear-layout-sidebar').toggleClass('lyear-aside-open');
|
||||
$("body").toggleClass('lyear-layout-sidebar-close');
|
||||
|
||||
if ($('.lyear-mask-modal').length == 0) {
|
||||
$('<div class="lyear-mask-modal"></div>').prependTo('body');
|
||||
} else {
|
||||
$( '.lyear-mask-modal' ).remove();
|
||||
}
|
||||
});
|
||||
|
||||
// 遮罩层
|
||||
$(document).on('click', '.lyear-mask-modal', function() {
|
||||
$( this ).remove();
|
||||
$('.lyear-layout-sidebar').toggleClass('lyear-aside-open');
|
||||
$('body').toggleClass('lyear-layout-sidebar-close');
|
||||
});
|
||||
|
||||
// 侧边栏导航
|
||||
$(document).on('click', '.nav-item-has-subnav > a', function() {
|
||||
$subnavToggle = jQuery( this );
|
||||
$navHasSubnav = $subnavToggle.parent();
|
||||
$topHasSubNav = $subnavToggle.parents('.nav-item-has-subnav').last();
|
||||
$subnav = $navHasSubnav.find('.nav-subnav').first();
|
||||
$viSubHeight = $navHasSubnav.siblings().find('.nav-subnav:visible').outerHeight();
|
||||
$scrollBox = $('.lyear-layout-sidebar-info');
|
||||
$navHasSubnav.siblings().find('.nav-subnav:visible').slideUp(500).parent().removeClass('open');
|
||||
$subnav.slideToggle( 300, function() {
|
||||
$navHasSubnav.toggleClass( 'open' );
|
||||
|
||||
// 新增滚动条处理
|
||||
var scrollHeight = 0;
|
||||
pervTotal = $topHasSubNav.prevAll().length,
|
||||
boxHeight = $scrollBox.outerHeight(),
|
||||
innerHeight = $('.sidebar-main').outerHeight(),
|
||||
thisScroll = $scrollBox.scrollTop(),
|
||||
thisSubHeight = $(this).outerHeight(),
|
||||
footHeight = 121;
|
||||
|
||||
if (footHeight + innerHeight - boxHeight >= (pervTotal * 48)) {
|
||||
scrollHeight = pervTotal * 48;
|
||||
}
|
||||
if ($subnavToggle.parents('.nav-item-has-subnav').length == 1) {
|
||||
$scrollBox.animate({scrollTop: scrollHeight}, 300);
|
||||
} else {
|
||||
// 子菜单操作
|
||||
if (typeof($viSubHeight) != 'undefined' && $viSubHeight != null) {
|
||||
scrollHeight = thisScroll + thisSubHeight - $viSubHeight;
|
||||
$scrollBox.animate({scrollTop: scrollHeight}, 300);
|
||||
} else {
|
||||
if ((thisScroll + boxHeight - $scrollBox[0].scrollHeight) == 0) {
|
||||
scrollHeight = thisScroll - thisSubHeight;
|
||||
$scrollBox.animate({scrollTop: scrollHeight}, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 读取cookie中的主题设置
|
||||
var the_logo_bg = $.cookie('the_logo_bg'),
|
||||
the_header_bg = $.cookie('the_header_bg'),
|
||||
the_sidebar_bg = $.cookie('the_sidebar_bg');
|
||||
|
||||
if (the_logo_bg) $('body').attr('data-logobg', the_logo_bg);
|
||||
if (the_header_bg) $('body').attr('data-headerbg', the_header_bg);
|
||||
if (the_sidebar_bg) $('body').attr('data-sidebarbg', the_sidebar_bg);
|
||||
|
||||
// 处理主题配色下拉选中
|
||||
$(".dropdown-skin :radio").each(function(){
|
||||
var $this = $(this),
|
||||
radioName = $this.attr('name');
|
||||
switch (radioName) {
|
||||
case 'logo_bg':
|
||||
$this.val() == the_logo_bg && $this.prop("checked", true);
|
||||
break;
|
||||
case 'header_bg':
|
||||
$this.val() == the_header_bg && $this.prop("checked", true);
|
||||
break;
|
||||
case 'sidebar_bg':
|
||||
$this.val() == the_sidebar_bg && $this.prop("checked", true);
|
||||
}
|
||||
});
|
||||
|
||||
// 设置主题配色
|
||||
setTheme = function(input_name, data_name) {
|
||||
$("input[name='"+input_name+"']").click(function(){
|
||||
$('body').attr(data_name, $(this).val());
|
||||
$.cookie('the_'+input_name, $(this).val());
|
||||
});
|
||||
}
|
||||
setTheme('sidebar_bg', 'data-sidebarbg');
|
||||
setTheme('logo_bg', 'data-logobg');
|
||||
setTheme('header_bg', 'data-headerbg');
|
||||
|
||||
// 选项卡
|
||||
$('#iframe-content').multitabs({
|
||||
iframe : true,
|
||||
refresh : 'no', // iframe中页面是否刷新,'no':'从不刷新','nav':'点击菜单刷新','all':'菜单和tab点击都刷新'
|
||||
nav: {
|
||||
backgroundColor: '#ffffff',
|
||||
maxTabs : 35, // 选项卡最大值
|
||||
},
|
||||
init : [{
|
||||
type : 'main',
|
||||
title : '仪表盘',
|
||||
url : '/dashboard'
|
||||
}]
|
||||
});
|
||||
|
||||
$(document).on('click', '.nav-item .multitabs', function() {
|
||||
$('.nav-item').removeClass('active');
|
||||
$('.nav-subnav li').removeClass('active');
|
||||
$(this).parent('li').addClass('active');
|
||||
$(this).parents('.nav-item-has-subnav').addClass('open').first().addClass('active');
|
||||
});
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
@@ -0,0 +1,2 @@
|
||||
/*! jquery.cookie v1.4.1 | MIT */
|
||||
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?a(require("jquery")):a(jQuery)}(function(a){function b(a){return h.raw?a:encodeURIComponent(a)}function c(a){return h.raw?a:decodeURIComponent(a)}function d(a){return b(h.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(g," ")),h.json?JSON.parse(a):a}catch(b){}}function f(b,c){var d=h.raw?b:e(b);return a.isFunction(c)?c(d):d}var g=/\+/g,h=a.cookie=function(e,g,i){if(void 0!==g&&!a.isFunction(g)){if(i=a.extend({},h.defaults,i),"number"==typeof i.expires){var j=i.expires,k=i.expires=new Date;k.setTime(+k+864e5*j)}return document.cookie=[b(e),"=",d(g),i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}for(var l=e?void 0:{},m=document.cookie?document.cookie.split("; "):[],n=0,o=m.length;o>n;n++){var p=m[n].split("="),q=c(p.shift()),r=p.join("=");if(e&&e===q){l=f(r,g);break}e||void 0===(r=f(r))||(l[q]=r)}return l};h.defaults={},a.removeCookie=function(b,c){return void 0===a.cookie(b)?!1:(a.cookie(b,"",a.extend({},c,{expires:-1})),!a.cookie(b))}});
|
||||
Vendored
+2
File diff suppressed because one or more lines are too long
Vendored
+141
@@ -0,0 +1,141 @@
|
||||
;jQuery( function() {
|
||||
// 工具提示
|
||||
if($('[data-toggle="tooltip"]')[0]) {
|
||||
$('[data-toggle="tooltip"]').tooltip({
|
||||
"container" : 'body',
|
||||
});
|
||||
}
|
||||
|
||||
// POP弹出框
|
||||
if($('[data-toggle="popover"]')[0]) {
|
||||
$('[data-toggle="popover"]').popover();
|
||||
}
|
||||
|
||||
// 关闭卡片
|
||||
$(document).on('click', '.card-btn-close', function() {
|
||||
$(this).closest('.card').fadeOut(150, function() {
|
||||
if ($(this).parent().children().length == 1) {
|
||||
$(this).parent().remove();
|
||||
} else {
|
||||
$(this).remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 卡片收缩与打开
|
||||
$(document).on('click', '.card-btn-slide', function(){
|
||||
$(this).toggleClass('rotate-180').closest('.card').find('.card-body').slideToggle();
|
||||
});
|
||||
|
||||
/**
|
||||
* 如果页面中需要用到滚动条,请先导入perfect-scrollbar.min.js
|
||||
*/
|
||||
if($('.lyear-scroll')[0]) {
|
||||
$('.lyear-scroll').each(function(){
|
||||
new PerfectScrollbar(this, {
|
||||
swipeEasing: false,
|
||||
suppressScrollX: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 颜色选取
|
||||
jQuery('.js-colorpicker').each(function() {
|
||||
var $colorpicker = jQuery(this);
|
||||
var $colorpickerMode = $colorpicker.data('colorpicker-mode') ? $colorpicker.data('colorpicker-mode') : 'auto';
|
||||
$colorpicker.colorpicker({
|
||||
'format': $colorpickerMode,
|
||||
});
|
||||
});
|
||||
|
||||
// 日期选择器
|
||||
jQuery("[data-provide = 'datepicker']").each(function() {
|
||||
var options = {
|
||||
language: 'zh-CN', // 默认简体中文
|
||||
multidateSeparator: ', ' // 默认多个日期用,分隔
|
||||
}
|
||||
|
||||
options = $.extend( options, getDataOptions( $(this) ));
|
||||
|
||||
if ( $(this).prop("tagName") != 'INPUT' ) {
|
||||
options.inputs = [$(this).find('input:first'), $(this).find('input:last')];
|
||||
}
|
||||
|
||||
$(this).datepicker(options);
|
||||
});
|
||||
|
||||
// 时间选择器
|
||||
jQuery("[data-provide = 'clockpicker']").each(function() {
|
||||
$(this).clockpicker({
|
||||
donetext: 'Done'
|
||||
});
|
||||
});
|
||||
|
||||
// 时间日期选择器
|
||||
jQuery("[data-provide = 'datetimepicker']").each(function() {
|
||||
var options = {
|
||||
locale: moment.locale(),
|
||||
}
|
||||
|
||||
options = $.extend( options, getDataOptions( $(this) ));
|
||||
|
||||
if ( $(this).prop("tagName") != 'INPUT' ) {
|
||||
options.inputs = [$(this).find('input:first'), $(this).find('input:last')];
|
||||
}
|
||||
console.log(options);
|
||||
$(this).datetimepicker(options);
|
||||
});
|
||||
|
||||
// 标签
|
||||
$('.js-tags-input').each(function() {
|
||||
var $this = $(this);
|
||||
$this.tagsInput({
|
||||
height: $this.data('height') ? $this.data('height') : '36px',
|
||||
width: '100%',
|
||||
defaultText: $this.attr("placeholder"),
|
||||
removeWithBackspace: true,
|
||||
delimiter: [',']
|
||||
});
|
||||
});
|
||||
|
||||
// 复选框全选
|
||||
$("#check-all").change(function () {
|
||||
if ($boxname = $(this).data('name')) {
|
||||
$(this).closest('table').find("input[name='" + $boxname + "']").prop('checked', $(this).prop("checked"));
|
||||
} else {
|
||||
$(this).closest('table').find(".custom-checkbox input[type='checkbox']").prop('checked', $(this).prop("checked"));
|
||||
}
|
||||
});
|
||||
|
||||
// iframe打开tab
|
||||
$(document).on('click', '.js-create-tab', function(){
|
||||
parent.$(parent.document).data('multitabs').create({
|
||||
iframe : true,
|
||||
title : $(this).data('title') ? $(this).data('title') : '标题',
|
||||
url : $(this).data('url') ? $(this).data('url') : '/dashboard'
|
||||
}, true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
// 参考国外模板的写法,获取当前的配置,以data-*(*指插件原有的配置名)
|
||||
getDataOptions = function(el, castList) {
|
||||
var options = {};
|
||||
|
||||
$.each( $(el).data(), function(key, value){
|
||||
|
||||
key = dataToOption(key);
|
||||
|
||||
if ( key == 'provide' ) {
|
||||
return;
|
||||
}
|
||||
options[key] = value;
|
||||
});
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
dataToOption = function(name) {
|
||||
return name.replace(/-([a-z])/g, function(x){return x[1].toUpperCase();});
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Vendored
+4
File diff suppressed because one or more lines are too long
@@ -0,0 +1,45 @@
|
||||
<!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/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">
|
||||
<header class="card-header"><div class="card-title"> 配置信息</div></header>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">MySQL</h5>
|
||||
<p>主库信息:{{.MySQL.Write.Addr}},账号:{{.MySQL.Write.User}},数据库:{{.MySQL.Write.Name}}</p>
|
||||
<p>从库信息:{{.MySQL.Read.Addr}},账号:{{.MySQL.Read.User}},数据库:{{.MySQL.Read.Name}}</p>
|
||||
<p>最大连接数:{{ .MySQL.Base.MaxOpenConn }}</p>
|
||||
<p>空闲连接数:{{ .MySQL.Base.MaxIdleConn }}</p>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Redis</h5>
|
||||
<p>地址:{{ .Redis.Addr }} </p>
|
||||
<p>最大连接数:{{ .Redis.PoolSize }} </p>
|
||||
<p>空闲连接数:{{ .Redis.MinIdleConns }} </p>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Mail</h5>
|
||||
<p>邮箱服务器:{{ .Mail.Host }} </p>
|
||||
<p>发件人邮箱地址:{{ .Mail.User }} </p>
|
||||
<p>收件人邮箱地址:{{ .Mail.To }} </p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,89 @@
|
||||
<!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/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-secondary">
|
||||
<header class="card-header">
|
||||
<div class="card-title">内存信息</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p>总量:{{ .MemTotal }}</p>
|
||||
<p>已使用:{{ .MemUsed }}</p>
|
||||
<p>使用率:<span class="badge
|
||||
{{if gt .MemUsedPercent 95.0}} badge-danger
|
||||
{{else if gt .MemUsedPercent 90.0 }} badge-warning
|
||||
{{else}} badge-success
|
||||
{{end}} ">{{ .MemUsedPercent }}%</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-secondary">
|
||||
<header class="card-header">
|
||||
<div class="card-title">硬盘信息</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p>总量:{{ .DiskTotal }}</p>
|
||||
<p>已使用:{{ .DiskUsed }}</p>
|
||||
<p>使用率:<span class="badge
|
||||
{{if gt .DiskUsedPercent 95.0}} badge-danger
|
||||
{{else if gt .DiskUsedPercent 90.0 }} badge-warning
|
||||
{{else}} badge-success
|
||||
{{end}}">{{ .DiskUsedPercent }}%</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-secondary">
|
||||
<header class="card-header">
|
||||
<div class="card-title">CPU 信息</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p>CPU:{{ .CpuName }}</p>
|
||||
<p>核数:{{ .CpuCores }}</p>
|
||||
<p>CPU 使用率:<span class="badge
|
||||
{{if gt .CpuUsedPercent 95.0}} badge-danger
|
||||
{{else if gt .CpuUsedPercent 90.0 }} badge-warning
|
||||
{{else}} badge-success
|
||||
{{end}}">{{ .CpuUsedPercent }}%</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-secondary">
|
||||
<header class="card-header">
|
||||
<div class="card-title">项目信息</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p>操作系统:{{ .GoOS }} <span class="badge badge-brown"> {{ .GoArch }} </span></p>
|
||||
<p>项目地址:{{ .ProjectPath }}</p>
|
||||
<p>项目域名:{{ .Host }} <span class="badge badge-secondary"> {{ .Env }} </span></p>
|
||||
<p>GOPATH:{{ .GoPath }}</p>
|
||||
<p>Version:{{ .GoVersion }}</p>
|
||||
<p>Goroutine:{{ .Goroutine }}</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,90 @@
|
||||
<!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/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-6">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title"> <code>gormgen</code> 代码生成工具</div></header>
|
||||
<div class="card-body">
|
||||
<div class="callout callout-warning mb-3">注意:请在 <code>Mac</code> 或 <code>Linux</code> 环境执行。</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tableSelect">选择数据表,可进行多选:</label>
|
||||
<select multiple="" class="form-control" id="tableSelect" style="height: 260px;">
|
||||
{{range .}}
|
||||
<option value="{{ .Name }}">{{ .Name }} -- 备注:{{ .Comment }}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<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>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title">执行结果</div></header>
|
||||
<div class="card-body">
|
||||
<pre id="resultDiv"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("#btnOk").click(function(){
|
||||
var tables = [];
|
||||
var options = $("#tableSelect").find("option:selected");
|
||||
|
||||
if (options.length < 1) {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请选择数据表。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
$("#resultDiv").text("");
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
tables.push(options.eq(i).val()); // 将所有的值赋给数组
|
||||
}
|
||||
|
||||
$.post("/gormgen_exec",{tables:tables.join(',')},function (data) {
|
||||
$("#resultDiv").text(data);
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,80 @@
|
||||
<!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/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-6">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title"> <code>handlergen</code> 代码生成工具</div></header>
|
||||
<div class="card-body">
|
||||
<div class="callout callout-warning mb-3">注意:请在 <code>Mac</code> 或 <code>Linux</code> 环境执行。</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleFormControlInput1">handler 名称,例如:<code>user_handler</code></label>
|
||||
<input type="text" class="form-control" id="handlerName" placeholder="请输入 handler 名称">
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<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>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title">执行结果</div></header>
|
||||
<div class="card-body">
|
||||
<pre id="resultDiv"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("#btnOk").click(function(){
|
||||
var handlerName = $("#handlerName").val();
|
||||
if (!handlerName) {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入 handler 名称。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
$("#resultDiv").text("");
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
|
||||
$.post("/handlergen_exec",{name:handlerName},function (data) {
|
||||
$("#resultDiv").text(data);
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,65 @@
|
||||
<!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/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title">初始化</div></header>
|
||||
<div class="card-body">
|
||||
<div class="callout callout-warning mb-3">注意:请在 <code>Mac</code> 或 <code>Linux</code> 环境执行。</div>
|
||||
<p>初始化项目所需信息:</p>
|
||||
|
||||
<p><i class="mdi mdi-checkbox-marked-circle"></i> <span>MySQL 数据表:<code>user_demo</code> ;</span></p>
|
||||
|
||||
<p>
|
||||
<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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title">执行结果</div></header>
|
||||
<div class="card-body">
|
||||
<pre id="resultDiv"></pre>
|
||||
</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">
|
||||
$(document).ready(function() {
|
||||
$("#btnOk").click(function(){
|
||||
|
||||
$("#resultDiv").text("");
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
|
||||
$.post("/init_exec","",function (data) {
|
||||
$("#resultDiv").text(data);
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,211 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
|
||||
<title>管理面板</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="bootstrap/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/js/bootstrap-multitabs/multitabs.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/css/animate.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/css/style.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="lyear-layout-web">
|
||||
<div class="lyear-layout-container">
|
||||
<!--左侧导航-->
|
||||
<aside class="lyear-layout-sidebar">
|
||||
|
||||
<!-- logo -->
|
||||
<div id="logo" class="sidebar-header">
|
||||
<a href="/">
|
||||
<img src="bootstrap/images/logo-sidebar.png"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="lyear-layout-sidebar-info lyear-scroll">
|
||||
|
||||
<nav class="sidebar-main">
|
||||
<ul class="nav-drawer">
|
||||
<li class="nav-item active"> <a class="multitabs" href="/dashboard"><i class="mdi mdi-home"></i> <span>仪表盘</span></a> </li>
|
||||
<li class="nav-item"> <a class="multitabs" href="/configinfo"><i class="mdi mdi-settings-box"></i> <span>配置信息</span></a> </li>
|
||||
|
||||
<li class="nav-item nav-item-has-subnav">
|
||||
<a href="javascript:void(0)"><i class="mdi mdi-code-not-equal-variant"></i> <span>代码生成</span></a>
|
||||
<ul class="nav nav-subnav">
|
||||
<li> <a class="multitabs" href="/init">初始化</a> </li>
|
||||
<li> <a class="multitabs" href="/gormgen">gormgen</a> </li>
|
||||
<li> <a class="multitabs" href="/handlergen">handlergen</a> </li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item"> <a href="/swagger/index.html" target="_blank" ><i class="mdi mdi-file-document-box"></i> <span>接口文档</span></a> </li>
|
||||
<li class="nav-item"> <a href="/graphql" target="_blank" ><i class="mdi mdi-file-document-box-search"></i> <span>GraphQL</span></a> </li>
|
||||
<li class="nav-item"> <a href="/metrics" target="_blank" ><i class="mdi mdi-speedometer"></i> <span>接口指标</span></a> </li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
<!--End 左侧导航-->
|
||||
|
||||
<!--头部信息-->
|
||||
<header class="lyear-layout-header">
|
||||
|
||||
<nav class="navbar">
|
||||
|
||||
<div class="navbar-left">
|
||||
<div class="lyear-aside-toggler">
|
||||
<span class="lyear-toggler-bar"></span>
|
||||
<span class="lyear-toggler-bar"></span>
|
||||
<span class="lyear-toggler-bar"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="navbar-right d-flex align-items-center">
|
||||
|
||||
<!--切换主题配色-->
|
||||
<li class="dropdown dropdown-skin">
|
||||
<span data-toggle="dropdown" class="icon-item"><i class="mdi mdi-palette"></i></span>
|
||||
<ul class="dropdown-menu dropdown-menu-right" data-stopPropagation="true">
|
||||
<li class="drop-title"><p>LOGO</p></li>
|
||||
<li class="drop-skin-li clearfix">
|
||||
<span class="inverse">
|
||||
<input type="radio" name="logo_bg" value="default" id="logo_bg_1" checked>
|
||||
<label for="logo_bg_1"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_2" id="logo_bg_2">
|
||||
<label for="logo_bg_2"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_3" id="logo_bg_3">
|
||||
<label for="logo_bg_3"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_4" id="logo_bg_4">
|
||||
<label for="logo_bg_4"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_5" id="logo_bg_5">
|
||||
<label for="logo_bg_5"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_6" id="logo_bg_6">
|
||||
<label for="logo_bg_6"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_7" id="logo_bg_7">
|
||||
<label for="logo_bg_7"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_8" id="logo_bg_8">
|
||||
<label for="logo_bg_8"></label>
|
||||
</span>
|
||||
</li>
|
||||
<li class="drop-title"><p>头部</p></li>
|
||||
<li class="drop-skin-li clearfix">
|
||||
<span class="inverse">
|
||||
<input type="radio" name="header_bg" value="default" id="header_bg_1" checked>
|
||||
<label for="header_bg_1"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_2" id="header_bg_2">
|
||||
<label for="header_bg_2"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_3" id="header_bg_3">
|
||||
<label for="header_bg_3"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_4" id="header_bg_4">
|
||||
<label for="header_bg_4"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_5" id="header_bg_5">
|
||||
<label for="header_bg_5"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_6" id="header_bg_6">
|
||||
<label for="header_bg_6"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_7" id="header_bg_7">
|
||||
<label for="header_bg_7"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_8" id="header_bg_8">
|
||||
<label for="header_bg_8"></label>
|
||||
</span>
|
||||
</li>
|
||||
<li class="drop-title"><p>侧边栏</p></li>
|
||||
<li class="drop-skin-li clearfix">
|
||||
<span class="inverse">
|
||||
<input type="radio" name="sidebar_bg" value="default" id="sidebar_bg_1" checked>
|
||||
<label for="sidebar_bg_1"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_2" id="sidebar_bg_2">
|
||||
<label for="sidebar_bg_2"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_3" id="sidebar_bg_3">
|
||||
<label for="sidebar_bg_3"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_4" id="sidebar_bg_4">
|
||||
<label for="sidebar_bg_4"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_5" id="sidebar_bg_5">
|
||||
<label for="sidebar_bg_5"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_6" id="sidebar_bg_6">
|
||||
<label for="sidebar_bg_6"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_7" id="sidebar_bg_7">
|
||||
<label for="sidebar_bg_7"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_8" id="sidebar_bg_8">
|
||||
<label for="sidebar_bg_8"></label>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!--切换主题配色-->
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
<!--End 头部信息-->
|
||||
|
||||
<!--页面主要内容-->
|
||||
<main class="lyear-layout-content">
|
||||
|
||||
<div id="iframe-content"></div>
|
||||
|
||||
</main>
|
||||
<!--End 页面主要内容-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/perfect-scrollbar.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/jquery.cookie.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/index.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
## 执行命令
|
||||
在根目录下执行脚本:`./scripts/gormgen.sh addr user pass name tables`;
|
||||
- addr:数据库地址,例如:127.0.0.1:3306
|
||||
- user:账号,例如:root
|
||||
- pass:密码,例如:root
|
||||
- name:数据库名称,例如:go_gin_api
|
||||
- tables:表名,默认为 *,多个表名可用“,”分割,例如:user_demo
|
||||
|
||||
例如:
|
||||
```
|
||||
./scripts/gormgen.sh 127.0.0.1:3306 root root go_gin_api user_demo
|
||||
```
|
||||
|
||||
## 参考
|
||||
- https://github.com/MohamedBassem/gormgen
|
||||
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/cmd/gormgen/pkg"
|
||||
)
|
||||
|
||||
var (
|
||||
input string
|
||||
structs []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flagStructs := flag.String("structs", "", "[Required] The name of schema structs to generate structs for, comma seperated\n")
|
||||
flagInput := flag.String("input", "", "[Required] The name of the input file dir\n")
|
||||
flag.Parse()
|
||||
|
||||
if *flagStructs == "" || *flagInput == "" {
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
structs = strings.Split(*flagStructs, ",")
|
||||
input = *flagInput
|
||||
}
|
||||
|
||||
func main() {
|
||||
gen := pkg.NewGenerator(input)
|
||||
p := pkg.NewParser(input)
|
||||
if err := gen.ParserAST(p, structs).Generate().Format().Flush(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
// fieldConfig
|
||||
type fieldConfig struct {
|
||||
FieldName string
|
||||
ColumnName string
|
||||
FieldType string
|
||||
HumpName string
|
||||
}
|
||||
|
||||
// structConfig
|
||||
type structConfig struct {
|
||||
config
|
||||
StructName string
|
||||
OnlyFields []fieldConfig
|
||||
OptionFields []fieldConfig
|
||||
}
|
||||
|
||||
type ImportPkg struct {
|
||||
Pkg string
|
||||
}
|
||||
|
||||
type structHelpers struct {
|
||||
Titelize func(string) string
|
||||
}
|
||||
|
||||
type config struct {
|
||||
PkgName string
|
||||
Helpers structHelpers
|
||||
QueryBuilderName string
|
||||
}
|
||||
|
||||
// The Generator is the one responsible for generating the code, adding the imports, formating, and writing it to the file.
|
||||
type Generator struct {
|
||||
buf map[string]*bytes.Buffer
|
||||
inputFile string
|
||||
config config
|
||||
structConfigs []structConfig
|
||||
}
|
||||
|
||||
// NewGenerator function creates an instance of the generator given the name of the output file as an argument.
|
||||
func NewGenerator(outputFile string) *Generator {
|
||||
return &Generator{
|
||||
buf: map[string]*bytes.Buffer{},
|
||||
inputFile: outputFile,
|
||||
}
|
||||
}
|
||||
|
||||
// ParserAST parse by go file
|
||||
func (g *Generator) ParserAST(p *Parser, structs []string) (ret *Generator) {
|
||||
for _, v := range structs {
|
||||
g.buf[gorm.ToDBName(v)] = new(bytes.Buffer)
|
||||
}
|
||||
g.structConfigs = p.Parse()
|
||||
g.config.PkgName = p.pkg.Name
|
||||
g.config.Helpers = structHelpers{
|
||||
Titelize: strings.Title,
|
||||
}
|
||||
g.config.QueryBuilderName = SQLColumnToHumpStyle(p.pkg.Name) + "QueryBuilder"
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *Generator) checkConfig() (err error) {
|
||||
if len(g.config.PkgName) == 0 {
|
||||
err = errors.New("package name dose'n set")
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(g.structConfigs); i++ {
|
||||
g.structConfigs[i].config = g.config
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Generate executes the template and store it in an internal buffer.
|
||||
func (g *Generator) Generate() *Generator {
|
||||
if err := g.checkConfig(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, v := range g.structConfigs {
|
||||
if _, ok := g.buf[gorm.ToDBName(v.StructName)]; !ok {
|
||||
continue
|
||||
}
|
||||
if err := outputTemplate.Execute(g.buf[gorm.ToDBName(v.StructName)], v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
// Format function formats the output of the generation.
|
||||
func (g *Generator) Format() *Generator {
|
||||
for k := range g.buf {
|
||||
formattedOutput, err := format.Source(g.buf[k].Bytes())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.buf[k] = bytes.NewBuffer(formattedOutput)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
// Flush function writes the output to the output file.
|
||||
func (g *Generator) Flush() error {
|
||||
for k := range g.buf {
|
||||
filename := g.inputFile + "/gen_" + strings.ToLower(k) + ".go"
|
||||
if err := ioutil.WriteFile(filename, g.buf[k].Bytes(), 0777); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Println(" └── file : ", fmt.Sprintf("%s_repo/gen_%s.go", strings.ToLower(k), strings.ToLower(k)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
// The Parser is used to parse a directory and expose information about the structs defined in the files of this directory.
|
||||
type Parser struct {
|
||||
dir string
|
||||
pkg *build.Package
|
||||
parsedFiles []*ast.File
|
||||
}
|
||||
|
||||
// NewParser create a new parser instance.
|
||||
func NewParser(dir string) *Parser {
|
||||
return &Parser{
|
||||
dir: dir,
|
||||
}
|
||||
}
|
||||
|
||||
// getPackage parse dir get go file and package
|
||||
func (p *Parser) getPackage() {
|
||||
pkg, err := build.Default.ImportDir(p.dir, build.ImportComment)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot process directory %s: %s", p.dir, err)
|
||||
}
|
||||
p.pkg = pkg
|
||||
|
||||
}
|
||||
|
||||
// parseGoFiles parse go file
|
||||
func (p *Parser) parseGoFiles() {
|
||||
var parsedFiles []*ast.File
|
||||
fs := token.NewFileSet()
|
||||
for _, file := range p.pkg.GoFiles {
|
||||
file = p.dir + "/" + file
|
||||
parsedFile, err := parser.ParseFile(fs, file, nil, 0)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing package: %s: %s\n", file, err)
|
||||
}
|
||||
parsedFiles = append(parsedFiles, parsedFile)
|
||||
}
|
||||
p.parsedFiles = parsedFiles
|
||||
}
|
||||
|
||||
// parseTypes parse type of struct
|
||||
func (p *Parser) parseTypes(file *ast.File) (ret []structConfig) {
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
decl, ok := n.(*ast.GenDecl)
|
||||
if !ok || decl.Tok != token.TYPE {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, spec := range decl.Specs {
|
||||
var (
|
||||
data structConfig
|
||||
)
|
||||
typeSpec, _ok := spec.(*ast.TypeSpec)
|
||||
if !_ok {
|
||||
continue
|
||||
}
|
||||
// We only care about struct declaration (for now)
|
||||
var structType *ast.StructType
|
||||
if structType, ok = typeSpec.Type.(*ast.StructType); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
data.StructName = typeSpec.Name.Name
|
||||
for _, v := range structType.Fields.List {
|
||||
var (
|
||||
optionField fieldConfig
|
||||
)
|
||||
|
||||
if t, _ok := v.Type.(*ast.Ident); _ok {
|
||||
optionField.FieldType = t.String()
|
||||
} else {
|
||||
if v.Tag != nil {
|
||||
if strings.Contains(v.Tag.Value, "gorm") && strings.Contains(v.Tag.Value, "time") {
|
||||
optionField.FieldType = "time.Time"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(v.Names) > 0 {
|
||||
optionField.FieldName = v.Names[0].String()
|
||||
optionField.ColumnName = gorm.ToDBName(optionField.FieldName)
|
||||
optionField.HumpName = SQLColumnToHumpStyle(optionField.ColumnName)
|
||||
}
|
||||
|
||||
data.OptionFields = append(data.OptionFields, optionField)
|
||||
}
|
||||
|
||||
ret = append(ret, data)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse should be called before any type querying for the parser. It takes the directory to be parsed and extracts all the structs defined in this directory.
|
||||
func (p *Parser) Parse() (ret []structConfig) {
|
||||
var (
|
||||
data []structConfig
|
||||
)
|
||||
p.getPackage()
|
||||
p.parseGoFiles()
|
||||
for _, f := range p.parsedFiles {
|
||||
data = append(data, p.parseTypes(f)...)
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package pkg
|
||||
|
||||
import "text/template"
|
||||
|
||||
// Make sure that the template compiles during package initialization
|
||||
func parseTemplateOrPanic(t string) *template.Template {
|
||||
tpl, err := template.New("output_template").Parse(t)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tpl
|
||||
}
|
||||
|
||||
var outputTemplate = parseTemplateOrPanic(`
|
||||
///////////////////////////////////////////////////////////
|
||||
// THIS FILE IS AUTO GENERATED by gormgen, DON'T EDIT IT //
|
||||
// ANY CHANGES DONE HERE WILL BE LOST //
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
package {{.PkgName}}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func NewModel() *{{.StructName}} {
|
||||
return new({{.StructName}})
|
||||
}
|
||||
|
||||
func NewQueryBuilder() *{{.QueryBuilderName}} {
|
||||
return new({{.QueryBuilderName}})
|
||||
}
|
||||
|
||||
func (t *{{.StructName}}) 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
|
||||
}
|
||||
|
||||
func (t *{{.StructName}}) Delete(db *gorm.DB) (err error) {
|
||||
if err = db.Delete(t).Error; err != nil {
|
||||
return errors.Wrap(err, "delete err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *{{.StructName}}) Updates(db *gorm.DB, m map[string]interface{}) (err error) {
|
||||
if err = db.Model(&{{.StructName}}{}).Where("id = ?", t.Id).Updates(m).Error; err != nil {
|
||||
return errors.Wrap(err, "updates err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type {{.QueryBuilderName}} struct {
|
||||
order []string
|
||||
where []struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}
|
||||
limit int
|
||||
offset int
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) 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 *{{.QueryBuilderName}}) Count(db *gorm.DB) (int64, error) {
|
||||
var c int64
|
||||
res := qb.buildQuery(db).Model(&{{.StructName}}{}).Count(&c)
|
||||
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
|
||||
c = 0
|
||||
}
|
||||
return c, res.Error
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) First(db *gorm.DB) (*{{.StructName}}, error) {
|
||||
ret := &{{.StructName}}{}
|
||||
res := qb.buildQuery(db).First(ret)
|
||||
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
|
||||
ret = nil
|
||||
}
|
||||
return ret, res.Error
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) QueryOne(db *gorm.DB) (*{{.StructName}}, error) {
|
||||
qb.limit = 1
|
||||
ret, err := qb.QueryAll(db)
|
||||
if len(ret) > 0 {
|
||||
return ret[0], err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) QueryAll(db *gorm.DB) ([]*{{.StructName}}, error) {
|
||||
var ret []*{{.StructName}}
|
||||
err := qb.buildQuery(db).Find(&ret).Error
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) Limit(limit int) *{{.QueryBuilderName}} {
|
||||
qb.limit = limit
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *{{.QueryBuilderName}}) Offset(offset int) *{{.QueryBuilderName}} {
|
||||
qb.offset = offset
|
||||
return qb
|
||||
}
|
||||
|
||||
{{$queryBuilderName := .QueryBuilderName}}
|
||||
{{range .OptionFields}}
|
||||
func (qb *{{$queryBuilderName}}) Where{{call $.Helpers.Titelize .FieldName}}(p db_repo.Predicate, value {{.FieldType}}) *{{$queryBuilderName}} {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "{{.ColumnName}}", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *{{$queryBuilderName}}) Where{{call $.Helpers.Titelize .FieldName}}In(value []{{.FieldType}}) *{{$queryBuilderName}} {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "{{.ColumnName}}", "IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *{{$queryBuilderName}}) Where{{call $.Helpers.Titelize .FieldName}}NotIn(value []{{.FieldType}}) *{{$queryBuilderName}} {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "{{.ColumnName}}", "NOT IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *{{$queryBuilderName}}) OrderBy{{call $.Helpers.Titelize .FieldName}}(asc bool) *{{$queryBuilderName}} {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "{{.ColumnName}} " + order)
|
||||
return qb
|
||||
}
|
||||
{{end}}
|
||||
`)
|
||||
@@ -0,0 +1,18 @@
|
||||
package pkg
|
||||
|
||||
import "strings"
|
||||
|
||||
// SQLColumnToHumpStyle sql转换成驼峰模式
|
||||
func SQLColumnToHumpStyle(in string) (ret string) {
|
||||
for i := 0; i < len(in); i++ {
|
||||
if i > 0 && in[i-1] == '_' && in[i] != '_' {
|
||||
s := strings.ToUpper(string(in[i]))
|
||||
ret += s
|
||||
} else if in[i] == '_' {
|
||||
continue
|
||||
} else {
|
||||
ret += string(in[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
## 执行命令
|
||||
|
||||
```$xslt
|
||||
// test_handler 为 ./internal/api/controller/ 中的包名
|
||||
./scripts/handlergen.sh test_handler
|
||||
```
|
||||
|
||||
## 模板文件参考
|
||||
|
||||
```go
|
||||
package test_handler
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ Handler = (*handler)(nil)
|
||||
|
||||
type Handler interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
|
||||
// Create 创建用户
|
||||
// @Tags Test
|
||||
// @Router /test/create [post]
|
||||
Create() core.HandlerFunc
|
||||
|
||||
// Update 编辑用户
|
||||
// @Tags Test
|
||||
// @Router /test/update [post]
|
||||
Update() core.HandlerFunc
|
||||
|
||||
// Delete 删除用户
|
||||
// @Tags Test
|
||||
// @Router /test/delete [post]
|
||||
Delete() core.HandlerFunc
|
||||
|
||||
// Detail 用户详情
|
||||
// @Tags Test
|
||||
// @Router /test/detail [post]
|
||||
Detail() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
userService user_service.UserService
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
userService: user_service.NewUserService(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) i() {}
|
||||
|
||||
```
|
||||
|
||||
以上会生成 4 个文件
|
||||
- func_create.go
|
||||
- func_update.go
|
||||
- func_delete.go
|
||||
- func_detail.go
|
||||
|
||||
## func_create.go 参考
|
||||
|
||||
```go
|
||||
package test_handler
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
type createRequest struct{}
|
||||
|
||||
type createResponse struct{}
|
||||
|
||||
// Create 创建用户
|
||||
// @Summary 创建用户
|
||||
// @Description 创建用户
|
||||
// @Tags Test
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Request body createRequest true "请求信息"
|
||||
// @Success 200 {object} createResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /test/create [post]
|
||||
func (h *handler) Create() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
@@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/dave/dst"
|
||||
"github.com/dave/dst/decorator"
|
||||
)
|
||||
|
||||
var handlerName string
|
||||
|
||||
func init() {
|
||||
handler := flag.String("handler", "", "请输入需要生成的 handler 名称\n")
|
||||
flag.Parse()
|
||||
|
||||
handlerName = strings.ToLower(*handler)
|
||||
}
|
||||
|
||||
func main() {
|
||||
fs := token.NewFileSet()
|
||||
filePath := fmt.Sprintf("./internal/api/controller/%s", handlerName)
|
||||
parsedFile, err := decorator.ParseFile(fs, filePath+"/handler.go", nil, 0)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing package: %s: %s\n", filePath, err)
|
||||
}
|
||||
|
||||
files, _ := ioutil.ReadDir(filePath)
|
||||
if len(files) > 1 {
|
||||
log.Fatalf("请先确保 %s 目录中,有且仅有 handler.go 一个文件。", filePath)
|
||||
}
|
||||
|
||||
dst.Inspect(parsedFile, func(n dst.Node) bool {
|
||||
decl, ok := n.(*dst.GenDecl)
|
||||
if !ok || decl.Tok != token.TYPE {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, spec := range decl.Specs {
|
||||
typeSpec, _ok := spec.(*dst.TypeSpec)
|
||||
if !_ok {
|
||||
continue
|
||||
}
|
||||
|
||||
var interfaceType *dst.InterfaceType
|
||||
if interfaceType, ok = typeSpec.Type.(*dst.InterfaceType); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, v := range interfaceType.Methods.List {
|
||||
if len(v.Names) > 0 {
|
||||
if v.Names[0].String() == "i" {
|
||||
continue
|
||||
}
|
||||
|
||||
filepath := "./internal/api/controller/" + handlerName
|
||||
filename := fmt.Sprintf("%s/func_%s.go", filepath, strings.ToLower(v.Names[0].String()))
|
||||
funcFile, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0766)
|
||||
if err != nil {
|
||||
fmt.Printf("create and open func file error %v\n", err.Error())
|
||||
}
|
||||
fmt.Println(" └── file : ", filename)
|
||||
|
||||
funcContent := fmt.Sprintf("package %s\n\n", handlerName)
|
||||
funcContent += "import (\n"
|
||||
funcContent += `"github.com/xinliangnote/go-gin-api/internal/pkg/core"`
|
||||
funcContent += "\n)\n\n"
|
||||
funcContent += fmt.Sprintf("\n\ntype %sRequest struct {}\n\n", Lcfirst(v.Names[0].String()))
|
||||
funcContent += fmt.Sprintf("type %sResponse struct {}\n\n", Lcfirst(v.Names[0].String()))
|
||||
|
||||
// 首行注释
|
||||
funcContent += fmt.Sprintf("%s\n", v.Decorations().Start.All()[0])
|
||||
|
||||
nameArr := strings.Split(v.Decorations().Start.All()[0], v.Names[0].String())
|
||||
funcContent += fmt.Sprintf("// @Summary%s \n", nameArr[1])
|
||||
funcContent += fmt.Sprintf("// @Description%s \n", nameArr[1])
|
||||
// Tags
|
||||
funcContent += fmt.Sprintf("%s \n", v.Decorations().Start.All()[1])
|
||||
funcContent += fmt.Sprintf("// @Accept json \n")
|
||||
funcContent += fmt.Sprintf("// @Produce json \n")
|
||||
funcContent += fmt.Sprintf("// @Param Request body %sRequest true \"请求信息\" \n", Lcfirst(v.Names[0].String()))
|
||||
funcContent += fmt.Sprintf("// @Success 200 {object} %sResponse \n", Lcfirst(v.Names[0].String()))
|
||||
funcContent += fmt.Sprintf("// @Failure 400 {object} code.Failure \n")
|
||||
// Router
|
||||
funcContent += fmt.Sprintf("%s \n", v.Decorations().Start.All()[2])
|
||||
funcContent += fmt.Sprintf("func (h *handler) %s() core.HandlerFunc { \n return func(c core.Context) {\n\n}}", v.Names[0].String())
|
||||
|
||||
funcFile.WriteString(funcContent)
|
||||
funcFile.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func Lcfirst(str string) string {
|
||||
for i, v := range str {
|
||||
return string(unicode.ToLower(v)) + str[i+1:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
## 执行命令
|
||||
在根目录下执行脚本:`./scripts/init.sh addr user pass name`;
|
||||
- addr:数据库地址,例如:127.0.0.1:3306
|
||||
- user:账号,例如:root
|
||||
- pass:密码,例如:root
|
||||
- name:数据库名称,例如:go_gin_api
|
||||
|
||||
例如:
|
||||
```
|
||||
./scripts/init.sh 127.0.0.1:3306 root root go_gin_api
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/cmd/init/db/mysql"
|
||||
)
|
||||
|
||||
var (
|
||||
dbAddr string
|
||||
dbUser string
|
||||
dbPass string
|
||||
dbName string
|
||||
)
|
||||
|
||||
func init() {
|
||||
addr := flag.String("addr", "", "请输入 db 地址,例如:127.0.0.1:3306\n")
|
||||
user := flag.String("user", "", "请输入 db 用户名\n")
|
||||
pass := flag.String("pass", "", "请输入 db 密码\n")
|
||||
name := flag.String("name", "", "请输入 db 名称\n")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
dbAddr = *addr
|
||||
dbUser = *user
|
||||
dbPass = *pass
|
||||
dbName = strings.ToLower(*name)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 初始化 DB
|
||||
db, err := mysql.New(dbAddr, dbUser, dbPass, dbName)
|
||||
if err != nil {
|
||||
log.Fatal("new db err: ", err.Error())
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := db.DbClose(); err != nil {
|
||||
log.Fatal("db close err: ", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
// 创建 user_demo 表
|
||||
err = db.GetDb().Exec(mysql.CreateUserDemoTableSql()).Error
|
||||
if err != nil {
|
||||
log.Fatal("create user_demo table err: ", err.Error())
|
||||
}
|
||||
|
||||
log.Println("create user_demo table success")
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
var _ Repo = (*dbRepo)(nil)
|
||||
|
||||
type Repo interface {
|
||||
i()
|
||||
GetDb() *gorm.DB
|
||||
DbClose() error
|
||||
}
|
||||
|
||||
type dbRepo struct {
|
||||
DbConn *gorm.DB
|
||||
}
|
||||
|
||||
func New(dbAddr, dbUser, dbPass, dbName string) (Repo, error) {
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=%t&loc=%s",
|
||||
dbUser,
|
||||
dbPass,
|
||||
dbAddr,
|
||||
dbName,
|
||||
true,
|
||||
"Local")
|
||||
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
SingularTable: true,
|
||||
},
|
||||
//Logger: logger.Default.LogMode(logger.Info), // 日志配置
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("[db connection failed] Database name: %s", dbName))
|
||||
}
|
||||
|
||||
db.Set("gorm:table_options", "CHARSET=utf8mb4")
|
||||
|
||||
return &dbRepo{
|
||||
DbConn: db,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *dbRepo) i() {}
|
||||
|
||||
func (d *dbRepo) GetDb() *gorm.DB {
|
||||
return d.DbConn
|
||||
}
|
||||
|
||||
func (d *dbRepo) DbClose() error {
|
||||
sqlDB, err := d.DbConn.DB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sqlDB.Close()
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package mysql
|
||||
|
||||
//CREATE TABLE `user_demo` (
|
||||
//`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
//`user_name` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',
|
||||
//`nick_name` varchar(100) NOT NULL DEFAULT '' COMMENT '昵称',
|
||||
//`mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',
|
||||
//`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',
|
||||
//`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
//`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
//PRIMARY KEY (`id`)
|
||||
//) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户Demo表';
|
||||
|
||||
func CreateUserDemoTableSql() (sql string) {
|
||||
sql = "CREATE TABLE `user_demo` ("
|
||||
sql += "`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',"
|
||||
sql += "`user_name` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',"
|
||||
sql += "`nick_name` varchar(100) NOT NULL DEFAULT '' COMMENT '昵称',"
|
||||
sql += "`mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',"
|
||||
sql += "`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',"
|
||||
sql += "`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',"
|
||||
sql += "`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',"
|
||||
sql += "PRIMARY KEY (`id`)"
|
||||
sql += ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户Demo表';"
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/cmd/mysqlmd/mysql"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type tableInfo struct {
|
||||
Name string `db:"table_name"` // name
|
||||
Comment sql.NullString `db:"table_comment"` // comment
|
||||
}
|
||||
|
||||
type tableColumn struct {
|
||||
OrdinalPosition uint16 `db:"ORDINAL_POSITION"` // position
|
||||
ColumnName string `db:"COLUMN_NAME"` // name
|
||||
ColumnType string `db:"COLUMN_TYPE"` // column_type
|
||||
DataType string `db:"DATA_TYPE"` // data_type
|
||||
ColumnKey sql.NullString `db:"COLUMN_KEY"` // key
|
||||
IsNullable string `db:"IS_NULLABLE"` // nullable
|
||||
Extra sql.NullString `db:"EXTRA"` // extra
|
||||
ColumnComment sql.NullString `db:"COLUMN_COMMENT"` // comment
|
||||
ColumnDefault sql.NullString `db:"COLUMN_DEFAULT"` // default value
|
||||
}
|
||||
|
||||
var (
|
||||
dbAddr string
|
||||
dbUser string
|
||||
dbPass string
|
||||
dbName string
|
||||
genTables string
|
||||
)
|
||||
|
||||
func init() {
|
||||
addr := flag.String("addr", "", "请输入 db 地址,例如:127.0.0.1:3306\n")
|
||||
user := flag.String("user", "", "请输入 db 用户名\n")
|
||||
pass := flag.String("pass", "", "请输入 db 密码\n")
|
||||
name := flag.String("name", "", "请输入 db 名称\n")
|
||||
table := flag.String("tables", "*", "请输入 table 名称,默认为“*”,多个可用“,”分割\n")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
dbAddr = *addr
|
||||
dbUser = *user
|
||||
dbPass = *pass
|
||||
dbName = strings.ToLower(*name)
|
||||
genTables = strings.ToLower(*table)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 初始化 DB
|
||||
db, err := mysql.New(dbAddr, dbUser, dbPass, dbName)
|
||||
if err != nil {
|
||||
log.Fatal("new db err", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := db.DbClose(); err != nil {
|
||||
log.Println("db close err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
tables, err := queryTables(db.GetDb(), dbName, genTables)
|
||||
if err != nil {
|
||||
log.Println("query tables of database err", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
|
||||
filepath := "./internal/api/repository/db_repo/" + table.Name + "_repo"
|
||||
_ = os.Mkdir(filepath, 0766)
|
||||
fmt.Println("create dir : ", filepath)
|
||||
|
||||
mdName := fmt.Sprintf("%s/gen_table.md", filepath)
|
||||
mdFile, err := os.OpenFile(mdName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0766)
|
||||
if err != nil {
|
||||
fmt.Printf("markdown file error %v\n", err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println(" └── file : ", table.Name+"_repo/gen_table.md")
|
||||
|
||||
modelName := fmt.Sprintf("%s/gen_model.go", filepath)
|
||||
modelFile, err := os.OpenFile(modelName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0766)
|
||||
if err != nil {
|
||||
fmt.Printf("create and open model file error %v\n", err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println(" └── file : ", table.Name+"_repo/gen_model.go")
|
||||
|
||||
modelContent := fmt.Sprintf("package %s%s\n", table.Name, "_repo")
|
||||
modelContent += fmt.Sprintf(`import "time"`)
|
||||
modelContent += fmt.Sprintf("\n\n// %s \n", table.Comment.String)
|
||||
modelContent += fmt.Sprintf("//go:generate gormgen -structs %s -input . \n", capitalize(table.Name))
|
||||
modelContent += fmt.Sprintf("type %s struct {\n", capitalize(table.Name))
|
||||
|
||||
tableContent := fmt.Sprintf("#### %s.%s \n", dbName, table.Name)
|
||||
if table.Comment.String != "" {
|
||||
tableContent += table.Comment.String + "\n"
|
||||
}
|
||||
tableContent += "\n" +
|
||||
"| 序号 | 名称 | 描述 | 类型 | 键 | 为空 | 额外 | 默认值 |\n" +
|
||||
"| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: |\n"
|
||||
|
||||
columnInfo, columnInfoErr := queryTableColumn(db.GetDb(), dbName, table.Name)
|
||||
if columnInfoErr != nil {
|
||||
continue
|
||||
}
|
||||
for _, info := range columnInfo {
|
||||
tableContent += fmt.Sprintf(
|
||||
"| %d | %s | %s | %s | %s | %s | %s | %s |\n",
|
||||
info.OrdinalPosition,
|
||||
info.ColumnName,
|
||||
strings.ReplaceAll(strings.ReplaceAll(info.ColumnComment.String, "|", "\\|"), "\n", ""),
|
||||
info.ColumnType,
|
||||
info.ColumnKey.String,
|
||||
info.IsNullable,
|
||||
info.Extra.String,
|
||||
info.ColumnDefault.String,
|
||||
)
|
||||
|
||||
if textType(info.DataType) == "time.Time" {
|
||||
modelContent += fmt.Sprintf("%s %s `%s` // %s\n", capitalize(info.ColumnName), textType(info.DataType), "gorm:\"time\"", info.ColumnComment.String)
|
||||
} else {
|
||||
modelContent += fmt.Sprintf("%s %s // %s\n", capitalize(info.ColumnName), textType(info.DataType), info.ColumnComment.String)
|
||||
}
|
||||
}
|
||||
|
||||
mdFile.WriteString(tableContent)
|
||||
mdFile.Close()
|
||||
|
||||
modelContent += "}\n"
|
||||
modelFile.WriteString(modelContent)
|
||||
modelFile.Close()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func queryTables(db *gorm.DB, dbName string, tableName string) ([]tableInfo, error) {
|
||||
var tableCollect []tableInfo
|
||||
var tableArray []string
|
||||
var commentArray []sql.NullString
|
||||
|
||||
sqlTables := fmt.Sprintf("SELECT `table_name`,`table_comment` FROM `information_schema`.`tables` WHERE `table_schema`= '%s'", dbName)
|
||||
rows, err := db.Raw(sqlTables).Rows()
|
||||
if err != nil {
|
||||
return tableCollect, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var info tableInfo
|
||||
err = rows.Scan(&info.Name, &info.Comment)
|
||||
if err != nil {
|
||||
fmt.Printf("execute query tables action error,had ignored, detail is [%v]\n", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
tableCollect = append(tableCollect, info)
|
||||
tableArray = append(tableArray, info.Name)
|
||||
commentArray = append(commentArray, info.Comment)
|
||||
}
|
||||
|
||||
// filter tables when specified tables params
|
||||
if tableName != "*" {
|
||||
tableCollect = nil
|
||||
chooseTables := strings.Split(tableName, ",")
|
||||
indexMap := make(map[int]int)
|
||||
for _, item := range chooseTables {
|
||||
subIndexMap := getTargetIndexMap(tableArray, item)
|
||||
for k, v := range subIndexMap {
|
||||
if _, ok := indexMap[k]; ok {
|
||||
continue
|
||||
}
|
||||
indexMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if len(indexMap) != 0 {
|
||||
for _, v := range indexMap {
|
||||
var info tableInfo
|
||||
info.Name = tableArray[v]
|
||||
info.Comment = commentArray[v]
|
||||
tableCollect = append(tableCollect, info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tableCollect, err
|
||||
}
|
||||
|
||||
func queryTableColumn(db *gorm.DB, dbName string, tableName string) ([]tableColumn, error) {
|
||||
// 定义承载列信息的切片
|
||||
var columns []tableColumn
|
||||
|
||||
sqlTableColumn := fmt.Sprintf("SELECT `ORDINAL_POSITION`,`COLUMN_NAME`,`COLUMN_TYPE`,`DATA_TYPE`,`COLUMN_KEY`,`IS_NULLABLE`,`EXTRA`,`COLUMN_COMMENT`,`COLUMN_DEFAULT` FROM `information_schema`.`columns` WHERE `table_schema`= '%s' AND `table_name`= '%s' ORDER BY `ORDINAL_POSITION` ASC",
|
||||
dbName, tableName)
|
||||
|
||||
rows, err := db.Raw(sqlTableColumn).Rows()
|
||||
if err != nil {
|
||||
fmt.Printf("execute query table column action error, detail is [%v]\n", err.Error())
|
||||
return columns, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var column tableColumn
|
||||
err = rows.Scan(
|
||||
&column.OrdinalPosition,
|
||||
&column.ColumnName,
|
||||
&column.ColumnType,
|
||||
&column.DataType,
|
||||
&column.ColumnKey,
|
||||
&column.IsNullable,
|
||||
&column.Extra,
|
||||
&column.ColumnComment,
|
||||
&column.ColumnDefault)
|
||||
if err != nil {
|
||||
fmt.Printf("query table column scan error, detail is [%v]\n", err.Error())
|
||||
return columns, err
|
||||
}
|
||||
columns = append(columns, column)
|
||||
}
|
||||
|
||||
return columns, err
|
||||
}
|
||||
|
||||
func getTargetIndexMap(tableNameArr []string, item string) map[int]int {
|
||||
indexMap := make(map[int]int)
|
||||
for i := 0; i < len(tableNameArr); i++ {
|
||||
if match, _ := regexp.MatchString(item, tableNameArr[i]); match {
|
||||
if _, ok := indexMap[i]; ok {
|
||||
continue
|
||||
}
|
||||
indexMap[i] = i
|
||||
}
|
||||
}
|
||||
return indexMap
|
||||
}
|
||||
|
||||
func capitalize(s string) string {
|
||||
var upperStr string
|
||||
chars := strings.Split(s, "_")
|
||||
for _, val := range chars {
|
||||
vv := []rune(val)
|
||||
for i := 0; i < len(vv); i++ {
|
||||
if i == 0 {
|
||||
if vv[i] >= 97 && vv[i] <= 122 {
|
||||
vv[i] -= 32
|
||||
upperStr += string(vv[i])
|
||||
}
|
||||
} else {
|
||||
upperStr += string(vv[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return upperStr
|
||||
}
|
||||
|
||||
func textType(s string) string {
|
||||
var mysqlTypeToGoType = map[string]string{
|
||||
"tinyint": "int32",
|
||||
"smallint": "int32",
|
||||
"mediumint": "int32",
|
||||
"int": "int32",
|
||||
"integer": "int64",
|
||||
"bigint": "int64",
|
||||
"float": "float64",
|
||||
"double": "float64",
|
||||
"decimal": "float64",
|
||||
"date": "string",
|
||||
"time": "string",
|
||||
"year": "string",
|
||||
"datetime": "time.Time",
|
||||
"timestamp": "time.Time",
|
||||
"char": "string",
|
||||
"varchar": "string",
|
||||
"tinyblob": "string",
|
||||
"tinytext": "string",
|
||||
"blob": "string",
|
||||
"text": "string",
|
||||
"mediumblob": "string",
|
||||
"mediumtext": "string",
|
||||
"longblob": "string",
|
||||
"longtext": "string",
|
||||
}
|
||||
return mysqlTypeToGoType[s]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
var _ Repo = (*dbRepo)(nil)
|
||||
|
||||
type Repo interface {
|
||||
i()
|
||||
GetDb() *gorm.DB
|
||||
DbClose() error
|
||||
}
|
||||
|
||||
type dbRepo struct {
|
||||
DbConn *gorm.DB
|
||||
}
|
||||
|
||||
func New(dbAddr, dbUser, dbPass, dbName string) (Repo, error) {
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=%t&loc=%s",
|
||||
dbUser,
|
||||
dbPass,
|
||||
dbAddr,
|
||||
dbName,
|
||||
true,
|
||||
"Local")
|
||||
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
SingularTable: true,
|
||||
},
|
||||
//Logger: logger.Default.LogMode(logger.Info), // 日志配置
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("[db connection failed] Database name: %s", dbName))
|
||||
}
|
||||
|
||||
db.Set("gorm:table_options", "CHARSET=utf8mb4")
|
||||
|
||||
return &dbRepo{
|
||||
DbConn: db,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *dbRepo) i() {}
|
||||
|
||||
func (d *dbRepo) GetDb() *gorm.DB {
|
||||
return d.DbConn
|
||||
}
|
||||
|
||||
func (d *dbRepo) DbClose() error {
|
||||
sqlDB, err := d.DbConn.DB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sqlDB.Close()
|
||||
}
|
||||
+4
-9
@@ -54,15 +54,10 @@ type Config struct {
|
||||
ExpireDuration time.Duration `toml:"expireDuration"`
|
||||
} `toml:"jwt"`
|
||||
|
||||
Aes struct {
|
||||
Key string `toml:"key"`
|
||||
Iv string `toml:"iv"`
|
||||
} `toml:"aes"`
|
||||
|
||||
Rsa struct {
|
||||
Private string `toml:"private"`
|
||||
Public string `toml:"public"`
|
||||
} `toml:"rsa"`
|
||||
URLToken struct {
|
||||
Secret string `toml:"secret"`
|
||||
ExpireDuration time.Duration `toml:"expireDuration"`
|
||||
} `toml:"urlToken"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -34,46 +34,6 @@ to = "" # 收件人邮箱,多个可以逗号(,)分割
|
||||
secret = 'i1ydX9RtHyuJTrw7frcu' # JWT secret
|
||||
expireDuration = 24 # JWT ExpiresAt 过期时间(单位:小时)
|
||||
|
||||
[aes]
|
||||
key = 'IgkibX71IEf382PT'
|
||||
iv = 'IgkibX71IEf382PT'
|
||||
|
||||
[rsa]
|
||||
private = '-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpgIBAAKCAQEA1O3p0JN0/RrP7eY3f81izPf16FS0WMNGCJkd+y5c6yBzUvN0
|
||||
IEeoxiIWIBhoMKH0pzlzBg0rfttojSodOgNom/UCAzAYEgdIsNee5LSN/7e0T2/Q
|
||||
vsIAHINuA8gI8fGoGiSA2TEzpUo6aVXwhZT34GGRdrSJ+m4iVk/Kt95tavBNk+ND
|
||||
VSeb5xAjxBchT5BjAMMlE0ffGZb0MMjjO5+e9Tn8f99M2VMqpzXHXZzv1ABmqufz
|
||||
S20iWcSvnjhWcJ9hiKwO8Z30GgJyACmml+HMxLYEFN9h2MWYgxLm9Z0rLMrWwMM+
|
||||
E2rCs8tsxAD5sO9RZMJPl1C0FIsMR53ngqbzowIDAQABAoIBAQCO1RE1ItUlO6kj
|
||||
Un0ENAgEqojAUqGvsT33Yo7kAZO+/cOeb0UEqk0iq5bf7L9ncBynWDg6ZPc6X3/g
|
||||
wdFdKxAvHck9zjM3VL+EMP+bNyrR0K8ZYk5Kx+Q/PEK+Mp8dfRdgggAUsZaNWB+a
|
||||
rVVspiMo1wo28KBl5x8NevTnJkOLqXAyB7UyLWqnOL1fb988lZvZPR7ZUYroVIZa
|
||||
pyXtZcafIJeKyQ3bvWI5+eFqOe61Z4Bx1+TpfZ3fKfSDW0vhxzNqaimOa8jSXtMJ
|
||||
jMeOctL4nZ0TPo/jS3I+XlaH4ZQlFLuUWGscpxwfEeBN23I8HRLkZXJsw66yvRN3
|
||||
s4bUKPXRAoGBAP/3oSZAECvfsYYzs76tnrAmR/0GxCqgguxDlWn5DowQzdWFOdHC
|
||||
ZbTo/hUVoMSQnO1EKCFlnBS+wg/3TuIzUO0ewC1aeT7qHbOMDl0zKbNpS2Z9/j+U
|
||||
zro+qz7XmkWolMCfmDrCrw9CtCxcMSII+ajbI8SAgFVMz9XnDt+xW9E9AoGBANT0
|
||||
4F6kCUJTEyqf2+v84tjQ2wGIF6XtZPU9JR806zeMyahQ9F6z3hY8BYb0tIy5b3uJ
|
||||
VlJ9TG1qg/t59TWxIq43mYSUJHe0aJi3ilooObQtHlhPu8nwmmX47sX0PyG2hMoD
|
||||
kBVxTpTDmBaDz7O9uBnlMXJN5qEygctaixpEbmZfAoGBAMBA9kEMjRjnAyeRXcgy
|
||||
D6aumhNqKZz6wltCx864yjxZwsBFOJBcOpgPCAg+HmqFU9jCAIJVF05dmNT1I8Ky
|
||||
WG5BUoa+FaMzpOtenstRylh/Far9pyGKW1t4BpdEyRLY9CFZvbUk1OfZagqHlD/E
|
||||
DgDN16eX/MwUzWYUDg/l3tjhAoGBAKGip/ZNjVWRFpggs9z/mfK1O7WC5Wgksp9N
|
||||
ZLK2CN6l9p3RrFmBLk00C4HulGfHi+15RVLhFbRqx3iFje/N3iPbwaMWikNtZIKd
|
||||
tN5Pb9To9gJTqpZRD+/cLOeFRrHBBjMK1z7fPKS/fN2B+JFVq7nD827t3+J0In4F
|
||||
4FT0odMDAoGBAJk3ELB/FHY8xzZ4jF1wG/a1CK681Xm6SuU5KIELDSAUNoou6OPG
|
||||
mS8gU20MMPAeV2z7khyDcSxlHsUyL73eLeaakbQov9NMW7cc99XX4wnP4W7FRpmr
|
||||
QbHmKuHIRFHCFv+XX8c0aK2mDZMUlzJdy4FgD/YCEZ7kZMZKyvZW/ZuV
|
||||
-----END RSA PRIVATE KEY-----'
|
||||
|
||||
public = '-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1O3p0JN0/RrP7eY3f81i
|
||||
zPf16FS0WMNGCJkd+y5c6yBzUvN0IEeoxiIWIBhoMKH0pzlzBg0rfttojSodOgNo
|
||||
m/UCAzAYEgdIsNee5LSN/7e0T2/QvsIAHINuA8gI8fGoGiSA2TEzpUo6aVXwhZT3
|
||||
4GGRdrSJ+m4iVk/Kt95tavBNk+NDVSeb5xAjxBchT5BjAMMlE0ffGZb0MMjjO5+e
|
||||
9Tn8f99M2VMqpzXHXZzv1ABmqufzS20iWcSvnjhWcJ9hiKwO8Z30GgJyACmml+HM
|
||||
xLYEFN9h2MWYgxLm9Z0rLMrWwMM+E2rCs8tsxAD5sO9RZMJPl1C0FIsMR53ngqbz
|
||||
owIDAQAB
|
||||
-----END PUBLIC KEY-----'
|
||||
|
||||
[urlToken]
|
||||
secret = 'i1ydX9RtHyuJTrw7frcu' # URL Token secret
|
||||
expireDuration = 10 # URL Token ExpiresAt 过期时间(单位:分钟)
|
||||
+114
-23
@@ -43,9 +43,15 @@ var doc = `{
|
||||
"summary": "获取授权信息",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/demo.authResponse"
|
||||
"$ref": "#/definitions/demo_handler.authResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +81,7 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "用户信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -92,6 +98,18 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,11 +130,11 @@ var doc = `{
|
||||
"parameters": [
|
||||
{
|
||||
"description": "请求信息",
|
||||
"name": "RequestInfo",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.CreateRequest"
|
||||
"$ref": "#/definitions/user_handler.createRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -129,9 +147,21 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.CreateResponse"
|
||||
"$ref": "#/definitions/user_handler.createResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +169,7 @@ var doc = `{
|
||||
},
|
||||
"/user/delete/{id}": {
|
||||
"patch": {
|
||||
"description": "删除用户 - 更新 is_deleted = 1",
|
||||
"description": "删除用户",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -149,7 +179,7 @@ var doc = `{
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "删除用户 - 更新 is_deleted = 1",
|
||||
"summary": "删除用户",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
@@ -168,7 +198,22 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息"
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_handler.deleteResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,9 +249,21 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.DetailResponse"
|
||||
"$ref": "#/definitions/user_handler.detailResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,11 +285,11 @@ var doc = `{
|
||||
"parameters": [
|
||||
{
|
||||
"description": "请求信息",
|
||||
"name": "RequestInfo",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.UpdateNickNameByIDRequest"
|
||||
"$ref": "#/definitions/user_handler.updateNickNameByIDRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -245,9 +302,21 @@ var doc = `{
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.UpdateNickNameByIDResponse"
|
||||
"$ref": "#/definitions/user_handler.updateNickNameByIDResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,7 +324,20 @@ var doc = `{
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"demo.authResponse": {
|
||||
"code.Failure": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"description": "业务码",
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"description": "描述信息",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"demo_handler.authResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"authorization": {
|
||||
@@ -268,7 +350,7 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.CreateRequest": {
|
||||
"user_handler.createRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mobile": {
|
||||
@@ -285,7 +367,7 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.CreateResponse": {
|
||||
"user_handler.createResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -294,7 +376,16 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.DetailResponse": {
|
||||
"user_handler.deleteResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "用户主键ID",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_handler.detailResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -302,7 +393,7 @@ var doc = `{
|
||||
"type": "integer"
|
||||
},
|
||||
"mobile": {
|
||||
"description": "手机号",
|
||||
"description": "手机号(脱敏)",
|
||||
"type": "string"
|
||||
},
|
||||
"nick_name": {
|
||||
@@ -315,7 +406,7 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.UpdateNickNameByIDRequest": {
|
||||
"user_handler.updateNickNameByIDRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -328,7 +419,7 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.UpdateNickNameByIDResponse": {
|
||||
"user_handler.updateNickNameByIDResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+114
-23
@@ -26,9 +26,15 @@
|
||||
"summary": "获取授权信息",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/demo.authResponse"
|
||||
"$ref": "#/definitions/demo_handler.authResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +64,7 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "用户信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -75,6 +81,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,11 +113,11 @@
|
||||
"parameters": [
|
||||
{
|
||||
"description": "请求信息",
|
||||
"name": "RequestInfo",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.CreateRequest"
|
||||
"$ref": "#/definitions/user_handler.createRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -112,9 +130,21 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.CreateResponse"
|
||||
"$ref": "#/definitions/user_handler.createResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +152,7 @@
|
||||
},
|
||||
"/user/delete/{id}": {
|
||||
"patch": {
|
||||
"description": "删除用户 - 更新 is_deleted = 1",
|
||||
"description": "删除用户",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@@ -132,7 +162,7 @@
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "删除用户 - 更新 is_deleted = 1",
|
||||
"summary": "删除用户",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
@@ -151,7 +181,22 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息"
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_handler.deleteResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,9 +232,21 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.DetailResponse"
|
||||
"$ref": "#/definitions/user_handler.detailResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,11 +268,11 @@
|
||||
"parameters": [
|
||||
{
|
||||
"description": "请求信息",
|
||||
"name": "RequestInfo",
|
||||
"name": "Request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.UpdateNickNameByIDRequest"
|
||||
"$ref": "#/definitions/user_handler.updateNickNameByIDRequest"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -228,9 +285,21 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "返回信息",
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/user_model.UpdateNickNameByIDResponse"
|
||||
"$ref": "#/definitions/user_handler.updateNickNameByIDResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,7 +307,20 @@
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"demo.authResponse": {
|
||||
"code.Failure": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"description": "业务码",
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"description": "描述信息",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"demo_handler.authResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"authorization": {
|
||||
@@ -251,7 +333,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.CreateRequest": {
|
||||
"user_handler.createRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mobile": {
|
||||
@@ -268,7 +350,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.CreateResponse": {
|
||||
"user_handler.createResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -277,7 +359,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.DetailResponse": {
|
||||
"user_handler.deleteResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "用户主键ID",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_handler.detailResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -285,7 +376,7 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"mobile": {
|
||||
"description": "手机号",
|
||||
"description": "手机号(脱敏)",
|
||||
"type": "string"
|
||||
},
|
||||
"nick_name": {
|
||||
@@ -298,7 +389,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.UpdateNickNameByIDRequest": {
|
||||
"user_handler.updateNickNameByIDRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -311,7 +402,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user_model.UpdateNickNameByIDResponse": {
|
||||
"user_handler.updateNickNameByIDResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+84
-23
@@ -1,5 +1,14 @@
|
||||
definitions:
|
||||
demo.authResponse:
|
||||
code.Failure:
|
||||
properties:
|
||||
code:
|
||||
description: 业务码
|
||||
type: integer
|
||||
message:
|
||||
description: 描述信息
|
||||
type: string
|
||||
type: object
|
||||
demo_handler.authResponse:
|
||||
properties:
|
||||
authorization:
|
||||
description: 签名
|
||||
@@ -8,7 +17,7 @@ definitions:
|
||||
description: 过期时间
|
||||
type: integer
|
||||
type: object
|
||||
user_model.CreateRequest:
|
||||
user_handler.createRequest:
|
||||
properties:
|
||||
mobile:
|
||||
description: 手机号
|
||||
@@ -20,19 +29,25 @@ definitions:
|
||||
description: 用户名
|
||||
type: string
|
||||
type: object
|
||||
user_model.CreateResponse:
|
||||
user_handler.createResponse:
|
||||
properties:
|
||||
id:
|
||||
description: 主键ID
|
||||
type: integer
|
||||
type: object
|
||||
user_model.DetailResponse:
|
||||
user_handler.deleteResponse:
|
||||
properties:
|
||||
id:
|
||||
description: 用户主键ID
|
||||
type: integer
|
||||
type: object
|
||||
user_handler.detailResponse:
|
||||
properties:
|
||||
id:
|
||||
description: 用户主键ID
|
||||
type: integer
|
||||
mobile:
|
||||
description: 手机号
|
||||
description: 手机号(脱敏)
|
||||
type: string
|
||||
nick_name:
|
||||
description: 昵称
|
||||
@@ -41,7 +56,7 @@ definitions:
|
||||
description: 用户名
|
||||
type: string
|
||||
type: object
|
||||
user_model.UpdateNickNameByIDRequest:
|
||||
user_handler.updateNickNameByIDRequest:
|
||||
properties:
|
||||
id:
|
||||
description: 用户主键ID
|
||||
@@ -50,7 +65,7 @@ definitions:
|
||||
description: 昵称
|
||||
type: string
|
||||
type: object
|
||||
user_model.UpdateNickNameByIDResponse:
|
||||
user_handler.updateNickNameByIDResponse:
|
||||
properties:
|
||||
id:
|
||||
description: 用户主键ID
|
||||
@@ -74,9 +89,13 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 返回信息
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/demo.authResponse'
|
||||
$ref: '#/definitions/demo_handler.authResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 获取授权信息
|
||||
tags:
|
||||
- Demo
|
||||
@@ -95,7 +114,7 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 用户信息
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
properties:
|
||||
@@ -107,6 +126,14 @@ paths:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: Trace 示例
|
||||
tags:
|
||||
- Demo
|
||||
@@ -118,10 +145,10 @@ paths:
|
||||
parameters:
|
||||
- description: 请求信息
|
||||
in: body
|
||||
name: RequestInfo
|
||||
name: Request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/user_model.CreateRequest'
|
||||
$ref: '#/definitions/user_handler.createRequest'
|
||||
- description: 签名
|
||||
in: header
|
||||
name: Authorization
|
||||
@@ -131,9 +158,17 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 返回信息
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/user_model.CreateResponse'
|
||||
$ref: '#/definitions/user_handler.createResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 创建用户
|
||||
tags:
|
||||
- User
|
||||
@@ -141,7 +176,7 @@ paths:
|
||||
patch:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 删除用户 - 更新 is_deleted = 1
|
||||
description: 删除用户
|
||||
parameters:
|
||||
- description: 用户ID
|
||||
in: path
|
||||
@@ -157,8 +192,18 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 返回信息
|
||||
summary: 删除用户 - 更新 is_deleted = 1
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/user_handler.deleteResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 删除用户
|
||||
tags:
|
||||
- User
|
||||
/user/info/{username}:
|
||||
@@ -181,9 +226,17 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 返回信息
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/user_model.DetailResponse'
|
||||
$ref: '#/definitions/user_handler.detailResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 用户详情
|
||||
tags:
|
||||
- User
|
||||
@@ -195,10 +248,10 @@ paths:
|
||||
parameters:
|
||||
- description: 请求信息
|
||||
in: body
|
||||
name: RequestInfo
|
||||
name: Request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/user_model.UpdateNickNameByIDRequest'
|
||||
$ref: '#/definitions/user_handler.updateNickNameByIDRequest'
|
||||
- description: 签名
|
||||
in: header
|
||||
name: Authorization
|
||||
@@ -208,9 +261,17 @@ paths:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 返回信息
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/user_model.UpdateNickNameByIDResponse'
|
||||
$ref: '#/definitions/user_handler.updateNickNameByIDResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
tags:
|
||||
- User
|
||||
|
||||
@@ -4,31 +4,37 @@ go 1.15
|
||||
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.13.0
|
||||
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
|
||||
github.com/dave/dst v0.26.2
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/gin-contrib/pprof v1.2.1
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||
github.com/go-openapi/spec v0.20.0 // indirect
|
||||
github.com/go-redis/redis/v7 v7.4.0
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/google/go-cmp v0.5.4 // indirect
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/koketama/errors v1.0.0
|
||||
github.com/onsi/ginkgo v1.14.2 // indirect
|
||||
github.com/onsi/gomega v1.10.4 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v0.9.3
|
||||
github.com/rs/cors v1.7.0
|
||||
github.com/shirou/gopsutil v3.21.2+incompatible
|
||||
github.com/spf13/cast v1.3.0
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/swaggo/gin-swagger v1.3.0
|
||||
github.com/swaggo/swag v1.7.0
|
||||
github.com/tklauser/go-sysconf v0.3.4 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.1.0
|
||||
go.uber.org/multierr v1.5.0
|
||||
go.uber.org/zap v1.16.0
|
||||
golang.org/x/mod v0.4.0 // indirect
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect
|
||||
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0
|
||||
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a // indirect
|
||||
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a
|
||||
google.golang.org/grpc v1.27.0
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
|
||||
@@ -19,11 +19,14 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 h1:5sXbqlSomvdjlRbWyNqkPsJ3Fg+tQZCbgeX1VGljbQY=
|
||||
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
|
||||
github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs=
|
||||
@@ -33,6 +36,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
@@ -55,9 +59,17 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/dave/dst v0.26.2 h1:lnxLAKI3tx7MgLNVDirFCsDTlTG9nKTk7GcptKcWSwY=
|
||||
github.com/dave/dst v0.26.2/go.mod h1:UMDJuIRPfyUCC78eFuB+SV/WI8oDeyFDvM/JR6NI3IU=
|
||||
github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ=
|
||||
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
||||
github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e/go.mod h1:qZqlPyPvfsDJt+3wHJ1EvSXDuVjFTK0j2p/ca+gtsb8=
|
||||
github.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
@@ -65,6 +77,8 @@ github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMN
|
||||
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
@@ -89,6 +103,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
@@ -123,6 +139,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -155,6 +173,7 @@ github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181127221834-b4f47329b966/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
@@ -192,8 +211,12 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
@@ -209,6 +232,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/koketama/errors v1.0.0 h1:4JO7SGSkBROkTuacd+cgPtBDr7fisixXoajkNh4eW3M=
|
||||
github.com/koketama/errors v1.0.0/go.mod h1:SIU03UGMkhp6tyB5+EvhDI9i9ZDa4rQolJCt9ZrFrbU=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@@ -220,6 +245,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
@@ -237,6 +264,8 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
@@ -306,8 +335,11 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shirou/gopsutil v3.21.2+incompatible h1:U+YvJfjCh6MslYlIAXvPtzhW3YZEtc9uncueUNpD/0A=
|
||||
github.com/shirou/gopsutil v3.21.2+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
@@ -345,6 +377,10 @@ github.com/swaggo/gin-swagger v1.3.0/go.mod h1:oy1BRA6WvgtCp848lhxce7BnWH4C8Bxa0
|
||||
github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
|
||||
github.com/swaggo/swag v1.7.0 h1:5bCA/MTLQoIqDXXyHfOpMeDvL9j68OY/udlK4pQoo4E=
|
||||
github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo=
|
||||
github.com/tklauser/go-sysconf v0.3.4 h1:HT8SVixZd3IzLdfs/xlpq0jeSfTX57g1v6wB1EuzV7M=
|
||||
github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek=
|
||||
github.com/tklauser/numcpus v0.2.1 h1:ct88eFm+Q7m2ZfXJdan1xYoXKlmwsfP+k88q05KvlZc=
|
||||
github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
@@ -363,6 +399,7 @@ github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUd
|
||||
github.com/vektah/gqlparser/v2 v2.1.0 h1:uiKJ+T5HMGGQM2kRKQ8Pxw8+Zq9qhhZhz/lieYvCMns=
|
||||
github.com/vektah/gqlparser/v2 v2.1.0/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
@@ -378,12 +415,15 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
golang.org/x/arch v0.0.0-20180920145803-b19384d3c130/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -406,9 +446,11 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -428,6 +470,9 @@ golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
@@ -442,9 +487,11 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -461,6 +508,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -469,8 +517,8 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341 h1:2/QtM1mL37YmcsT8HaDNHDgTqqFVw+zr8UzMiBVLzYU=
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -508,6 +556,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a h1:pdfjQ7VswBeGam3EpuEJ4e8EAb7JgaubV570LO/SIQM=
|
||||
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
@@ -575,6 +624,8 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
## init
|
||||
|
||||
项目初始脚本。
|
||||
|
||||
- DB 相关 SQL;
|
||||
@@ -1,10 +0,0 @@
|
||||
CREATE TABLE `user_demo` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`user_name` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',
|
||||
`nick_name` varchar(100) NOT NULL DEFAULT '' COMMENT '昵称',
|
||||
`mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',
|
||||
`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户Demo表';
|
||||
@@ -1,7 +1,6 @@
|
||||
## 错误码规则
|
||||
|
||||
- 错误码需在 `code.go` 文件中定义。
|
||||
- 错误码需为 > 1 的数,反之表示正确。
|
||||
- 错误码需在 `code` 包中进行定义。
|
||||
|
||||
#### 错误码为 5 位数
|
||||
|
||||
@@ -9,6 +8,6 @@
|
||||
| :------ | :------ | :------ |
|
||||
| 服务级错误码 | 模块级错误码 | 具体错误码 |
|
||||
|
||||
- 服务级别错误码:1 位数进行表示,比如 1 为系统级错误;2 为普通错误,通常是由用户非法操作引起。
|
||||
- 服务级错误码:1 位数进行表示,比如 1 为系统级错误;2 为普通错误,通常是由用户非法操作引起。
|
||||
- 模块级错误码:2 位数进行表示,比如 01 为用户模块;02 为订单模块。
|
||||
- 具体错误码:2 位数进行表示,比如 01 为手机号不合法;02 为验证码输入错误。
|
||||
- 具体的错误码:2 位数进行表示,比如 01 为手机号不合法;02 为验证码输入错误。
|
||||
+36
-19
@@ -1,28 +1,45 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
var (
|
||||
// OK
|
||||
OK = errno.NewError(http.StatusOK, 1, "OK")
|
||||
// 错误时返回结构
|
||||
type Failure struct {
|
||||
Code int `json:"code"` // 业务码
|
||||
Message string `json:"message"` // 描述信息
|
||||
}
|
||||
|
||||
const (
|
||||
// 服务级错误码
|
||||
ErrServer = errno.NewError(http.StatusInternalServerError, 10101, http.StatusText(http.StatusInternalServerError))
|
||||
ErrManyRequest = errno.NewError(http.StatusTooManyRequests, 10102, http.StatusText(http.StatusTooManyRequests))
|
||||
ErrParamBind = errno.NewError(http.StatusBadRequest, 10103, "参数信息有误")
|
||||
ErrAuthorization = errno.NewError(http.StatusUnauthorized, 10104, "签名信息有误")
|
||||
ServerError = 10101
|
||||
TooManyRequests = 10102
|
||||
ParamBindError = 10103
|
||||
AuthorizationError = 10104
|
||||
CallHTTPError = 10105
|
||||
ResubmitError = 10106
|
||||
ResubmitMsg = 10107
|
||||
|
||||
// 模块级错误码 - 用户模块
|
||||
ErrUser = errno.NewError(http.StatusBadRequest, 20101, "非法用户")
|
||||
ErrUserName = errno.NewError(http.StatusBadRequest, 20102, "账号不能为空")
|
||||
ErrUserCreate = errno.NewError(http.StatusBadRequest, 20103, "创建用户失败")
|
||||
ErrUserUpdate = errno.NewError(http.StatusBadRequest, 20104, "更新用户失败")
|
||||
ErrUserSearch = errno.NewError(http.StatusBadRequest, 20105, "查询用户失败")
|
||||
ErrUserHTTP = errno.NewError(http.StatusBadRequest, 20106, "调用他方接口失败")
|
||||
IllegalUserName = 20101
|
||||
UserCreateError = 20102
|
||||
UserUpdateError = 20103
|
||||
UserSearchError = 20104
|
||||
|
||||
// ...
|
||||
)
|
||||
|
||||
var codeText = map[int]string{
|
||||
ServerError: "Internal Server Error",
|
||||
TooManyRequests: "Too Many Requests",
|
||||
ParamBindError: "参数信息有误",
|
||||
AuthorizationError: "签名信息有误",
|
||||
CallHTTPError: "调用第三方 HTTP 接口失败",
|
||||
ResubmitError: "Resubmit Error",
|
||||
ResubmitMsg: "请勿重复提交",
|
||||
|
||||
IllegalUserName: "非法用户名",
|
||||
UserCreateError: "创建用户失败",
|
||||
UserUpdateError: "更新用户失败",
|
||||
UserSearchError: "查询用户失败",
|
||||
}
|
||||
|
||||
func Text(code int) string {
|
||||
return codeText[code]
|
||||
}
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/third_party_request/go_gin_api_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/p"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/token"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Demo struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
userService user_service.UserService
|
||||
}
|
||||
|
||||
func NewDemo(logger *zap.Logger, db db.Repo, cache cache.Repo) *Demo {
|
||||
return &Demo{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
userService: user_service.NewUserService(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Demo) Get() core.HandlerFunc {
|
||||
type request struct {
|
||||
Name string `uri:"name"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
return func(c core.Context) {
|
||||
req := new(request)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.Name != "Tom" {
|
||||
c.AbortWithError(code.ErrUser.WithErr(errors.New("req.Name != Tom")))
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(code.OK.WithData(&response{
|
||||
Name: "Tom",
|
||||
Job: "Student",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Demo) Post() core.HandlerFunc {
|
||||
type request struct {
|
||||
Name string `form:"name"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
return func(c core.Context) {
|
||||
req := new(request)
|
||||
if err := c.ShouldBindPostForm(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.Name != "Jack" {
|
||||
c.AbortWithError(code.ErrUser.WithErr(errors.New("req.Name != Jack")))
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(code.OK.WithData(&response{
|
||||
Name: "Jack",
|
||||
Job: "Teacher",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
type authResponse struct {
|
||||
Authorization string `json:"authorization"` // 签名
|
||||
ExpireTime int64 `json:"expire_time"` // 过期时间
|
||||
}
|
||||
|
||||
type traceResponse []struct {
|
||||
Name string `json:"name"` //用户名
|
||||
Job string `json:"job"` //工作
|
||||
}
|
||||
|
||||
// 获取授权信息
|
||||
// @Summary 获取授权信息
|
||||
// @Description 获取授权信息
|
||||
// @Tags Demo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} authResponse "返回信息"
|
||||
// @Router /auth/get [post]
|
||||
func (d *Demo) Auth() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
cfg := configs.Get().JWT
|
||||
tokenString, err := token.New(cfg.Secret).Sign(1, "xinliangnote", time.Hour*cfg.ExpireDuration)
|
||||
if err != nil {
|
||||
c.AbortWithError(code.ErrAuthorization.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
res := new(authResponse)
|
||||
res.Authorization = tokenString
|
||||
res.ExpireTime = time.Now().Add(time.Hour * cfg.ExpireDuration).Unix()
|
||||
|
||||
c.Payload(code.OK.WithData(res))
|
||||
}
|
||||
}
|
||||
|
||||
// Trace 示例
|
||||
// @Summary Trace 示例
|
||||
// @Description Trace 示例
|
||||
// @Tags Demo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} traceResponse "用户信息"
|
||||
// @Router /demo/trace [get]
|
||||
func (d *Demo) Trace() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
// 三方请求信息
|
||||
res1, err := go_gin_api_repo.DemoGet("Tom",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
httpclient.WithTrace(c.Trace()),
|
||||
httpclient.WithLogger(c.Logger()),
|
||||
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, go_gin_api_repo.DemoGetRetryVerify),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
d.logger.Error("get [demo/get] err", zap.Error(err))
|
||||
c.AbortWithError(code.ErrUserHTTP.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 调试信息
|
||||
p.Println("res1.Data.Name", res1.Data.Name, p.WithTrace(c.Trace()))
|
||||
|
||||
// 三方请求信息
|
||||
res2, err := go_gin_api_repo.DemoPost("Jack",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
httpclient.WithTrace(c.Trace()),
|
||||
httpclient.WithLogger(c.Logger()),
|
||||
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, go_gin_api_repo.DemoPostRetryVerify),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
d.logger.Error("post [demo/post] err", zap.Error(err))
|
||||
c.AbortWithError(code.ErrUserHTTP.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 调试信息
|
||||
p.Println("res2.Data.Name",
|
||||
res2.Data.Name,
|
||||
p.WithTrace(c.Trace()),
|
||||
)
|
||||
|
||||
// 执行 SQL 信息
|
||||
d.userService.GetUserByUserName(c, "test_user")
|
||||
|
||||
// 执行 Redis 信息
|
||||
_ = d.cache.Set("name", "tom", time.Minute*10, cache.WithTrace(c.Trace()))
|
||||
val, _ := d.cache.Get("name", cache.WithTrace(c.Trace()))
|
||||
p.Println("redis-name", val, p.WithTrace(c.Trace()))
|
||||
|
||||
//// 执行 gRPC 信息
|
||||
//conn, err := grpclient.New("127.0.0.1:9001",
|
||||
// grpclient.WithDialTimeout(time.Second*5),
|
||||
// grpclient.WithTrace(c.Trace()),
|
||||
// grpclient.WithSign(func(message []byte) (authorization string, err error) {
|
||||
// return grpclient.GenerateSign("abcdef", message)
|
||||
// }),
|
||||
//)
|
||||
//if err != nil {
|
||||
// d.logger.Error("grpc conn err", zap.Error(err))
|
||||
//}
|
||||
//defer conn.Close()
|
||||
//
|
||||
//// 初始化客户端
|
||||
//client := hello.NewHelloClient(conn)
|
||||
//
|
||||
//// 调用 SayHello 方法
|
||||
//ctx := context.Background()
|
||||
//// 设置 SayHello 超时间
|
||||
//ctx, cancel := context.WithTimeout(ctx, time.Second*3)
|
||||
//defer cancel()
|
||||
//
|
||||
//client.SayHello(ctx, &hello.HelloRequest{Name: "Hello World"})
|
||||
|
||||
data := &traceResponse{
|
||||
{
|
||||
Name: res1.Data.Name,
|
||||
Job: res1.Data.Job,
|
||||
},
|
||||
{
|
||||
Name: res2.Data.Name,
|
||||
Job: res2.Data.Job,
|
||||
},
|
||||
}
|
||||
c.Payload(code.OK.WithData(data))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package demo_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/token"
|
||||
)
|
||||
|
||||
type authResponse struct {
|
||||
Authorization string `json:"authorization"` // 签名
|
||||
ExpireTime int64 `json:"expire_time"` // 过期时间
|
||||
}
|
||||
|
||||
// 获取授权信息
|
||||
// @Summary 获取授权信息
|
||||
// @Description 获取授权信息
|
||||
// @Tags Demo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} authResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /auth/get [post]
|
||||
func (h *handler) Auth() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
cfg := configs.Get().JWT
|
||||
tokenString, err := token.New(cfg.Secret).JwtSign(1, "xinliangnote", time.Hour*cfg.ExpireDuration)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.AuthorizationError,
|
||||
code.Text(code.AuthorizationError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res := new(authResponse)
|
||||
res.Authorization = tokenString
|
||||
res.ExpireTime = time.Now().Add(time.Hour * cfg.ExpireDuration).Unix()
|
||||
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package demo_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (h *handler) Get() core.HandlerFunc {
|
||||
type request struct {
|
||||
Name string `uri:"name"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
return func(c core.Context) {
|
||||
req := new(request)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Name != "Tom" {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.IllegalUserName,
|
||||
code.Text(code.IllegalUserName)).WithErr(errors.New("req.Name != Tom")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(&response{
|
||||
Name: "Tom",
|
||||
Job: "Student",
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package demo_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (h *handler) Post() core.HandlerFunc {
|
||||
type request struct {
|
||||
Name string `form:"name"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
return func(c core.Context) {
|
||||
req := new(request)
|
||||
if err := c.ShouldBindPostForm(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Name != "Jack" {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.IllegalUserName,
|
||||
code.Text(code.IllegalUserName)).WithErr(errors.New("req.Name != Jack")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(&response{
|
||||
Name: "Jack",
|
||||
Job: "Teacher",
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package demo_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/third_party_request/go_gin_api"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/p"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type traceResponse []struct {
|
||||
Name string `json:"name"` //用户名
|
||||
Job string `json:"job"` //工作
|
||||
}
|
||||
|
||||
// Trace 示例
|
||||
// @Summary Trace 示例
|
||||
// @Description Trace 示例
|
||||
// @Tags Demo
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} traceResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Failure 401 {object} code.Failure
|
||||
// @Router /demo/trace [get]
|
||||
func (h *handler) Trace() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
// 三方请求信息
|
||||
res1, err := go_gin_api.DemoGet("Tom",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
httpclient.WithTrace(c.Trace()),
|
||||
httpclient.WithLogger(c.Logger()),
|
||||
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, go_gin_api.DemoGetRetryVerify),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
h.logger.Error("get [demo/get] err", zap.Error(err))
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.CallHTTPError,
|
||||
code.Text(code.CallHTTPError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// 调试信息
|
||||
p.Println("res1.Name", res1.Name, p.WithTrace(c.Trace()))
|
||||
|
||||
// 三方请求信息
|
||||
res2, err := go_gin_api.DemoPost("Jack",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
httpclient.WithTrace(c.Trace()),
|
||||
httpclient.WithLogger(c.Logger()),
|
||||
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, go_gin_api.DemoPostRetryVerify),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
h.logger.Error("post [demo/post] err", zap.Error(err))
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.CallHTTPError,
|
||||
code.Text(code.CallHTTPError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// 调试信息
|
||||
p.Println("res2.Name",
|
||||
res2.Name,
|
||||
p.WithTrace(c.Trace()),
|
||||
)
|
||||
|
||||
// 执行 SQL 信息
|
||||
h.userService.GetUserByUserName(c, "test_user")
|
||||
|
||||
// 执行 Redis 信息
|
||||
_ = h.cache.Set("name", "tom", time.Minute*10, cache.WithTrace(c.Trace()))
|
||||
val, _ := h.cache.Get("name", cache.WithTrace(c.Trace()))
|
||||
p.Println("redis-name", val, p.WithTrace(c.Trace()))
|
||||
|
||||
// 初始化客户端
|
||||
// client := hello.NewHelloClient(d.grpConn.Conn())
|
||||
// client.SayHello(grpc.ContextWithValueAndTimeout(c, time.Second*3), &hello.HelloRequest{Name: "Hello World"})
|
||||
|
||||
data := &traceResponse{
|
||||
{
|
||||
Name: res1.Name,
|
||||
Job: res1.Job,
|
||||
},
|
||||
{
|
||||
Name: res2.Name,
|
||||
Job: res2.Job,
|
||||
},
|
||||
}
|
||||
c.Payload(data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package demo_handler
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/grpc"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ Handler = (*handler)(nil)
|
||||
|
||||
type Handler interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
// 示例:支持 get 请求的方法
|
||||
Get() core.HandlerFunc
|
||||
// 示例:支持 post 请求的方法
|
||||
Post() core.HandlerFunc
|
||||
|
||||
// 获取授权信息
|
||||
// @Tags Demo
|
||||
// @Router /auth/get [post]
|
||||
Auth() core.HandlerFunc
|
||||
|
||||
// Trace 示例
|
||||
// @Tags Demo
|
||||
// @Router /demo/trace [get]
|
||||
Trace() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
grpConn grpc.ClientConn
|
||||
userService user_service.UserService
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo, grpConn grpc.ClientConn) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
grpConn: grpConn,
|
||||
userService: user_service.NewUserService(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) i() {}
|
||||
@@ -0,0 +1,77 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type createRequest struct {
|
||||
UserName string `json:"user_name"` // 用户名
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
Mobile string `json:"mobile"` // 手机号
|
||||
}
|
||||
|
||||
type createResponse struct {
|
||||
Id int32 `json:"id"` // 主键ID
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
// @Summary 创建用户
|
||||
// @Description 创建用户
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Request body createRequest true "请求信息"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} createResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Failure 401 {object} code.Failure
|
||||
// @Router /user/create [post]
|
||||
func (h *handler) Create() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(createRequest)
|
||||
res := new(createResponse)
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if req.UserName == "" {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.IllegalUserName,
|
||||
code.Text(code.IllegalUserName)).WithErr(errors.New("req.UserName = ''")),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
createUserData := new(user_service.CreateUserInfo)
|
||||
createUserData.Mobile = req.Mobile
|
||||
createUserData.NickName = req.NickName
|
||||
createUserData.UserName = req.UserName
|
||||
|
||||
id, err := h.userService.Create(c, createUserData)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.UserCreateError,
|
||||
code.Text(code.UserCreateError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = id
|
||||
c.Payload(res)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type deleteRequest struct {
|
||||
Id int32 `uri:"id"` // 用户ID
|
||||
}
|
||||
|
||||
type deleteResponse struct {
|
||||
Id int32 `json:"id"` // 用户主键ID
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
// @Summary 删除用户
|
||||
// @Description 删除用户
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "用户ID"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} deleteResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Failure 401 {object} code.Failure
|
||||
// @Router /user/delete/{id} [patch]
|
||||
func (h *handler) Delete() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(deleteRequest)
|
||||
res := new(deleteResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
err := h.userService.Delete(c, req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.UserUpdateError,
|
||||
code.Text(code.UserUpdateError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = req.Id
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/ddm"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type detailRequest struct {
|
||||
UserName string `uri:"username"` // 用户名
|
||||
}
|
||||
|
||||
type detailResponse struct {
|
||||
Id int32 `json:"id"` // 用户主键ID
|
||||
UserName string `json:"user_name"` // 用户名
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
Mobile ddm.Mobile `json:"mobile"` // 手机号(脱敏)
|
||||
}
|
||||
|
||||
// 用户详情
|
||||
// @Summary 用户详情
|
||||
// @Description 用户详情
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param username path string true "用户名"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} detailResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Failure 401 {object} code.Failure
|
||||
// @Router /user/info/{username} [get]
|
||||
func (h *handler) Detail() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(detailRequest)
|
||||
res := new(detailResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userService.GetUserByUserName(c, req.UserName)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.UserSearchError,
|
||||
code.Text(code.UserSearchError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = user.Id
|
||||
res.UserName = user.UserName
|
||||
res.NickName = user.NickName
|
||||
res.Mobile = ddm.Mobile(user.Mobile)
|
||||
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
)
|
||||
|
||||
type updateNickNameByIDRequest struct {
|
||||
Id int32 `json:"id"` // 用户主键ID
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
}
|
||||
|
||||
type updateNickNameByIDResponse struct {
|
||||
Id int32 `json:"id"` // 用户主键ID
|
||||
}
|
||||
|
||||
// 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Summary 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Description 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Request body updateNickNameByIDRequest true "请求信息"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} updateNickNameByIDResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Failure 401 {object} code.Failure
|
||||
// @Router /user/update [put]
|
||||
func (h *handler) UpdateNickNameByID() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(updateNickNameByIDRequest)
|
||||
res := new(updateNickNameByIDResponse)
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
code.Text(code.ParamBindError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
err := h.userService.UpdateNickNameByID(c, req.Id, req.NickName)
|
||||
if err != nil {
|
||||
c.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.UserUpdateError,
|
||||
code.Text(code.UserUpdateError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = req.Id
|
||||
|
||||
c.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ Handler = (*handler)(nil)
|
||||
|
||||
type Handler interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
|
||||
// Create 创建用户
|
||||
// @Tags User
|
||||
// @Router /user/create [post]
|
||||
Create() core.HandlerFunc
|
||||
|
||||
// UpdateNickNameByID 编辑用户 - 通过主键ID更新用户昵称
|
||||
// @Tags User
|
||||
// @Router /user/update [put]
|
||||
UpdateNickNameByID() core.HandlerFunc
|
||||
|
||||
// Delete 删除用户
|
||||
// @Tags User
|
||||
// @Router /user/delete/{id} [patch]
|
||||
Delete() core.HandlerFunc
|
||||
|
||||
// Detail 用户详情
|
||||
// @Tags User
|
||||
// @Router /user/info/{username} [get]
|
||||
Detail() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
userService user_service.UserService
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
userService: user_service.NewUserService(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) i() {}
|
||||
@@ -1,176 +0,0 @@
|
||||
package user_handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/model/user_model"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/ddm"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ UserDemo = (*userDemo)(nil)
|
||||
|
||||
type UserDemo interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
|
||||
// Create 创建用户
|
||||
Create() core.HandlerFunc
|
||||
|
||||
// UpdateNickNameByID 编辑用户 - 通过主键ID更新用户昵称
|
||||
UpdateNickNameByID() core.HandlerFunc
|
||||
|
||||
// Delete 删除用户 - 通过主键ID更新 is_deleted = 1
|
||||
Delete() core.HandlerFunc
|
||||
|
||||
// Detail 用户详情
|
||||
Detail() core.HandlerFunc
|
||||
}
|
||||
|
||||
type userDemo struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
userService user_service.UserService
|
||||
}
|
||||
|
||||
func NewUserDemo(logger *zap.Logger, db db.Repo, cache cache.Repo) UserDemo {
|
||||
return &userDemo{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
userService: user_service.NewUserService(db, cache),
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userDemo) i() {}
|
||||
|
||||
// 创建用户
|
||||
// @Summary 创建用户
|
||||
// @Description 创建用户
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param RequestInfo body user_model.CreateRequest true "请求信息"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} user_model.CreateResponse "返回信息"
|
||||
// @Router /user/create [post]
|
||||
func (u *userDemo) Create() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(user_model.CreateRequest)
|
||||
res := new(user_model.CreateResponse)
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
if req.UserName == "" {
|
||||
c.AbortWithError(code.ErrUserName.WithErr(errors.New("req.UserName = ''")))
|
||||
return
|
||||
}
|
||||
|
||||
id, err := u.userService.Create(c, req)
|
||||
if err != nil {
|
||||
c.AbortWithError(code.ErrUserCreate.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = id
|
||||
c.Payload(code.OK.WithData(res))
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Summary 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Description 编辑用户 - 通过用户主键ID更新用户昵称
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param RequestInfo body user_model.UpdateNickNameByIDRequest true "请求信息"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} user_model.UpdateNickNameByIDResponse "返回信息"
|
||||
// @Router /user/update [put]
|
||||
func (u *userDemo) UpdateNickNameByID() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(user_model.UpdateNickNameByIDRequest)
|
||||
res := new(user_model.UpdateNickNameByIDResponse)
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
err := u.userService.UpdateNickNameByID(c, req.Id, req.NickName)
|
||||
if err != nil {
|
||||
c.AbortWithError(code.ErrUserUpdate.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = req.Id
|
||||
c.Payload(code.OK.WithData(res))
|
||||
}
|
||||
}
|
||||
|
||||
// 删除用户 - 更新 is_deleted = 1
|
||||
// @Summary 删除用户 - 更新 is_deleted = 1
|
||||
// @Description 删除用户 - 更新 is_deleted = 1
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "用户ID"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 "返回信息"
|
||||
// @Router /user/delete/{id} [patch]
|
||||
func (u *userDemo) Delete() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(user_model.DeleteRequest)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
err := u.userService.Delete(c, req.Id)
|
||||
if err != nil {
|
||||
c.AbortWithError(code.ErrUserUpdate.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
c.Payload(code.OK.WithData(nil))
|
||||
}
|
||||
}
|
||||
|
||||
// 用户详情
|
||||
// @Summary 用户详情
|
||||
// @Description 用户详情
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param username path string true "用户名"
|
||||
// @Param Authorization header string true "签名"
|
||||
// @Success 200 {object} user_model.DetailResponse "返回信息"
|
||||
// @Router /user/info/{username} [get]
|
||||
func (u *userDemo) Detail() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
req := new(user_model.DetailRequest)
|
||||
res := new(user_model.DetailResponse)
|
||||
if err := c.ShouldBindURI(req); err != nil {
|
||||
c.AbortWithError(code.ErrParamBind.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := u.userService.GetUserByUserName(c, req.UserName)
|
||||
if err != nil {
|
||||
c.AbortWithError(code.ErrUserSearch.WithErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
res.Id = user.Id
|
||||
res.UserName = user.UserName
|
||||
res.NickName = user.NickName
|
||||
res.Mobile = ddm.Mobile(user.Mobile)
|
||||
c.Payload(code.OK.WithData(res))
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
## model
|
||||
|
||||
实体层。
|
||||
|
||||
- 请求实体、返回实体。
|
||||
- 数据库实体。
|
||||
|
||||
命名规范:
|
||||
|
||||
- 包名以 `_model` 结尾。
|
||||
@@ -1,63 +0,0 @@
|
||||
package user_model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/ddm"
|
||||
)
|
||||
|
||||
// 用户Demo表
|
||||
type UserDemo struct {
|
||||
Id uint `gorm:"column:id;primary_key;AUTO_INCREMENT"` // 主键
|
||||
UserName string `gorm:"column:user_name;NOT NULL"` // 用户名
|
||||
NickName string `gorm:"column:nick_name;NOT NULL"` // 昵称
|
||||
Mobile string `gorm:"column:mobile;NOT NULL"` // 手机号
|
||||
IsDeleted int `gorm:"column:is_deleted;default:-1;NOT NULL"` // 是否删除 1:是 -1:否
|
||||
CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;NOT NULL"` // 创建时间
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;NOT NULL"` // 更新时间
|
||||
}
|
||||
|
||||
func (m *UserDemo) TableName() string {
|
||||
return "user_demo"
|
||||
}
|
||||
|
||||
// user_handler Create Request
|
||||
type CreateRequest struct {
|
||||
UserName string `json:"user_name"` // 用户名
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
Mobile string `json:"mobile"` // 手机号
|
||||
}
|
||||
|
||||
// user_handler Create Response
|
||||
type CreateResponse struct {
|
||||
Id uint `json:"id"` // 主键ID
|
||||
}
|
||||
|
||||
// user_handler UpdateNickNameByID Request
|
||||
type UpdateNickNameByIDRequest struct {
|
||||
Id uint `json:"id"` // 用户主键ID
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
}
|
||||
|
||||
// user_handler UpdateNickNameByID Response
|
||||
type UpdateNickNameByIDResponse struct {
|
||||
Id uint `json:"id"` // 用户主键ID
|
||||
}
|
||||
|
||||
// user_handler Delete Request
|
||||
type DeleteRequest struct {
|
||||
Id uint `uri:"id"` // 用户ID
|
||||
}
|
||||
|
||||
// user_handler Detail Request
|
||||
type DetailRequest struct {
|
||||
UserName string `uri:"username"` // 用户名
|
||||
}
|
||||
|
||||
// user_handler Detail Response
|
||||
type DetailResponse struct {
|
||||
Id uint `json:"id"` // 用户主键ID
|
||||
UserName string `json:"user_name"` // 用户名
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
Mobile ddm.Mobile `json:"mobile"` // 手机号(脱敏)
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
## repository
|
||||
|
||||
数据访问层。
|
||||
#### 数据访问层。
|
||||
|
||||
- `./db_repo` 访问 DB 数据
|
||||
- `./cache_repo` 访问 Cache 数据
|
||||
- `./third_party_request` 访问外部 HTTP 接口数据。
|
||||
|
||||
SQL 建议:
|
||||
- 禁止使用 SQL k v 拼接,好处是避免 SQL 注入;
|
||||
- 禁止使用连表查询,好处是易扩展,比如分库分表;
|
||||
- 禁止使用万能方法,好处是便于后期维护,比如字段调整;
|
||||
- 禁止使用删除方法,好处是避免数据丢失;
|
||||
#### SQL 建议:
|
||||
- 建议每张表需包含字段:主键(id)、标记删除(is_deteled)、创建时间(created_at)、更新时间(updated_at)
|
||||
|
||||
```mysql
|
||||
@@ -19,11 +14,12 @@ SQL 建议:
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
```
|
||||
什么是万能方法?
|
||||
|
||||
指的是特别灵活的查询,比如通过非固定的参数返回全部字段,建议做到需要什么返回什么,不要返回大而全的数据,更新时也不能传递什么参数更新什么参数,更新字段要提前约定好。
|
||||
|
||||
命名规范:
|
||||
#### 命名规范:
|
||||
|
||||
- 包名应以 `_repo` 结尾;
|
||||
- `./db_repo` 目录下的包名以 `数据表名`+ `_repo` 命名;
|
||||
|
||||
#### 脚本生成 MySQL CURD
|
||||
|
||||
- 使用脚本自动生成基于表结构的 CURD 代码,见文档:`./cmd/gormgen/README.md`
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
## 使用示例
|
||||
|
||||
以 `user_demo` 为例:
|
||||
|
||||
```go
|
||||
// 查询:多条 + 分页
|
||||
page := 2
|
||||
num := 2
|
||||
offset := (page - 1) * num
|
||||
|
||||
user, err = user_demo_repo.NewQueryBuilder().
|
||||
WhereIdNotIn([]int32{1, 2, 3}).
|
||||
WhereUserName(db_repo.EqualPredicate, "tom").
|
||||
Limit(num).
|
||||
Offset(offset).
|
||||
QueryAll(u.db.GetDbR().WithContext(ctx.RequestContext()))
|
||||
|
||||
// 查询:总数
|
||||
count, err := user_demo_repo.NewQueryBuilder().
|
||||
WhereIdNotIn([]int32{1, 2, 3}).
|
||||
WhereUserName(db_repo.EqualPredicate, "tom").
|
||||
Count(u.db.GetDbR().WithContext(ctx.RequestContext()))
|
||||
|
||||
// 查询:单条
|
||||
user, err = user_demo_repo.NewQueryBuilder().
|
||||
WhereUserName(db_repo.EqualPredicate, "tom").
|
||||
QueryOne(u.db.GetDbR().WithContext(ctx.RequestContext()))
|
||||
|
||||
// 创建
|
||||
model := user_demo_repo.NewModel()
|
||||
model.UserName = user.UserName
|
||||
model.NickName = user.NickName
|
||||
model.Mobile = user.Mobile
|
||||
|
||||
id, err = model.Create(u.db.GetDbW().WithContext(ctx.RequestContext()))
|
||||
|
||||
// 编辑
|
||||
model := user_demo_repo.NewModel()
|
||||
model.Id = id
|
||||
|
||||
data := map[string]interface{}{
|
||||
"nick_name": nickname,
|
||||
}
|
||||
|
||||
err = model.Updates(u.db.GetDbW().WithContext(ctx.RequestContext()), data)
|
||||
|
||||
// 删除
|
||||
model := user_demo_repo.NewModel()
|
||||
model.Id = id
|
||||
err = model.Delete(u.db.GetDbW().WithContext(ctx.RequestContext()))
|
||||
```
|
||||
@@ -0,0 +1,14 @@
|
||||
package db_repo
|
||||
|
||||
// Predicate is a string that acts as a condition in the where clause
|
||||
type Predicate string
|
||||
|
||||
var (
|
||||
EqualPredicate = Predicate("=")
|
||||
NotEqualPredicate = Predicate("<>")
|
||||
GreaterThanPredicate = Predicate(">")
|
||||
GreaterThanOrEqualPredicate = Predicate(">=")
|
||||
SmallerThanPredicate = Predicate("<")
|
||||
SmallerThanOrEqualPredicate = Predicate("<=")
|
||||
LikePredicate = Predicate("LIKE")
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
package user_demo_repo
|
||||
|
||||
import "time"
|
||||
|
||||
// 用户Demo表
|
||||
//go:generate gormgen -structs UserDemo -input .
|
||||
type UserDemo struct {
|
||||
Id int32 // 主键
|
||||
UserName string // 用户名
|
||||
NickName string // 昵称
|
||||
Mobile string // 手机号
|
||||
IsDeleted int32 // 是否删除 1:是 -1:否
|
||||
CreatedAt time.Time `gorm:"time"` // 创建时间
|
||||
UpdatedAt time.Time `gorm:"time"` // 更新时间
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#### go_gin_api.user_demo
|
||||
用户Demo表
|
||||
|
||||
| 序号 | 名称 | 描述 | 类型 | 键 | 为空 | 额外 | 默认值 |
|
||||
| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: |
|
||||
| 1 | id | 主键 | int(11) unsigned | PRI | NO | auto_increment | |
|
||||
| 2 | user_name | 用户名 | varchar(32) | | NO | | |
|
||||
| 3 | nick_name | 昵称 | varchar(100) | | NO | | |
|
||||
| 4 | mobile | 手机号 | varchar(20) | | NO | | |
|
||||
| 5 | is_deleted | 是否删除 1:是 -1:否 | tinyint(1) | | NO | | -1 |
|
||||
| 6 | created_at | 创建时间 | timestamp | | NO | | CURRENT_TIMESTAMP |
|
||||
| 7 | updated_at | 更新时间 | timestamp | | NO | on update CURRENT_TIMESTAMP | CURRENT_TIMESTAMP |
|
||||
@@ -0,0 +1,411 @@
|
||||
///////////////////////////////////////////////////////////
|
||||
// THIS FILE IS AUTO GENERATED by gormgen, DON'T EDIT IT //
|
||||
// ANY CHANGES DONE HERE WILL BE LOST //
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
package user_demo_repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func NewModel() *UserDemo {
|
||||
return new(UserDemo)
|
||||
}
|
||||
|
||||
func NewQueryBuilder() *userDemoRepoQueryBuilder {
|
||||
return new(userDemoRepoQueryBuilder)
|
||||
}
|
||||
|
||||
func (t *UserDemo) 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
|
||||
}
|
||||
|
||||
func (t *UserDemo) Delete(db *gorm.DB) (err error) {
|
||||
if err = db.Delete(t).Error; err != nil {
|
||||
return errors.Wrap(err, "delete err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *UserDemo) Updates(db *gorm.DB, m map[string]interface{}) (err error) {
|
||||
if err = db.Model(&UserDemo{}).Where("id = ?", t.Id).Updates(m).Error; err != nil {
|
||||
return errors.Wrap(err, "updates err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type userDemoRepoQueryBuilder struct {
|
||||
order []string
|
||||
where []struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}
|
||||
limit int
|
||||
offset int
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) 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 *userDemoRepoQueryBuilder) Count(db *gorm.DB) (int64, error) {
|
||||
var c int64
|
||||
res := qb.buildQuery(db).Model(&UserDemo{}).Count(&c)
|
||||
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
|
||||
c = 0
|
||||
}
|
||||
return c, res.Error
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) First(db *gorm.DB) (*UserDemo, error) {
|
||||
ret := &UserDemo{}
|
||||
res := qb.buildQuery(db).First(ret)
|
||||
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
|
||||
ret = nil
|
||||
}
|
||||
return ret, res.Error
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) QueryOne(db *gorm.DB) (*UserDemo, error) {
|
||||
qb.limit = 1
|
||||
ret, err := qb.QueryAll(db)
|
||||
if len(ret) > 0 {
|
||||
return ret[0], err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) QueryAll(db *gorm.DB) ([]*UserDemo, error) {
|
||||
var ret []*UserDemo
|
||||
err := qb.buildQuery(db).Find(&ret).Error
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) Limit(limit int) *userDemoRepoQueryBuilder {
|
||||
qb.limit = limit
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) Offset(offset int) *userDemoRepoQueryBuilder {
|
||||
qb.offset = offset
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereId(p db_repo.Predicate, value int32) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "id", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereIdIn(value []int32) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "id", "IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereIdNotIn(value []int32) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "id", "NOT IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderById(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "id "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereUserName(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "user_name", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereUserNameIn(value []string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "user_name", "IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereUserNameNotIn(value []string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "user_name", "NOT IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByUserName(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "user_name "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereNickName(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "nick_name", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereNickNameIn(value []string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "nick_name", "IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereNickNameNotIn(value []string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "nick_name", "NOT IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByNickName(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "nick_name "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereMobile(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "mobile", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereMobileIn(value []string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "mobile", "IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereMobileNotIn(value []string) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "mobile", "NOT IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByMobile(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "mobile "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereIsDeleted(p db_repo.Predicate, value int32) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "is_deleted", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereIsDeletedIn(value []int32) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "is_deleted", "IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereIsDeletedNotIn(value []int32) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "is_deleted", "NOT IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByIsDeleted(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "is_deleted "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereCreatedAt(p db_repo.Predicate, value time.Time) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "created_at", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereCreatedAtIn(value []time.Time) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "created_at", "IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereCreatedAtNotIn(value []time.Time) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "created_at", "NOT IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByCreatedAt(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "created_at "+order)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereUpdatedAt(p db_repo.Predicate, value time.Time) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "updated_at", p),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereUpdatedAtIn(value []time.Time) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "updated_at", "IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) WhereUpdatedAtNotIn(value []time.Time) *userDemoRepoQueryBuilder {
|
||||
qb.where = append(qb.where, struct {
|
||||
prefix string
|
||||
value interface{}
|
||||
}{
|
||||
fmt.Sprintf("%v %v ?", "updated_at", "NOT IN"),
|
||||
value,
|
||||
})
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *userDemoRepoQueryBuilder) OrderByUpdatedAt(asc bool) *userDemoRepoQueryBuilder {
|
||||
order := "DESC"
|
||||
if asc {
|
||||
order = "ASC"
|
||||
}
|
||||
|
||||
qb.order = append(qb.order, "updated_at "+order)
|
||||
return qb
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package user_demo_repo
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/model/user_model"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var _ UserRepo = (*userRepo)(nil)
|
||||
|
||||
type UserRepo interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
Create(ctx core.Context, user user_model.UserDemo) (id uint, err error)
|
||||
UpdateNickNameByID(ctx core.Context, id uint, username string) (err error)
|
||||
GetUserByUserName(ctx core.Context, username string) (*user_model.UserDemo, error)
|
||||
Delete(ctx core.Context, id uint) (err error)
|
||||
getUserByID(ctx core.Context, id uint) (*user_model.UserDemo, error)
|
||||
}
|
||||
|
||||
type userRepo struct {
|
||||
db db.Repo
|
||||
}
|
||||
|
||||
func NewUserRepo(db db.Repo) UserRepo {
|
||||
return &userRepo{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userRepo) i() {}
|
||||
|
||||
func (u *userRepo) Create(ctx core.Context, user user_model.UserDemo) (id uint, err error) {
|
||||
err = u.db.GetDbW().WithContext(ctx.RequestContext()).Create(&user).Error
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "[user_repo] create user err")
|
||||
}
|
||||
return user.Id, nil
|
||||
}
|
||||
|
||||
func (u *userRepo) getUserByID(ctx core.Context, id uint) (*user_model.UserDemo, error) {
|
||||
data := new(user_model.UserDemo)
|
||||
err := u.db.GetDbR().
|
||||
WithContext(ctx.RequestContext()).First(data, id).Where("is_deleted = ?", -1).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, errors.Wrap(err, "[user_demo] get user data err")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (u *userRepo) UpdateNickNameByID(ctx core.Context, id uint, nickname string) (err error) {
|
||||
user, err := u.getUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "[user_demo] get user data err")
|
||||
}
|
||||
return u.db.GetDbW().WithContext(ctx.RequestContext()).Model(user).Update("nick_name", nickname).Error
|
||||
}
|
||||
|
||||
func (u *userRepo) Delete(ctx core.Context, id uint) (err error) {
|
||||
user, err := u.getUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "[user_demo] get user data err")
|
||||
}
|
||||
return u.db.GetDbW().WithContext(ctx.RequestContext()).Model(user).Update("is_deleted", 1).Error
|
||||
}
|
||||
|
||||
func (u *userRepo) GetUserByUserName(ctx core.Context, username string) (*user_model.UserDemo, error) {
|
||||
data := new(user_model.UserDemo)
|
||||
err := u.db.GetDbR().
|
||||
WithContext(ctx.RequestContext()).
|
||||
Select([]string{"id", "user_name", "nick_name", "mobile"}).
|
||||
Where("user_name = ? and is_deleted = ?", username, -1).
|
||||
First(data).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, errors.Wrap(err, "[user_demo] get user data err")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package go_gin_api_repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type demoGetResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
} `json:"data"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type demoPostResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
} `json:"data"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func DemoGet(name string, opts ...httpclient.Option) (res *demoGetResponse, err error) {
|
||||
api := "http://127.0.0.1:9999/demo/get/" + name
|
||||
body, err := httpclient.Get(api, nil, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = new(demoGetResponse)
|
||||
err = json.Unmarshal(body, res)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "DemoGet json unmarshal error")
|
||||
}
|
||||
|
||||
if res.Code != 1 {
|
||||
return nil, errors.New(fmt.Sprintf("code err: %d-%s", res.Code, res.Msg))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func DemoGetRetryVerify(body []byte) (shouldRetry bool) {
|
||||
if len(body) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
}
|
||||
resp := new(Response)
|
||||
if err := json.Unmarshal(body, resp); err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// 例如 无需重试的 code 码,code !=1 需要重试
|
||||
successCode := 1
|
||||
return resp.Code != successCode
|
||||
}
|
||||
|
||||
func DemoPost(name string, opts ...httpclient.Option) (res *demoPostResponse, err error) {
|
||||
api := "http://127.0.0.1:9999/demo/post"
|
||||
params := url.Values{}
|
||||
params.Set("name", name)
|
||||
body, err := httpclient.PostForm(api, params, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = new(demoPostResponse)
|
||||
err = json.Unmarshal(body, res)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "DemoPost json unmarshal error")
|
||||
}
|
||||
|
||||
if res.Code != 1 {
|
||||
return nil, errors.New(fmt.Sprintf("code err: %d-%s", res.Code, res.Msg))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func DemoPostRetryVerify(body []byte) (shouldRetry bool) {
|
||||
if len(body) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
}
|
||||
resp := new(Response)
|
||||
if err := json.Unmarshal(body, resp); err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// 例如 无需重试的 code 码,code !=1 需要重试
|
||||
successCode := 1
|
||||
return resp.Code != successCode
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package go_gin_api_repo
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
func MockDemoGet() (body []byte) {
|
||||
res := new(demoGetResponse)
|
||||
res.Code = 1
|
||||
res.Msg = "ok"
|
||||
res.Data.Name = "AA"
|
||||
res.Data.Job = "AA_JOB"
|
||||
|
||||
body, _ = json.Marshal(res)
|
||||
return body
|
||||
}
|
||||
|
||||
func MockDemoPost() (body []byte) {
|
||||
res := new(demoPostResponse)
|
||||
res.Code = 1
|
||||
res.Msg = "ok"
|
||||
res.Data.Name = "BB"
|
||||
res.Data.Job = "BB_JOB"
|
||||
|
||||
body, _ = json.Marshal(res)
|
||||
return body
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package go_gin_api_repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/third_party_request"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
)
|
||||
|
||||
var authorization = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOjEwLCJVc2VyTmFtZSI6IjEyMyIsImV4cCI6MTYxMDcxODA3NCwiaWF0IjoxNjEwNjMxNjc0LCJpc3MiOiJnby1naW4tYXBpIiwibmJmIjoxNjEwNjMxNjc0fQ.S3T4MaIaz3XjkbJ-xkMDkwzuZ_jfZ8ZRf4cPMz0oXBE"
|
||||
|
||||
func TestDemoGet(t *testing.T) {
|
||||
res, err := DemoGet("Tom",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
//httpclient.WithTrace(ctx.Trace()),
|
||||
//httpclient.WithLogger(ctx.Logger()),
|
||||
httpclient.WithHeader("Authorization", authorization),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, retryVerify),
|
||||
httpclient.WithOnFailedAlarm("接口告警", new(third_party_request.AlarmEmail), alarmVerify),
|
||||
//httpclient.WithMock(MockDemoGet),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Log("get [demo/get] err", err)
|
||||
}
|
||||
|
||||
t.Log(res)
|
||||
}
|
||||
|
||||
func TestDemoPost(t *testing.T) {
|
||||
res, err := DemoPost("Jack",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
//httpclient.WithTrace(ctx.Trace()),
|
||||
//httpclient.WithLogger(ctx.Logger()),
|
||||
httpclient.WithHeader("Authorization", authorization),
|
||||
httpclient.WithMock(MockDemoPost),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Log("post [demo/post] err", err)
|
||||
}
|
||||
|
||||
t.Log(res)
|
||||
}
|
||||
|
||||
// 设置重试规则
|
||||
func retryVerify(body []byte) (shouldRetry bool) {
|
||||
if len(body) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
}
|
||||
resp := new(Response)
|
||||
if err := json.Unmarshal(body, resp); err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return resp.Code != 1
|
||||
}
|
||||
|
||||
// 设置告警规则
|
||||
func alarmVerify(body []byte) (shouldAlarm bool) {
|
||||
if len(body) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
}
|
||||
resp := new(Response)
|
||||
if err := json.Unmarshal(body, resp); err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return resp.Code != 1
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/token"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func AuthHandler(ctx core.Context) (userId int64, userName string, err errno.Error) {
|
||||
auth := ctx.GetHeader("Authorization")
|
||||
if auth == "" {
|
||||
err = code.ErrAuthorization.WithErr(errors.New("Header 中缺少 Authorization 参数"))
|
||||
return
|
||||
}
|
||||
|
||||
cfg := configs.Get().JWT
|
||||
claims, errParse := token.New(cfg.Secret).Parse(auth)
|
||||
if errParse != nil {
|
||||
err = code.ErrAuthorization.WithErr(errParse)
|
||||
return
|
||||
}
|
||||
|
||||
userId = claims.UserID
|
||||
if userId <= 0 {
|
||||
err = code.ErrAuthorization.WithErr(errors.New("claims.UserID <= 0 "))
|
||||
return
|
||||
}
|
||||
userName = claims.UserName
|
||||
return
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/controller/demo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/controller/user_handler"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/router/middleware/auth"
|
||||
"github.com/xinliangnote/go-gin-api/internal/graph/handler"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/metrics"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/notify"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo) (core.Mux, error) {
|
||||
|
||||
if logger == nil {
|
||||
return nil, errors.New("logger required")
|
||||
}
|
||||
|
||||
mux, err := core.New(logger,
|
||||
core.WithEnableCors(),
|
||||
core.WithEnableRate(),
|
||||
core.WithPanicNotify(notify.OnPanicNotify),
|
||||
core.WithRecordMetrics(metrics.RecordMetrics),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
demoHandler := demo.NewDemo(logger, db, cache)
|
||||
userHandler := user_handler.NewUserDemo(logger, db, cache)
|
||||
gqlHandler := handler.New(logger, db, cache)
|
||||
|
||||
gql := mux.Group("/graphql")
|
||||
{
|
||||
gql.GET("", gqlHandler.Playground())
|
||||
gql.POST("/query", gqlHandler.Query())
|
||||
}
|
||||
|
||||
// user_demo CURD
|
||||
user := mux.Group("/user", core.WrapAuthHandler(auth.AuthHandler))
|
||||
{
|
||||
user.POST("/create", userHandler.Create())
|
||||
user.PUT("/update", userHandler.UpdateNickNameByID())
|
||||
user.PATCH("/delete/:id", userHandler.Delete())
|
||||
user.GET("/info/:username", core.AliasForRecordMetrics("/user/info"), userHandler.Detail())
|
||||
}
|
||||
|
||||
// auth
|
||||
a := mux.Group("/auth")
|
||||
{
|
||||
a.POST("/get", demoHandler.Auth())
|
||||
}
|
||||
|
||||
// demo
|
||||
d := mux.Group("/demo", core.WrapAuthHandler(auth.AuthHandler)) //使用 auth 验证
|
||||
{
|
||||
// 为了演示 Trace ,增加了一些看起来无意义的调试信息和 SQL 信息。
|
||||
d.GET("/trace", demoHandler.Trace())
|
||||
|
||||
// 模拟数据
|
||||
d.GET("get/:name", core.AliasForRecordMetrics("/demo/get"), demoHandler.Get())
|
||||
d.POST("post", demoHandler.Post())
|
||||
}
|
||||
|
||||
return mux, nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_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 _ UserService = (*userSer)(nil)
|
||||
|
||||
type UserService interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
|
||||
Create(ctx core.Context, user *CreateUserInfo) (id int32, err error)
|
||||
UpdateNickNameByID(ctx core.Context, id int32, username string) (err error)
|
||||
GetUserByUserName(ctx core.Context, username string) (user *user_demo_repo.UserDemo, err error)
|
||||
Delete(ctx core.Context, id int32) (err error)
|
||||
}
|
||||
|
||||
type userSer struct {
|
||||
db db.Repo
|
||||
cache cache.Repo
|
||||
}
|
||||
|
||||
func NewUserService(db db.Repo, cache cache.Repo) UserService {
|
||||
return &userSer{
|
||||
db: db,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userSer) i() {}
|
||||
@@ -0,0 +1,25 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
type CreateUserInfo struct {
|
||||
UserName string `json:"user_name"` // 用户名
|
||||
NickName string `json:"nick_name"` // 昵称
|
||||
Mobile string `json:"mobile"` // 手机号
|
||||
}
|
||||
|
||||
func (u *userSer) Create(ctx core.Context, user *CreateUserInfo) (id int32, err error) {
|
||||
model := user_demo_repo.NewModel()
|
||||
model.UserName = user.UserName
|
||||
model.NickName = user.NickName
|
||||
model.Mobile = user.Mobile
|
||||
|
||||
id, err = model.Create(u.db.GetDbW().WithContext(ctx.RequestContext()))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
func (u *userSer) Delete(ctx core.Context, id int32) (err error) {
|
||||
model := user_demo_repo.NewModel()
|
||||
model.Id = id
|
||||
err = model.Delete(u.db.GetDbW().WithContext(ctx.RequestContext()))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
func (u *userSer) GetUserByUserName(ctx core.Context, username string) (user *user_demo_repo.UserDemo, err error) {
|
||||
user, err = user_demo_repo.NewQueryBuilder().
|
||||
WhereUserName(db_repo.EqualPredicate, username).
|
||||
QueryOne(u.db.GetDbR().WithContext(ctx.RequestContext()))
|
||||
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
user = user_demo_repo.NewModel()
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
func (u *userSer) UpdateNickNameByID(ctx core.Context, id int32, nickname string) (err error) {
|
||||
model := user_demo_repo.NewModel()
|
||||
model.Id = id
|
||||
|
||||
data := map[string]interface{}{
|
||||
"nick_name": nickname,
|
||||
}
|
||||
|
||||
err = model.Updates(u.db.GetDbW().WithContext(ctx.RequestContext()), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package user_service
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/model/user_model"
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_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 _ UserService = (*userSer)(nil)
|
||||
|
||||
type UserService interface {
|
||||
// i 为了避免被其他包实现
|
||||
i()
|
||||
|
||||
Create(ctx core.Context, user *user_model.CreateRequest) (id uint, err error)
|
||||
UpdateNickNameByID(ctx core.Context, id uint, username string) (err error)
|
||||
GetUserByUserName(ctx core.Context, username string) (user *user_model.UserDemo, err error)
|
||||
Delete(ctx core.Context, id uint) (err error)
|
||||
}
|
||||
|
||||
type userSer struct {
|
||||
db db.Repo
|
||||
cache cache.Repo
|
||||
userRepo user_demo_repo.UserRepo
|
||||
}
|
||||
|
||||
func NewUserService(db db.Repo, cache cache.Repo) UserService {
|
||||
userRepo := user_demo_repo.NewUserRepo(db)
|
||||
return &userSer{
|
||||
db: db,
|
||||
cache: cache,
|
||||
userRepo: userRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userSer) i() {}
|
||||
|
||||
func (u *userSer) Create(ctx core.Context, user *user_model.CreateRequest) (id uint, err error) {
|
||||
create := user_model.UserDemo{
|
||||
UserName: user.UserName,
|
||||
NickName: user.NickName,
|
||||
Mobile: user.Mobile,
|
||||
}
|
||||
|
||||
id, err = u.userRepo.Create(ctx, create)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (u *userSer) UpdateNickNameByID(ctx core.Context, id uint, username string) (err error) {
|
||||
err = u.userRepo.UpdateNickNameByID(ctx, id, username)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *userSer) GetUserByUserName(ctx core.Context, username string) (user *user_model.UserDemo, err error) {
|
||||
user, err = u.userRepo.GetUserByUserName(ctx, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (u *userSer) Delete(ctx core.Context, id uint) (err error) {
|
||||
err = u.userRepo.Delete(ctx, id)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+15
-5
@@ -1,8 +1,11 @@
|
||||
package third_party_request
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/configs"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/mail"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 实现 AlarmObject 告警
|
||||
@@ -10,15 +13,22 @@ var _ httpclient.AlarmObject = (*AlarmEmail)(nil)
|
||||
|
||||
type AlarmEmail struct{}
|
||||
|
||||
// 邮件告警方式
|
||||
func (a *AlarmEmail) Send(subject, body string) error {
|
||||
cfg := configs.Get().Mail
|
||||
if cfg.Host == "" || cfg.Port == 0 || cfg.User == "" || cfg.Pass == "" || cfg.To == "" {
|
||||
return errors.New("mail config error")
|
||||
}
|
||||
|
||||
options := &mail.Options{
|
||||
MailHost: "smtp.163.com",
|
||||
MailPort: 465,
|
||||
MailUser: "xx@163.com",
|
||||
MailPass: "",
|
||||
MailTo: "",
|
||||
MailHost: cfg.Host,
|
||||
MailPort: cfg.Port,
|
||||
MailUser: cfg.User,
|
||||
MailPass: cfg.Pass,
|
||||
MailTo: cfg.To,
|
||||
Subject: subject,
|
||||
Body: body,
|
||||
}
|
||||
|
||||
return mail.Send(options)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package go_gin_api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 接口地址
|
||||
var demoGetApi = "http://127.0.0.1:9999/demo/get/"
|
||||
|
||||
// 接口返回结构
|
||||
type demoGetResponse struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
// 发起请求
|
||||
func DemoGet(name string, opts ...httpclient.Option) (res *demoGetResponse, err error) {
|
||||
api := demoGetApi + name
|
||||
body, err := httpclient.Get(api, nil, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = new(demoGetResponse)
|
||||
err = json.Unmarshal(body, res)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "DemoGet json unmarshal error")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 设置重试规则
|
||||
func DemoGetRetryVerify(body []byte) (shouldRetry bool) {
|
||||
if len(body) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 设置告警规则
|
||||
func DemoGetAlarmVerify(body []byte) (shouldAlarm bool) {
|
||||
if len(body) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 设置 Mock 数据
|
||||
func DemoGetMock() (body []byte) {
|
||||
res := new(demoGetResponse)
|
||||
res.Name = "AA"
|
||||
res.Job = "AA_JOB"
|
||||
|
||||
body, _ = json.Marshal(res)
|
||||
return body
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package go_gin_api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/third_party_request"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
)
|
||||
|
||||
var demoGetAuthorization = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOjEsIlVzZXJOYW1lIjoieGlubGlhbmdub3RlIiwiZXhwIjoxNjEzODI3MTEzLCJpYXQiOjE2MTM3NDA3MTMsIm5iZiI6MTYxMzc0MDcxM30.SnooP1ikO33ryGPdohsmOKqISa-bWzMkMvUNb5f2zc0"
|
||||
|
||||
func TestDemoGet(t *testing.T) {
|
||||
res, err := DemoGet("Tom",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
//httpclient.WithTrace(ctx.Trace()),
|
||||
//httpclient.WithLogger(ctx.Logger()),
|
||||
httpclient.WithHeader("Authorization", demoGetAuthorization),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, DemoGetRetryVerify),
|
||||
httpclient.WithOnFailedAlarm("接口告警", new(third_party_request.AlarmEmail), DemoGetAlarmVerify),
|
||||
httpclient.WithMock(DemoGetMock),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Log("get [demo/get] err", err)
|
||||
}
|
||||
|
||||
t.Log(res)
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package go_gin_api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 接口地址
|
||||
var demoPostApi = "http://127.0.0.1:9999/demo/post/"
|
||||
|
||||
// 接口返回结构
|
||||
type demoPostResponse struct {
|
||||
Name string `json:"name"`
|
||||
Job string `json:"job"`
|
||||
}
|
||||
|
||||
// 发起请求
|
||||
func DemoPost(name string, opts ...httpclient.Option) (res *demoPostResponse, err error) {
|
||||
api := demoPostApi
|
||||
params := url.Values{}
|
||||
params.Set("name", name)
|
||||
body, err := httpclient.PostForm(api, params, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = new(demoPostResponse)
|
||||
err = json.Unmarshal(body, res)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "DemoPost json unmarshal error")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 设置重试规则
|
||||
func DemoPostRetryVerify(body []byte) (shouldRetry bool) {
|
||||
if len(body) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 设置告警规则
|
||||
func DemoPostAlarmVerify(body []byte) (shouldAlarm bool) {
|
||||
if len(body) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 设置 Mock 数据
|
||||
func DemoPostMock() (body []byte) {
|
||||
res := new(demoPostResponse)
|
||||
res.Name = "BB"
|
||||
res.Job = "BB_JOB"
|
||||
|
||||
body, _ = json.Marshal(res)
|
||||
return body
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package go_gin_api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/third_party_request"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
|
||||
)
|
||||
|
||||
var demoPostAuthorization = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOjEsIlVzZXJOYW1lIjoieGlubGlhbmdub3RlIiwiZXhwIjoxNjEzODI3MTEzLCJpYXQiOjE2MTM3NDA3MTMsIm5iZiI6MTYxMzc0MDcxM30.SnooP1ikO33ryGPdohsmOKqISa-bWzMkMvUNb5f2zc0"
|
||||
|
||||
func TestDemoPost(t *testing.T) {
|
||||
res, err := DemoPost("Jack",
|
||||
httpclient.WithTTL(time.Second*5),
|
||||
//httpclient.WithTrace(ctx.Trace()),
|
||||
//httpclient.WithLogger(ctx.Logger()),
|
||||
httpclient.WithHeader("Authorization", demoPostAuthorization),
|
||||
httpclient.WithOnFailedRetry(3, time.Second*1, DemoPostRetryVerify),
|
||||
httpclient.WithOnFailedAlarm("接口告警", new(third_party_request.AlarmEmail), DemoPostAlarmVerify),
|
||||
httpclient.WithMock(DemoPostMock),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Log("post [demo/post] err", err)
|
||||
}
|
||||
|
||||
t.Log(res)
|
||||
}
|
||||
Vendored
+13
-3
@@ -34,8 +34,9 @@ type Repo interface {
|
||||
Expire(key string, ttl time.Duration) bool
|
||||
ExpireAt(key string, ttl time.Time) bool
|
||||
Del(keys ...string) bool
|
||||
Exists(keys ...string) bool
|
||||
Incr(key string, options ...Option) int64
|
||||
Close()
|
||||
Close() error
|
||||
}
|
||||
|
||||
type cacheRepo struct {
|
||||
@@ -148,6 +149,15 @@ func (c *cacheRepo) ExpireAt(key string, ttl time.Time) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
//
|
||||
func (c *cacheRepo) Exists(keys ...string) bool {
|
||||
if len(keys) == 0 {
|
||||
return true
|
||||
}
|
||||
value, _ := c.client.Exists(keys...).Result()
|
||||
return value > 0
|
||||
}
|
||||
|
||||
// Del del some key from redis
|
||||
func (c *cacheRepo) Del(keys ...string) bool {
|
||||
if len(keys) == 0 {
|
||||
@@ -179,8 +189,8 @@ func (c *cacheRepo) Incr(key string, options ...Option) int64 {
|
||||
}
|
||||
|
||||
// Close close redis client
|
||||
func (c *cacheRepo) Close() {
|
||||
c.client.Close()
|
||||
func (c *cacheRepo) Close() error {
|
||||
return c.client.Close()
|
||||
}
|
||||
|
||||
// WithTrace 设置trace信息
|
||||
|
||||
@@ -90,13 +90,16 @@ type Context interface {
|
||||
setLogger(logger *zap.Logger)
|
||||
|
||||
// Payload 正确返回
|
||||
Payload(payload errno.Error)
|
||||
getPayload() errno.Error
|
||||
Payload(payload interface{})
|
||||
getPayload() interface{}
|
||||
|
||||
// GraphPayload GraphQL返回值 与 api 返回结构不同
|
||||
GraphPayload(payload interface{})
|
||||
getGraphPayload() interface{}
|
||||
|
||||
// HTML 返回界面
|
||||
HTML(name string, obj interface{})
|
||||
|
||||
// AbortWithError 错误返回
|
||||
AbortWithError(err errno.Error)
|
||||
abortError() errno.Error
|
||||
@@ -120,6 +123,10 @@ type Context interface {
|
||||
Alias() string
|
||||
setAlias(path string)
|
||||
|
||||
// RequestInputParams 获取所有参数
|
||||
RequestInputParams() url.Values
|
||||
// RequestPostFormParams 获取 PostForm 参数
|
||||
RequestPostFormParams() url.Values
|
||||
// Request 获取 Request 对象
|
||||
Request() *http.Request
|
||||
// RawData 获取 Request.Body
|
||||
@@ -225,14 +232,14 @@ func (c *context) setLogger(logger *zap.Logger) {
|
||||
c.ctx.Set(_LoggerName, logger)
|
||||
}
|
||||
|
||||
func (c *context) getPayload() errno.Error {
|
||||
func (c *context) getPayload() interface{} {
|
||||
if payload, ok := c.ctx.Get(_PayloadName); ok != false {
|
||||
return payload.(errno.Error)
|
||||
return payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *context) Payload(payload errno.Error) {
|
||||
func (c *context) Payload(payload interface{}) {
|
||||
c.ctx.Set(_PayloadName, payload)
|
||||
}
|
||||
|
||||
@@ -247,6 +254,10 @@ func (c *context) GraphPayload(payload interface{}) {
|
||||
c.ctx.Set(_GraphPayloadName, payload)
|
||||
}
|
||||
|
||||
func (c *context) HTML(name string, obj interface{}) {
|
||||
c.ctx.HTML(200, name+".html", obj)
|
||||
}
|
||||
|
||||
func (c *context) Header() http.Header {
|
||||
header := c.ctx.Request.Header
|
||||
|
||||
@@ -326,6 +337,18 @@ func (c *context) setAlias(path string) {
|
||||
}
|
||||
}
|
||||
|
||||
// RequestInputParams 获取所有参数
|
||||
func (c *context) RequestInputParams() url.Values {
|
||||
_ = c.ctx.Request.ParseForm()
|
||||
return c.ctx.Request.Form
|
||||
}
|
||||
|
||||
// RequestPostFormParams 获取 PostForm 参数
|
||||
func (c *context) RequestPostFormParams() url.Values {
|
||||
_ = c.ctx.Request.ParseForm()
|
||||
return c.ctx.Request.PostForm
|
||||
}
|
||||
|
||||
// Request 获取 Request
|
||||
func (c *context) Request() *http.Request {
|
||||
return c.ctx.Request
|
||||
|
||||
+41
-21
@@ -246,6 +246,9 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
fmt.Println(color.Green(fmt.Sprintf("* [register port %s]", configs.ProjectPort())))
|
||||
fmt.Println(color.Green(fmt.Sprintf("* [register env %s]", env.Active().Value())))
|
||||
|
||||
mux.engine.StaticFS("bootstrap", http.Dir("./assets/bootstrap"))
|
||||
mux.engine.LoadHTMLGlob("./assets/templates/**/*")
|
||||
|
||||
// withoutLogPaths 这些请求,默认不记录日志
|
||||
withoutTracePaths := map[string]bool{
|
||||
"/metrics": true,
|
||||
@@ -273,13 +276,17 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
}
|
||||
|
||||
if !opt.disablePProf {
|
||||
pprof.Register(mux.engine) // register pprof to gin
|
||||
fmt.Println(color.Green("* [register pprof]"))
|
||||
if !env.Active().IsPro() {
|
||||
pprof.Register(mux.engine) // register pprof to gin
|
||||
fmt.Println(color.Green("* [register pprof]"))
|
||||
}
|
||||
}
|
||||
|
||||
if !opt.disableSwagger {
|
||||
mux.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // register swagger
|
||||
fmt.Println(color.Green("* [register swagger]"))
|
||||
if !env.Active().IsPro() {
|
||||
mux.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // register swagger
|
||||
fmt.Println(color.Green("* [register swagger]"))
|
||||
}
|
||||
}
|
||||
|
||||
if !opt.disablePrometheus {
|
||||
@@ -336,7 +343,11 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
if err := recover(); err != nil {
|
||||
stackInfo := string(debug.Stack())
|
||||
logger.Error("got panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo))
|
||||
context.AbortWithError(code.ErrServer)
|
||||
context.AbortWithError(errno.NewError(
|
||||
http.StatusInternalServerError,
|
||||
code.ServerError,
|
||||
code.Text(code.ServerError)),
|
||||
)
|
||||
|
||||
if notify := opt.panicNotify; notify != nil {
|
||||
notify(context, err, stackInfo)
|
||||
@@ -348,7 +359,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
}
|
||||
|
||||
var (
|
||||
response errno.Error
|
||||
response interface{}
|
||||
businessCode int
|
||||
businessCodeMsg string
|
||||
abortErr error
|
||||
@@ -364,23 +375,28 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
if err := context.abortError(); err != nil { // customer err
|
||||
multierr.AppendInto(&abortErr, err.GetErr())
|
||||
response = err
|
||||
businessCode = err.GetBusinessCode()
|
||||
businessCodeMsg = err.GetMsg()
|
||||
|
||||
if x := context.Trace(); x != nil {
|
||||
context.SetHeader(trace.Header, x.ID())
|
||||
traceId = x.ID()
|
||||
}
|
||||
|
||||
ctx.JSON(err.GetHttpCode(), &code.Failure{
|
||||
Code: businessCode,
|
||||
Message: businessCodeMsg,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
response = context.getPayload()
|
||||
}
|
||||
|
||||
if response != nil {
|
||||
if x := context.Trace(); x != nil {
|
||||
context.SetHeader(trace.Header, x.ID())
|
||||
response.WithID(x.ID())
|
||||
traceId = x.ID()
|
||||
} else {
|
||||
response.WithID("")
|
||||
if response != nil {
|
||||
if x := context.Trace(); x != nil {
|
||||
context.SetHeader(trace.Header, x.ID())
|
||||
traceId = x.ID()
|
||||
}
|
||||
ctx.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
businessCode = response.GetBusinessCode()
|
||||
businessCodeMsg = response.GetMsg()
|
||||
ctx.JSON(response.GetHttpCode(), response)
|
||||
}
|
||||
|
||||
graphResponse = context.getGraphPayload()
|
||||
@@ -464,7 +480,11 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
defer releaseContext(context)
|
||||
|
||||
if !limiter.Allow() {
|
||||
context.AbortWithError(code.ErrManyRequest)
|
||||
context.AbortWithError(errno.NewError(
|
||||
http.StatusTooManyRequests,
|
||||
code.TooManyRequests,
|
||||
code.Text(code.TooManyRequests)),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -489,7 +509,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
|
||||
Host: ctx.Host(),
|
||||
Status: "ok",
|
||||
}
|
||||
ctx.Payload(code.OK.WithData(resp))
|
||||
ctx.Payload(resp)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
var _ Repo = (*dbRepo)(nil)
|
||||
@@ -80,6 +81,9 @@ func dbConnect(user, pass, addr, dbName string) (*gorm.DB, error) {
|
||||
"Local")
|
||||
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
SingularTable: true,
|
||||
},
|
||||
//Logger: logger.Default.LogMode(logger.Info), // 日志配置
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var _ ClientConn = (*clientConn)(nil)
|
||||
|
||||
type ClientConn interface {
|
||||
i()
|
||||
Conn() *grpc.ClientConn
|
||||
}
|
||||
|
||||
type clientConn struct {
|
||||
conn *grpc.ClientConn
|
||||
}
|
||||
|
||||
func New() (ClientConn, error) {
|
||||
|
||||
// TODO 需从配置文件中获取
|
||||
//target := "127.0.0.1:9988"
|
||||
//secret := "abcdef"
|
||||
//
|
||||
//clientInterceptor := NewClientInterceptor(func(message []byte) (authorization string, err error) {
|
||||
// return GenerateSign(secret, message)
|
||||
//})
|
||||
//
|
||||
//conn, err := grpclient.New(target,
|
||||
// grpclient.WithKeepAlive(keepAlive),
|
||||
// grpclient.WithDialTimeout(time.Second*5),
|
||||
// grpclient.WithUnaryInterceptor(clientInterceptor.UnaryInterceptor),
|
||||
//)
|
||||
//
|
||||
//return &clientConn{
|
||||
// conn: conn,
|
||||
//}, err
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *clientConn) i() {}
|
||||
|
||||
func (c *clientConn) Conn() *grpc.ClientConn {
|
||||
return c.conn
|
||||
}
|
||||
|
||||
func ContextWithValueAndTimeout(value interface{}, duration time.Duration) context.Context {
|
||||
ctx, _ := context.WithTimeout(context.Background(), duration)
|
||||
return context.WithValue(ctx, ClientWithContextKey, value)
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/api/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/notify"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/p"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/time_parse"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/trace"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const (
|
||||
// Trace ID
|
||||
TraceID = "trace-id"
|
||||
)
|
||||
|
||||
type clientWithContextKeyType struct{ name string }
|
||||
|
||||
var ClientWithContextKey = clientWithContextKeyType{"_client_with_context"}
|
||||
|
||||
// ClientInterceptor the client's interceptor
|
||||
type ClientInterceptor struct {
|
||||
sign Sign
|
||||
}
|
||||
|
||||
// NewClientInterceptor create a client interceptor
|
||||
func NewClientInterceptor(sign Sign) *ClientInterceptor {
|
||||
return &ClientInterceptor{
|
||||
sign: sign,
|
||||
}
|
||||
}
|
||||
|
||||
// UnaryInterceptor a interceptor for client unary operations
|
||||
func (c *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
var (
|
||||
invokerErr error
|
||||
ts = time.Now()
|
||||
coreContext = ctx.Value(ClientWithContextKey).(core.Context)
|
||||
)
|
||||
|
||||
defer func() { // double recover for safety
|
||||
if err := recover(); err != nil {
|
||||
stackInfo := string(debug.Stack())
|
||||
coreContext.Logger().Error("UnaryInterceptor got double panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo))
|
||||
coreContext.AbortWithError(errno.NewError(
|
||||
http.StatusInternalServerError,
|
||||
code.ServerError,
|
||||
code.Text(code.ServerError)),
|
||||
)
|
||||
notify.OnPanicNotify(coreContext, err, stackInfo)
|
||||
}
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
stackInfo := string(debug.Stack())
|
||||
coreContext.Logger().Error("UnaryInterceptor got panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo))
|
||||
coreContext.AbortWithError(errno.NewError(
|
||||
http.StatusInternalServerError,
|
||||
code.ServerError,
|
||||
code.Text(code.ServerError)),
|
||||
)
|
||||
notify.OnPanicNotify(coreContext, err, stackInfo)
|
||||
}
|
||||
|
||||
if coreContext.Trace() != nil {
|
||||
var mapReq, mapReply map[string]interface{}
|
||||
mapReq, err := ProtoMessage2Map(req.(proto.Message))
|
||||
if err != nil {
|
||||
p.Println("req ProtoMessage2Map err", err, p.WithTrace(coreContext.Trace()))
|
||||
}
|
||||
|
||||
mapReply, err = ProtoMessage2Map(reply.(proto.Message))
|
||||
if err != nil {
|
||||
p.Println("reply ProtoMessage2Map err", err, p.WithTrace(coreContext.Trace()))
|
||||
}
|
||||
|
||||
meta, _ := metadata.FromOutgoingContext(ctx)
|
||||
|
||||
gRPCTrace := new(trace.Grpc)
|
||||
gRPCTrace.Timestamp = time_parse.CSTLayoutString()
|
||||
gRPCTrace.Addr = cc.Target()
|
||||
gRPCTrace.Method = method
|
||||
gRPCTrace.Meta = meta
|
||||
gRPCTrace.Request = mapReq
|
||||
gRPCTrace.Response = mapReply
|
||||
gRPCTrace.CostSeconds = time.Since(ts).Seconds()
|
||||
|
||||
if invokerErr != nil {
|
||||
statusErr, ok := status.FromError(invokerErr)
|
||||
if ok {
|
||||
gRPCTrace.Code = statusErr.Code().String()
|
||||
gRPCTrace.Message = statusErr.Message()
|
||||
}
|
||||
}
|
||||
|
||||
coreContext.Trace().AppendGRPC(gRPCTrace)
|
||||
}
|
||||
}()
|
||||
|
||||
if c.sign != nil {
|
||||
var (
|
||||
raw string
|
||||
signature string
|
||||
err error
|
||||
)
|
||||
|
||||
if req != nil {
|
||||
if raw, err = ProtoMessage2JSON(req.(proto.Message)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if signature, err = c.sign([]byte(raw)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
meta, _ := metadata.FromOutgoingContext(ctx)
|
||||
if meta == nil {
|
||||
meta = make(metadata.MD)
|
||||
}
|
||||
|
||||
meta.Set(ProxyAuthorization, signature)
|
||||
ctx = metadata.NewOutgoingContext(ctx, meta)
|
||||
}
|
||||
|
||||
if coreContext.Trace() != nil {
|
||||
meta, _ := metadata.FromOutgoingContext(ctx)
|
||||
if meta == nil {
|
||||
meta = make(metadata.MD)
|
||||
}
|
||||
|
||||
meta.Set(TraceID, coreContext.Trace().ID())
|
||||
ctx = metadata.NewOutgoingContext(ctx, meta)
|
||||
}
|
||||
|
||||
invokerErr = invoker(ctx, method, req, reply, cc, opts...)
|
||||
return invokerErr
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/keepalive"
|
||||
)
|
||||
|
||||
var (
|
||||
keepAlive = &keepalive.ClientParameters{
|
||||
Time: 10 * time.Second,
|
||||
Timeout: time.Second,
|
||||
PermitWithoutStream: true,
|
||||
}
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user