Переглянути джерело

Merge pull request #4 from flipped-aurora/master

拉取最新仓库
ipanghu 3 роки тому
батько
коміт
1a2231325d

+ 53 - 43
server/README.md

@@ -1,44 +1,54 @@
+## server项目结构
+
+```shell
+├── api
+│   └── v1
+├── config
+├── core
+├── docs
+├── global
+├── initialize
+│   └── internal
+├── middleware
+├── model
+│   ├── request
+│   └── response
+├── packfile
+├── resource
+│   ├── excel
+│   ├── page
+│   └── template
+├── router
+├── service
+├── source
+└── utils
+    ├── timer
+    └── upload
+```
+
+| 文件夹       | 说明                    | 描述                        |
+| ------------ | ----------------------- | --------------------------- |
+| `api`        | api层                   | api层 |
+| `--v1`       | v1版本接口              | v1版本接口                  |
+| `config`     | 配置包                  | config.yaml对应的配置结构体 |
+| `core`       | 核心文件                | 核心组件(zap, viper, server)的初始化 |
+| `docs`       | swagger文档目录         | swagger文档目录 |
+| `global`     | 全局对象                | 全局对象 |
+| `initialize` | 初始化 | router,redis,gorm,validator, timer的初始化 |
+| `--internal` | 初始化内部函数 | gorm 的 longger 自定义,在此文件夹的函数只能由 `initialize` 层进行调用 |
+| `middleware` | 中间件层 | 用于存放 `gin` 中间件代码 |
+| `model`      | 模型层                  | 模型对应数据表              |
+| `--request`  | 入参结构体              | 接收前端发送到后端的数据。  |
+| `--response` | 出参结构体              | 返回给前端的数据结构体      |
+| `packfile`   | 静态文件打包            | 静态文件打包 |
+| `resource`   | 静态资源文件夹          | 负责存放静态文件                |
+| `--excel` | excel导入导出默认路径 | excel导入导出默认路径 |
+| `--page` | 表单生成器 | 表单生成器 打包后的dist |
+| `--template` | 模板 | 模板文件夹,存放的是代码生成器的模板 |
+| `router`     | 路由层                  | 路由层 |
+| `service`    | service层               | 存放业务逻辑问题 |
+| `source` | source层 | 存放初始化数据的函数 |
+| `utils`      | 工具包                  | 工具函数封装            |
+| `--timer` | timer | 定时器接口封装 |
+| `--upload`      | oss                  | oss接口封装        |
 
-整理代码结构
-``` lua
-web
-├── api/v1 -- 主要API
-|   ├── sys_initdb.go -- ico
-|   └── sys_user.go --  
-├── config -- 配置文件 设定操作的结构体
-|   ├── auto_code.go -- ico captcha.go
-|   ├── ... -- ico captcha.go
-|   └── zap.go -- core
-├── core -- 主要结构代码
-|   ├── server_other.go -- ico captcha.go
-|   ├── ... -- ico captcha.go
-|   └── zap.go -- 
-├── docs -- 文档系统
-|   ├── docs.go -- ico captcha.go
-|   ├── swagger.json -- json
-|   └── swagger.yaml -- yaml  
-├── global -- global
-├── initialize -- initialize 
-├── middleware -- 中间键
-├── model -- global
-│   ├── request  -- 所有请求model结构体
-|   |   ├── common.go 
-|   |   ├── ...
-|   |   └── sys_user.go -- yaml  
-|   ├── response  -- 返回数据
-|   |   ├── common.go 
-|   |   ├── ...
-|   |   └── sys_user.go -- yaml  
-├── packfile -- 文件写入
-├── resource -- 资源文件
-├── router -- 路由
-├── service -- service层
-├── source -- 文件目录操作 
-├── utils
-├── config.yaml  -- 
-├── Dockerfile  -- docker配置
-├── go.mod    -- mod 配置
-├── go.sum -- sum
-├── latest_log  -- vue-cli 配置
-└── main.go  -- package.json
-```

