diff --git a/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js b/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js new file mode 100644 index 0000000..5a394bf --- /dev/null +++ b/assets/bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js @@ -0,0 +1,589 @@ +/* ========================================================== + * bootstrap-maxlength.js v1.9.0 + * + * Copyright (c) 2013-2020 Maurizio Napoleoni; + * + * Licensed under the terms of the MIT license. + * See: https://github.com/mimo84/bootstrap-maxlength/blob/master/LICENSE + * ========================================================== */ +/*global jQuery*/ + +(function ($) { + 'use strict'; + /** + * We need an event when the elements are destroyed + * because if an input is removed, we have to remove the + * maxlength object associated (if any). + * From: + * http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom + */ + if (!$.event.special.destroyed) { + $.event.special.destroyed = { + remove: function (o) { + if (o.handler) { + o.handler(); + } + } + }; + } + + + $.fn.extend({ + maxlength: function (options, callback) { + var documentBody = $('body'), + defaults = { + showOnReady: false, // true to always show when indicator is ready + alwaysShow: true, // if true the indicator it's always shown. + threshold: 0, // Represents how many chars left are needed to show up the counter + warningClass: 'small form-text text-muted', + limitReachedClass: 'small form-text text-danger', + separator: ' / ', + preText: '', + postText: '', + showMaxLength: true, + placement: 'bottom-right-inside', + message: null, // an alternative way to provide the message text + showCharsTyped: true, // show the number of characters typed and not the number of characters remaining + validate: false, // if the browser doesn't support the maxlength attribute, attempt to type more than the indicated chars, will be prevented. + utf8: false, // counts using bytesize rather than length. eg: '£' is counted as 2 characters. + appendToParent: false, // append the indicator to the input field's parent instead of body + twoCharLinebreak: true, // count linebreak as 2 characters to match IE/Chrome textarea validation. As well as DB storage. + customMaxAttribute: null, // null = use maxlength attribute and browser functionality, string = use specified attribute instead. + allowOverMax: false, // Form submit validation is handled on your own. when maxlength has been exceeded 'overmax' class added to element + zIndex: 1099 + }; + + if ($.isFunction(options) && !callback) { + callback = options; + options = {}; + } + options = $.extend(defaults, options); + + + /** + * Return the byte count of the specified character in UTF8 encoding. + * Note: This won't cover UTF-8 characters that are 4 bytes long. + * + * @param input + * @return {number} + */ + function utf8CharByteCount(character) { + var c = character.charCodeAt(); + // Not c then 0, else c < 128 then 1, else c < 2048 then 2, else 3 + return !c ? 0 : c < 128 ? 1 : c < 2048 ? 2 : 3; + } + + /** + * Return the length of the specified input in UTF8 encoding. + * + * @param input + * @return {number} + */ + function utf8Length(string) { + return string.split("") + .map(utf8CharByteCount) + // Prevent reduce from throwing an error if the string is empty. + .concat(0) + .reduce(function (sum, val) { + return sum + val; + }); + } + + /** + * Return the length of the specified input. + * + * @param input + * @return {number} + */ + function inputLength(input) { + var text = input.val(); + + if (options.twoCharLinebreak) { + // Count all line breaks as 2 characters + text = text.replace(/\r(?!\n)|\n(?!\r)/g, '\r\n'); + } else { + // Remove all double-character (\r\n) linebreaks, so they're counted only once. + text = text.replace(/(?:\r\n|\r|\n)/g, '\n'); + } + + var currentLength = 0; + + if (options.utf8) { + currentLength = utf8Length(text); + } else { + currentLength = text.length; + } + + // Remove "C:\fakepath\" from counter when using file input + // Fix https://github.com/mimo84/bootstrap-maxlength/issues/146 + if (input.prop("type") === "file" && input.val() !== "") { + currentLength -= 12; + } + + return currentLength; + } + + /** + * Truncate the text of the specified input. + * + * @param input + * @param limit + */ + function truncateChars(input, maxlength) { + var text = input.val(); + + if (options.twoCharLinebreak) { + text = text.replace(/\r(?!\n)|\n(?!\r)/g, '\r\n'); + + if (text[text.length - 1] === '\n') { + maxlength -= text.length % 2; + } + } + + if (options.utf8) { + var indexedSize = text.split("").map(utf8CharByteCount); + for ( + var removedBytes = 0, + bytesPastMax = utf8Length(text) - maxlength; removedBytes < bytesPastMax; removedBytes += indexedSize.pop() + ); + maxlength -= (maxlength - indexedSize.length); + } + + input.val(text.substr(0, maxlength)); + } + + /** + * Return true if the indicator should be showing up. + * + * @param input + * @param threshold + * @param maxlength + * @return {number} + */ + function charsLeftThreshold(input, threshold, maxlength) { + var output = true; + if (!options.alwaysShow && (maxlength - inputLength(input) > threshold)) { + output = false; + } + return output; + } + + /** + * Returns how many chars are left to complete the fill up of the form. + * + * @param input + * @param maxlength + * @return {number} + */ + function remainingChars(input, maxlength) { + var length = maxlength - inputLength(input); + return length; + } + + /** + * When called displays the indicator. + * + * @param indicator + */ + function showRemaining(currentInput, indicator) { + indicator.css({ + display: 'block' + }); + currentInput.trigger('maxlength.shown'); + } + + /** + * When called shows the indicator. + * + * @param indicator + */ + function hideRemaining(currentInput, indicator) { + + if (options.alwaysShow) { + return; + } + + indicator.css({ + display: 'none' + }); + currentInput.trigger('maxlength.hidden'); + } + + /** + * This function updates the value in the indicator + * + * @param maxLengthThisInput + * @param typedChars + * @return String + */ + function updateMaxLengthHTML(currentInputText, maxLengthThisInput, typedChars) { + var output = ''; + if (options.message) { + if (typeof options.message === 'function') { + output = options.message(currentInputText, maxLengthThisInput); + } else { + output = options.message.replace('%charsTyped%', typedChars) + .replace('%charsRemaining%', maxLengthThisInput - typedChars) + .replace('%charsTotal%', maxLengthThisInput); + } + } else { + if (options.preText) { + output += options.preText; + } + if (!options.showCharsTyped) { + output += maxLengthThisInput - typedChars; + } else { + output += typedChars; + } + if (options.showMaxLength) { + output += options.separator + maxLengthThisInput; + } + if (options.postText) { + output += options.postText; + } + } + return output; + } + + /** + * This function updates the value of the counter in the indicator. + * Wants as parameters: the number of remaining chars, the element currently managed, + * the maxLength for the current input and the indicator generated for it. + * + * @param remaining + * @param currentInput + * @param maxLengthCurrentInput + * @param maxLengthIndicator + */ + function manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator) { + if (maxLengthIndicator) { + maxLengthIndicator.html(updateMaxLengthHTML(currentInput.val(), maxLengthCurrentInput, (maxLengthCurrentInput - remaining))); + + if (remaining > 0) { + if (charsLeftThreshold(currentInput, options.threshold, maxLengthCurrentInput)) { + showRemaining(currentInput, maxLengthIndicator.removeClass(options.limitReachedClass).addClass(options.warningClass)); + } else { + hideRemaining(currentInput, maxLengthIndicator); + } + } else { + showRemaining(currentInput, maxLengthIndicator.removeClass(options.warningClass).addClass(options.limitReachedClass)); + } + } + + if (options.customMaxAttribute) { + // class to use for form validation on custom maxlength attribute + if (remaining < 0) { + currentInput.addClass('overmax'); + } else { + currentInput.removeClass('overmax'); + } + } + } + + /** + * This function returns an object containing all the + * informations about the position of the current input + * + * @param currentInput + * @return object {bottom height left right top width} + * + */ + function getPosition(currentInput) { + var el = currentInput[0]; + return $.extend({}, (typeof el.getBoundingClientRect === 'function') ? el.getBoundingClientRect() : { + width: el.offsetWidth, + height: el.offsetHeight + }, currentInput.offset()); + } + + /** + * This function places the maxLengthIndicator based on placement config object. + * + * @param {object} placement + * @param {$} maxLengthIndicator + * @return null + * + */ + function placeWithCSS(placement, maxLengthIndicator) { + if (!placement || !maxLengthIndicator) { + return; + } + + var POSITION_KEYS = [ + 'top', + 'bottom', + 'left', + 'right', + 'position' + ]; + + var cssPos = {}; + + // filter css properties to position + $.each(POSITION_KEYS, function (i, key) { + var val = options.placement[key]; + if (typeof val !== 'undefined') { + cssPos[key] = val; + } + }); + + maxLengthIndicator.css(cssPos); + + return; + } + + + /** + * This function places the maxLengthIndicator at the + * top / bottom / left / right of the currentInput + * + * @param currentInput + * @param maxLengthIndicator + * @return null + * + */ + function place(currentInput, maxLengthIndicator) { + var pos = getPosition(currentInput); + + // Supports custom placement handler + if ($.type(options.placement) === 'function') { + options.placement(currentInput, maxLengthIndicator, pos); + return; + } + + // Supports custom placement via css positional properties + if ($.isPlainObject(options.placement)) { + placeWithCSS(options.placement, maxLengthIndicator); + return; + } + + var inputOuter = currentInput.outerWidth(), + outerWidth = maxLengthIndicator.outerWidth(), + actualWidth = maxLengthIndicator.width(), + actualHeight = maxLengthIndicator.height(); + + // get the right position if the indicator is appended to the input's parent + if (options.appendToParent) { + pos.top -= currentInput.parent().offset().top; + pos.left -= currentInput.parent().offset().left; + } + + switch (options.placement) { + case 'bottom': + maxLengthIndicator.css({ + top: pos.top + pos.height, + left: pos.left + pos.width / 2 - actualWidth / 2 + }); + break; + case 'top': + maxLengthIndicator.css({ + top: pos.top - actualHeight, + left: pos.left + pos.width / 2 - actualWidth / 2 + }); + break; + case 'left': + maxLengthIndicator.css({ + top: pos.top + pos.height / 2 - actualHeight / 2, + left: pos.left - actualWidth + }); + break; + case 'right': + maxLengthIndicator.css({ + top: pos.top + pos.height / 2 - actualHeight / 2, + left: pos.left + pos.width + }); + break; + case 'bottom-right': + maxLengthIndicator.css({ + top: pos.top + pos.height, + left: pos.left + pos.width + }); + break; + case 'top-right': + maxLengthIndicator.css({ + top: pos.top - actualHeight, + left: pos.left + inputOuter + }); + break; + case 'top-left': + maxLengthIndicator.css({ + top: pos.top - actualHeight, + left: pos.left - outerWidth + }); + break; + case 'bottom-left': + maxLengthIndicator.css({ + top: pos.top + currentInput.outerHeight(), + left: pos.left - outerWidth + }); + break; + case 'centered-right': + maxLengthIndicator.css({ + top: pos.top + (actualHeight / 2), + left: pos.left + inputOuter - outerWidth - 3 + }); + break; + + // Some more options for placements + case 'bottom-right-inside': + maxLengthIndicator.css({ + top: pos.top + pos.height, + left: pos.left + pos.width - outerWidth + }); + break; + case 'top-right-inside': + maxLengthIndicator.css({ + top: pos.top - actualHeight, + left: pos.left + inputOuter - outerWidth + }); + break; + case 'top-left-inside': + maxLengthIndicator.css({ + top: pos.top - actualHeight, + left: pos.left + }); + break; + case 'bottom-left-inside': + maxLengthIndicator.css({ + top: pos.top + currentInput.outerHeight(), + left: pos.left + }); + break; + } + } + + /** + * This function returns true if the indicator position needs to + * be recalculated when the currentInput changes + * + * @return {boolean} + * + */ + function isPlacementMutable() { + return options.placement === 'bottom-right-inside' || options.placement === 'top-right-inside' || typeof options.placement === 'function' || (options.message && typeof options.message === 'function'); + } + + /** + * This function retrieves the maximum length of currentInput + * + * @param currentInput + * @return {number} + * + */ + function getMaxLength(currentInput) { + var max = currentInput.attr('maxlength') || options.customMaxAttribute; + + if (options.customMaxAttribute && !options.allowOverMax) { + var custom = currentInput.attr(options.customMaxAttribute); + if (!max || custom < max) { + max = custom; + } + } + + if (!max) { + max = currentInput.attr('size'); + } + return max; + } + + return this.each(function () { + + var currentInput = $(this), + maxLengthCurrentInput, + maxLengthIndicator; + + $(window).resize(function () { + if (maxLengthIndicator) { + place(currentInput, maxLengthIndicator); + } + }); + + function firstInit() { + var maxlengthContent = updateMaxLengthHTML(currentInput.val(), maxLengthCurrentInput, '0'); + maxLengthCurrentInput = getMaxLength(currentInput); + + if (!maxLengthIndicator) { + maxLengthIndicator = $('').css({ + display: 'none', + position: 'absolute', + whiteSpace: 'nowrap', + zIndex: options.zIndex + }).html(maxlengthContent); + } + + // We need to detect resizes if we are dealing with a textarea: + if (currentInput.is('textarea')) { + currentInput.data('maxlenghtsizex', currentInput.outerWidth()); + currentInput.data('maxlenghtsizey', currentInput.outerHeight()); + + currentInput.mouseup(function () { + if (currentInput.outerWidth() !== currentInput.data('maxlenghtsizex') || currentInput.outerHeight() !== currentInput.data('maxlenghtsizey')) { + place(currentInput, maxLengthIndicator); + } + + currentInput.data('maxlenghtsizex', currentInput.outerWidth()); + currentInput.data('maxlenghtsizey', currentInput.outerHeight()); + }); + } + + if (options.appendToParent) { + currentInput.parent().append(maxLengthIndicator); + currentInput.parent().css('position', 'relative'); + } else { + documentBody.append(maxLengthIndicator); + } + + var remaining = remainingChars(currentInput, getMaxLength(currentInput)); + manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator); + place(currentInput, maxLengthIndicator); + } + + if (options.showOnReady) { + currentInput.ready(function () { + firstInit(); + }); + } else { + currentInput.focus(function () { + firstInit(); + }); + } + + currentInput.on('maxlength.reposition', function () { + place(currentInput, maxLengthIndicator); + }); + + + currentInput.on('destroyed', function () { + if (maxLengthIndicator) { + maxLengthIndicator.remove(); + } + }); + + currentInput.on('blur', function () { + if (maxLengthIndicator && !options.showOnReady) { + maxLengthIndicator.remove(); + } + }); + + currentInput.on('input', function () { + var maxlength = getMaxLength(currentInput), + remaining = remainingChars(currentInput, maxlength), + output = true; + + if (options.validate && remaining < 0) { + truncateChars(currentInput, maxlength); + output = false; + } else { + manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator); + } + + if (isPlacementMutable()) { + place(currentInput, maxLengthIndicator); + } + + return output; + }); + }); + } + }); +}(jQuery)); \ No newline at end of file diff --git a/assets/bootstrap/js/index.min.js b/assets/bootstrap/js/index.min.js index 49884cb..b85f6bd 100644 --- a/assets/bootstrap/js/index.min.js +++ b/assets/bootstrap/js/index.min.js @@ -113,7 +113,7 @@ // 选项卡 $('#iframe-content').multitabs({ iframe : true, - refresh : 'no', // iframe中页面是否刷新,'no':'从不刷新','nav':'点击菜单刷新','all':'菜单和tab点击都刷新' + refresh : 'nav', // iframe中页面是否刷新,'no':'从不刷新','nav':'点击菜单刷新','all':'菜单和tab点击都刷新' nav: { backgroundColor: '#ffffff', maxTabs : 35, // 选项卡最大值 diff --git a/assets/bootstrap/js/jquery.pagination.js b/assets/bootstrap/js/jquery.pagination.js new file mode 100644 index 0000000..771e816 --- /dev/null +++ b/assets/bootstrap/js/jquery.pagination.js @@ -0,0 +1,292 @@ +/** + * pagination.js 1.5.1 + * A jQuery plugin to provide simple yet fully customisable pagination. + * @version 1.5.1 + * @author mss + * @url https://github.com/Maxiaoxiang/jQuery-plugins + * + * @调用方法 + * $(selector).pagination(option, callback); + * -此处callback是初始化调用,option里的callback是点击页码后调用 + * + * -- example -- + * $(selector).pagination({ + * ... // 配置参数 + * callback: function(api) { + * console.log('点击页码调用该回调'); //切换页码时执行一次回调 + * } + * }, function(){ + * console.log('初始化'); //插件初始化时调用该方法,比如请求第一次接口来初始化分页配置 + * }); + */ +; +(function (factory) { + if (typeof define === "function" && (define.amd || define.cmd) && !jQuery) { + // AMD或CMD + define(["jquery"], factory); + } else if (typeof module === 'object' && module.exports) { + // Node/CommonJS + module.exports = function (root, jQuery) { + if (jQuery === undefined) { + if (typeof window !== 'undefined') { + jQuery = require('jquery'); + } else { + jQuery = require('jquery')(root); + } + } + factory(jQuery); + return jQuery; + }; + } else { + //Browser globals + factory(jQuery); + } +}(function ($) { + + //配置参数 + var defaults = { + totalData: 0, //数据总条数 + showData: 0, //每页显示的条数 + pageCount: 9, //总页数,默认为9 + current: 1, //当前第几页 + prevCls: 'prev', //上一页class + nextCls: 'next', //下一页class + prevContent: '<', //上一页内容 + nextContent: '>', //下一页内容 + activeCls: 'active', //当前页选中状态 + coping: false, //首页和尾页 + isHide: false, //当前页数为0页或者1页时不显示分页 + homePage: '', //首页节点内容 + endPage: '', //尾页节点内容 + keepShowPN: false, //是否一直显示上一页下一页 + mode: 'unfixed', //分页模式,unfixed:不固定页码数量,fixed:固定页码数量 + count: 4, //mode为unfixed时显示当前选中页前后页数,mode为fixed显示页码总数 + jump: false, //跳转到指定页数 + jumpIptCls: 'jump-ipt', //文本框内容 + jumpBtnCls: 'jump-btn', //跳转按钮 + jumpBtn: '跳转', //跳转按钮文本 + callback: function () {} //回调 + }; + + var Pagination = function (element, options) { + //全局变量 + var opts = options, //配置 + current, //当前页 + $document = $(document), + $obj = $(element); //容器 + + /** + * 设置总页数 + * @param {int} page 页码 + * @return opts.pageCount 总页数配置 + */ + this.setPageCount = function (page) { + return opts.pageCount = page; + }; + + /** + * 获取总页数 + * 如果配置了总条数和每页显示条数,将会自动计算总页数并略过总页数配置,反之 + * @return {int} 总页数 + */ + this.getPageCount = function () { + return opts.totalData && opts.showData ? Math.ceil(parseInt(opts.totalData) / opts.showData) : opts.pageCount; + }; + + /** + * 获取当前页 + * @return {int} 当前页码 + */ + this.getCurrent = function () { + return current; + }; + + /** + * 填充数据 + * @param {int} 页码 + */ + this.filling = function (index) { + var html = ''; + current = parseInt(index) || parseInt(opts.current); //当前页码 + var pageCount = this.getPageCount(); //获取的总页数 + switch (opts.mode) { //配置模式 + case 'fixed': //固定按钮模式 + html += '
  • ' + opts.prevContent + '
  • '; + if (opts.coping) { + var home = opts.coping && opts.homePage ? opts.homePage : '1'; + html += '
  • ' + home + '
  • '; + } + var start = current > opts.count - 1 ? current + opts.count - 1 > pageCount ? current - (opts.count - (pageCount - current)) : current - 2 : 1; + var end = current + opts.count - 1 > pageCount ? pageCount : start + opts.count; + for (; start <= end; start++) { + if (start != current) { + html += '
  • ' + start + '
  • '; + } else { + html += '
  • ' + start + '
  • '; + } + } + if (opts.coping) { + var _end = opts.coping && opts.endPage ? opts.endPage : pageCount; + html += '
  • ' + _end + '
  • '; + } + html += '
  • ' + opts.nextContent + '
  • '; + break; + + // if (opts.keepShowPN || current > 1) { //上一页 + // html += '' + opts.prevContent + ''; + // } else { + // if (opts.keepShowPN == false) { + // $obj.find('.' + opts.prevCls) && $obj.find('.' + opts.prevCls).remove(); + // } + // } + // if (current >= opts.count + 2 && current != 1 && pageCount != opts.count) { + // var home = opts.coping && opts.homePage ? opts.homePage : '1'; + // html += opts.coping ? '' + home + '...' : ''; + // } + // var start = (current - opts.count) <= 1 ? 1 : (current - opts.count); + // var end = (current + opts.count) >= pageCount ? pageCount : (current + opts.count); + // for (; start <= end; start++) { + // if (start <= pageCount && start >= 1) { + // if (start != current) { + // html += '' + start + ''; + // } else { + // html += '' + start + ''; + // } + // } + // } + // if (current + opts.count < pageCount && current >= 1 && pageCount > opts.count) { + // var end = opts.coping && opts.endPage ? opts.endPage : pageCount; + // html += opts.coping ? '...' + end + '' : ''; + // } + // if (opts.keepShowPN || current < pageCount) { //下一页 + // html += '' + opts.nextContent + ''; + // } else { + // if (opts.keepShowPN == false) { + // $obj.find('.' + opts.nextCls) && $obj.find('.' + opts.nextCls).remove(); + // } + // } + // break; + + case 'unfixed': //不固定按钮模式 + if (opts.keepShowPN || current > 1) { //上一页 + html += '' + opts.prevContent + ''; + } else { + if (opts.keepShowPN == false) { + $obj.find('.' + opts.prevCls) && $obj.find('.' + opts.prevCls).remove(); + } + } + if (current >= opts.count + 2 && current != 1 && pageCount != opts.count) { + var home = opts.coping && opts.homePage ? opts.homePage : '1'; + html += opts.coping ? '' + home + '...' : ''; + } + var start = (current - opts.count) <= 1 ? 1 : (current - opts.count); + var end = (current + opts.count) >= pageCount ? pageCount : (current + opts.count); + for (; start <= end; start++) { + if (start <= pageCount && start >= 1) { + if (start != current) { + html += '' + start + ''; + } else { + html += '' + start + ''; + } + } + } + if (current + opts.count < pageCount && current >= 1 && pageCount > opts.count) { + var end = opts.coping && opts.endPage ? opts.endPage : pageCount; + html += opts.coping ? '...' + end + '' : ''; + } + if (opts.keepShowPN || current < pageCount) { //下一页 + html += '' + opts.nextContent + ''; + } else { + if (opts.keepShowPN == false) { + $obj.find('.' + opts.nextCls) && $obj.find('.' + opts.nextCls).remove(); + } + } + break; + case 'easy': //简单模式 + break; + default: + } + html += opts.jump ? '' + opts.jumpBtn + '' : ''; + $obj.empty().html(html); + }; + + //绑定事件 + this.eventBind = function () { + var that = this; + var pageCount = that.getPageCount(); //总页数 + var index = 1; + $obj.off().on('click', 'a', function () { + if ($(this).hasClass(opts.nextCls)) { + if (parseInt($obj.find('.' + opts.activeCls).text()) >= pageCount) { + $(this).addClass('disabled'); + return false; + } else { + index = parseInt($obj.find('.' + opts.activeCls).text()) + 1; + } + } else if ($(this).hasClass(opts.prevCls)) { + if (parseInt($obj.find('.' + opts.activeCls).text()) <= 1) { + $(this).addClass('disabled'); + return false; + } else { + index = parseInt($obj.find('.' + opts.activeCls).text()) - 1; + } + } else if ($(this).hasClass(opts.jumpBtnCls)) { + if ($obj.find('.' + opts.jumpIptCls).val() !== '') { + index = parseInt($obj.find('.' + opts.jumpIptCls).val()); + } else { + return; + } + } else { + index = parseInt($(this).data('page')); + } + that.filling(index); + typeof opts.callback === 'function' && opts.callback(that); + }); + //输入跳转的页码 + $obj.on('input propertychange', '.' + opts.jumpIptCls, function () { + var $this = $(this); + var val = $this.val(); + var reg = /[^\d]/g; + if (reg.test(val)) $this.val(val.replace(reg, '')); + (parseInt(val) > pageCount) && $this.val(pageCount); + if (parseInt(val) === 0) $this.val(1); //最小值为1 + }); + //回车跳转指定页码 + $document.keydown(function (e) { + if (e.keyCode == 13 && $obj.find('.' + opts.jumpIptCls).val()) { + var index = parseInt($obj.find('.' + opts.jumpIptCls).val()); + that.filling(index); + typeof opts.callback === 'function' && opts.callback(that); + } + }); + }; + + //初始化 + this.init = function () { + this.filling(opts.current); + this.eventBind(); + if (opts.isHide && this.getPageCount() == '1' || this.getPageCount() == '0') { + $obj.hide(); + } else { + $obj.show(); + } + }; + this.init(); + }; + + $.fn.pagination = function (parameter, callback) { + if (typeof parameter == 'function') { //重载 + callback = parameter; + parameter = {}; + } else { + parameter = parameter || {}; + callback = callback || function () {}; + } + var options = $.extend({}, defaults, parameter); + return this.each(function () { + var pagination = new Pagination(this, options); + callback(pagination); + }); + }; + +})); \ No newline at end of file diff --git a/assets/bootstrap/js/util.js b/assets/bootstrap/js/util.js new file mode 100644 index 0000000..bb8b4df --- /dev/null +++ b/assets/bootstrap/js/util.js @@ -0,0 +1,34 @@ +function IsJson(str) { + if (typeof str == 'string') { + try { + var obj = JSON.parse(str); + if (typeof obj == 'object' && obj) { + return true; + } else { + return false; + } + } catch (e) { + console.log('error:' + str + '!!!' + e); + return false; + } + } + console.log('It is not a string!') +} + +function AjaxError(response) { + var errCode = response.status; + var errMsg = response.responseText; + + if (IsJson(response.responseText)) { + const errInfo = JSON.parse(response.responseText); + errCode = errInfo.code; + errMsg = errInfo.message; + } + + $.alert({ + title: '错误提示', + icon: 'mdi mdi-alert', + type: 'red', + content: '错误码:' + errCode + '
    ' + '错误信息:' + errMsg, + }); +} diff --git a/assets/templates/authorized/authorized_add.html b/assets/templates/authorized/authorized_add.html new file mode 100644 index 0000000..08e4a38 --- /dev/null +++ b/assets/templates/authorized/authorized_add.html @@ -0,0 +1,138 @@ + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    新增调用方
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    + + +
    +
    +
    +
    +
    +
    + + + + + + + + + diff --git a/assets/templates/authorized/authorized_api.html b/assets/templates/authorized/authorized_api.html new file mode 100644 index 0000000..31de8f3 --- /dev/null +++ b/assets/templates/authorized/authorized_api.html @@ -0,0 +1,226 @@ + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    接口授权
    +
    +
    + + + +
    +
    + +
    + +
    + +
    +
    + 输入接口地址 +
    + +
    + + + + +
    +
    +
    + +
    +
    +
    +
    已授权接口
    +
    +
    + +
    +
    +
    +
    +
    + + + + + + + + + + diff --git a/assets/templates/authorized/authorized_list.html b/assets/templates/authorized/authorized_list.html new file mode 100644 index 0000000..8321a3a --- /dev/null +++ b/assets/templates/authorized/authorized_list.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + +
    +
    + +
    +
    +
    +
    + 新增 +
    +
    + +
    +
    + + + + + + + + + + + + + + + +
    编号调用方对接人创建日期更新日期状态操作
    +
    +
      +
        + +
      +
    +
    +
    +
    + +
    +
    + + + + + + + + diff --git a/assets/templates/gencode/gencode_init.html b/assets/templates/gencode/gencode_init.html index 049cfd5..c7489c9 100644 --- a/assets/templates/gencode/gencode_init.html +++ b/assets/templates/gencode/gencode_init.html @@ -3,6 +3,7 @@ + @@ -16,9 +17,9 @@
    注意:请在 MacLinux 环境执行。

    初始化项目所需信息:

    -

    MySQL 数据表:user_demo

    - +

    MySQL 数据表:authorized

    +

    MySQL 数据表:authorized_api

    + + +

    + + + +
    +
    +
    +
    hashids 解密
    +
    +
    + +
    +
    + 密文 +
    + +
    + +
    +
    + 数字 +
    + +
    + + + + +
    +
    +
    + + + + + + + + + + + diff --git a/cmd/init/db/main.go b/cmd/init/db/main.go index 9d02561..e5d8255 100644 --- a/cmd/init/db/main.go +++ b/cmd/init/db/main.go @@ -2,6 +2,7 @@ package main import ( "flag" + "fmt" "log" "strings" @@ -42,11 +43,34 @@ func main() { } }() + // 开启事务 + tx := db.GetDb().Begin() + // 创建 user_demo 表 - err = db.GetDb().Exec(mysql.CreateUserDemoTableSql()).Error + err = tx.Exec(mysql.CreateUserDemoTableSql()).Error if err != nil { + tx.Rollback() log.Fatal("create user_demo table err: ", err.Error()) } + fmt.Println("create user_demo table success") + + // 创建 authorized 表 + err = tx.Exec(mysql.CreateAuthorizedTableSql()).Error + if err != nil { + tx.Rollback() + log.Fatal("create authorized table err: ", err.Error()) + } + fmt.Println("create authorized table success") + + // 创建 authorized_api 表 + err = tx.Exec(mysql.CreateAuthorizedAPITableSql()).Error + if err != nil { + tx.Rollback() + log.Fatal("create authorized_api table err: ", err.Error()) + } + fmt.Println("create authorized_api table success") + + // 完成事务 + tx.Commit() - log.Println("create user_demo table success") } diff --git a/cmd/init/db/mysql/table_authorized.go b/cmd/init/db/mysql/table_authorized.go new file mode 100644 index 0000000..ec6a60c --- /dev/null +++ b/cmd/init/db/mysql/table_authorized.go @@ -0,0 +1,37 @@ +package mysql + +//CREATE TABLE `authorized` ( +//`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', +//`business_key` varchar(32) NOT NULL DEFAULT '' COMMENT '调用方key', +//`business_secret` varchar(60) NOT NULL DEFAULT '' COMMENT '调用方secret', +//`business_developer` varchar(60) NOT NULL DEFAULT '' COMMENT '调用方对接人', +//`remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注', +//`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用 1:是 -1:否', +//`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否', +//`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', +//`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人', +//`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', +//`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人', +//PRIMARY KEY (`id`), +//UNIQUE KEY `unique_business_key` (`business_key`) +//) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='已授权的调用方表'; + +func CreateAuthorizedTableSql() (sql string) { + sql = "CREATE TABLE `authorized` (" + sql += "`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键'," + sql += "`business_key` varchar(32) NOT NULL DEFAULT '' COMMENT '调用方key'," + sql += "`business_secret` varchar(60) NOT NULL DEFAULT '' COMMENT '调用方secret'," + sql += "`business_developer` varchar(60) NOT NULL DEFAULT '' COMMENT '调用方对接人'," + sql += "`remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注'," + sql += "`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用 1:是 -1:否'," + sql += "`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否'," + sql += "`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'," + sql += "`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人'," + sql += "`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'," + sql += "`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人'," + sql += "PRIMARY KEY (`id`)," + sql += "UNIQUE KEY `unique_business_key` (`business_key`)" + sql += ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='已授权的调用方表';" + + return +} diff --git a/cmd/init/db/mysql/table_authorized_api.go b/cmd/init/db/mysql/table_authorized_api.go new file mode 100644 index 0000000..c89620b --- /dev/null +++ b/cmd/init/db/mysql/table_authorized_api.go @@ -0,0 +1,31 @@ +package mysql + +//CREATE TABLE `authorized_api` ( +//`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', +//`business_key` varchar(30) NOT NULL DEFAULT '' COMMENT '调用方key', +//`method` varchar(30) NOT NULL DEFAULT '' COMMENT '请求方式', +//`api` varchar(100) NOT NULL DEFAULT '' COMMENT '请求地址', +//`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否', +//`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', +//`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人', +//`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', +//`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人', +//PRIMARY KEY (`id`) +//) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='已授权接口地址'; + +func CreateAuthorizedAPITableSql() (sql string) { + sql = "CREATE TABLE `authorized_api` (" + sql += "`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键'," + sql += "`business_key` varchar(30) NOT NULL DEFAULT '' COMMENT '调用方key'," + sql += "`method` varchar(30) NOT NULL DEFAULT '' COMMENT '请求方式'," + sql += "`api` varchar(100) 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 += "`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人'," + sql += "`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'," + sql += "`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人'," + sql += "PRIMARY KEY (`id`)" + sql += ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='已授权的调用方表';" + + return +} diff --git a/cmd/mfmt/main.go b/cmd/mfmt/main.go new file mode 100644 index 0000000..e5ec5fd --- /dev/null +++ b/cmd/mfmt/main.go @@ -0,0 +1,195 @@ +package main + +import ( + "bufio" + "bytes" + "crypto/sha256" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "github.com/koketama/errors" + "go.uber.org/zap" + "golang.org/x/tools/go/packages" +) + +var stdlib = make(map[string]bool) + +func init() { + pkgs, err := packages.Load(nil, "std") + if err != nil { + log.Fatal("get go stdlib err", zap.Error(err)) + } + + for _, pkg := range pkgs { + if !strings.HasPrefix(pkg.ID, "vendor") { + stdlib[pkg.ID] = true + } + } +} + +var module string + +func init() { + file, err := os.Open("./go.mod") + if err != nil { + log.Fatal("no go.mod file found", zap.Error(err)) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if strings.HasPrefix(line, "module ") { + module = strings.TrimSpace(line[7:]) + break + } + } + + if module == "" { + log.Fatal("go.mod illegal") + } +} + +func main() { + err := filepath.Walk("./", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() || strings.HasPrefix(path, ".") || strings.HasPrefix(path, "vendor") || strings.HasSuffix(path, ".pb.go") || filepath.Ext(path) != ".go" { + return nil + } + + raw, err := ioutil.ReadFile(path) + if err != nil { + return errors.Wrapf(err, "read file %s err", path) + } + + digest0 := sha256.Sum256(raw) + if raw, err = format.Source(raw); err != nil { + return errors.Wrapf(err, "format file %s err", path) + } + + file, err := parser.ParseFile(token.NewFileSet(), "", raw, 0) + if err != nil { + return errors.Wrapf(err, "parse file %s err", path) + } + + var first, last int + var imports []*ast.ImportSpec + comments := make(map[string]string) + + ast.Inspect(file, func(n ast.Node) bool { + switch spec := n.(type) { + case *ast.ImportSpec: + if first == 0 { + first = int(spec.Pos()) + } + last = int(spec.End()) + + imports = append(imports, spec) + + k := last - 1 + for ; k < len(raw); k++ { + if raw[k] == '\r' || raw[k] == '\n' { + break + } + } + + comment := string(raw[last-1 : k]) + if index := strings.Index(comment, "//"); index != -1 { + comments[spec.Path.Value] = strings.TrimSpace(comment[index+2:]) + } + } + return true + }) + + if imports != nil { + buf := bytes.NewBuffer(nil) + buf.Write(raw[:first-2]) + buf.WriteString(sort(imports, comments)) + buf.Write(raw[last-1:]) + + if raw, err = format.Source(buf.Bytes()); err != nil { + return errors.Wrapf(err, "double format file %s err", path) + } + } + + digest1 := sha256.Sum256(raw) + if !bytes.Equal(digest0[:], digest1[:]) { + fmt.Println(path) + } + + if err = ioutil.WriteFile(path, raw, info.Mode()); err != nil { + return errors.Wrapf(err, "write file %s err", path) + } + + return nil + }) + if err != nil { + log.Fatal("scan project err", zap.Error(err)) + } +} + +func sort(imports []*ast.ImportSpec, comments map[string]string) string { + system := bytes.NewBuffer(nil) + group := bytes.NewBuffer(nil) + others := bytes.NewBuffer(nil) + + for _, pkg := range imports { + value := strings.Trim(pkg.Path.Value, `"`) + switch { + case stdlib[value]: + if pkg.Name != nil { + system.WriteString(pkg.Name.String()) + system.WriteString(" ") + } + + system.WriteString(pkg.Path.Value) + if comment, ok := comments[pkg.Path.Value]; ok { + system.WriteString(" ") + system.WriteString("// ") + system.WriteString(comment) + } + system.WriteString("\n") + + case strings.HasPrefix(value, module): + if pkg.Name != nil { + group.WriteString(pkg.Name.String()) + group.WriteString(" ") + } + + group.WriteString(pkg.Path.Value) + if comment, ok := comments[pkg.Path.Value]; ok { + group.WriteString(" ") + group.WriteString("// ") + group.WriteString(comment) + } + group.WriteString("\n") + + default: + if pkg.Name != nil { + others.WriteString(pkg.Name.String()) + others.WriteString(" ") + } + + others.WriteString(pkg.Path.Value) + if comment, ok := comments[pkg.Path.Value]; ok { + others.WriteString(" ") + others.WriteString("// ") + others.WriteString(comment) + } + others.WriteString("\n") + } + } + + return fmt.Sprintf("%s\n%s\n%s", system.String(), group.String(), others.String()) +} diff --git a/configs/configs.go b/configs/configs.go index 2e456ae..79db8c0 100644 --- a/configs/configs.go +++ b/configs/configs.go @@ -58,6 +58,11 @@ type Config struct { Secret string `toml:"secret"` ExpireDuration time.Duration `toml:"expireDuration"` } `toml:"urlToken"` + + HashIds struct { + Secret string `toml:"secret"` + Length int `toml:"length"` + } `toml:"hashids"` } func init() { diff --git a/configs/fat_configs.toml b/configs/fat_configs.toml index 6dbc47a..dbd0910 100644 --- a/configs/fat_configs.toml +++ b/configs/fat_configs.toml @@ -36,4 +36,8 @@ expireDuration = 24 # JWT ExpiresAt 过期时间(单位:小时) [urlToken] secret = 'i1ydX9RtHyuJTrw7frcu' # URL Token secret -expireDuration = 10 # URL Token ExpiresAt 过期时间(单位:分钟) \ No newline at end of file +expireDuration = 10 # URL Token ExpiresAt 过期时间(单位:分钟) + +[hashids] +secret = '6ab6122836cfef95f8db' # hashids secret +length = 12 # hashids length \ No newline at end of file diff --git a/docs/docs.go b/docs/docs.go index 4e2d349..10aee1c 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -28,6 +28,378 @@ var doc = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/api/authorized": { + "get": { + "description": "调用方列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "调用方列表", + "parameters": [ + { + "type": "integer", + "description": "第几页", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "每页显示条数", + "name": "page_size", + "in": "query" + }, + { + "type": "string", + "description": "调用方key", + "name": "business_key", + "in": "query" + }, + { + "type": "string", + "description": "调用方secret", + "name": "business_secret", + "in": "query" + }, + { + "type": "string", + "description": "调用方对接人", + "name": "business_developer", + "in": "query" + }, + { + "type": "string", + "description": "备注", + "name": "remark", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.listResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + }, + "post": { + "description": "新增调用方", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "新增调用方", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/authorized_handler.createRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.createResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/authorized/used": { + "patch": { + "description": "更新调用方为启用/禁用", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "更新调用方为启用/禁用", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/authorized_handler.updateUsedRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.updateUsedResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/authorized/{id}": { + "delete": { + "description": "删除调用方", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "删除调用方", + "parameters": [ + { + "type": "string", + "description": "hashId", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.deleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/authorized_api": { + "get": { + "description": "调用方接口地址列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "调用方接口地址列表", + "parameters": [ + { + "type": "string", + "description": "调用方key", + "name": "business_key", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.listAPIResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + }, + "post": { + "description": "授权调用方接口地址", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "授权调用方接口地址", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/authorized_handler.createAPIRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.createAPIResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/authorized_api/{id}": { + "delete": { + "description": "删除调用方接口地址", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "删除调用方接口地址", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.deleteAPIResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/tool/hashids/decode/{id}": { + "get": { + "description": "HashIds 解密", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.tool" + ], + "summary": "HashIds 解密", + "parameters": [ + { + "type": "string", + "description": "需解密的密文", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/tool_handler.hashIdsDecodeResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/tool/hashids/encode/{id}": { + "get": { + "description": "HashIds 加密", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.tool" + ], + "summary": "HashIds 加密", + "parameters": [ + { + "type": "string", + "description": "需加密的数字", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/tool_handler.hashIdsEncodeResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/auth/get": { "post": { "description": "获取授权信息", @@ -114,6 +486,166 @@ var doc = `{ } } }, + "/test/create": { + "post": { + "description": "创建用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "创建用户", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/test_handler.createRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/test_handler.createResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/test/delete": { + "post": { + "description": "删除用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "删除用户", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/test_handler.deleteRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/test_handler.deleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/test/detail": { + "post": { + "description": "用户详情", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "用户详情", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/test_handler.detailRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/test_handler.detailResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/test/update": { + "post": { + "description": "编辑用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "编辑用户", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/test_handler.updateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/test_handler.updateResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/user/create": { "post": { "description": "创建用户", @@ -324,6 +856,204 @@ var doc = `{ } }, "definitions": { + "authorized_handler.createAPIRequest": { + "type": "object", + "properties": { + "api": { + "description": "请求地址", + "type": "string" + }, + "id": { + "description": "HashID", + "type": "string" + }, + "method": { + "description": "请求方法", + "type": "string" + } + } + }, + "authorized_handler.createAPIResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "authorized_handler.createRequest": { + "type": "object", + "properties": { + "business_developer": { + "description": "调用方对接人", + "type": "string" + }, + "business_key": { + "description": "调用方key", + "type": "string" + }, + "remark": { + "description": "备注", + "type": "string" + } + } + }, + "authorized_handler.createResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "authorized_handler.deleteAPIResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "authorized_handler.deleteResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "authorized_handler.listAPIData": { + "type": "object", + "properties": { + "api": { + "description": "调用方对接人", + "type": "string" + }, + "business_key": { + "description": "调用方key", + "type": "string" + }, + "hash_id": { + "description": "hashID", + "type": "string" + }, + "method": { + "description": "调用方secret", + "type": "string" + } + } + }, + "authorized_handler.listAPIResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/authorized_handler.listAPIData" + } + } + } + }, + "authorized_handler.listData": { + "type": "object", + "properties": { + "business_developer": { + "description": "调用方对接人", + "type": "string" + }, + "business_key": { + "description": "调用方key", + "type": "string" + }, + "business_secret": { + "description": "调用方secret", + "type": "string" + }, + "created_at": { + "description": "创建时间", + "type": "string" + }, + "created_user": { + "description": "创建人", + "type": "string" + }, + "hashid": { + "description": "hashid", + "type": "string" + }, + "id": { + "description": "ID", + "type": "integer" + }, + "is_used": { + "description": "是否启用 1:是 -1:否", + "type": "integer" + }, + "remark": { + "description": "备注", + "type": "string" + }, + "updated_at": { + "description": "更新时间", + "type": "string" + }, + "updated_user": { + "description": "更新人", + "type": "string" + } + } + }, + "authorized_handler.listResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/authorized_handler.listData" + } + }, + "pagination": { + "type": "object", + "properties": { + "current_page": { + "type": "integer" + }, + "pre_page_count": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + } + } + }, + "authorized_handler.updateUsedRequest": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "string" + }, + "used": { + "description": "是否启用 1:是 -1:否", + "type": "integer" + } + } + }, + "authorized_handler.updateUsedResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, "code.Failure": { "type": "object", "properties": { @@ -350,6 +1080,48 @@ var doc = `{ } } }, + "test_handler.createRequest": { + "type": "object" + }, + "test_handler.createResponse": { + "type": "object" + }, + "test_handler.deleteRequest": { + "type": "object" + }, + "test_handler.deleteResponse": { + "type": "object" + }, + "test_handler.detailRequest": { + "type": "object" + }, + "test_handler.detailResponse": { + "type": "object" + }, + "test_handler.updateRequest": { + "type": "object" + }, + "test_handler.updateResponse": { + "type": "object" + }, + "tool_handler.hashIdsDecodeResponse": { + "type": "object", + "properties": { + "val": { + "description": "解密后的值", + "type": "integer" + } + } + }, + "tool_handler.hashIdsEncodeResponse": { + "type": "object", + "properties": { + "val": { + "description": "加密后的值", + "type": "string" + } + } + }, "user_handler.createRequest": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 818bbcb..6785c45 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -11,6 +11,378 @@ }, "host": "127.0.0.1:9999", "paths": { + "/api/authorized": { + "get": { + "description": "调用方列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "调用方列表", + "parameters": [ + { + "type": "integer", + "description": "第几页", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "每页显示条数", + "name": "page_size", + "in": "query" + }, + { + "type": "string", + "description": "调用方key", + "name": "business_key", + "in": "query" + }, + { + "type": "string", + "description": "调用方secret", + "name": "business_secret", + "in": "query" + }, + { + "type": "string", + "description": "调用方对接人", + "name": "business_developer", + "in": "query" + }, + { + "type": "string", + "description": "备注", + "name": "remark", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.listResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + }, + "post": { + "description": "新增调用方", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "新增调用方", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/authorized_handler.createRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.createResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/authorized/used": { + "patch": { + "description": "更新调用方为启用/禁用", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "更新调用方为启用/禁用", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/authorized_handler.updateUsedRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.updateUsedResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/authorized/{id}": { + "delete": { + "description": "删除调用方", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "删除调用方", + "parameters": [ + { + "type": "string", + "description": "hashId", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.deleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/authorized_api": { + "get": { + "description": "调用方接口地址列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "调用方接口地址列表", + "parameters": [ + { + "type": "string", + "description": "调用方key", + "name": "business_key", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.listAPIResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + }, + "post": { + "description": "授权调用方接口地址", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "授权调用方接口地址", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/authorized_handler.createAPIRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.createAPIResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/authorized_api/{id}": { + "delete": { + "description": "删除调用方接口地址", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.authorized" + ], + "summary": "删除调用方接口地址", + "parameters": [ + { + "type": "integer", + "description": "主键ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/authorized_handler.deleteAPIResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/tool/hashids/decode/{id}": { + "get": { + "description": "HashIds 解密", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.tool" + ], + "summary": "HashIds 解密", + "parameters": [ + { + "type": "string", + "description": "需解密的密文", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/tool_handler.hashIdsDecodeResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/tool/hashids/encode/{id}": { + "get": { + "description": "HashIds 加密", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.tool" + ], + "summary": "HashIds 加密", + "parameters": [ + { + "type": "string", + "description": "需加密的数字", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/tool_handler.hashIdsEncodeResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/auth/get": { "post": { "description": "获取授权信息", @@ -97,6 +469,166 @@ } } }, + "/test/create": { + "post": { + "description": "创建用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "创建用户", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/test_handler.createRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/test_handler.createResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/test/delete": { + "post": { + "description": "删除用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "删除用户", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/test_handler.deleteRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/test_handler.deleteResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/test/detail": { + "post": { + "description": "用户详情", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "用户详情", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/test_handler.detailRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/test_handler.detailResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/test/update": { + "post": { + "description": "编辑用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Test" + ], + "summary": "编辑用户", + "parameters": [ + { + "description": "请求信息", + "name": "Request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/test_handler.updateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/test_handler.updateResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, "/user/create": { "post": { "description": "创建用户", @@ -307,6 +839,204 @@ } }, "definitions": { + "authorized_handler.createAPIRequest": { + "type": "object", + "properties": { + "api": { + "description": "请求地址", + "type": "string" + }, + "id": { + "description": "HashID", + "type": "string" + }, + "method": { + "description": "请求方法", + "type": "string" + } + } + }, + "authorized_handler.createAPIResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "authorized_handler.createRequest": { + "type": "object", + "properties": { + "business_developer": { + "description": "调用方对接人", + "type": "string" + }, + "business_key": { + "description": "调用方key", + "type": "string" + }, + "remark": { + "description": "备注", + "type": "string" + } + } + }, + "authorized_handler.createResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "authorized_handler.deleteAPIResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "authorized_handler.deleteResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, + "authorized_handler.listAPIData": { + "type": "object", + "properties": { + "api": { + "description": "调用方对接人", + "type": "string" + }, + "business_key": { + "description": "调用方key", + "type": "string" + }, + "hash_id": { + "description": "hashID", + "type": "string" + }, + "method": { + "description": "调用方secret", + "type": "string" + } + } + }, + "authorized_handler.listAPIResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/authorized_handler.listAPIData" + } + } + } + }, + "authorized_handler.listData": { + "type": "object", + "properties": { + "business_developer": { + "description": "调用方对接人", + "type": "string" + }, + "business_key": { + "description": "调用方key", + "type": "string" + }, + "business_secret": { + "description": "调用方secret", + "type": "string" + }, + "created_at": { + "description": "创建时间", + "type": "string" + }, + "created_user": { + "description": "创建人", + "type": "string" + }, + "hashid": { + "description": "hashid", + "type": "string" + }, + "id": { + "description": "ID", + "type": "integer" + }, + "is_used": { + "description": "是否启用 1:是 -1:否", + "type": "integer" + }, + "remark": { + "description": "备注", + "type": "string" + }, + "updated_at": { + "description": "更新时间", + "type": "string" + }, + "updated_user": { + "description": "更新人", + "type": "string" + } + } + }, + "authorized_handler.listResponse": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/authorized_handler.listData" + } + }, + "pagination": { + "type": "object", + "properties": { + "current_page": { + "type": "integer" + }, + "pre_page_count": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + } + } + }, + "authorized_handler.updateUsedRequest": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "string" + }, + "used": { + "description": "是否启用 1:是 -1:否", + "type": "integer" + } + } + }, + "authorized_handler.updateUsedResponse": { + "type": "object", + "properties": { + "id": { + "description": "主键ID", + "type": "integer" + } + } + }, "code.Failure": { "type": "object", "properties": { @@ -333,6 +1063,48 @@ } } }, + "test_handler.createRequest": { + "type": "object" + }, + "test_handler.createResponse": { + "type": "object" + }, + "test_handler.deleteRequest": { + "type": "object" + }, + "test_handler.deleteResponse": { + "type": "object" + }, + "test_handler.detailRequest": { + "type": "object" + }, + "test_handler.detailResponse": { + "type": "object" + }, + "test_handler.updateRequest": { + "type": "object" + }, + "test_handler.updateResponse": { + "type": "object" + }, + "tool_handler.hashIdsDecodeResponse": { + "type": "object", + "properties": { + "val": { + "description": "解密后的值", + "type": "integer" + } + } + }, + "tool_handler.hashIdsEncodeResponse": { + "type": "object", + "properties": { + "val": { + "description": "加密后的值", + "type": "string" + } + } + }, "user_handler.createRequest": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index af3b2e5..2005941 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,4 +1,141 @@ definitions: + authorized_handler.createAPIRequest: + properties: + api: + description: 请求地址 + type: string + id: + description: HashID + type: string + method: + description: 请求方法 + type: string + type: object + authorized_handler.createAPIResponse: + properties: + id: + description: 主键ID + type: integer + type: object + authorized_handler.createRequest: + properties: + business_developer: + description: 调用方对接人 + type: string + business_key: + description: 调用方key + type: string + remark: + description: 备注 + type: string + type: object + authorized_handler.createResponse: + properties: + id: + description: 主键ID + type: integer + type: object + authorized_handler.deleteAPIResponse: + properties: + id: + description: 主键ID + type: integer + type: object + authorized_handler.deleteResponse: + properties: + id: + description: 主键ID + type: integer + type: object + authorized_handler.listAPIData: + properties: + api: + description: 调用方对接人 + type: string + business_key: + description: 调用方key + type: string + hash_id: + description: hashID + type: string + method: + description: 调用方secret + type: string + type: object + authorized_handler.listAPIResponse: + properties: + list: + items: + $ref: '#/definitions/authorized_handler.listAPIData' + type: array + type: object + authorized_handler.listData: + properties: + business_developer: + description: 调用方对接人 + type: string + business_key: + description: 调用方key + type: string + business_secret: + description: 调用方secret + type: string + created_at: + description: 创建时间 + type: string + created_user: + description: 创建人 + type: string + hashid: + description: hashid + type: string + id: + description: ID + type: integer + is_used: + description: 是否启用 1:是 -1:否 + type: integer + remark: + description: 备注 + type: string + updated_at: + description: 更新时间 + type: string + updated_user: + description: 更新人 + type: string + type: object + authorized_handler.listResponse: + properties: + list: + items: + $ref: '#/definitions/authorized_handler.listData' + type: array + pagination: + properties: + current_page: + type: integer + pre_page_count: + type: integer + total: + type: integer + type: object + type: object + authorized_handler.updateUsedRequest: + properties: + id: + description: 主键ID + type: string + used: + description: 是否启用 1:是 -1:否 + type: integer + type: object + authorized_handler.updateUsedResponse: + properties: + id: + description: 主键ID + type: integer + type: object code.Failure: properties: code: @@ -17,6 +154,34 @@ definitions: description: 过期时间 type: integer type: object + test_handler.createRequest: + type: object + test_handler.createResponse: + type: object + test_handler.deleteRequest: + type: object + test_handler.deleteResponse: + type: object + test_handler.detailRequest: + type: object + test_handler.detailResponse: + type: object + test_handler.updateRequest: + type: object + test_handler.updateResponse: + type: object + tool_handler.hashIdsDecodeResponse: + properties: + val: + description: 解密后的值 + type: integer + type: object + tool_handler.hashIdsEncodeResponse: + properties: + val: + description: 加密后的值 + type: string + type: object user_handler.createRequest: properties: mobile: @@ -80,6 +245,250 @@ info: title: swagger 接口文档 version: "2.0" paths: + /api/authorized: + get: + consumes: + - application/json + description: 调用方列表 + parameters: + - description: 第几页 + in: query + name: page + type: integer + - description: 每页显示条数 + in: query + name: page_size + type: string + - description: 调用方key + in: query + name: business_key + type: string + - description: 调用方secret + in: query + name: business_secret + type: string + - description: 调用方对接人 + in: query + name: business_developer + type: string + - description: 备注 + in: path + name: remark + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/authorized_handler.listResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 调用方列表 + tags: + - API.authorized + post: + consumes: + - application/json + description: 新增调用方 + parameters: + - description: 请求信息 + in: body + name: Request + required: true + schema: + $ref: '#/definitions/authorized_handler.createRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/authorized_handler.createResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 新增调用方 + tags: + - API.authorized + /api/authorized/{id}: + delete: + consumes: + - application/json + description: 删除调用方 + parameters: + - description: hashId + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/authorized_handler.deleteResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 删除调用方 + tags: + - API.authorized + /api/authorized/used: + patch: + consumes: + - application/json + description: 更新调用方为启用/禁用 + parameters: + - description: 请求信息 + in: body + name: Request + required: true + schema: + $ref: '#/definitions/authorized_handler.updateUsedRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/authorized_handler.updateUsedResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 更新调用方为启用/禁用 + tags: + - API.authorized + /api/authorized_api: + get: + consumes: + - application/json + description: 调用方接口地址列表 + parameters: + - description: 调用方key + in: query + name: business_key + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/authorized_handler.listAPIResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 调用方接口地址列表 + tags: + - API.authorized + post: + consumes: + - application/json + description: 授权调用方接口地址 + parameters: + - description: 请求信息 + in: body + name: Request + required: true + schema: + $ref: '#/definitions/authorized_handler.createAPIRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/authorized_handler.createAPIResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 授权调用方接口地址 + tags: + - API.authorized + /api/authorized_api/{id}: + delete: + consumes: + - application/json + description: 删除调用方接口地址 + parameters: + - description: 主键ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/authorized_handler.deleteAPIResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 删除调用方接口地址 + tags: + - API.authorized + /api/tool/hashids/decode/{id}: + get: + consumes: + - application/json + description: HashIds 解密 + parameters: + - description: 需解密的密文 + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/tool_handler.hashIdsDecodeResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: HashIds 解密 + tags: + - API.tool + /api/tool/hashids/encode/{id}: + get: + consumes: + - application/json + description: HashIds 加密 + parameters: + - description: 需加密的数字 + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/tool_handler.hashIdsEncodeResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: HashIds 加密 + tags: + - API.tool /auth/get: post: consumes: @@ -137,6 +546,110 @@ paths: summary: Trace 示例 tags: - Demo + /test/create: + post: + consumes: + - application/json + description: 创建用户 + parameters: + - description: 请求信息 + in: body + name: Request + required: true + schema: + $ref: '#/definitions/test_handler.createRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/test_handler.createResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 创建用户 + tags: + - Test + /test/delete: + post: + consumes: + - application/json + description: 删除用户 + parameters: + - description: 请求信息 + in: body + name: Request + required: true + schema: + $ref: '#/definitions/test_handler.deleteRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/test_handler.deleteResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 删除用户 + tags: + - Test + /test/detail: + post: + consumes: + - application/json + description: 用户详情 + parameters: + - description: 请求信息 + in: body + name: Request + required: true + schema: + $ref: '#/definitions/test_handler.detailRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/test_handler.detailResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 用户详情 + tags: + - Test + /test/update: + post: + consumes: + - application/json + description: 编辑用户 + parameters: + - description: 请求信息 + in: body + name: Request + required: true + schema: + $ref: '#/definitions/test_handler.updateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/test_handler.updateResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 编辑用户 + tags: + - Test /user/create: post: consumes: diff --git a/go.mod b/go.mod index ab6570b..fd326c3 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( 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/speps/go-hashids v2.0.0+incompatible github.com/spf13/cast v1.3.0 github.com/spf13/viper v1.7.1 github.com/swaggo/gin-swagger v1.3.0 diff --git a/go.sum b/go.sum index 4266976..4ec269a 100644 --- a/go.sum +++ b/go.sum @@ -351,6 +351,8 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/speps/go-hashids v2.0.0+incompatible h1:kSfxGfESueJKTx0mpER9Y/1XHl+FVQjtCqRyYcviFbw= +github.com/speps/go-hashids v2.0.0+incompatible/go.mod h1:P7hqPzMdnZOfyIk+xrlG1QaSMw+gCBdHKsBDnhpaZvc= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= diff --git a/internal/api/code/code.go b/internal/api/code/code.go index 3efbeb1..6ddea55 100644 --- a/internal/api/code/code.go +++ b/internal/api/code/code.go @@ -15,6 +15,7 @@ const ( CallHTTPError = 10105 ResubmitError = 10106 ResubmitMsg = 10107 + HashIdsDecodeError = 10108 // 模块级错误码 - 用户模块 IllegalUserName = 20101 @@ -22,7 +23,15 @@ const ( UserUpdateError = 20103 UserSearchError = 20104 - // ... + // 授权调用方 + AuthorizedCreateError = 30101 + AuthorizedListError = 30102 + AuthorizedDeleteError = 30103 + AuthorizedUpdateError = 30104 + AuthorizedDetailError = 30105 + AuthorizedCreateAPIError = 30106 + AuthorizedListAPIError = 30107 + AuthorizedDeleteAPIError = 30108 ) var codeText = map[int]string{ @@ -33,11 +42,21 @@ var codeText = map[int]string{ CallHTTPError: "调用第三方 HTTP 接口失败", ResubmitError: "Resubmit Error", ResubmitMsg: "请勿重复提交", + HashIdsDecodeError: "ID 参数有误", IllegalUserName: "非法用户名", UserCreateError: "创建用户失败", UserUpdateError: "更新用户失败", UserSearchError: "查询用户失败", + + AuthorizedCreateError: "创建调用方失败", + AuthorizedListError: "获取调用方列表页失败", + AuthorizedDeleteError: "删除调用方失败", + AuthorizedUpdateError: "更新调用方失败", + AuthorizedDetailError: "获取调用方详情失败", + AuthorizedCreateAPIError: "创建调用方API地址失败", + AuthorizedListAPIError: "获取调用方API地址列表失败", + AuthorizedDeleteAPIError: "删除调用方API地址失败", } func Text(code int) string { diff --git a/internal/api/controller/authorized_handler/func_create.go b/internal/api/controller/authorized_handler/func_create.go new file mode 100755 index 0000000..0ee0c19 --- /dev/null +++ b/internal/api/controller/authorized_handler/func_create.go @@ -0,0 +1,63 @@ +package authorized_handler + +import ( + "net/http" + + "github.com/xinliangnote/go-gin-api/internal/api/code" + "github.com/xinliangnote/go-gin-api/internal/api/service/authorized_service" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/pkg/errno" +) + +type createRequest struct { + BusinessKey string `json:"business_key"` // 调用方key + BusinessDeveloper string `json:"business_developer"` // 调用方对接人 + Remark string `json:"remark"` // 备注 +} + +type createResponse struct { + Id int32 `json:"id"` // 主键ID +} + +// Create 新增调用方 +// @Summary 新增调用方 +// @Description 新增调用方 +// @Tags API.authorized +// @Accept json +// @Produce json +// @Param Request body createRequest true "请求信息" +// @Success 200 {object} createResponse +// @Failure 400 {object} code.Failure +// @Router /api/authorized [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 + } + + createData := new(authorized_service.CreateAuthorizedData) + createData.BusinessKey = req.BusinessKey + createData.BusinessDeveloper = req.BusinessDeveloper + createData.Remark = req.Remark + + id, err := h.authorizedService.Create(c, createData) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.AuthorizedCreateError, + code.Text(code.AuthorizedCreateError)).WithErr(err), + ) + return + } + + res.Id = id + c.Payload(res) + } +} diff --git a/internal/api/controller/authorized_handler/func_createapi.go b/internal/api/controller/authorized_handler/func_createapi.go new file mode 100755 index 0000000..df28c93 --- /dev/null +++ b/internal/api/controller/authorized_handler/func_createapi.go @@ -0,0 +1,86 @@ +package authorized_handler + +import ( + "net/http" + + "github.com/xinliangnote/go-gin-api/internal/api/code" + "github.com/xinliangnote/go-gin-api/internal/api/service/authorized_service" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/pkg/errno" +) + +type createAPIRequest struct { + Id string `json:"id"` // HashID + Method string `json:"method"` // 请求方法 + API string `json:"api"` // 请求地址 +} + +type createAPIResponse struct { + Id int32 `json:"id"` // 主键ID +} + +// CreateAPI 授权调用方接口地址 +// @Summary 授权调用方接口地址 +// @Description 授权调用方接口地址 +// @Tags API.authorized +// @Accept json +// @Produce json +// @Param Request body createAPIRequest true "请求信息" +// @Success 200 {object} createAPIResponse +// @Failure 400 {object} code.Failure +// @Router /api/authorized_api [post] +func (h *handler) CreateAPI() core.HandlerFunc { + return func(c core.Context) { + req := new(createAPIRequest) + res := new(createAPIResponse) + if err := c.ShouldBindJSON(req); err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.ParamBindError, + code.Text(code.ParamBindError)).WithErr(err), + ) + return + } + + ids, err := h.hashids.HashidsDecode(req.Id) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.HashIdsDecodeError, + code.Text(code.HashIdsDecodeError)).WithErr(err), + ) + return + } + + id := int32(ids[0]) + + // 通过 id 查询出 business_key + authorizedInfo, err := h.authorizedService.Detail(c, id) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.AuthorizedDetailError, + code.Text(code.AuthorizedDetailError)).WithErr(err), + ) + return + } + + createAPIData := new(authorized_service.CreateAuthorizedAPIData) + createAPIData.BusinessKey = authorizedInfo.BusinessKey + createAPIData.Method = req.Method + createAPIData.API = req.API + + createId, err := h.authorizedService.CreateAPI(c, createAPIData) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.AuthorizedCreateAPIError, + code.Text(code.AuthorizedCreateAPIError)).WithErr(err), + ) + return + } + + res.Id = createId + c.Payload(res) + } +} diff --git a/internal/api/controller/authorized_handler/func_delete.go b/internal/api/controller/authorized_handler/func_delete.go new file mode 100755 index 0000000..502a37b --- /dev/null +++ b/internal/api/controller/authorized_handler/func_delete.go @@ -0,0 +1,67 @@ +package authorized_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 string `uri:"id"` // HashID +} + +type deleteResponse struct { + Id int32 `json:"id"` // 主键ID +} + +// Delete 删除调用方 +// @Summary 删除调用方 +// @Description 删除调用方 +// @Tags API.authorized +// @Accept json +// @Produce json +// @Param id path string true "hashId" +// @Success 200 {object} deleteResponse +// @Failure 400 {object} code.Failure +// @Router /api/authorized/{id} [delete] +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 + } + + ids, err := h.hashids.HashidsDecode(req.Id) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.HashIdsDecodeError, + code.Text(code.HashIdsDecodeError)).WithErr(err), + ) + return + } + + id := int32(ids[0]) + + err = h.authorizedService.Delete(c, id) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.AuthorizedDeleteError, + code.Text(code.AuthorizedDeleteError)).WithErr(err), + ) + return + } + + res.Id = id + c.Payload(res) + } +} diff --git a/internal/api/controller/authorized_handler/func_deleteapi.go b/internal/api/controller/authorized_handler/func_deleteapi.go new file mode 100755 index 0000000..5ed0eed --- /dev/null +++ b/internal/api/controller/authorized_handler/func_deleteapi.go @@ -0,0 +1,67 @@ +package authorized_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 deleteAPIRequest struct { + Id string `uri:"id"` // HashID +} + +type deleteAPIResponse struct { + Id int32 `json:"id"` // 主键ID +} + +// DeleteAPI 删除调用方接口地址 +// @Summary 删除调用方接口地址 +// @Description 删除调用方接口地址 +// @Tags API.authorized +// @Accept json +// @Produce json +// @Param id path int true "主键ID" +// @Success 200 {object} deleteAPIResponse +// @Failure 400 {object} code.Failure +// @Router /api/authorized_api/{id} [delete] +func (h *handler) DeleteAPI() core.HandlerFunc { + return func(c core.Context) { + req := new(deleteAPIRequest) + res := new(deleteAPIResponse) + if err := c.ShouldBindURI(req); err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.ParamBindError, + code.Text(code.ParamBindError)).WithErr(err), + ) + return + } + + ids, err := h.hashids.HashidsDecode(req.Id) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.HashIdsDecodeError, + code.Text(code.HashIdsDecodeError)).WithErr(err), + ) + return + } + + id := int32(ids[0]) + + err = h.authorizedService.DeleteAPI(c, id) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.AuthorizedDeleteAPIError, + code.Text(code.AuthorizedDeleteAPIError)).WithErr(err), + ) + return + } + + res.Id = id + c.Payload(res) + } +} diff --git a/internal/api/controller/authorized_handler/func_list.go b/internal/api/controller/authorized_handler/func_list.go new file mode 100755 index 0000000..3ff0aaf --- /dev/null +++ b/internal/api/controller/authorized_handler/func_list.go @@ -0,0 +1,142 @@ +package authorized_handler + +import ( + "net/http" + + "github.com/xinliangnote/go-gin-api/internal/api/code" + "github.com/xinliangnote/go-gin-api/internal/api/service/authorized_service" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/pkg/errno" + "github.com/xinliangnote/go-gin-api/pkg/time_parse" + + "github.com/spf13/cast" + "go.uber.org/zap" +) + +type listRequest struct { + Page int `form:"page"` // 第几页 + PageSize int `form:"page_size"` // 每页显示条数 + BusinessKey string `form:"business_key"` // 调用方key + BusinessSecret string `form:"business_secret"` // 调用方secret + BusinessDeveloper string `form:"business_developer"` // 调用方对接人 + Remark string `form:"remark"` // 备注 +} + +type listData struct { + Id int `json:"id"` // ID + HashID string `json:"hashid"` // hashid + BusinessKey string `json:"business_key"` // 调用方key + BusinessSecret string `json:"business_secret"` // 调用方secret + BusinessDeveloper string `json:"business_developer"` // 调用方对接人 + Remark string `json:"remark"` // 备注 + IsUsed int `json:"is_used"` // 是否启用 1:是 -1:否 + CreatedAt string `json:"created_at"` // 创建时间 + CreatedUser string `json:"created_user"` // 创建人 + UpdatedAt string `json:"updated_at"` // 更新时间 + UpdatedUser string `json:"updated_user"` // 更新人 +} + +type listResponse struct { + List []listData `json:"list"` + Pagination struct { + Total int `json:"total"` + CurrentPage int `json:"current_page"` + PrePageCount int `json:"pre_page_count"` + } `json:"pagination"` +} + +// List 调用方列表 +// @Summary 调用方列表 +// @Description 调用方列表 +// @Tags API.authorized +// @Accept json +// @Produce json +// @Param page query int false "第几页" +// @Param page_size query string false "每页显示条数" +// @Param business_key query string false "调用方key" +// @Param business_secret query string false "调用方secret" +// @Param business_developer query string false "调用方对接人" +// @Param remark path string false "备注" +// @Success 200 {object} listResponse +// @Failure 400 {object} code.Failure +// @Router /api/authorized [get] +func (h *handler) List() core.HandlerFunc { + return func(c core.Context) { + req := new(listRequest) + res := new(listResponse) + if err := c.ShouldBindForm(req); err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.ParamBindError, + code.Text(code.ParamBindError)).WithErr(err), + ) + return + } + + page := req.Page + if page == 0 { + page = 1 + } + + pageSize := req.PageSize + if pageSize == 0 { + pageSize = 10 + } + + searchData := new(authorized_service.SearchData) + searchData.Page = page + searchData.PageSize = pageSize + searchData.BusinessKey = req.BusinessKey + searchData.BusinessSecret = req.BusinessSecret + searchData.Remark = req.Remark + + resListData, err := h.authorizedService.PageList(c, searchData) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.AuthorizedListError, + code.Text(code.AuthorizedListError)).WithErr(err), + ) + return + } + + resCountData, err := h.authorizedService.PageListCount(c, searchData) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.AuthorizedListError, + code.Text(code.AuthorizedListError)).WithErr(err), + ) + return + } + res.Pagination.Total = cast.ToInt(resCountData) + res.Pagination.PrePageCount = pageSize + res.Pagination.CurrentPage = page + res.List = make([]listData, len(resListData)) + + for k, v := range resListData { + hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) + if err != nil { + h.logger.Info("hashids err", zap.Error(err)) + } + + data := listData{ + Id: cast.ToInt(v.Id), + HashID: hashId, + BusinessKey: v.BusinessKey, + BusinessSecret: v.BusinessSecret, + BusinessDeveloper: v.BusinessDeveloper, + Remark: v.Remark, + IsUsed: cast.ToInt(v.IsUsed), + CreatedAt: v.CreatedAt.Format(time_parse.CSTLayout), + CreatedUser: v.CreatedUser, + UpdatedAt: v.UpdatedAt.Format(time_parse.CSTLayout), + UpdatedUser: v.UpdatedUser, + } + + res.List[k] = data + } + + c.Payload(res) + } +} diff --git a/internal/api/controller/authorized_handler/func_listapi.go b/internal/api/controller/authorized_handler/func_listapi.go new file mode 100755 index 0000000..f4f3e73 --- /dev/null +++ b/internal/api/controller/authorized_handler/func_listapi.go @@ -0,0 +1,109 @@ +package authorized_handler + +import ( + "net/http" + + "github.com/xinliangnote/go-gin-api/internal/api/code" + "github.com/xinliangnote/go-gin-api/internal/api/service/authorized_service" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/pkg/errno" + + "github.com/spf13/cast" + "go.uber.org/zap" +) + +type listAPIRequest struct { + Id string `form:"id"` // hashID +} + +type listAPIData struct { + HashId string `json:"hash_id"` // hashID + BusinessKey string `json:"business_key"` // 调用方key + Method string `json:"method"` // 调用方secret + API string `json:"api"` // 调用方对接人 +} + +type listAPIResponse struct { + List []listAPIData `json:"list"` +} + +// ListAPI 调用方接口地址列表 +// @Summary 调用方接口地址列表 +// @Description 调用方接口地址列表 +// @Tags API.authorized +// @Accept json +// @Produce json +// @Param business_key query string false "调用方key" +// @Success 200 {object} listAPIResponse +// @Failure 400 {object} code.Failure +// @Router /api/authorized_api [get] +func (h *handler) ListAPI() core.HandlerFunc { + return func(c core.Context) { + req := new(listAPIRequest) + res := new(listAPIResponse) + if err := c.ShouldBindForm(req); err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.ParamBindError, + code.Text(code.ParamBindError)).WithErr(err), + ) + return + } + + ids, err := h.hashids.HashidsDecode(req.Id) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.HashIdsDecodeError, + code.Text(code.HashIdsDecodeError)).WithErr(err), + ) + return + } + + id := int32(ids[0]) + + // 通过 id 查询出 business_key + authorizedInfo, err := h.authorizedService.Detail(c, id) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.AuthorizedDetailError, + code.Text(code.AuthorizedDetailError)).WithErr(err), + ) + return + } + + searchAPIData := new(authorized_service.SearchAPIData) + searchAPIData.BusinessKey = authorizedInfo.BusinessKey + + resListData, err := h.authorizedService.ListAPI(c, searchAPIData) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.AuthorizedListAPIError, + code.Text(code.AuthorizedListAPIError)).WithErr(err), + ) + return + } + + res.List = make([]listAPIData, len(resListData)) + + for k, v := range resListData { + hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) + if err != nil { + h.logger.Info("hashids err", zap.Error(err)) + } + + data := listAPIData{ + HashId: hashId, + BusinessKey: v.BusinessKey, + Method: v.Method, + API: v.Api, + } + + res.List[k] = data + } + + c.Payload(res) + } +} diff --git a/internal/api/controller/authorized_handler/func_updateused.go b/internal/api/controller/authorized_handler/func_updateused.go new file mode 100755 index 0000000..16ac351 --- /dev/null +++ b/internal/api/controller/authorized_handler/func_updateused.go @@ -0,0 +1,68 @@ +package authorized_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 updateUsedRequest struct { + Id string `json:"id"` // 主键ID + Used int32 `json:"used"` // 是否启用 1:是 -1:否 +} + +type updateUsedResponse struct { + Id int32 `json:"id"` // 主键ID +} + +// UpdateUsed 更新调用方为启用/禁用 +// @Summary 更新调用方为启用/禁用 +// @Description 更新调用方为启用/禁用 +// @Tags API.authorized +// @Accept json +// @Produce json +// @Param Request body updateUsedRequest true "请求信息" +// @Success 200 {object} updateUsedResponse +// @Failure 400 {object} code.Failure +// @Router /api/authorized/used [patch] +func (h *handler) UpdateUsed() core.HandlerFunc { + return func(c core.Context) { + req := new(updateUsedRequest) + res := new(updateUsedResponse) + if err := c.ShouldBindJSON(req); err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.ParamBindError, + code.Text(code.ParamBindError)).WithErr(err), + ) + return + } + + ids, err := h.hashids.HashidsDecode(req.Id) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.HashIdsDecodeError, + code.Text(code.HashIdsDecodeError)).WithErr(err), + ) + return + } + + id := int32(ids[0]) + + err = h.authorizedService.UpdateUsed(c, id, req.Used) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.AuthorizedUpdateError, + code.Text(code.AuthorizedUpdateError)).WithErr(err), + ) + return + } + + res.Id = id + c.Payload(res) + } +} diff --git a/internal/api/controller/authorized_handler/handler.go b/internal/api/controller/authorized_handler/handler.go new file mode 100644 index 0000000..0aa6cdc --- /dev/null +++ b/internal/api/controller/authorized_handler/handler.go @@ -0,0 +1,71 @@ +package authorized_handler + +import ( + "github.com/xinliangnote/go-gin-api/configs" + "github.com/xinliangnote/go-gin-api/internal/api/service/authorized_service" + "github.com/xinliangnote/go-gin-api/internal/pkg/cache" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" + "github.com/xinliangnote/go-gin-api/pkg/hash" + + "go.uber.org/zap" +) + +var _ Handler = (*handler)(nil) + +type Handler interface { + i() + + // Create 新增调用方 + // @Tags API.authorized + // @Router /api/authorized [post] + Create() core.HandlerFunc + + // CreateAPI 授权调用方接口地址 + // @Tags API.authorized + // @Router /api/authorized_api [post] + CreateAPI() core.HandlerFunc + + // List 调用方列表 + // @Tags API.authorized + // @Router /api/authorized [get] + List() core.HandlerFunc + + // ListAPI 调用方接口地址列表 + // @Tags API.authorized + // @Router /api/authorized_api [get] + ListAPI() core.HandlerFunc + + // Delete 删除调用方 + // @Tags API.authorized + // @Router /api/authorized/{id} [delete] + Delete() core.HandlerFunc + + // DeleteAPI 删除调用方接口地址 + // @Tags API.authorized + // @Router /api/authorized_api/{id} [delete] + DeleteAPI() core.HandlerFunc + + // UpdateUsed 更新调用方为启用/禁用 + // @Tags API.authorized + // @Router /api/authorized/used [patch] + UpdateUsed() core.HandlerFunc +} + +type handler struct { + logger *zap.Logger + cache cache.Repo + authorizedService authorized_service.Service + hashids hash.Hash +} + +func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler { + return &handler{ + logger: logger, + cache: cache, + authorizedService: authorized_service.New(db, cache), + hashids: hash.New(configs.Get().HashIds.Secret, configs.Get().HashIds.Length), + } +} + +func (h *handler) i() {} diff --git a/internal/api/controller/tool_handler/func_hashidsdecode.go b/internal/api/controller/tool_handler/func_hashidsdecode.go new file mode 100755 index 0000000..061e00b --- /dev/null +++ b/internal/api/controller/tool_handler/func_hashidsdecode.go @@ -0,0 +1,56 @@ +package tool_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 hashIdsDecodeRequest struct { + Id string `uri:"id"` // 需解密的密文 +} + +type hashIdsDecodeResponse struct { + Val int `json:"val"` // 解密后的值 +} + +// HashIdsDecode HashIds 解密 +// @Summary HashIds 解密 +// @Description HashIds 解密 +// @Tags API.tool +// @Accept json +// @Produce json +// @Param id path string true "需解密的密文" +// @Success 200 {object} hashIdsDecodeResponse +// @Failure 400 {object} code.Failure +// @Router /api/tool/hashids/decode/{id} [get] +func (h *handler) HashIdsDecode() core.HandlerFunc { + return func(c core.Context) { + req := new(hashIdsDecodeRequest) + res := new(hashIdsDecodeResponse) + if err := c.ShouldBindURI(req); err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.ParamBindError, + code.Text(code.ParamBindError)).WithErr(err), + ) + return + } + + hashId, err := h.hashids.HashidsDecode(req.Id) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.HashIdsDecodeError, + code.Text(code.HashIdsDecodeError)).WithErr(err), + ) + return + } + + res.Val = hashId[0] + + c.Payload(res) + } +} diff --git a/internal/api/controller/tool_handler/func_hashidsencode.go b/internal/api/controller/tool_handler/func_hashidsencode.go new file mode 100755 index 0000000..4a7957f --- /dev/null +++ b/internal/api/controller/tool_handler/func_hashidsencode.go @@ -0,0 +1,58 @@ +package tool_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/spf13/cast" +) + +type hashIdsEncodeRequest struct { + Id int32 `uri:"id"` // 需加密的数字 +} + +type hashIdsEncodeResponse struct { + Val string `json:"val"` // 加密后的值 +} + +// HashIdsEncode HashIds 加密 +// @Summary HashIds 加密 +// @Description HashIds 加密 +// @Tags API.tool +// @Accept json +// @Produce json +// @Param id path string true "需加密的数字" +// @Success 200 {object} hashIdsEncodeResponse +// @Failure 400 {object} code.Failure +// @Router /api/tool/hashids/encode/{id} [get] +func (h *handler) HashIdsEncode() core.HandlerFunc { + return func(c core.Context) { + req := new(hashIdsEncodeRequest) + res := new(hashIdsEncodeResponse) + if err := c.ShouldBindURI(req); err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.ParamBindError, + code.Text(code.ParamBindError)).WithErr(err), + ) + return + } + + hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(req.Id)}) + if err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.HashIdsDecodeError, + code.Text(code.HashIdsDecodeError)).WithErr(err), + ) + return + } + + res.Val = hashId + + c.Payload(res) + } +} diff --git a/internal/api/controller/tool_handler/handler.go b/internal/api/controller/tool_handler/handler.go new file mode 100644 index 0000000..b516a2b --- /dev/null +++ b/internal/api/controller/tool_handler/handler.go @@ -0,0 +1,43 @@ +package tool_handler + +import ( + "github.com/xinliangnote/go-gin-api/configs" + "github.com/xinliangnote/go-gin-api/internal/pkg/cache" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" + "github.com/xinliangnote/go-gin-api/pkg/hash" + + "go.uber.org/zap" +) + +var _ Handler = (*handler)(nil) + +type Handler interface { + i() + + // HashIdsEncode HashIds 加密 + // @Tags API.tool + // @Router /api/tool/hashids/encode/{id} [get] + HashIdsEncode() core.HandlerFunc + + // HashIdsDecode HashIds 解密 + // @Tags API.tool + // @Router /api/tool/hashids/decode/{id} [get] + HashIdsDecode() core.HandlerFunc +} + +type handler struct { + logger *zap.Logger + cache cache.Repo + hashids hash.Hash +} + +func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler { + return &handler{ + logger: logger, + cache: cache, + hashids: hash.New(configs.Get().HashIds.Secret, configs.Get().HashIds.Length), + } +} + +func (h *handler) i() {} diff --git a/internal/api/repository/db_repo/authorized_api_repo/gen_authorized_api.go b/internal/api/repository/db_repo/authorized_api_repo/gen_authorized_api.go new file mode 100755 index 0000000..b28c583 --- /dev/null +++ b/internal/api/repository/db_repo/authorized_api_repo/gen_authorized_api.go @@ -0,0 +1,497 @@ +/////////////////////////////////////////////////////////// +// THIS FILE IS AUTO GENERATED by gormgen, DON'T EDIT IT // +// ANY CHANGES DONE HERE WILL BE LOST // +/////////////////////////////////////////////////////////// + +package authorized_api_repo + +import ( + "fmt" + "time" + + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" + + "github.com/pkg/errors" + "gorm.io/gorm" +) + +func NewModel() *AuthorizedApi { + return new(AuthorizedApi) +} + +func NewQueryBuilder() *authorizedApiRepoQueryBuilder { + return new(authorizedApiRepoQueryBuilder) +} + +func (t *AuthorizedApi) 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 *AuthorizedApi) Delete(db *gorm.DB) (err error) { + if err = db.Delete(t).Error; err != nil { + return errors.Wrap(err, "delete err") + } + return nil +} + +func (t *AuthorizedApi) Updates(db *gorm.DB, m map[string]interface{}) (err error) { + if err = db.Model(&AuthorizedApi{}).Where("id = ?", t.Id).Updates(m).Error; err != nil { + return errors.Wrap(err, "updates err") + } + return nil +} + +type authorizedApiRepoQueryBuilder struct { + order []string + where []struct { + prefix string + value interface{} + } + limit int + offset int +} + +func (qb *authorizedApiRepoQueryBuilder) 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 *authorizedApiRepoQueryBuilder) Count(db *gorm.DB) (int64, error) { + var c int64 + res := qb.buildQuery(db).Model(&AuthorizedApi{}).Count(&c) + if res.Error != nil && res.Error == gorm.ErrRecordNotFound { + c = 0 + } + return c, res.Error +} + +func (qb *authorizedApiRepoQueryBuilder) First(db *gorm.DB) (*AuthorizedApi, error) { + ret := &AuthorizedApi{} + res := qb.buildQuery(db).First(ret) + if res.Error != nil && res.Error == gorm.ErrRecordNotFound { + ret = nil + } + return ret, res.Error +} + +func (qb *authorizedApiRepoQueryBuilder) QueryOne(db *gorm.DB) (*AuthorizedApi, error) { + qb.limit = 1 + ret, err := qb.QueryAll(db) + if len(ret) > 0 { + return ret[0], err + } + return nil, err +} + +func (qb *authorizedApiRepoQueryBuilder) QueryAll(db *gorm.DB) ([]*AuthorizedApi, error) { + var ret []*AuthorizedApi + err := qb.buildQuery(db).Find(&ret).Error + return ret, err +} + +func (qb *authorizedApiRepoQueryBuilder) Limit(limit int) *authorizedApiRepoQueryBuilder { + qb.limit = limit + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) Offset(offset int) *authorizedApiRepoQueryBuilder { + qb.offset = offset + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereId(p db_repo.Predicate, value int32) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "id", p), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereIdIn(value []int32) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "id", "IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereIdNotIn(value []int32) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "id", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) OrderById(asc bool) *authorizedApiRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "id "+order) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereBusinessKey(p db_repo.Predicate, value string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_key", p), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereBusinessKeyIn(value []string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_key", "IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereBusinessKeyNotIn(value []string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_key", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) OrderByBusinessKey(asc bool) *authorizedApiRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "business_key "+order) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereMethod(p db_repo.Predicate, value string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "method", p), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereMethodIn(value []string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "method", "IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereMethodNotIn(value []string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "method", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) OrderByMethod(asc bool) *authorizedApiRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "method "+order) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereApi(p db_repo.Predicate, value string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "api", p), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereApiIn(value []string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "api", "IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereApiNotIn(value []string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "api", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) OrderByApi(asc bool) *authorizedApiRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "api "+order) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereIsDeleted(p db_repo.Predicate, value int32) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "is_deleted", p), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereIsDeletedIn(value []int32) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "is_deleted", "IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereIsDeletedNotIn(value []int32) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "is_deleted", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) OrderByIsDeleted(asc bool) *authorizedApiRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "is_deleted "+order) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereCreatedAt(p db_repo.Predicate, value time.Time) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_at", p), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereCreatedAtIn(value []time.Time) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_at", "IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereCreatedAtNotIn(value []time.Time) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_at", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) OrderByCreatedAt(asc bool) *authorizedApiRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "created_at "+order) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereCreatedUser(p db_repo.Predicate, value string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_user", p), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereCreatedUserIn(value []string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_user", "IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereCreatedUserNotIn(value []string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_user", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) OrderByCreatedUser(asc bool) *authorizedApiRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "created_user "+order) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereUpdatedAt(p db_repo.Predicate, value time.Time) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_at", p), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereUpdatedAtIn(value []time.Time) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_at", "IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereUpdatedAtNotIn(value []time.Time) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_at", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) OrderByUpdatedAt(asc bool) *authorizedApiRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "updated_at "+order) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereUpdatedUser(p db_repo.Predicate, value string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_user", p), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereUpdatedUserIn(value []string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_user", "IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) WhereUpdatedUserNotIn(value []string) *authorizedApiRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_user", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedApiRepoQueryBuilder) OrderByUpdatedUser(asc bool) *authorizedApiRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "updated_user "+order) + return qb +} diff --git a/internal/api/repository/db_repo/authorized_api_repo/gen_model.go b/internal/api/repository/db_repo/authorized_api_repo/gen_model.go new file mode 100755 index 0000000..c41bd7d --- /dev/null +++ b/internal/api/repository/db_repo/authorized_api_repo/gen_model.go @@ -0,0 +1,17 @@ +package authorized_api_repo + +import "time" + +// 已授权的调用方表 +//go:generate gormgen -structs AuthorizedApi -input . +type AuthorizedApi struct { + Id int32 // 主键 + BusinessKey string // 调用方key + Method string // 请求方式 + Api string // 请求地址 + IsDeleted int32 // 是否删除 1:是 -1:否 + CreatedAt time.Time `gorm:"time"` // 创建时间 + CreatedUser string // 创建人 + UpdatedAt time.Time `gorm:"time"` // 更新时间 + UpdatedUser string // 更新人 +} diff --git a/internal/api/repository/db_repo/authorized_api_repo/gen_table.md b/internal/api/repository/db_repo/authorized_api_repo/gen_table.md new file mode 100755 index 0000000..107b410 --- /dev/null +++ b/internal/api/repository/db_repo/authorized_api_repo/gen_table.md @@ -0,0 +1,14 @@ +#### go_gin_api.authorized_api +已授权的调用方表 + +| 序号 | 名称 | 描述 | 类型 | 键 | 为空 | 额外 | 默认值 | +| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | +| 1 | id | 主键 | int(11) unsigned | PRI | NO | auto_increment | | +| 2 | business_key | 调用方key | varchar(30) | | NO | | | +| 3 | method | 请求方式 | varchar(30) | | NO | | | +| 4 | api | 请求地址 | varchar(100) | | NO | | | +| 5 | is_deleted | 是否删除 1:是 -1:否 | tinyint(1) | | NO | | -1 | +| 6 | created_at | 创建时间 | timestamp | | NO | | CURRENT_TIMESTAMP | +| 7 | created_user | 创建人 | varchar(60) | | NO | | | +| 8 | updated_at | 更新时间 | timestamp | | NO | on update CURRENT_TIMESTAMP | CURRENT_TIMESTAMP | +| 9 | updated_user | 更新人 | varchar(60) | | NO | | | diff --git a/internal/api/repository/db_repo/authorized_repo/gen_authorized.go b/internal/api/repository/db_repo/authorized_repo/gen_authorized.go new file mode 100755 index 0000000..d6c6199 --- /dev/null +++ b/internal/api/repository/db_repo/authorized_repo/gen_authorized.go @@ -0,0 +1,583 @@ +/////////////////////////////////////////////////////////// +// THIS FILE IS AUTO GENERATED by gormgen, DON'T EDIT IT // +// ANY CHANGES DONE HERE WILL BE LOST // +/////////////////////////////////////////////////////////// + +package authorized_repo + +import ( + "fmt" + "time" + + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" + + "github.com/pkg/errors" + "gorm.io/gorm" +) + +func NewModel() *Authorized { + return new(Authorized) +} + +func NewQueryBuilder() *authorizedRepoQueryBuilder { + return new(authorizedRepoQueryBuilder) +} + +func (t *Authorized) 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 *Authorized) Delete(db *gorm.DB) (err error) { + if err = db.Delete(t).Error; err != nil { + return errors.Wrap(err, "delete err") + } + return nil +} + +func (t *Authorized) Updates(db *gorm.DB, m map[string]interface{}) (err error) { + if err = db.Model(&Authorized{}).Where("id = ?", t.Id).Updates(m).Error; err != nil { + return errors.Wrap(err, "updates err") + } + return nil +} + +type authorizedRepoQueryBuilder struct { + order []string + where []struct { + prefix string + value interface{} + } + limit int + offset int +} + +func (qb *authorizedRepoQueryBuilder) 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 *authorizedRepoQueryBuilder) Count(db *gorm.DB) (int64, error) { + var c int64 + res := qb.buildQuery(db).Model(&Authorized{}).Count(&c) + if res.Error != nil && res.Error == gorm.ErrRecordNotFound { + c = 0 + } + return c, res.Error +} + +func (qb *authorizedRepoQueryBuilder) First(db *gorm.DB) (*Authorized, error) { + ret := &Authorized{} + res := qb.buildQuery(db).First(ret) + if res.Error != nil && res.Error == gorm.ErrRecordNotFound { + ret = nil + } + return ret, res.Error +} + +func (qb *authorizedRepoQueryBuilder) QueryOne(db *gorm.DB) (*Authorized, error) { + qb.limit = 1 + ret, err := qb.QueryAll(db) + if len(ret) > 0 { + return ret[0], err + } + return nil, err +} + +func (qb *authorizedRepoQueryBuilder) QueryAll(db *gorm.DB) ([]*Authorized, error) { + var ret []*Authorized + err := qb.buildQuery(db).Find(&ret).Error + return ret, err +} + +func (qb *authorizedRepoQueryBuilder) Limit(limit int) *authorizedRepoQueryBuilder { + qb.limit = limit + return qb +} + +func (qb *authorizedRepoQueryBuilder) Offset(offset int) *authorizedRepoQueryBuilder { + qb.offset = offset + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereId(p db_repo.Predicate, value int32) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "id", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereIdIn(value []int32) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "id", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereIdNotIn(value []int32) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "id", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderById(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "id "+order) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereBusinessKey(p db_repo.Predicate, value string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_key", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereBusinessKeyIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_key", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereBusinessKeyNotIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_key", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderByBusinessKey(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "business_key "+order) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereBusinessSecret(p db_repo.Predicate, value string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_secret", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereBusinessSecretIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_secret", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereBusinessSecretNotIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_secret", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderByBusinessSecret(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "business_secret "+order) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereBusinessDeveloper(p db_repo.Predicate, value string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_developer", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereBusinessDeveloperIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_developer", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereBusinessDeveloperNotIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "business_developer", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderByBusinessDeveloper(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "business_developer "+order) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereRemark(p db_repo.Predicate, value string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "remark", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereRemarkIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "remark", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereRemarkNotIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "remark", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderByRemark(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "remark "+order) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereIsUsed(p db_repo.Predicate, value int32) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "is_used", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereIsUsedIn(value []int32) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "is_used", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereIsUsedNotIn(value []int32) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "is_used", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderByIsUsed(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "is_used "+order) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereIsDeleted(p db_repo.Predicate, value int32) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "is_deleted", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereIsDeletedIn(value []int32) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "is_deleted", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereIsDeletedNotIn(value []int32) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "is_deleted", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderByIsDeleted(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "is_deleted "+order) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereCreatedAt(p db_repo.Predicate, value time.Time) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_at", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereCreatedAtIn(value []time.Time) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_at", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereCreatedAtNotIn(value []time.Time) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_at", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderByCreatedAt(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "created_at "+order) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereCreatedUser(p db_repo.Predicate, value string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_user", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereCreatedUserIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_user", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereCreatedUserNotIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "created_user", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderByCreatedUser(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "created_user "+order) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereUpdatedAt(p db_repo.Predicate, value time.Time) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_at", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereUpdatedAtIn(value []time.Time) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_at", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereUpdatedAtNotIn(value []time.Time) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_at", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderByUpdatedAt(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "updated_at "+order) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereUpdatedUser(p db_repo.Predicate, value string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_user", p), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereUpdatedUserIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_user", "IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) WhereUpdatedUserNotIn(value []string) *authorizedRepoQueryBuilder { + qb.where = append(qb.where, struct { + prefix string + value interface{} + }{ + fmt.Sprintf("%v %v ?", "updated_user", "NOT IN"), + value, + }) + return qb +} + +func (qb *authorizedRepoQueryBuilder) OrderByUpdatedUser(asc bool) *authorizedRepoQueryBuilder { + order := "DESC" + if asc { + order = "ASC" + } + + qb.order = append(qb.order, "updated_user "+order) + return qb +} diff --git a/internal/api/repository/db_repo/authorized_repo/gen_model.go b/internal/api/repository/db_repo/authorized_repo/gen_model.go new file mode 100755 index 0000000..4cb7d00 --- /dev/null +++ b/internal/api/repository/db_repo/authorized_repo/gen_model.go @@ -0,0 +1,19 @@ +package authorized_repo + +import "time" + +// 已授权的调用方表 +//go:generate gormgen -structs Authorized -input . +type Authorized struct { + Id int32 // 主键 + BusinessKey string // 调用方key + BusinessSecret string // 调用方secret + BusinessDeveloper string // 调用方对接人 + Remark string // 备注 + IsUsed int32 // 是否启用 1:是 -1:否 + IsDeleted int32 // 是否删除 1:是 -1:否 + CreatedAt time.Time `gorm:"time"` // 创建时间 + CreatedUser string // 创建人 + UpdatedAt time.Time `gorm:"time"` // 更新时间 + UpdatedUser string // 更新人 +} diff --git a/internal/api/repository/db_repo/authorized_repo/gen_table.md b/internal/api/repository/db_repo/authorized_repo/gen_table.md new file mode 100755 index 0000000..4a7b212 --- /dev/null +++ b/internal/api/repository/db_repo/authorized_repo/gen_table.md @@ -0,0 +1,16 @@ +#### go_gin_api.authorized +已授权的调用方表 + +| 序号 | 名称 | 描述 | 类型 | 键 | 为空 | 额外 | 默认值 | +| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | +| 1 | id | 主键 | int(11) unsigned | PRI | NO | auto_increment | | +| 2 | business_key | 调用方key | varchar(32) | | NO | | | +| 3 | business_secret | 调用方secret | varchar(60) | | NO | | | +| 4 | business_developer | 调用方对接人 | varchar(60) | | NO | | | +| 5 | remark | 备注 | varchar(255) | | NO | | | +| 6 | is_used | 是否启用 1:是 -1:否 | tinyint(1) | | NO | | -1 | +| 7 | is_deleted | 是否删除 1:是 -1:否 | tinyint(1) | | NO | | -1 | +| 8 | created_at | 创建时间 | timestamp | | NO | | CURRENT_TIMESTAMP | +| 9 | created_user | 创建人 | varchar(60) | | NO | | | +| 10 | updated_at | 更新时间 | timestamp | | NO | on update CURRENT_TIMESTAMP | CURRENT_TIMESTAMP | +| 11 | updated_user | 更新人 | varchar(60) | | NO | | | diff --git a/internal/api/service/authorized_service/service.go b/internal/api/service/authorized_service/service.go new file mode 100644 index 0000000..9695400 --- /dev/null +++ b/internal/api/service/authorized_service/service.go @@ -0,0 +1,41 @@ +package authorized_service + +import ( + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_api_repo" + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/cache" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" +) + +var _ Service = (*service)(nil) + +type Service interface { + i() + + Create(ctx core.Context, authorizedData *CreateAuthorizedData) (id int32, err error) + List(ctx core.Context, searchData *SearchData) (listData []*authorized_repo.Authorized, err error) + PageList(ctx core.Context, searchData *SearchData) (listData []*authorized_repo.Authorized, err error) + PageListCount(ctx core.Context, searchData *SearchData) (total int64, err error) + UpdateUsed(ctx core.Context, id int32, used int32) (err error) + Delete(ctx core.Context, id int32) (err error) + Detail(ctx core.Context, id int32) (info *authorized_repo.Authorized, err error) + + CreateAPI(ctx core.Context, authorizedAPIData *CreateAuthorizedAPIData) (id int32, err error) + ListAPI(ctx core.Context, searchAPIData *SearchAPIData) (listData []*authorized_api_repo.AuthorizedApi, err error) + DeleteAPI(ctx core.Context, id int32) (err error) +} + +type service struct { + db db.Repo + cache cache.Repo +} + +func New(db db.Repo, cache cache.Repo) Service { + return &service{ + db: db, + cache: cache, + } +} + +func (s *service) i() {} diff --git a/internal/api/service/authorized_service/service_create.go b/internal/api/service/authorized_service/service_create.go new file mode 100644 index 0000000..89538b9 --- /dev/null +++ b/internal/api/service/authorized_service/service_create.go @@ -0,0 +1,37 @@ +package authorized_service + +import ( + "crypto/rand" + "encoding/hex" + "io" + + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +type CreateAuthorizedData struct { + BusinessKey string `json:"business_key"` // 调用方key + BusinessDeveloper string `json:"business_developer"` // 调用方对接人 + Remark string `json:"remark"` // 备注 +} + +func (s *service) Create(ctx core.Context, authorizedData *CreateAuthorizedData) (id int32, err error) { + buf := make([]byte, 10) + io.ReadFull(rand.Reader, buf) + secret := string(hex.EncodeToString(buf)) + + model := authorized_repo.NewModel() + model.BusinessKey = authorizedData.BusinessKey + model.BusinessSecret = secret + model.BusinessDeveloper = authorizedData.BusinessDeveloper + model.Remark = authorizedData.Remark + model.CreatedUser = "system" // TODO + model.IsUsed = 1 + model.IsDeleted = -1 + + id, err = model.Create(s.db.GetDbW().WithContext(ctx.RequestContext())) + if err != nil { + return 0, err + } + return +} diff --git a/internal/api/service/authorized_service/service_createapi.go b/internal/api/service/authorized_service/service_createapi.go new file mode 100644 index 0000000..5bd9e16 --- /dev/null +++ b/internal/api/service/authorized_service/service_createapi.go @@ -0,0 +1,27 @@ +package authorized_service + +import ( + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_api_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +type CreateAuthorizedAPIData struct { + BusinessKey string `json:"business_key"` // 调用方key + Method string `json:"method"` // 请求方法 + API string `json:"api"` // 请求地址 +} + +func (s *service) CreateAPI(ctx core.Context, authorizedAPIData *CreateAuthorizedAPIData) (id int32, err error) { + model := authorized_api_repo.NewModel() + model.BusinessKey = authorizedAPIData.BusinessKey + model.Method = authorizedAPIData.Method + model.Api = authorizedAPIData.API + model.CreatedUser = "system" // TODO + model.IsDeleted = -1 + + id, err = model.Create(s.db.GetDbW().WithContext(ctx.RequestContext())) + if err != nil { + return 0, err + } + return +} diff --git a/internal/api/service/authorized_service/service_delete.go b/internal/api/service/authorized_service/service_delete.go new file mode 100644 index 0000000..cacdf3f --- /dev/null +++ b/internal/api/service/authorized_service/service_delete.go @@ -0,0 +1,23 @@ +package authorized_service + +import ( + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +func (s *service) Delete(ctx core.Context, id int32) (err error) { + model := authorized_repo.NewModel() + model.Id = id + + data := map[string]interface{}{ + "is_deleted": 1, + "updated_user": "system", // TODO + } + + err = model.Updates(s.db.GetDbW().WithContext(ctx.RequestContext()), data) + if err != nil { + return err + } + + return +} diff --git a/internal/api/service/authorized_service/service_deleteapi.go b/internal/api/service/authorized_service/service_deleteapi.go new file mode 100644 index 0000000..cff2ca1 --- /dev/null +++ b/internal/api/service/authorized_service/service_deleteapi.go @@ -0,0 +1,23 @@ +package authorized_service + +import ( + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_api_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +func (s *service) DeleteAPI(ctx core.Context, id int32) (err error) { + model := authorized_api_repo.NewModel() + model.Id = id + + data := map[string]interface{}{ + "is_deleted": 1, + "updated_user": "system", // TODO + } + + err = model.Updates(s.db.GetDbW().WithContext(ctx.RequestContext()), data) + if err != nil { + return err + } + + return +} diff --git a/internal/api/service/authorized_service/service_detail.go b/internal/api/service/authorized_service/service_detail.go new file mode 100644 index 0000000..22a493a --- /dev/null +++ b/internal/api/service/authorized_service/service_detail.go @@ -0,0 +1,21 @@ +package authorized_service + +import ( + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +func (s *service) Detail(ctx core.Context, id int32) (info *authorized_repo.Authorized, err error) { + qb := authorized_repo.NewQueryBuilder() + qb = qb.WhereIsDeleted(db_repo.EqualPredicate, -1) + + qb.WhereId(db_repo.EqualPredicate, id) + + info, err = qb.First(s.db.GetDbR().WithContext(ctx.RequestContext())) + if err != nil { + return nil, err + } + + return +} diff --git a/internal/api/service/authorized_service/service_list.go b/internal/api/service/authorized_service/service_list.go new file mode 100644 index 0000000..cfe7df4 --- /dev/null +++ b/internal/api/service/authorized_service/service_list.go @@ -0,0 +1,34 @@ +package authorized_service + +import ( + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +func (s *service) List(ctx core.Context, searchData *SearchData) (listData []*authorized_repo.Authorized, err error) { + + qb := authorized_repo.NewQueryBuilder() + qb = qb.WhereIsDeleted(db_repo.EqualPredicate, -1) + + if searchData.BusinessKey != "" { + qb.WhereBusinessKey(db_repo.EqualPredicate, searchData.BusinessKey) + } + + if searchData.BusinessSecret != "" { + qb.WhereBusinessSecret(db_repo.EqualPredicate, searchData.BusinessSecret) + } + + if searchData.BusinessDeveloper != "" { + qb.WhereBusinessDeveloper(db_repo.EqualPredicate, searchData.BusinessDeveloper) + } + + listData, err = qb. + OrderById(false). + QueryAll(s.db.GetDbR().WithContext(ctx.RequestContext())) + if err != nil { + return nil, err + } + + return +} diff --git a/internal/api/service/authorized_service/service_listapi.go b/internal/api/service/authorized_service/service_listapi.go new file mode 100644 index 0000000..18aa0a5 --- /dev/null +++ b/internal/api/service/authorized_service/service_listapi.go @@ -0,0 +1,30 @@ +package authorized_service + +import ( + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_api_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +type SearchAPIData struct { + BusinessKey string `json:"business_key"` // 调用方key +} + +func (s *service) ListAPI(ctx core.Context, searchAPIData *SearchAPIData) (listData []*authorized_api_repo.AuthorizedApi, err error) { + + qb := authorized_api_repo.NewQueryBuilder() + qb = qb.WhereIsDeleted(db_repo.EqualPredicate, -1) + + if searchAPIData.BusinessKey != "" { + qb.WhereBusinessKey(db_repo.EqualPredicate, searchAPIData.BusinessKey) + } + + listData, err = qb. + OrderById(false). + QueryAll(s.db.GetDbR().WithContext(ctx.RequestContext())) + if err != nil { + return nil, err + } + + return +} diff --git a/internal/api/service/authorized_service/service_pagelist.go b/internal/api/service/authorized_service/service_pagelist.go new file mode 100644 index 0000000..bbb4d28 --- /dev/null +++ b/internal/api/service/authorized_service/service_pagelist.go @@ -0,0 +1,57 @@ +package authorized_service + +import ( + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +type SearchData struct { + Page int `json:"page"` // 第几页 + PageSize int `json:"page_size"` // 每页显示条数 + BusinessKey string `json:"business_key"` // 调用方key + BusinessSecret string `json:"business_secret"` // 调用方secret + BusinessDeveloper string `json:"business_developer"` // 调用方对接人 + Remark string `json:"remark"` // 备注 +} + +func (s *service) PageList(ctx core.Context, searchData *SearchData) (listData []*authorized_repo.Authorized, err error) { + + page := searchData.Page + if page == 0 { + page = 1 + } + + pageSize := searchData.PageSize + if pageSize == 0 { + pageSize = 10 + } + + offset := (page - 1) * pageSize + + qb := authorized_repo.NewQueryBuilder() + qb = qb.WhereIsDeleted(db_repo.EqualPredicate, -1) + + if searchData.BusinessKey != "" { + qb.WhereBusinessKey(db_repo.EqualPredicate, searchData.BusinessKey) + } + + if searchData.BusinessSecret != "" { + qb.WhereBusinessSecret(db_repo.EqualPredicate, searchData.BusinessSecret) + } + + if searchData.BusinessDeveloper != "" { + qb.WhereBusinessDeveloper(db_repo.EqualPredicate, searchData.BusinessDeveloper) + } + + listData, err = qb. + Limit(pageSize). + Offset(offset). + OrderById(false). + QueryAll(s.db.GetDbR().WithContext(ctx.RequestContext())) + if err != nil { + return nil, err + } + + return +} diff --git a/internal/api/service/authorized_service/service_pagelistcount.go b/internal/api/service/authorized_service/service_pagelistcount.go new file mode 100644 index 0000000..2ea88f3 --- /dev/null +++ b/internal/api/service/authorized_service/service_pagelistcount.go @@ -0,0 +1,31 @@ +package authorized_service + +import ( + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +func (s *service) PageListCount(ctx core.Context, searchData *SearchData) (total int64, err error) { + qb := authorized_repo.NewQueryBuilder() + qb = qb.WhereIsDeleted(db_repo.EqualPredicate, -1) + + if searchData.BusinessKey != "" { + qb.WhereBusinessKey(db_repo.EqualPredicate, searchData.BusinessKey) + } + + if searchData.BusinessSecret != "" { + qb.WhereBusinessSecret(db_repo.EqualPredicate, searchData.BusinessSecret) + } + + if searchData.BusinessDeveloper != "" { + qb.WhereBusinessDeveloper(db_repo.EqualPredicate, searchData.BusinessDeveloper) + } + + total, err = qb.Count(s.db.GetDbR().WithContext(ctx.RequestContext())) + if err != nil { + return 0, err + } + + return +} diff --git a/internal/api/service/authorized_service/service_updateused.go b/internal/api/service/authorized_service/service_updateused.go new file mode 100644 index 0000000..b67b25b --- /dev/null +++ b/internal/api/service/authorized_service/service_updateused.go @@ -0,0 +1,23 @@ +package authorized_service + +import ( + "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/authorized_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +func (s *service) UpdateUsed(ctx core.Context, id int32, used int32) (err error) { + model := authorized_repo.NewModel() + model.Id = id + + data := map[string]interface{}{ + "is_used": used, + "updated_user": "system", // TODO + } + + err = model.Updates(s.db.GetDbW().WithContext(ctx.RequestContext()), data) + if err != nil { + return err + } + + return +} diff --git a/internal/router/router_api.go b/internal/router/router_api.go index 590d0b9..d52a4ca 100644 --- a/internal/router/router_api.go +++ b/internal/router/router_api.go @@ -1,7 +1,9 @@ package router import ( + "github.com/xinliangnote/go-gin-api/internal/api/controller/authorized_handler" "github.com/xinliangnote/go-gin-api/internal/api/controller/demo_handler" + "github.com/xinliangnote/go-gin-api/internal/api/controller/tool_handler" "github.com/xinliangnote/go-gin-api/internal/api/controller/user_handler" "github.com/xinliangnote/go-gin-api/internal/pkg/core" ) @@ -33,4 +35,24 @@ func setApiRouter(r *resource) { user.PATCH("/delete/:id", userHandler.Delete()) user.GET("/info/:username", core.AliasForRecordMetrics("/user/info"), userHandler.Detail()) } + + // api + api := r.mux.Group("/api") + { + // authorized + authorizedHandler := authorized_handler.New(r.logger, r.db, r.cache) + api.POST("/authorized", authorizedHandler.Create()) + api.GET("/authorized", authorizedHandler.List()) + api.PATCH("/authorized/used", authorizedHandler.UpdateUsed()) + api.DELETE("/authorized/:id", authorizedHandler.Delete()) + + api.POST("/authorized_api", authorizedHandler.CreateAPI()) + api.GET("/authorized_list", authorizedHandler.ListAPI()) + api.DELETE("/authorized_api/:id", authorizedHandler.DeleteAPI()) + + // tool + toolHandler := tool_handler.New(r.logger, r.db, r.cache) + api.GET("/tool/hashids/encode/:id", toolHandler.HashIdsEncode()) + api.GET("/tool/hashids/decode/:id", toolHandler.HashIdsDecode()) + } } diff --git a/internal/router/router_web.go b/internal/router/router_web.go index 7299b47..2e14d7d 100644 --- a/internal/router/router_web.go +++ b/internal/router/router_web.go @@ -1,10 +1,12 @@ package router import ( + "github.com/xinliangnote/go-gin-api/internal/web/controller/authorized_handler" "github.com/xinliangnote/go-gin-api/internal/web/controller/configinfo_handler" "github.com/xinliangnote/go-gin-api/internal/web/controller/dashboard_handler" "github.com/xinliangnote/go-gin-api/internal/web/controller/gencode_handler" "github.com/xinliangnote/go-gin-api/internal/web/controller/index_handler" + "github.com/xinliangnote/go-gin-api/internal/web/controller/tool_handler" ) func setWebRouter(r *resource) { @@ -13,6 +15,8 @@ func setWebRouter(r *resource) { dashboardHandler := dashboard_handler.New(r.logger, r.db, r.cache) genCodeHandler := gencode_handler.New(r.logger, r.db, r.cache) configInfoHandler := configinfo_handler.New(r.logger, r.db, r.cache) + authorizedHandler := authorized_handler.New(r.logger, r.db, r.cache) + toolHandler := tool_handler.New(r.logger, r.db, r.cache) web := r.mux.Group("", r.middles.DisableLog()) { @@ -35,5 +39,13 @@ func setWebRouter(r *resource) { web.GET("/handlergen", genCodeHandler.HandlerView()) web.POST("/handlergen_exec", genCodeHandler.HandlerExecute()) + // 调用方 + web.GET("/authorized/list", authorizedHandler.ListView()) + web.GET("/authorized/add", authorizedHandler.AddView()) + web.GET("/authorized/api/:id", authorizedHandler.ApiView()) + + // 工具箱 + web.GET("/tool/hashids", toolHandler.HashIdsView()) + } } diff --git a/internal/web/controller/authorized_handler/func_addview.go b/internal/web/controller/authorized_handler/func_addview.go new file mode 100644 index 0000000..2283f17 --- /dev/null +++ b/internal/web/controller/authorized_handler/func_addview.go @@ -0,0 +1,9 @@ +package authorized_handler + +import "github.com/xinliangnote/go-gin-api/internal/pkg/core" + +func (h *handler) AddView() core.HandlerFunc { + return func(c core.Context) { + c.HTML("authorized_add", nil) + } +} diff --git a/internal/web/controller/authorized_handler/func_apiview.go b/internal/web/controller/authorized_handler/func_apiview.go new file mode 100644 index 0000000..4cab5af --- /dev/null +++ b/internal/web/controller/authorized_handler/func_apiview.go @@ -0,0 +1,36 @@ +package authorized_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 apiViewRequest struct { + Id string `uri:"id"` // 主键ID +} + +type apiViewResponse struct { + HashID string `json:"hash_id"` // hashID +} + +func (h *handler) ApiView() core.HandlerFunc { + return func(c core.Context) { + req := new(apiViewRequest) + if err := c.ShouldBindURI(req); err != nil { + c.AbortWithError(errno.NewError( + http.StatusBadRequest, + code.ParamBindError, + code.Text(code.ParamBindError)).WithErr(err), + ) + return + } + + obj := new(apiViewResponse) + obj.HashID = req.Id + + c.HTML("authorized_api", obj) + } +} diff --git a/internal/web/controller/authorized_handler/func_listview.go b/internal/web/controller/authorized_handler/func_listview.go new file mode 100644 index 0000000..358d142 --- /dev/null +++ b/internal/web/controller/authorized_handler/func_listview.go @@ -0,0 +1,9 @@ +package authorized_handler + +import "github.com/xinliangnote/go-gin-api/internal/pkg/core" + +func (h *handler) ListView() core.HandlerFunc { + return func(c core.Context) { + c.HTML("authorized_list", nil) + } +} diff --git a/internal/web/controller/authorized_handler/handler.go b/internal/web/controller/authorized_handler/handler.go new file mode 100644 index 0000000..ecb0a91 --- /dev/null +++ b/internal/web/controller/authorized_handler/handler.go @@ -0,0 +1,35 @@ +package authorized_handler + +import ( + "github.com/xinliangnote/go-gin-api/internal/pkg/cache" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" + + "go.uber.org/zap" +) + +var _ Handler = (*handler)(nil) + +type Handler interface { + i() + + AddView() core.HandlerFunc + ApiView() core.HandlerFunc + ListView() core.HandlerFunc +} + +type handler struct { + db db.Repo + logger *zap.Logger + cache cache.Repo +} + +func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler { + return &handler{ + logger: logger, + cache: cache, + db: db, + } +} + +func (h *handler) i() {} diff --git a/internal/web/controller/tool_handler/func_hashidsview.go b/internal/web/controller/tool_handler/func_hashidsview.go new file mode 100644 index 0000000..d493389 --- /dev/null +++ b/internal/web/controller/tool_handler/func_hashidsview.go @@ -0,0 +1,12 @@ +package tool_handler + +import ( + "github.com/xinliangnote/go-gin-api/configs" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +func (h *handler) HashIdsView() core.HandlerFunc { + return func(c core.Context) { + c.HTML("tool_hashids", configs.Get()) + } +} diff --git a/internal/web/controller/tool_handler/handler.go b/internal/web/controller/tool_handler/handler.go new file mode 100644 index 0000000..0ee1ca4 --- /dev/null +++ b/internal/web/controller/tool_handler/handler.go @@ -0,0 +1,31 @@ +package tool_handler + +import ( + "github.com/xinliangnote/go-gin-api/internal/pkg/cache" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" + + "go.uber.org/zap" +) + +var _ Handler = (*handler)(nil) + +type Handler interface { + i() + + HashIdsView() core.HandlerFunc +} + +type handler struct { + logger *zap.Logger + cache cache.Repo +} + +func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler { + return &handler{ + logger: logger, + cache: cache, + } +} + +func (h *handler) i() {} diff --git a/pkg/hash/hash.go b/pkg/hash/hash.go new file mode 100644 index 0000000..e708b76 --- /dev/null +++ b/pkg/hash/hash.go @@ -0,0 +1,25 @@ +package hash + +var _ Hash = (*hash)(nil) + +type Hash interface { + i() + + // hashids + HashidsEncode(params []int) (string, error) + HashidsDecode(hash string) ([]int, error) +} + +type hash struct { + secret string + length int +} + +func New(secret string, length int) Hash { + return &hash{ + secret: secret, + length: length, + } +} + +func (h *hash) i() {} diff --git a/pkg/hash/hash_hashids.go b/pkg/hash/hash_hashids.go new file mode 100644 index 0000000..e84296f --- /dev/null +++ b/pkg/hash/hash_hashids.go @@ -0,0 +1,41 @@ +package hash + +import ( + "github.com/speps/go-hashids" +) + +func (h *hash) HashidsEncode(params []int) (string, error) { + hd := hashids.NewData() + hd.Salt = h.secret + hd.MinLength = h.length + + hashID, err := hashids.NewWithData(hd) + if err != nil { + return "", err + } + + hashStr, err := hashID.Encode(params) + if err != nil { + return "", err + } + + return hashStr, nil +} + +func (h *hash) HashidsDecode(hash string) ([]int, error) { + hd := hashids.NewData() + hd.Salt = h.secret + hd.MinLength = h.length + + hashID, err := hashids.NewWithData(hd) + if err != nil { + return nil, err + } + + ids, err := hashID.DecodeWithError(hash) + if err != nil { + return nil, err + } + + return ids, nil +} diff --git a/pkg/hash/hash_hashids_test.go b/pkg/hash/hash_hashids_test.go new file mode 100644 index 0000000..f2ffcf7 --- /dev/null +++ b/pkg/hash/hash_hashids_test.go @@ -0,0 +1,18 @@ +package hash + +import "testing" + +const secret = "i1ydX9RtHyuJTrw7frcu" +const length = 12 + +func TestHashidsEncode(t *testing.T) { + str, _ := New(secret, length).HashidsEncode([]int{99}) + t.Log(str) + + //GyV5pJqXvwAR +} + +func TestHashidsDecode(t *testing.T) { + ids, _ := New(secret, length).HashidsDecode("GyV5pJqXvwAR") + t.Log(ids) +}