提交 75bf558a 编写于 作者: Mr.奇淼('s avatar Mr.奇淼(

Merge branch 'master' of https://github.com/flipped-aurora/gin-vue-admin into gva_workflow

 Conflicts:
	server/initialize/router.go
	server/router/sys_workflow.go
......@@ -216,6 +216,11 @@ func DeleteUser(c *gin.Context) {
response.FailWithMessage(err.Error(), c)
return
}
jwtId := getUserID(c)
if jwtId == uint(reqId.Id) {
response.FailWithMessage("删除失败, 自杀失败", c)
return
}
if err := service.DeleteUser(reqId.Id); err != nil {
global.GVA_LOG.Error("删除失败!", zap.Any("err", err))
response.FailWithMessage("删除失败", c)
......
......@@ -30,7 +30,7 @@ func RunWindowsServer() {
fmt.Printf(`
欢迎使用 Gin-Vue-Admin
当前版本:V2.3.6
当前版本:V2.3.7
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
默认前端文件运行地址:http://127.0.0.1:8080
如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/docs/coffee
......
......@@ -24,25 +24,31 @@ func Routers() *gin.Engine {
Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
global.GVA_LOG.Info("register swagger handler")
// 方便统一添加路由组前缀 多服务器上线使用
ApiGroup := Router.Group("")
router.InitUserRouter(ApiGroup) // 注册用户路由
router.InitBaseRouter(ApiGroup) // 注册基础功能路由 不做鉴权
router.InitMenuRouter(ApiGroup) // 注册menu路由
router.InitAuthorityRouter(ApiGroup) // 注册角色路由
router.InitApiRouter(ApiGroup) // 注册功能api路由
router.InitFileUploadAndDownloadRouter(ApiGroup) // 文件上传下载功能路由
router.InitSimpleUploaderRouter(ApiGroup) // 断点续传(插件版)
router.InitCasbinRouter(ApiGroup) // 权限相关路由
router.InitJwtRouter(ApiGroup) // jwt相关路由
router.InitSystemRouter(ApiGroup) // system相关路由
router.InitCustomerRouter(ApiGroup) // 客户路由
router.InitAutoCodeRouter(ApiGroup) // 创建自动化代码
router.InitSysDictionaryDetailRouter(ApiGroup) // 字典详情管理
router.InitSysDictionaryRouter(ApiGroup) // 字典管理
router.InitSysOperationRecordRouter(ApiGroup) // 操作记录
router.InitEmailRouter(ApiGroup) // 邮件相关路由
router.InitWorkflowProcessRouter(ApiGroup) // 工作流创建相关接口
PublicGroup := Router.Group("")
{
router.InitBaseRouter(PublicGroup) // 注册基础功能路由 不做鉴权
}
PrivateGroup := Router.Group("")
PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
{
router.InitApiRouter(PrivateGroup) // 注册功能api路由
router.InitJwtRouter(PrivateGroup) // jwt相关路由
router.InitUserRouter(PrivateGroup) // 注册用户路由
router.InitMenuRouter(PrivateGroup) // 注册menu路由
router.InitEmailRouter(PrivateGroup) // 邮件相关路由
router.InitSystemRouter(PrivateGroup) // system相关路由
router.InitCasbinRouter(PrivateGroup) // 权限相关路由
router.InitWorkflowRouter(PrivateGroup) // 工作流相关路由
router.InitCustomerRouter(PrivateGroup) // 客户路由
router.InitAutoCodeRouter(PrivateGroup) // 创建自动化代码
router.InitAuthorityRouter(PrivateGroup) // 注册角色路由
router.InitSimpleUploaderRouter(PrivateGroup) // 断点续传(插件版)
router.InitSysDictionaryRouter(PrivateGroup) // 字典管理
router.InitSysOperationRecordRouter(PrivateGroup) // 操作记录
router.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
router.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
router.InitWorkflowProcessRouter(PrivateGroup) // 工作流创建相关接口
}
global.GVA_LOG.Info("router register success")
return Router
}
......@@ -42,6 +42,7 @@ func JWTAuth() gin.HandlerFunc {
return
}
if err, _ = service.FindUserByUuid(claims.UUID.String()); err != nil {
_ = service.JsonInBlacklist(model.JwtBlacklist{Jwt: token})
response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
c.Abort()
}
......
......@@ -11,7 +11,6 @@ import (
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
)
......@@ -31,7 +30,7 @@ func OperationRecord() gin.HandlerFunc {
if claims, ok := c.Get("claims"); ok {
waitUse := claims.(*request.CustomClaims)
userId = int(waitUse.ID)
}else {
} else {
id, err := strconv.Atoi(c.Request.Header.Get("x-user-id"))
if err != nil {
userId = 0
......@@ -46,10 +45,11 @@ func OperationRecord() gin.HandlerFunc {
Body: string(body),
UserID: userId,
}
values := c.Request.Header.Values("content-type")
if len(values) >0 && strings.Contains(values[0], "boundary") {
record.Body = "file"
}
// 存在某些未知错误 TODO
//values := c.Request.Header.Values("content-type")
//if len(values) >0 && strings.Contains(values[0], "boundary") {
// record.Body = "file"
//}
writer := responseBodyWriter{
ResponseWriter: c.Writer,
body: &bytes.Buffer{},
......
......@@ -2,14 +2,11 @@ package router
import (
"gin-vue-admin/api/v1"
"gin-vue-admin/middleware"
"github.com/gin-gonic/gin"
)
func InitSimpleUploaderRouter(Router *gin.RouterGroup) {
ApiRouter := Router.Group("simpleUploader").
Use(middleware.JWTAuth()).
Use(middleware.CasbinHandler())
ApiRouter := Router.Group("simpleUploader")
{
ApiRouter.POST("upload", v1.SimpleUploaderUpload) // 上传功能
ApiRouter.GET("checkFileMd5", v1.CheckFileMd5) // 文件完整度验证
......
......@@ -7,10 +7,7 @@ import (
)
func InitCustomerRouter(Router *gin.RouterGroup) {
ApiRouter := Router.Group("customer").
Use(middleware.JWTAuth()).
Use(middleware.CasbinHandler()).
Use(middleware.OperationRecord())
ApiRouter := Router.Group("customer").Use(middleware.OperationRecord())
{
ApiRouter.POST("customer", v1.CreateExaCustomer) // 创建客户
ApiRouter.PUT("customer", v1.UpdateExaCustomer) // 更新客户
......
......@@ -7,7 +7,6 @@ import (
func InitFileUploadAndDownloadRouter(Router *gin.RouterGroup) {
FileUploadAndDownloadGroup := Router.Group("fileUploadAndDownload")
// .Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
{
FileUploadAndDownloadGroup.POST("/upload", v1.UploadFile) // 上传文件
FileUploadAndDownloadGroup.POST("/getFileList", v1.GetFileList) // 获取上传文件列表
......
......@@ -7,7 +7,7 @@ import (
)
func InitApiRouter(Router *gin.RouterGroup) {
ApiRouter := Router.Group("api").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
ApiRouter := Router.Group("api").Use(middleware.OperationRecord())
{
ApiRouter.POST("createApi", v1.CreateApi) // 创建Api
ApiRouter.POST("deleteApi", v1.DeleteApi) // 删除Api
......
......@@ -7,10 +7,7 @@ import (
)
func InitAuthorityRouter(Router *gin.RouterGroup) {
AuthorityRouter := Router.Group("authority").
Use(middleware.JWTAuth()).
Use(middleware.CasbinHandler()).
Use(middleware.OperationRecord())
AuthorityRouter := Router.Group("authority").Use(middleware.OperationRecord())
{
AuthorityRouter.POST("createAuthority", v1.CreateAuthority) // 创建角色
AuthorityRouter.POST("deleteAuthority", v1.DeleteAuthority) // 删除角色
......
......@@ -2,14 +2,11 @@ package router
import (
"gin-vue-admin/api/v1"
"gin-vue-admin/middleware"
"github.com/gin-gonic/gin"
)
func InitAutoCodeRouter(Router *gin.RouterGroup) {
AutoCodeRouter := Router.Group("autoCode").
Use(middleware.JWTAuth()).
Use(middleware.CasbinHandler())
AutoCodeRouter := Router.Group("autoCode")
{
AutoCodeRouter.POST("createTemp", v1.CreateTemp) // 创建自动化代码
AutoCodeRouter.GET("getTables", v1.GetTables) // 获取对应数据库的表
......
......@@ -7,10 +7,7 @@ import (
)
func InitCasbinRouter(Router *gin.RouterGroup) {
CasbinRouter := Router.Group("casbin").
Use(middleware.JWTAuth()).
Use(middleware.CasbinHandler()).
Use(middleware.OperationRecord())
CasbinRouter := Router.Group("casbin").Use(middleware.OperationRecord())
{
CasbinRouter.POST("updateCasbin", v1.UpdateCasbin)
CasbinRouter.POST("getPolicyPathByAuthorityId", v1.GetPolicyPathByAuthorityId)
......
......@@ -7,10 +7,7 @@ import (
)
func InitSysDictionaryRouter(Router *gin.RouterGroup) {
SysDictionaryRouter := Router.Group("sysDictionary").
Use(middleware.JWTAuth()).
Use(middleware.CasbinHandler()).
Use(middleware.OperationRecord())
SysDictionaryRouter := Router.Group("sysDictionary").Use(middleware.OperationRecord())
{
SysDictionaryRouter.POST("createSysDictionary", v1.CreateSysDictionary) // 新建SysDictionary
SysDictionaryRouter.DELETE("deleteSysDictionary", v1.DeleteSysDictionary) // 删除SysDictionary
......
......@@ -7,10 +7,7 @@ import (
)
func InitSysDictionaryDetailRouter(Router *gin.RouterGroup) {
SysDictionaryDetailRouter := Router.Group("sysDictionaryDetail").
Use(middleware.JWTAuth()).
Use(middleware.CasbinHandler()).
Use(middleware.OperationRecord())
SysDictionaryDetailRouter := Router.Group("sysDictionaryDetail").Use(middleware.OperationRecord())
{
SysDictionaryDetailRouter.POST("createSysDictionaryDetail", v1.CreateSysDictionaryDetail) // 新建SysDictionaryDetail
SysDictionaryDetailRouter.DELETE("deleteSysDictionaryDetail", v1.DeleteSysDictionaryDetail) // 删除SysDictionaryDetail
......
......@@ -7,7 +7,7 @@ import (
)
func InitEmailRouter(Router *gin.RouterGroup) {
UserRouter := Router.Group("email").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
UserRouter := Router.Group("email").Use(middleware.OperationRecord())
{
UserRouter.POST("emailTest", v1.EmailTest) // 发送测试邮件
}
......
......@@ -7,7 +7,7 @@ import (
)
func InitJwtRouter(Router *gin.RouterGroup) {
ApiRouter := Router.Group("jwt").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
ApiRouter := Router.Group("jwt").Use(middleware.OperationRecord())
{
ApiRouter.POST("jsonInBlacklist", v1.JsonInBlacklist) // jwt加入黑名单
}
......
......@@ -7,7 +7,7 @@ import (
)
func InitMenuRouter(Router *gin.RouterGroup) (R gin.IRoutes) {
MenuRouter := Router.Group("menu").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
MenuRouter := Router.Group("menu").Use(middleware.OperationRecord())
{
MenuRouter.POST("getMenu", v1.GetMenu) // 获取菜单树
MenuRouter.POST("getMenuList", v1.GetMenuList) // 分页获取基础menu列表
......
......@@ -7,15 +7,13 @@ import (
)
func InitSysOperationRecordRouter(Router *gin.RouterGroup) {
SysOperationRecordRouter := Router.Group("sysOperationRecord").
Use(middleware.JWTAuth()).
Use(middleware.CasbinHandler())
SysOperationRecordRouter := Router.Group("sysOperationRecord").Use(middleware.OperationRecord())
{
SysOperationRecordRouter.POST("createSysOperationRecord", v1.CreateSysOperationRecord) // 新建SysOperationRecord
SysOperationRecordRouter.DELETE("deleteSysOperationRecord", v1.DeleteSysOperationRecord) // 删除SysOperationRecord
SysOperationRecordRouter.POST("createSysOperationRecord", v1.CreateSysOperationRecord) // 新建SysOperationRecord
SysOperationRecordRouter.DELETE("deleteSysOperationRecord", v1.DeleteSysOperationRecord) // 删除SysOperationRecord
SysOperationRecordRouter.DELETE("deleteSysOperationRecordByIds", v1.DeleteSysOperationRecordByIds) // 批量删除SysOperationRecord
SysOperationRecordRouter.GET("findSysOperationRecord", v1.FindSysOperationRecord) // 根据ID获取SysOperationRecord
SysOperationRecordRouter.GET("getSysOperationRecordList", v1.GetSysOperationRecordList) // 获取SysOperationRecord列表
SysOperationRecordRouter.GET("findSysOperationRecord", v1.FindSysOperationRecord) // 根据ID获取SysOperationRecord
SysOperationRecordRouter.GET("getSysOperationRecordList", v1.GetSysOperationRecordList) // 获取SysOperationRecord列表
}
}
......@@ -7,7 +7,7 @@ import (
)
func InitSystemRouter(Router *gin.RouterGroup) {
SystemRouter := Router.Group("system").Use(middleware.JWTAuth(), middleware.CasbinHandler())
SystemRouter := Router.Group("system").Use(middleware.OperationRecord())
{
SystemRouter.POST("getSystemConfig", v1.GetSystemConfig) // 获取配置文件内容
SystemRouter.POST("setSystemConfig", v1.SetSystemConfig) // 设置配置文件内容
......
......@@ -7,10 +7,7 @@ import (
)
func InitUserRouter(Router *gin.RouterGroup) {
UserRouter := Router.Group("user").
Use(middleware.JWTAuth()).
Use(middleware.CasbinHandler()).
Use(middleware.OperationRecord())
UserRouter := Router.Group("user").Use(middleware.OperationRecord())
{
UserRouter.POST("register", v1.Register)
UserRouter.POST("changePassword", v1.ChangePassword) // 修改密码
......
......@@ -209,7 +209,6 @@ func addAutoMoveFile(data *tplData) {
}
}
//@author: [piexlmax](https://github.com/piexlmax)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: CreateApi
......@@ -259,14 +258,13 @@ func AutoCreateApi(a *model.AutoCodeStruct) (err error) {
err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
for _, v := range apiList {
var api model.SysApi
if err := tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error; err != nil {
return err
}
if err := tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
return err
if errors.Is(tx.Where("path = ? AND method = ?", v.Path, v.Method).First(&api).Error, gorm.ErrRecordNotFound) {
if err := tx.Create(&v).Error; err != nil { // 遇到错误时回滚事务
return err
}
}
}
return nil
})
return err
}
\ No newline at end of file
}
......@@ -14,6 +14,12 @@ import (
const breakpointDir = "./breakpointDir/"
const finishDir = "./fileDir/"
//@author: [piexlmax](https://github.com/piexlmax)
//@function: BreakPointContinue
//@description: 断点续传
//@param: content []byte, fileName string, contentNumber int, contentTotal int, fileMd5 string
//@return: error, string
func BreakPointContinue(content []byte, fileName string, contentNumber int, contentTotal int, fileMd5 string) (error, string) {
path := breakpointDir + fileMd5 + "/"
err := os.MkdirAll(path, os.ModePerm)
......@@ -25,6 +31,12 @@ func BreakPointContinue(content []byte, fileName string, contentNumber int, cont
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: CheckMd5
//@description: 检查Md5
//@param: content []byte, chunkMd5 string
//@return: CanUpload bool
func CheckMd5(content []byte, chunkMd5 string) (CanUpload bool) {
fileMd5 := MD5V(content)
if fileMd5 == chunkMd5 {
......@@ -34,6 +46,12 @@ func CheckMd5(content []byte, chunkMd5 string) (CanUpload bool) {
}
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: makeFileContent
//@description: 创建切片内容
//@param: content []byte, fileName string, FileDir string, contentNumber int
//@return: error, string
func makeFileContent(content []byte, fileName string, FileDir string, contentNumber int) (error, string) {
path := FileDir + fileName + "_" + strconv.Itoa(contentNumber)
f, err := os.Create(path)
......@@ -49,6 +67,12 @@ func makeFileContent(content []byte, fileName string, FileDir string, contentNum
return nil, path
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: makeFileContent
//@description: 创建切片文件
//@param: fileName string, FileMd5 string
//@return: error, string
func MakeFile(fileName string, FileMd5 string) (error, string) {
rd, err := ioutil.ReadDir(breakpointDir + FileMd5)
if err != nil {
......@@ -68,6 +92,12 @@ func MakeFile(fileName string, FileMd5 string) (error, string) {
return nil, finishDir + fileName
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: RemoveChunk
//@description: 移除切片
//@param: FileMd5 string
//@return: error
func RemoveChunk(FileMd5 string) error {
err := os.RemoveAll(breakpointDir + FileMd5)
return err
......
......@@ -2,25 +2,32 @@ package utils
import (
"bytes"
"context"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"sync"
)
//@author: [songzhibin97](https://github.com/songzhibin97)
//@interface_name: RunTask
//@description: Task接口
type RunTask interface {
AddTask()
RunTask()
}
// T: Task任务
//@author: [songzhibin97](https://github.com/songzhibin97)
//@struct_name: T
//@description: Task任务
type T struct {
sync.Mutex
// ch: 获取事件channel
// 获取事件channel
ch chan struct{}
closeChan chan struct{}
......@@ -28,14 +35,25 @@ type T struct {
// 记录process对象
p *os.Process
// f: 执行任务
// 执行任务
f func(chan struct{}) error
}
// NewT: 实例化方法
//@author: [songzhibin97](https://github.com/songzhibin97)
//@function: NewT
//@description: T的实例化方法
//@return: *T
func NewT() *T {
return newT(nil)
}
//@author: [songzhibin97](https://github.com/songzhibin97)
//@function: newT
//@description:
//@param: f func(chan struct{}) error
//@return: *T
func newT(f func(chan struct{}) error) *T {
t := &T{
Mutex: sync.Mutex{},
......@@ -49,6 +67,11 @@ func newT(f func(chan struct{}) error) *T {
return t
}
//@author: [songzhibin97](https://github.com/songzhibin97)
//@object: *T
//@function: AddTask
//@description: 添加任务
func (t *T) AddTask() {
if len(t.ch) == 1 {
return
......@@ -60,10 +83,14 @@ func (t *T) AddTask() {
// 直接丢弃这次任务
return
}
fmt.Println("::::发送任务->>>>>>>>")
t.ch <- struct{}{}
}
//@author: [songzhibin97](https://github.com/songzhibin97)
//@object: *T
//@function: RunTask
//@description: 启动任务
func (t *T) RunTask() {
fmt.Println("进入")
// 这里做的make 是用于关闭上一个执行的任务
......@@ -72,10 +99,10 @@ func (t *T) RunTask() {
go t.f(ch)
for {
_, ok := <-t.ch
ch <- struct{}{}
if !ok {
return
}
ch <- struct{}{}
// 等待上一个关闭
<-t.closeChan
go t.f(ch)
......@@ -83,23 +110,19 @@ func (t *T) RunTask() {
}
// DefaultF: 默认的StartFunction
func (t *T) DefaultF(ch chan struct{}) error {
//@author: [songzhibin97](https://github.com/songzhibin97)
//@object: t *T
//@function: DefaultF
//@description: 默认的StartFunction
//@param: ch chan struct{}
//@return: error
func (t *T) DefaultF(ch chan struct{}) error {
var buildCmd *exec.Cmd
var cmd *exec.Cmd
// 判断是否有makefile
_, err := os.Stat(filepath.Join("Makefile"))
if runtime.GOOS != "windows" && err == nil {
_, err := exec.LookPath("make")
if err == nil {
cmd = exec.Command("make", "run")
goto makefile
}
}
// 检测系统是否有编译环境
_, err = exec.LookPath("go")
_, err := exec.LookPath("go")
if err != nil {
return err
}
......@@ -107,58 +130,75 @@ func (t *T) DefaultF(ch chan struct{}) error {
switch runtime.GOOS {
case "windows":
buildCmd = exec.Command("go", "build", "-o", "gva.exe", "main.go")
buildCmd = exec.Command("go", "build", "-o", "server.exe", "main.go")
default:
buildCmd = exec.Command("go", "build", "-o", "gva", "main.go")
buildCmd = exec.Command("go", "build", "-o", "server", "main.go")
}
//cmd = exec.Command("go", "run", "main.go")
err = buildCmd.Run()
if err != nil {
return err
}
fmt.Println("build 执行完成")
// 执行
switch runtime.GOOS {
case "windows":
cmd = exec.Command("gva.exe")
cmd = exec.Command("server.exe")
default:
cmd = exec.Command("./gva")
cmd = exec.Command("./server")
}
makefile:
// 开始执行任务
t.echo(cmd)
ctx, cancel := context.WithCancel(context.Background())
err = t.echo(cmd, ctx)
<-ch
// 回收资源
err = cmd.Process.Kill()
fmt.Println("kill err", err)
fmt.Println("pid:", t.p.Pid, "->Kill")
err = t.p.Kill()
cancel()
// 发送关闭完成信号
t.closeChan <- struct{}{}
return err
}
// echo: 封装回显
func (t *T) echo(cmd *exec.Cmd) error {
//@author: [songzhibin97](https://github.com/songzhibin97)
//@object: t *T
//@function: echo
//@description: 封装回显
//@param: cmd *exec.Cmd, ctx context.Context
//@return: error
func (t *T) echo(cmd *exec.Cmd, ctx context.Context) error {
var stdoutBuf bytes.Buffer
stdoutIn, _ := cmd.StdoutPipe()
var errStdout, errStderr error
var errStdout error
stdout := io.MultiWriter(os.Stdout, &stdoutBuf)
err := cmd.Start()
if err != nil {
return err
}
go func() {
go func(ctx context.Context) {
_, errStdout = io.Copy(stdout, stdoutIn)
}()
select {
case <-ctx.Done():
return
default:
}
}(ctx)
t.p = cmd.Process
fmt.Println("pid", t.p.Pid)
go func() {
_ = cmd.Wait()
if errStdout != nil || errStderr != nil {
if errStdout != nil {
fmt.Printf("failed to capture stdout or stderr\n")
}
outStr := string(stdoutBuf.Bytes())
fmt.Printf("\nout:\n%s\n", outStr)
fmt.Printf("%s\n", string(stdoutBuf.Bytes()))
select {
case <-ctx.Done():
//_ = os.Stdout.Close()
return
default:
}
}()
return nil
}
......@@ -9,17 +9,31 @@ import (
"path/filepath"
)
// Watch: 监控对象
//@author: [songzhibin97](https://github.com/songzhibin97)
//@struct_name: Watch
//@description: 监控对象
type Watch struct {
*fsnotify.Watcher
}
//@author: [songzhibin97](https://github.com/songzhibin97)
//@function: NewWatch
//@description: Watch的实例化方法
//@return: *Watch
func NewWatch() *Watch {
obj, _ := fsnotify.NewWatcher()
return &Watch{obj}
}
// Watch: 监控对象
//@author: [songzhibin97](https://github.com/songzhibin97)
//@object: w *Watch
//@function: Watch
//@description: 监控对象
//@param: path string, t *T
//@return: error
func (w *Watch) Watch(path string, t *T) error {
// 先转化为绝对路径
path, err := filepath.Abs(path)
......@@ -81,7 +95,13 @@ func (w *Watch) Watch(path string, t *T) error {
}
// watchDir: 处理监控目录
//@author: [songzhibin97](https://github.com/songzhibin97)
//@object: w *Watch
//@function: watchDir
//@description: 处理监控目录
//@param: path string
//@return: error
func (w *Watch) watchDir(path string) error {
// 先将自己添加到监控
err := w.Add(path)
......@@ -113,7 +133,13 @@ func (w *Watch) watchDir(path string) error {
return err
}
// watchDir: 处理监控单文件
//@author: [songzhibin97](https://github.com/songzhibin97)
//@object: w *Watch
//@function: watchDir
//@description: 处理监控单文件
//@param: path string
//@return: error
func (w *Watch) watchFile(path string) error {
var err error
if chickPower(path) {
......@@ -122,14 +148,24 @@ func (w *Watch) watchFile(path string) error {
return err
}
// chickPower: 判断是否在可控范围内
//@author: [songzhibin97](https://github.com/songzhibin97)
//@function: chickPower
//@description: 判断是否在可控范围内
//@param: path string
//@return: error
func chickPower(name string) bool {
name = filepath.Ext(name)
fmt.Println(name)
return name == ".go" || name == ".yaml"
}
// addTask: 偏函数 简化发送任务
//@author: [songzhibin97](https://github.com/songzhibin97)
//@object: w *Watch
//@function: addTask
//@description: 偏函数 简化发送任务
//@param: path string
//@return: error
func (w *Watch) addTask(t *T, name string) {
if chickPower(name) {
fmt.Println("Add Task->>>>>>")
......
......@@ -6,11 +6,11 @@ import (
"os"
)
// @title PathExists
// @description 文件目录是否存在
// @auth (2020/04/05 20:22)
// @param path string
// @return err error
//@author: [piexlmax](https://github.com/piexlmax)
//@function: PathExists
//@description: 文件目录是否存在
//@param: path string
//@return: bool, error
func PathExists(path string) (bool, error) {
_, err := os.Stat(path)
......@@ -23,11 +23,11 @@ func PathExists(path string) (bool, error) {
return false, err
}
// @title createDir
// @description 批量创建文件夹
// @auth (2020/04/05 20:22)
// @param dirs string
// @return err error
//@author: [piexlmax](https://github.com/piexlmax)
//@function: CreateDir
//@description: 批量创建文件夹
//@param: dirs ...string
//@return: err error
func CreateDir(dirs ...string) (err error) {
for _, v := range dirs {
......
......@@ -11,12 +11,23 @@ import (
"github.com/jordan-wright/email"
)
//@author: [maplepie](https://github.com/maplepie)
//@function: Email
//@description: Email发送方法
//@param: subject string, body string
//@return: error
func Email(subject string, body string) error {
to := strings.Split(global.GVA_CONFIG.Email.To, ",")
return send(to, subject, body)
}
// ErrorToEmail Error 发送邮件
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: ErrorToEmail
//@description: 给email中间件错误发送邮件到指定邮箱
//@param: subject string, body string
//@return: error
func ErrorToEmail(subject string, body string) error {
to := strings.Split(global.GVA_CONFIG.Email.To, ",")
if to[len(to)-1] == "" { // 判断切片的最后一个元素是否为空,为空则移除
......@@ -25,11 +36,23 @@ func ErrorToEmail(subject string, body string) error {
return send(to, subject, body)
}
//@author: [maplepie](https://github.com/maplepie)
//@function: EmailTest
//@description: Email测试方法
//@param: subject string, body string
//@return: error
func EmailTest(subject string, body string) error {
to := []string{global.GVA_CONFIG.Email.From}
return send(to, subject, body)
}
//@author: [maplepie](https://github.com/maplepie)
//@function: send
//@description: Email发送方法
//@param: subject string, body string
//@return: error
func send(to []string, subject string, body string) error {
from := global.GVA_CONFIG.Email.From
nickname := global.GVA_CONFIG.Email.Nickname
......
......@@ -5,9 +5,12 @@ import (
"path/filepath"
)
// FileMove: 文件移动供外部调用
// src: 源位置 绝对路径相对路径都可以
// dst: 目标位置 绝对路径相对路径都可以 dst 必须为文件夹
//@author: [songzhibin97](https://github.com/songzhibin97)
//@function: FileMove
//@description: 文件移动供外部调用
//@param: src string, dst string(src: 源位置,绝对路径or相对路径, dst: 目标位置,绝对路径or相对路径,必须为文件夹)
//@return: err error
func FileMove(src string, dst string) (err error) {
if dst == "" {
return nil
......
......@@ -6,7 +6,12 @@ import (
"strings"
)
// 利用反射将结构体转化为map
//@author: [piexlmax](https://github.com/piexlmax)
//@function: StructToMap
//@description: 利用反射将结构体转化为map
//@param: obj interface{}
//@return: map[string]interface{}
func StructToMap(obj interface{}) map[string]interface{} {
obj1 := reflect.TypeOf(obj)
obj2 := reflect.ValueOf(obj)
......@@ -18,7 +23,12 @@ func StructToMap(obj interface{}) map[string]interface{} {
return data
}
//将数组格式化为字符串
//@author: [piexlmax](https://github.com/piexlmax)
//@function: ArrayToString
//@description: 将数组格式化为字符串
//@param: array []interface{}
//@return: string
func ArrayToString(array []interface{}) string {
return strings.Replace(strings.Trim(fmt.Sprint(array), "[]"), " ", ",", -1)
}
......@@ -5,6 +5,12 @@ import (
"encoding/hex"
)
//@author: [piexlmax](https://github.com/piexlmax)
//@function: MD5V
//@description: md5加密
//@param: str []byte
//@return: string
func MD5V(str []byte) string {
h := md5.New()
h.Write(str)
......
......@@ -11,7 +11,11 @@ import (
"time"
)
// GetWriteSyncer zap logger中加入file-rotatelogs
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: GetWriteSyncer
//@description: zap logger中加入file-rotatelogs
//@return: zapcore.WriteSyncer, error
func GetWriteSyncer() (zapcore.WriteSyncer, error) {
fileWriter, err := zaprotatelogs.New(
path.Join(global.GVA_CONFIG.Zap.Director, "%Y-%m-%d.log"),
......
......@@ -9,7 +9,11 @@ import (
"time"
)
// GetWriteSyncer zap logger中加入file-rotatelogs
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: GetWriteSyncer
//@description: zap logger中加入file-rotatelogs
//@return: zapcore.WriteSyncer, error
func GetWriteSyncer() (zapcore.WriteSyncer, error) {
fileWriter, err := zaprotatelogs.New(
path.Join(global.GVA_CONFIG.Zap.Director, "%Y-%m-%d.log"),
......
......@@ -50,7 +50,11 @@ type Disk struct {
UsedPercent int `json:"usedPercent"`
}
// InitOS OS信息
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: InitCPU
//@description: OS信息
//@return: o Os, err error
func InitOS() (o Os) {
o.GOOS = runtime.GOOS
o.NumCPU = runtime.NumCPU()
......@@ -60,7 +64,11 @@ func InitOS() (o Os) {
return o
}
// InitCPU CPU信息
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: InitCPU
//@description: CPU信息
//@return: c Cpu, err error
func InitCPU() (c Cpu, err error) {
if cores, err := cpu.Counts(false); err != nil {
return c, err
......@@ -75,7 +83,11 @@ func InitCPU() (c Cpu, err error) {
return c, nil
}
// InitRAM ARM信息
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: InitRAM
//@description: ARM信息
//@return: r Rrm, err error
func InitRAM() (r Rrm, err error) {
if u, err := mem.VirtualMemory(); err != nil{
return r, err
......@@ -87,7 +99,11 @@ func InitRAM() (r Rrm, err error) {
return r, nil
}
// InitDisk 硬盘信息
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: InitDisk
//@description: 硬盘信息
//@return: d Disk, err error
func InitDisk() (d Disk, err error) {
if u, err := disk.Usage("/"); err != nil{
return d, err
......
......@@ -14,8 +14,16 @@ import (
type Local struct{}
// UploadFile 上传文件
func (l Local) UploadFile(file *multipart.FileHeader) (string, string, error) {
//@author: [piexlmax](https://github.com/piexlmax)
//@author: [ccfish86](https://github.com/ccfish86)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@object: *Local
//@function: UploadFile
//@description: 上传文件
//@param: file *multipart.FileHeader
//@return: string, string, error
func (*Local) UploadFile(file *multipart.FileHeader) (string, string, error) {
// 读取文件后缀
ext := path.Ext(file.Filename)
// 读取文件名并加密
......@@ -55,8 +63,16 @@ func (l Local) UploadFile(file *multipart.FileHeader) (string, string, error) {
return p, filename, nil
}
// DeleteFile 删除文件
func (l Local) DeleteFile(key string) error {
//@author: [piexlmax](https://github.com/piexlmax)
//@author: [ccfish86](https://github.com/ccfish86)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@object: *Local
//@function: DeleteFile
//@description: 删除文件
//@param: key string
//@return: error
func (*Local) DeleteFile(key string) error {
p := global.GVA_CONFIG.Local.Path + "/" + key
if strings.Contains(p, global.GVA_CONFIG.Local.Path) {
if err := os.Remove(p); err != nil {
......
......@@ -14,7 +14,15 @@ import (
type Qiniu struct{}
// Upload 上传文件
//@author: [piexlmax](https://github.com/piexlmax)
//@author: [ccfish86](https://github.com/ccfish86)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@object: *Qiniu
//@function: UploadFile
//@description: 上传文件
//@param: file *multipart.FileHeader
//@return: string, string, error
func (*Qiniu) UploadFile(file *multipart.FileHeader) (string, string, error) {
putPolicy := storage.PutPolicy{Scope: global.GVA_CONFIG.Qiniu.Bucket}
mac := qbox.NewMac(global.GVA_CONFIG.Qiniu.AccessKey, global.GVA_CONFIG.Qiniu.SecretKey)
......@@ -39,7 +47,15 @@ func (*Qiniu) UploadFile(file *multipart.FileHeader) (string, string, error) {
return global.GVA_CONFIG.Qiniu.ImgPath + "/" + ret.Key, ret.Key, nil
}
// DeleteFile 删除文件
//@author: [piexlmax](https://github.com/piexlmax)
//@author: [ccfish86](https://github.com/ccfish86)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@object: *Qiniu
//@function: DeleteFile
//@description: 删除文件
//@param: key string
//@return: error
func (*Qiniu) DeleteFile(key string) error {
mac := qbox.NewMac(global.GVA_CONFIG.Qiniu.AccessKey, global.GVA_CONFIG.Qiniu.SecretKey)
cfg := qiniuConfig()
......@@ -51,7 +67,13 @@ func (*Qiniu) DeleteFile(key string) error {
return nil
}
// config 根据配置文件进行返回七牛云的配置
//@author: [SliverHorn](https://github.com/SliverHorn)
//@object: *Qiniu
//@function: qiniuConfig
//@description: 根据配置文件进行返回七牛云的配置
//@param: key string
//@return: error
func qiniuConfig() *storage.Config {
cfg := storage.Config{
UseHTTPS: global.GVA_CONFIG.Qiniu.UseHTTPS,
......
......@@ -5,13 +5,23 @@ import (
"mime/multipart"
)
var Oss OSS
//@author: [ccfish86](https://github.com/ccfish86)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@interface_name: OSS
//@description: OSS接口
type OSS interface {
UploadFile(file *multipart.FileHeader) (string, string, error)
DeleteFile(key string) error
}
//@author: [ccfish86](https://github.com/ccfish86)
//@author: [SliverHorn](https://github.com/SliverHorn)
//@function: NewOss
//@description: OSS接口
//@description: OSS的实例化方法
//@return: OSS
func NewOss() OSS {
switch global.GVA_CONFIG.System.OssType {
case "local":
......
package utils
import (
"gin-vue-admin/global"
"go.uber.org/zap"
"io"
"mime/multipart"
"os"
"path"
"strings"
"time"
)
func UploadFileLocal(file *multipart.FileHeader) (err error, localPath string, key string) {
// 读取文件后缀
ext := path.Ext(file.Filename)
// 读取文件名并加密
fileName := strings.TrimSuffix(file.Filename, ext)
fileName = MD5V([]byte(fileName))
// 拼接新文件名
lastName := fileName + "_" + time.Now().Format("20060102150405") + ext
// 读取全局变量的定义路径
savePath := global.GVA_CONFIG.Local.Path
// 尝试创建此路径
err = os.MkdirAll(savePath, os.ModePerm)
if err != nil {
global.GVA_LOG.Error("upload local file fail:", zap.Any("err", err))
return err, "", ""
}
// 拼接路径和文件名
dst := savePath + "/" + lastName
// 下面为上传逻辑
// 打开文件 defer 关闭
src, err := file.Open()
if err != nil {
global.GVA_LOG.Error("upload local file fail:", zap.Any("err", err))
return err, "", ""
}
defer src.Close()
// 创建文件 defer 关闭
out, err := os.Create(dst)
if err != nil {
global.GVA_LOG.Error("upload local file fail:", zap.Any("err", err))
return err, "", ""
}
defer out.Close()
// 传输(拷贝)文件
_, err = io.Copy(out, src)
if err != nil {
global.GVA_LOG.Error("upload local file fail:", zap.Any("err", err))
return err, "", ""
}
return nil, dst, lastName
}
package utils
import (
"context"
"fmt"
"gin-vue-admin/global"
"github.com/qiniu/api.v7/v7/auth/qbox"
"github.com/qiniu/api.v7/v7/storage"
"go.uber.org/zap"
"mime/multipart"
"time"
)
// 接收两个参数 一个文件流 一个 bucket 你的七牛云标准空间的名字
func UploadRemote(file *multipart.FileHeader) (err error, path string, key string) {
putPolicy := storage.PutPolicy{
Scope: global.GVA_CONFIG.Qiniu.Bucket,
}
mac := qbox.NewMac(global.GVA_CONFIG.Qiniu.AccessKey, global.GVA_CONFIG.Qiniu.SecretKey)
upToken := putPolicy.UploadToken(mac)
cfg := storage.Config{}
// 空间对应的机房
cfg.Zone = &storage.ZoneHuadong
// 是否使用https域名
cfg.UseHTTPS = false
// 上传是否使用CDN上传加速
cfg.UseCdnDomains = false
formUploader := storage.NewFormUploader(&cfg)
ret := storage.PutRet{}
putExtra := storage.PutExtra{
Params: map[string]string{
"x:name": "github logo",
},
}
f, e := file.Open()
if e != nil {
fmt.Println(e)
return e, "", ""
}
dataLen := file.Size
fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename) // 文件名格式 自己可以改 建议保证唯一性
err = formUploader.Put(context.Background(), &ret, upToken, fileKey, f, dataLen, &putExtra)
if err != nil {
global.GVA_LOG.Error("upload file fail:", zap.Any("err", err))
return err, "", ""
}
return err, global.GVA_CONFIG.Qiniu.ImgPath + "/" + ret.Key, ret.Key
}
func DeleteFile(key string) error {
mac := qbox.NewMac(global.GVA_CONFIG.Qiniu.AccessKey, global.GVA_CONFIG.Qiniu.SecretKey)
cfg := storage.Config{
// 是否使用https域名进行资源管理
UseHTTPS: false,
}
// 指定空间所在的区域,如果不指定将自动探测
// 如果没有特殊需求,默认不需要指定
// cfg.Zone=&storage.ZoneHuabei
bucketManager := storage.NewBucketManager(mac, &cfg)
err := bucketManager.Delete(global.GVA_CONFIG.Qiniu.Bucket, key)
if err != nil {
fmt.Println(err)
return err
}
return nil
}
......@@ -13,7 +13,12 @@ type RulesMap map[string]Rules
var CustomizeMap = make(map[string]Rules)
// 注册自定义规则方案建议在路由初始化层即注册
//@author: [piexlmax](https://github.com/piexlmax)
//@function: RegisterRule
//@description: 注册自定义规则方案建议在路由初始化层即注册
//@param: key string, rule Rules
//@return: err error
func RegisterRule(key string, rule Rules) (err error) {
if CustomizeMap[key] != nil {
return errors.New(key + "已注册,无法重复注册")
......@@ -23,42 +28,83 @@ func RegisterRule(key string, rule Rules) (err error) {
}
}
// 非空 不能为其对应类型的0值
//@author: [piexlmax](https://github.com/piexlmax)
//@function: NotEmpty
//@description: 非空 不能为其对应类型的0值
//@param: key string, rule Rules
//@return: err error
func NotEmpty() string {
return "notEmpty"
}
// 小于入参(<) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Lt
//@description: 小于入参(<) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Lt(mark string) string {
return "lt=" + mark
}
// 小于等于入参(<=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Le
//@description: 小于等于入参(<=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Le(mark string) string {
return "le=" + mark
}
// 等于入参(==) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Eq
//@description: 等于入参(==) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Eq(mark string) string {
return "eq=" + mark
}
// 不等于入参(!=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Ne
//@description: 不等于入参(!=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Ne(mark string) string {
return "ne=" + mark
}
// 大于等于入参(>=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Ge
//@description: 大于等于入参(>=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Ge(mark string) string {
return "ge=" + mark
}
// 大于入参(>) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Gt
//@description: 大于入参(>) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Gt(mark string) string {
return "gt=" + mark
}
// 校验方法 接收两个参数 入参实例,规则map
//
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Verify
//@description: 校验方法
//@param: st interface{}, roleMap Rules(入参实例,规则map)
//@return: err error
func Verify(st interface{}, roleMap Rules) (err error) {
compareMap := map[string]bool{
"lt": true,
......@@ -99,7 +145,12 @@ func Verify(st interface{}, roleMap Rules) (err error) {
return nil
}
// 长度和数字的校验方法 根据类型自动校验
//@author: [piexlmax](https://github.com/piexlmax)
//@function: compareVerify
//@description: 长度和数字的校验方法 根据类型自动校验
//@param: value reflect.Value, VerifyStr string
//@return: bool
func compareVerify(value reflect.Value, VerifyStr string) bool {
switch value.Kind() {
case reflect.String, reflect.Slice, reflect.Array:
......@@ -115,7 +166,12 @@ func compareVerify(value reflect.Value, VerifyStr string) bool {
}
}
// 非空校验
//@author: [piexlmax](https://github.com/piexlmax)
//@function: isBlank
//@description: 非空校验
//@param: value reflect.Value
//@return: bool
func isBlank(value reflect.Value) bool {
switch value.Kind() {
case reflect.String:
......@@ -134,6 +190,12 @@ func isBlank(value reflect.Value) bool {
return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: compare
//@description: 比较函数
//@param: value interface{}, VerifyStr string
//@return: bool
func compare(value interface{}, VerifyStr string) bool {
VerifyStrArr := strings.Split(VerifyStr, "=")
val := reflect.ValueOf(value)
......
......@@ -13,7 +13,6 @@ var (
AutoCodeVerify = Rules{"Abbreviation": {NotEmpty()}, "StructName": {NotEmpty()}, "PackageName": {NotEmpty()}, "Fields": {NotEmpty()}}
WorkFlowVerify = Rules{"WorkflowNickName": {NotEmpty()}, "WorkflowName": {NotEmpty()}, "WorkflowDescription": {NotEmpty()}, "WorkflowStepInfo": {NotEmpty()}}
AuthorityVerify = Rules{"AuthorityId": {NotEmpty()}, "AuthorityName": {NotEmpty()}, "ParentId": {NotEmpty()}}
DictionaryVerify = Rules{"Type": {NotEmpty()}, "ID": {NotEmpty()}}
AuthorityIdVerify = Rules{"AuthorityId": {NotEmpty()}}
OldAuthorityVerify = Rules{"OldAuthorityId": {NotEmpty()}}
ChangePasswordVerify = Rules{"Username": {NotEmpty()}, "Password": {NotEmpty()}, "NewPassword": {NotEmpty()}}
......
......@@ -7,6 +7,12 @@ import (
"strings"
)
//@author: [piexlmax](https://github.com/piexlmax)
//@function: ZipFiles
//@description: 压缩文件
//@param: filename string, files []string, oldform, newform string
//@return: error
func ZipFiles(filename string, files []string, oldform, newform string) error {
newZipFile, err := os.Create(filename)
......
......@@ -66,7 +66,7 @@ Vue.prototype.$echarts = echarts;
console.log(`
欢迎使用 Gin-Vue-Admin
当前版本:V2.3.6
当前版本:V2.3.7
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
默认前端文件运行地址:http://127.0.0.1:8080
如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/docs/coffee
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册