+ 2 - 2
server/config.docker.yaml

@@ -61,8 +61,8 @@ mysql:
   password: ''
   max-idle-conns: 10
   max-open-conns: 100
-  log-mode: false
-  log-zap: ""
+  log-mode: ""
+  log-zap: false
 
 # local configuration
 local:

+ 3 - 2
server/config.yaml

@@ -62,8 +62,8 @@ mysql:
   password: ''
   max-idle-conns: 10
   max-open-conns: 100
-  log-mode: false
-  log-zap: ""
+  log-mode: ""
+  log-zap: false
 
 # local configuration
 local:
@@ -104,6 +104,7 @@ aliyun-oss:
   access-key-secret: 'yourAccessKeySecret'
   bucket-name: 'yourBucketName'
   bucket-url: 'yourBucketUrl'
+  base-path: 'yourBasePath'
 
 # tencent cos configuration
 tencent-cos:

+ 2 - 2
server/config/gorm.go

@@ -8,8 +8,8 @@ type Mysql struct {
 	Password     string `mapstructure:"password" json:"password" yaml:"password"`                 // 数据库密码
 	MaxIdleConns int    `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` // 空闲中的最大连接数
 	MaxOpenConns int    `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
-	LogMode      bool   `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"`                  // 是否开启Gorm全局日志
-	LogZap       string `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"`
+	LogMode      string `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"`                  // 是否开启Gorm全局日志
+	LogZap       bool   `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"`                     // 是否通过zap写入日志文件
 }
 
 func (m *Mysql) Dsn() string {

+ 1 - 0
server/config/oss.go

@@ -20,6 +20,7 @@ type AliyunOSS struct {
 	AccessKeySecret string `mapstructure:"access-key-secret" json:"accessKeySecret" yaml:"access-key-secret"`
 	BucketName      string `mapstructure:"bucket-name" json:"bucketName" yaml:"bucket-name"`
 	BucketUrl       string `mapstructure:"bucket-url" json:"bucketUrl" yaml:"bucket-url"`
+	BasePath 		string `mapstructure:"base-path" json:"basePath" yaml:"base-path"`
 }
 type TencentCOS struct {
 	Bucket     string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`

+ 1 - 1
server/core/server.go

@@ -29,7 +29,7 @@ func RunWindowsServer() {
 
 	fmt.Printf(`
 	欢迎使用 Gin-Vue-Admin
-	当前版本:V2.4.2
+	当前版本:V2.4.3
     加群方式:微信号:shouzi_1994 QQ群:622360840
 	默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
 	默认前端文件运行地址:http://127.0.0.1:8080

+ 5 - 11
server/initialize/gorm.go

@@ -78,7 +78,7 @@ func GormMysql() *gorm.DB {
 		DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
 		SkipInitializeWithVersion: false, // 根据版本自动配置
 	}
-	if db, err := gorm.Open(mysql.New(mysqlConfig), gormConfig(m.LogMode)); err != nil {
+	if db, err := gorm.Open(mysql.New(mysqlConfig), gormConfig()); err != nil {
 		//global.GVA_LOG.Error("MySQL启动异常", zap.Any("err", err))
 		//os.Exit(0)
 		//return nil
@@ -97,9 +97,9 @@ func GormMysql() *gorm.DB {
 //@param: mod bool
 //@return: *gorm.Config
 
-func gormConfig(mod bool) *gorm.Config {
-	var config = &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}
-	switch global.GVA_CONFIG.Mysql.LogZap {
+func gormConfig() *gorm.Config {
+	config := &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}
+	switch global.GVA_CONFIG.Mysql.LogMode {
 	case "silent", "Silent":
 		config.Logger = internal.Default.LogMode(logger.Silent)
 	case "error", "Error":
@@ -108,14 +108,8 @@ func gormConfig(mod bool) *gorm.Config {
 		config.Logger = internal.Default.LogMode(logger.Warn)
 	case "info", "Info":
 		config.Logger = internal.Default.LogMode(logger.Info)
-	case "zap", "Zap":
-		config.Logger = internal.Default.LogMode(logger.Info)
 	default:
-		if mod {
-			config.Logger = internal.Default.LogMode(logger.Info)
-			break
-		}
-		config.Logger = internal.Default.LogMode(logger.Silent)
+		config.Logger = internal.Default.LogMode(logger.Info)
 	}
 	return config
 }

+ 21 - 51
server/initialize/internal/logger.go

@@ -4,7 +4,6 @@ import (
 	"context"
 	"fmt"
 	"gin-vue-admin/global"
-	"go.uber.org/zap"
 	"gorm.io/gorm/logger"
 	"gorm.io/gorm/utils"
 	"io/ioutil"
@@ -13,11 +12,6 @@ import (
 	"time"
 )
 
-// writer log writer interface
-type writer interface {
-	Printf(string, ...interface{})
-}
-
 type config struct {
 	SlowThreshold time.Duration
 	Colorful      bool
@@ -34,27 +28,27 @@ var (
 	Recorder = traceRecorder{Interface: Default, BeginAt: time.Now()}
 )
 
-func New(writer writer, config config) logger.Interface {
+func New(writer logger.Writer, config config) logger.Interface {
 	var (
 		infoStr      = "%s\n[info] "
 		warnStr      = "%s\n[warn] "
 		errStr       = "%s\n[error] "
-		traceStr     = "%s\n[%.3fms] [rows:%v] %s"
-		traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s"
-		traceErrStr  = "%s %s\n[%.3fms] [rows:%v] %s"
+		traceStr     = "%s\n[%.3fms] [rows:%v] %s\n"
+		traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s\n"
+		traceErrStr  = "%s %s\n[%.3fms] [rows:%v] %s\n"
 	)
 
 	if config.Colorful {
 		infoStr = logger.Green + "%s\n" + logger.Reset + logger.Green + "[info] " + logger.Reset
 		warnStr = logger.BlueBold + "%s\n" + logger.Reset + logger.Magenta + "[warn] " + logger.Reset
 		errStr = logger.Magenta + "%s\n" + logger.Reset + logger.Red + "[error] " + logger.Reset
-		traceStr = logger.Green + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s"
-		traceWarnStr = logger.Green + "%s " + logger.Yellow + "%s\n" + logger.Reset + logger.RedBold + "[%.3fms] " + logger.Yellow + "[rows:%v]" + logger.Magenta + " %s" + logger.Reset
-		traceErrStr = logger.RedBold + "%s " + logger.MagentaBold + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s"
+		traceStr = logger.Green + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s\n"
+		traceWarnStr = logger.Green + "%s " + logger.Yellow + "%s\n" + logger.Reset + logger.RedBold + "[%.3fms] " + logger.Yellow + "[rows:%v]" + logger.Magenta + " %s\n" + logger.Reset
+		traceErrStr = logger.RedBold + "%s " + logger.MagentaBold + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s\n"
 	}
 
-	return &customLogger{
-		writer:       writer,
+	return &_logger{
+		Writer:       writer,
 		config:       config,
 		infoStr:      infoStr,
 		warnStr:      warnStr,
@@ -65,43 +59,43 @@ func New(writer writer, config config) logger.Interface {
 	}
 }
 
-type customLogger struct {
-	writer
+type _logger struct {
 	config
+	logger.Writer
 	infoStr, warnStr, errStr            string
 	traceStr, traceErrStr, traceWarnStr string
 }
 
 // LogMode log mode
-func (c *customLogger) LogMode(level logger.LogLevel) logger.Interface {
+func (c *_logger) LogMode(level logger.LogLevel) logger.Interface {
 	newLogger := *c
 	newLogger.LogLevel = level
 	return &newLogger
 }
 
 // Info print info
-func (c *customLogger) Info(ctx context.Context, message string, data ...interface{}) {
+func (c *_logger) Info(ctx context.Context, message string, data ...interface{}) {
 	if c.LogLevel >= logger.Info {
 		c.Printf(c.infoStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...)
 	}
 }
 
 // Warn print warn messages
-func (c *customLogger) Warn(ctx context.Context, message string, data ...interface{}) {
+func (c *_logger) Warn(ctx context.Context, message string, data ...interface{}) {
 	if c.LogLevel >= logger.Warn {
 		c.Printf(c.warnStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...)
 	}
 }
 
 // Error print error messages
-func (c *customLogger) Error(ctx context.Context, message string, data ...interface{}) {
+func (c *_logger) Error(ctx context.Context, message string, data ...interface{}) {
 	if c.LogLevel >= logger.Error {
 		c.Printf(c.errStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...)
 	}
 }
 
 // Trace print sql message
-func (c *customLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
+func (c *_logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
 	if c.LogLevel > 0 {
 		elapsed := time.Since(begin)
 		switch {
@@ -131,35 +125,11 @@ func (c *customLogger) Trace(ctx context.Context, begin time.Time, fc func() (st
 	}
 }
 
-func (c *customLogger) Printf(message string, data ...interface{}) {
-	if global.GVA_CONFIG.Mysql.LogZap != "" {
-		switch len(data) {
-		case 0:
-			global.GVA_LOG.Info(message)
-		case 1:
-			global.GVA_LOG.Info("gorm", zap.Any("src", data[0]))
-		case 2:
-			global.GVA_LOG.Info("gorm", zap.Any("src", data[0]), zap.Any("duration", data[1]))
-		case 3:
-			global.GVA_LOG.Info("gorm", zap.Any("src", data[0]), zap.Any("duration", data[1]), zap.Any("rows", data[2]))
-		case 4:
-			global.GVA_LOG.Info("gorm", zap.Any("src", data[0]), zap.Any("duration", data[1]), zap.Any("rows", data[2]), zap.Any("sql", data[3]))
-		}
-		return
-	}
-	switch len(data) {
-	case 0:
-		c.writer.Printf(message, "")
-	case 1:
-		c.writer.Printf(message, data[0])
-	case 2:
-		c.writer.Printf(message, data[0], data[1])
-	case 3:
-		c.writer.Printf(message, data[0], data[1], data[2])
-	case 4:
-		c.writer.Printf(message, data[0], data[1], data[2], data[3])
-	case 5:
-		c.writer.Printf(message, data[0], data[1], data[2], data[3], data[4])
+func (c *_logger) Printf(message string, data ...interface{}) {
+	if global.GVA_CONFIG.Mysql.LogZap {
+		global.GVA_LOG.Info(fmt.Sprintf(message, data...))
+	} else {
+		c.Writer.Printf(message, data...)
 	}
 }
 

+ 0 - 1
server/initialize/timer.go

@@ -10,7 +10,6 @@ import (
 func Timer() {
 	if global.GVA_CONFIG.Timer.Start {
 		for _, detail := range global.GVA_CONFIG.Timer.Detail {
-			fmt.Println(detail)
 			go func(detail config.Detail) {
 				global.GVA_Timer.AddTaskByFunc("ClearDB", global.GVA_CONFIG.Timer.Spec, func() {
 					err := utils.ClearTable(global.GVA_DB, detail.TableName, detail.CompareField, detail.Interval)

+ 1 - 0
server/service/sys_base_menu.go

@@ -40,6 +40,7 @@ func UpdateBaseMenu(menu model.SysBaseMenu) (err error) {
 	var oldMenu model.SysBaseMenu
 	upDateMap := make(map[string]interface{})
 	upDateMap["keep_alive"] = menu.KeepAlive
+	upDateMap["close_tab"] = menu.CloseTab
 	upDateMap["default_menu"] = menu.DefaultMenu
 	upDateMap["parent_id"] = menu.ParentId
 	upDateMap["path"] = menu.Path

+ 1 - 1
server/source/authority_menu.go

@@ -17,7 +17,7 @@ func (a *authorityMenu) Init() error {
 		color.Danger.Println("\n[Mysql] --> authority_menu 视图已存在!")
 		return nil
 	}
-	if err := global.GVA_DB.Exec("CREATE ALGORITHM = UNDEFINED SQL SECURITY DEFINER VIEW `authority_menu` AS select `sys_base_menus`.`id` AS `id`,`sys_base_menus`.`created_at` AS `created_at`, `sys_base_menus`.`updated_at` AS `updated_at`, `sys_base_menus`.`deleted_at` AS `deleted_at`, `sys_base_menus`.`menu_level` AS `menu_level`,`sys_base_menus`.`parent_id` AS `parent_id`,`sys_base_menus`.`path` AS `path`,`sys_base_menus`.`name` AS `name`,`sys_base_menus`.`hidden` AS `hidden`,`sys_base_menus`.`component` AS `component`, `sys_base_menus`.`title`  AS `title`,`sys_base_menus`.`icon` AS `icon`,`sys_base_menus`.`sort` AS `sort`,`sys_authority_menus`.`sys_authority_authority_id` AS `authority_id`,`sys_authority_menus`.`sys_base_menu_id` AS `menu_id`,`sys_base_menus`.`keep_alive` AS `keep_alive`,`sys_base_menus`.`default_menu` AS `default_menu` from (`sys_authority_menus` join `sys_base_menus` on ((`sys_authority_menus`.`sys_base_menu_id` = `sys_base_menus`.`id`)))").Error; err != nil {
+	if err := global.GVA_DB.Exec("CREATE ALGORITHM = UNDEFINED SQL SECURITY DEFINER VIEW `authority_menu` AS select `sys_base_menus`.`id` AS `id`,`sys_base_menus`.`created_at` AS `created_at`, `sys_base_menus`.`updated_at` AS `updated_at`, `sys_base_menus`.`deleted_at` AS `deleted_at`, `sys_base_menus`.`menu_level` AS `menu_level`,`sys_base_menus`.`parent_id` AS `parent_id`,`sys_base_menus`.`path` AS `path`,`sys_base_menus`.`name` AS `name`,`sys_base_menus`.`hidden` AS `hidden`,`sys_base_menus`.`component` AS `component`, `sys_base_menus`.`title`  AS `title`,`sys_base_menus`.`icon` AS `icon`,`sys_base_menus`.`sort` AS `sort`,`sys_authority_menus`.`sys_authority_authority_id` AS `authority_id`,`sys_authority_menus`.`sys_base_menu_id` AS `menu_id`,`sys_base_menus`.`keep_alive` AS `keep_alive`,`sys_base_menus`.`close_tab` AS `close_tab`,`sys_base_menus`.`default_menu` AS `default_menu` from (`sys_authority_menus` join `sys_base_menus` on ((`sys_authority_menus`.`sys_base_menu_id` = `sys_base_menus`.`id`)))").Error; err != nil {
 		return err
 	}
 	color.Info.Println("\n[Mysql] --> authority_menu 视图创建成功!")

+ 2 - 2
server/utils/upload/aliyun_oss.go

@@ -6,7 +6,6 @@ import (
 	"github.com/aliyun/aliyun-oss-go-sdk/oss"
 	"go.uber.org/zap"
 	"mime/multipart"
-	"path/filepath"
 	"time"
 )
 
@@ -27,7 +26,8 @@ func (*AliyunOSS) UploadFile(file *multipart.FileHeader) (string, string, error)
 	}
 	defer f.Close() // 创建文件 defer 关闭
 	// 上传阿里云路径 文件名格式 自己可以改 建议保证唯一性
-	yunFileTmpPath := filepath.Join("uploads", time.Now().Format("2006-01-02")) + "/" + file.Filename
+	//yunFileTmpPath := filepath.Join("uploads", time.Now().Format("2006-01-02")) + "/" + file.Filename
+	yunFileTmpPath := global.GVA_CONFIG.AliyunOSS.BasePath + "/" + "uploads" + "/" + time.Now().Format("2006-01-02") + "/" + file.Filename
 
 	// 上传文件流。
 	err = bucket.PutObject(yunFileTmpPath, f)

+ 6 - 9
web/src/components/upload/image.vue

@@ -1,10 +1,3 @@
-<!--
-  <div>
-    带压缩的上传
-    <upload-image v-model="imageUrl" :fileSize="512" />
-    已上传文件 {{ imageUrl }}
-  </div>
--->
 
 <template>
   <div>
@@ -17,7 +10,7 @@
       :before-upload="beforeImageUpload"
       :multiple="false"
     >
-      <img v-if="imageUrl" :src="path + imageUrl" class="image">
+      <img v-if="imageUrl" :src="showImageUrl" class="image">
       <i v-else class="el-icon-plus image-uploader-icon" />
     </el-upload>
   </div>
@@ -53,7 +46,10 @@ export default {
     }
   },
   computed: {
-    ...mapGetters('user', ['userInfo', 'token'])
+    ...mapGetters('user', ['userInfo', 'token']),
+    showImageUrl() {
+      return (this.imageUrl && this.imageUrl.slice(0, 4) !== 'http') ? path + this.imageUrl : this.imageUrl
+    }
   },
   methods: {
     beforeImageUpload(file) {
@@ -70,6 +66,7 @@ export default {
       const { data } = res
       if (data.file) {
         this.$emit('change', data.file.url)
+        this.$emit('on-success')
       }
     }
   }

+ 1 - 1
web/src/core/gin-vue-admin.js

@@ -22,7 +22,7 @@ Vue.use(uploader)
 
 console.log(`
    欢迎使用 Gin-Vue-Admin
-   当前版本:V2.4.2
+   当前版本:V2.4.3
    加群方式:微信:shouzi_1994 QQ群:622360840
    默认自动化文档地址:http://127.0.0.1:${process.env.VUE_APP_SERVER_PORT}/swagger/index.html
    默认前端文件运行地址:http://127.0.0.1:${process.env.VUE_APP_CLI_PORT}

+ 1 - 1
web/src/view/example/upload/upload.vue

@@ -17,7 +17,7 @@
         </el-col>
         <el-col :span="12">
           带压缩的上传, (512(k)为压缩限制)
-          <upload-image v-model="imageUrl" :file-size="512" :max-w-h="1080" />
+          <upload-image v-model="imageUrl" :file-size="512" :max-w-h="1080" @on-success="getTableData" />
           已上传文件 {{ imageUrl }}
         </el-col>
       </el-row>

+ 12 - 2
web/src/view/systemTools/autoCode/index.vue

@@ -60,10 +60,20 @@
       <el-form-item label="文件名称" prop="packageName">
         <el-input v-model="form.packageName" placeholder="生成文件的默认名称(建议为驼峰格式,首字母小写,如sysXxxXxxx)" />
       </el-form-item>
-      <el-form-item label="自动创建api(将API相关信息注册到数据库中)">
+      <el-form-item>
+        <template slot="label">
+          <el-tooltip content="注:把自动生成的API注册进数据库" placement="bottom" effect="light">
+            <div> 自动创建API </div>
+          </el-tooltip>
+        </template>
         <el-checkbox v-model="form.autoCreateApiToSql" />
       </el-form-item>
-      <el-form-item label="自动移动文件">
+      <el-form-item>
+        <template slot="label">
+          <el-tooltip content="注:自动迁移生成的文件到ymal配置的对应位置" placement="bottom" effect="light">
+            <div> 自动移动文件 </div>
+          </el-tooltip>
+        </template>
         <el-checkbox v-model="form.autoMoveFile" />
       </el-form-item>
     </el-form>