Skip to content

Commit

Permalink
Merge pull request #63 from bufanyun/v2.0
Browse files Browse the repository at this point in the history
up
  • Loading branch information
maxbad committed Jun 21, 2024
2 parents 406e3ef + f78f44e commit 7bf0efe
Show file tree
Hide file tree
Showing 13 changed files with 6,112 additions and 5 deletions.
8 changes: 7 additions & 1 deletion server/internal/library/storager/upload_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"github.com/gogf/gf/v2/util/gconv"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
)

Expand Down Expand Up @@ -139,7 +141,11 @@ func MergePartFile(srcPath, dstPath string) (err error) {
if err != nil {
return err
}

sort.Slice(dir, func(i, j int) bool {
fiIndex, _ := strconv.Atoi(dir[i].Name())
fjIndex, _ := strconv.Atoi(dir[j].Name())
return fiIndex < fjIndex
})
for _, file := range dir {
filePath := filepath.Join(srcPath, file.Name())
if err = gfile.PutBytesAppend(dstPath, gfile.GetBytes(filePath)); err != nil {
Expand Down
61 changes: 61 additions & 0 deletions server/internal/library/storager/upload_local_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package storager

import (
"crypto/md5"
"errors"
"fmt"
"io"
"os"
"testing"
)

func TestMergePartFile(t *testing.T) {
srcFile, _ := os.Open("1.zip")
defer srcFile.Close()
fileInfo, err := srcFile.Stat()
if err != nil {
t.Error(err)
}
hash := md5.New()
_, err = io.Copy(hash, srcFile)
sum := hash.Sum(nil)
fileHash := fmt.Sprintf("%x", sum)

var singleSize = int64(1024 * 1024 * 100)
chunks := int(fileInfo.Size() / singleSize)
if fileInfo.Size()%singleSize != 0 {
chunks += 1
}
srcFile.Seek(0, io.SeekStart)
for j := 0; j < chunks; j++ {
partSize := singleSize
if j == chunks-1 {
partSize = fileInfo.Size() - int64(j)*singleSize
}
partData := make([]byte, partSize)
_, err = io.ReadFull(srcFile, partData)
pf, _ := os.Create(fmt.Sprintf("tmp/%d", j+1))
_, err = pf.Write(partData)
if err != nil {
t.Error(err)
return
}
pf.Close()
}

err = MergePartFile("tmp/", "2.zip")
if err != nil {
t.Error(err)
return
}

f2, _ := os.Open("2.zip")
hash2 := md5.New()
_, err = io.Copy(hash2, f2)
sum2 := hash.Sum(nil)
fileHash2 := fmt.Sprintf("%x", sum2)
if fileHash != fileHash2 {
t.Error(errors.New("hash mismatch"))
}

}
2 changes: 1 addition & 1 deletion server/storage/data/hotgo.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1719,7 +1719,7 @@ CREATE TABLE IF NOT EXISTS `hg_sys_attachment` (
`name` varchar(1000) DEFAULT NULL COMMENT '文件原始名',
`kind` varchar(16) DEFAULT NULL COMMENT '上传类型',
`mime_type` varchar(128) NOT NULL DEFAULT '' COMMENT '扩展类型',
`naive_type` varchar(32) NOT NULL COMMENT 'NaiveUI类型',
`naive_type` varchar(32) NOT NULL DEFAULT '' COMMENT 'NaiveUI类型',
`path` varchar(1000) DEFAULT NULL COMMENT '本地路径',
`file_url` varchar(1000) DEFAULT NULL COMMENT 'url',
`size` bigint(20) DEFAULT '0' COMMENT '文件大小',
Expand Down
5,134 changes: 5,134 additions & 0 deletions server/storage/data/sqlite/data.sql

Large diffs are not rendered by default.

656 changes: 656 additions & 0 deletions server/storage/data/sqlite/tables.sql

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions server/utility/db/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 数据库补丁支持

> 将用于抹平不同数据库的差异性以支撑基于数据库的部分特性

## 特性

- 增加对数据库表字段注释的支持特别是 sqlite 的注释支持
- 增加对数据库表名注释的支持特别是 sqlite 的注释支持

> 注意以上支持中由于 sqlite 的特殊性 sqlite 表创建的 sql 语句有所要求具体示例请参考 [sqlite_example.sql](./sqlite_example.sql)

54 changes: 54 additions & 0 deletions server/utility/db/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package db

import (
"context"
"fmt"

"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)

// 获取数据库表字段及注释
// usage:
//
// fields, err := db.GetFieldsWithComment(ctx, in.Table, in.Name)
// if err != nil {
// return
// }
// for _, v := range fields {}
func GetFieldsWithComment(ctx context.Context, tableName, dbTag string) (fields map[string]*gdb.TableField, err error) {
db := g.DB(dbTag)
fields, err = db.TableFields(ctx, tableName) // 使用 goframe 框架本身已完美支持 mysql 获取表字段及注释
dbConf := db.GetConfig()
switch dbConf.Type {
case "sqlite":
fields, err = fixSqliteFieldsComment(ctx, tableName, db, fields)
}
return
}

type TableComment struct {
Name string `json:"name"`
Comment string `json:"comment"`
}

// 获取数据库表字段及注释
func GetTablesWithComment(ctx context.Context, dbTag string) (tables []*TableComment, err error) {
db := g.DB(dbTag)
dbConf := db.GetConfig()
switch dbConf.Type {
case "mysql":
sql := "SELECT TABLE_NAME as name, TABLE_COMMENT as comment FROM information_schema.`TABLES` WHERE TABLE_SCHEMA = '%s'"
if err = db.Ctx(ctx).Raw(fmt.Sprintf(sql, dbConf.Name)).Scan(&tables); err != nil {
return
}
case "sqlite":
var tableNames []string
tableNames, err = db.Tables(ctx)
if err != nil {
return
}
tables, err = transSqliteTablesComment(ctx, tableNames, db)
}
return
}
45 changes: 45 additions & 0 deletions server/utility/db/sqlite_example.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
CREATE TABLE IF NOT EXISTS `user1` ( -- 用户管理
`id` INTEGER, -- 编号
`name` VARCHAR(50), -- 名称
`email` VARCHAR(255), -- 邮箱
`address` TEXT, -- 地址
`salt` VARCHAR(50), --
`password` VARCHAR(50), -- 密码
`mark` VARCHAR(255), -- 备注
`permission` TEXT, -- 权限
`created_user_id` INTEGER, -- 创建者编号
`created_at` DATETIME, -- 创建时间
`updated_at` DATETIME, -- 更新时间
`deleted_at` DATETIME, -- 删除时间
PRIMARY KEY(`id`)
);
CREATE TABLE "user2" ( -- 用户管理
"id" INTEGER, -- 编号
"name" VARCHAR(50), -- 名称
"email" VARCHAR(255), -- 邮箱
"address" TEXT, -- 地址
"salt" VARCHAR(50), --
"password" VARCHAR(50), -- 密码
"mark" VARCHAR(255), -- 备注
"permission" TEXT, -- 权限
"created_user_id" INTEGER, -- 创建者编号
"created_at" DATETIME, -- 创建时间
"updated_at" DATETIME, -- 更新时间
"deleted_at" DATETIME, -- 删除时间
PRIMARY KEY("id")
);
CREATE TABLE IF NOT EXISTS user3 ( -- 用户管理
id INTEGER, -- 编号
name VARCHAR(50), -- 名称
email VARCHAR(255), -- 邮箱
address TEXT, -- 地址
salt VARCHAR(50), --
password VARCHAR(50), -- 密码
mark VARCHAR(255), -- 备注
permission TEXT, -- 权限
created_user_id INTEGER, -- 创建者编号
created_at DATETIME, -- 创建时间
updated_at DATETIME, -- 更新时间
deleted_at DATETIME, -- 删除时间
PRIMARY KEY(id)
);
105 changes: 105 additions & 0 deletions server/utility/db/sqlite_fields.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package db

import (
"context"
"fmt"
"strings"

"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/database/gdb"
)

// func getSQLiteSchemaByCli(tableName string, db gdb.DB) (string, error) {
// dbConf := db.GetConfig()
// cmd := exec.Command("sqlite3", dbConf.Name, fmt.Sprintf(".schema %s", tableName))
// glog.Info(context.TODO(), "sqlite3", dbConf.Name, fmt.Sprintf("'.schema %s'", tableName))
// output, err := cmd.CombinedOutput()
// if err != nil {
// return "", err
// }
// return strings.TrimSpace(string(output)), nil
// }

func getSQLiteSchemaBySql(ctx context.Context, tableName string, db gdb.DB) (string, error) {
schemaRes, err := db.GetValue(ctx, fmt.Sprintf(`SELECT sql FROM sqlite_master WHERE type='table' AND name='%s';`, tableName))
if err != nil {
return "", err
}
return schemaRes.String(), nil
}

func getSQliteTableComments(createTableSql string) (tableComment, tableName string) {
// 按照换行符分割文本
lines := strings.Split(createTableSql, "\n")

// 循环输出每一行
for _, line := range lines {
// 检查 createTableSql 是否包含 comment
if strings.Contains(line, "--") {
if strings.Contains(line, "CREATE TABLE") {
tableName = getLastWord(strings.Split(line, "(")[0])
tableComment = strings.Split(line, "--")[1]
}
}
}
return
}

func getSQliteFieldsComments(createTableSql string) (fieldCommentMap gmap.Map, tableComment, tableName string) {
// 按照换行符分割文本
lines := strings.Split(createTableSql, "\n")

// 循环输出每一行
for _, line := range lines {
// 检查 createTableSql 是否包含 comment
if strings.Contains(line, "--") {
if strings.Contains(line, "CREATE TABLE") {
tableName = getLastWord(strings.Split(line, "(")[0])
tableComment = strings.Split(line, "--")[1]
} else {
firstWord := getFirstWord(line)
lastWord := getLastWord(line)
fieldCommentMap.Set(firstWord, lastWord)
}
}
}
return
}

func transSqliteTablesComment(ctx context.Context, tableNames []string, db gdb.DB) (tables []*TableComment, err error) {
schemaStr := ""
eleIgnore := "sqlite_sequence"
for _, v := range tableNames {
if v != eleIgnore {
schemaStr, err = getSQLiteSchemaBySql(ctx, v, db)
if err != nil {
return
}
comment, _ := getSQliteTableComments(schemaStr)
tables = append(tables, &TableComment{
Name: v,
Comment: comment,
})
}
}
return
}

func fixSqliteFieldsComment(ctx context.Context, tableName string, db gdb.DB, fields map[string]*gdb.TableField) (map[string]*gdb.TableField, error) {
// 记录: db.DoSelect 无法执行 .开头的命令
// schemaRes, err := db.DoSelect(ctx, dbConf.Link, fmt.Sprintf(`.schema %s`, d.QuoteWord(table)))
// 记录: 查询 sqlite_master 不响应任何结果
// s, err := db.Query(ctx, `select * from sqlite_master WHERE name="%s";`, tableName)
// schemaStr, err := getSQLiteSchemaBySql(tableName, db)
schemaStr, err := getSQLiteSchemaBySql(ctx, tableName, db)
if err != nil {
return fields, err
}
comments, _, _ := getSQliteFieldsComments(schemaStr)
for i := range fields {
if comments.Contains(i) {
fields[i].Comment = comments.Get(i).(string)
}
}
return fields, nil
}
29 changes: 29 additions & 0 deletions server/utility/db/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package db

import (
"strings"
"unicode"
)

// 判断字符是否为字母、数字或下划线
func isWordChar(r rune) bool {
return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_'
}

// 获取一行文本中的第一个完整单词
func getFirstWord(text string) string {
fields := strings.FieldsFunc(text, func(r rune) bool { return !isWordChar(r) })
if len(fields) > 0 {
return fields[0]
}
return ""
}

// 获取一行文本中的最后一个完整单词
func getLastWord(text string) string {
fields := strings.FieldsFunc(text, func(r rune) bool { return !isWordChar(r) })
if len(fields) > 0 {
return fields[len(fields)-1]
}
return ""
}
1 change: 1 addition & 0 deletions web/src/api/base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function UploadImage(params) {
const headers = {
Authorization: useUserStore.token,
uploadType: 'default',
'Content-Type': 'multipart/form-data',
};
return http.request({
url: '/upload/file',
Expand Down
10 changes: 7 additions & 3 deletions web/src/router/generator-routers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ export const routerGenerator = (routerMap, parent?): any[] => {

// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
currentRouter.path = currentRouter.path.replace('//', '/');
// 重定向
item.redirect && (currentRouter.redirect = item.redirect);
// 重定向 ,菜单类型为目录默认默认跳转
if(item.meta.type === 1){
item.redirect && (currentRouter.redirect = item.redirect);
}
// 是否有子菜单,并递归处理
if (item.children && item.children.length > 0) {
//如果未定义 redirect 默认第一个子路由为 redirect
!item.redirect && (currentRouter.redirect = `${item.path}/${item.children[0].path}`);
if(item.meta.type === 1) {
!item.redirect && (currentRouter.redirect = `${item.path}/${item.children[0].path}`);
}
// Recursion
currentRouter.children = routerGenerator(item.children, currentRouter);
}
Expand Down
1 change: 1 addition & 0 deletions web/src/views/permission/role/editDataAuth.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<n-tree-select
multiple
key-field="id"
label-field="name"
:options="deptList"
v-model:value="formValue.customDept"
:default-expand-all="true"
Expand Down

0 comments on commit 7bf0efe

Please sign in to comment.