Skip to content

Commit 7bf0efe

Browse files
authored
Merge pull request #63 from bufanyun/v2.0
up
2 parents 406e3ef + f78f44e commit 7bf0efe

File tree

13 files changed

+6112
-5
lines changed

13 files changed

+6112
-5
lines changed

server/internal/library/storager/upload_local.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/gogf/gf/v2/util/gconv"
1616
"os"
1717
"path/filepath"
18+
"sort"
19+
"strconv"
1820
"strings"
1921
)
2022

@@ -139,7 +141,11 @@ func MergePartFile(srcPath, dstPath string) (err error) {
139141
if err != nil {
140142
return err
141143
}
142-
144+
sort.Slice(dir, func(i, j int) bool {
145+
fiIndex, _ := strconv.Atoi(dir[i].Name())
146+
fjIndex, _ := strconv.Atoi(dir[j].Name())
147+
return fiIndex < fjIndex
148+
})
143149
for _, file := range dir {
144150
filePath := filepath.Join(srcPath, file.Name())
145151
if err = gfile.PutBytesAppend(dstPath, gfile.GetBytes(filePath)); err != nil {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package storager
2+
3+
import (
4+
"crypto/md5"
5+
"errors"
6+
"fmt"
7+
"io"
8+
"os"
9+
"testing"
10+
)
11+
12+
func TestMergePartFile(t *testing.T) {
13+
srcFile, _ := os.Open("1.zip")
14+
defer srcFile.Close()
15+
fileInfo, err := srcFile.Stat()
16+
if err != nil {
17+
t.Error(err)
18+
}
19+
hash := md5.New()
20+
_, err = io.Copy(hash, srcFile)
21+
sum := hash.Sum(nil)
22+
fileHash := fmt.Sprintf("%x", sum)
23+
24+
var singleSize = int64(1024 * 1024 * 100)
25+
chunks := int(fileInfo.Size() / singleSize)
26+
if fileInfo.Size()%singleSize != 0 {
27+
chunks += 1
28+
}
29+
srcFile.Seek(0, io.SeekStart)
30+
for j := 0; j < chunks; j++ {
31+
partSize := singleSize
32+
if j == chunks-1 {
33+
partSize = fileInfo.Size() - int64(j)*singleSize
34+
}
35+
partData := make([]byte, partSize)
36+
_, err = io.ReadFull(srcFile, partData)
37+
pf, _ := os.Create(fmt.Sprintf("tmp/%d", j+1))
38+
_, err = pf.Write(partData)
39+
if err != nil {
40+
t.Error(err)
41+
return
42+
}
43+
pf.Close()
44+
}
45+
46+
err = MergePartFile("tmp/", "2.zip")
47+
if err != nil {
48+
t.Error(err)
49+
return
50+
}
51+
52+
f2, _ := os.Open("2.zip")
53+
hash2 := md5.New()
54+
_, err = io.Copy(hash2, f2)
55+
sum2 := hash.Sum(nil)
56+
fileHash2 := fmt.Sprintf("%x", sum2)
57+
if fileHash != fileHash2 {
58+
t.Error(errors.New("hash mismatch"))
59+
}
60+
61+
}

server/storage/data/hotgo.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -1719,7 +1719,7 @@ CREATE TABLE IF NOT EXISTS `hg_sys_attachment` (
17191719
`name` varchar(1000) DEFAULT NULL COMMENT '文件原始名',
17201720
`kind` varchar(16) DEFAULT NULL COMMENT '上传类型',
17211721
`mime_type` varchar(128) NOT NULL DEFAULT '' COMMENT '扩展类型',
1722-
`naive_type` varchar(32) NOT NULL COMMENT 'NaiveUI类型',
1722+
`naive_type` varchar(32) NOT NULL DEFAULT '' COMMENT 'NaiveUI类型',
17231723
`path` varchar(1000) DEFAULT NULL COMMENT '本地路径',
17241724
`file_url` varchar(1000) DEFAULT NULL COMMENT 'url',
17251725
`size` bigint(20) DEFAULT '0' COMMENT '文件大小',

server/storage/data/sqlite/data.sql

+5,134
Large diffs are not rendered by default.

server/storage/data/sqlite/tables.sql

+656
Large diffs are not rendered by default.

server/utility/db/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# 数据库补丁支持
2+
3+
> 将用于抹平不同数据库的差异性,以支撑基于数据库的部分特性。
4+
5+
## 特性
6+
7+
- 增加对数据库表字段注释的支持,特别是 sqlite 的注释支持。
8+
- 增加对数据库表名注释的支持,特别是 sqlite 的注释支持。
9+
10+
> 注意以上支持中,由于 sqlite 的特殊性,对 sqlite 表创建的 sql 语句有所要求,具体示例请参考 [sqlite_example.sql](./sqlite_example.sql)
11+

server/utility/db/db.go

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package db
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/gogf/gf/v2/database/gdb"
8+
"github.com/gogf/gf/v2/frame/g"
9+
)
10+
11+
// 获取数据库表字段及注释
12+
// usage:
13+
//
14+
// fields, err := db.GetFieldsWithComment(ctx, in.Table, in.Name)
15+
// if err != nil {
16+
// return
17+
// }
18+
// for _, v := range fields {}
19+
func GetFieldsWithComment(ctx context.Context, tableName, dbTag string) (fields map[string]*gdb.TableField, err error) {
20+
db := g.DB(dbTag)
21+
fields, err = db.TableFields(ctx, tableName) // 使用 goframe 框架本身已完美支持 mysql 获取表字段及注释
22+
dbConf := db.GetConfig()
23+
switch dbConf.Type {
24+
case "sqlite":
25+
fields, err = fixSqliteFieldsComment(ctx, tableName, db, fields)
26+
}
27+
return
28+
}
29+
30+
type TableComment struct {
31+
Name string `json:"name"`
32+
Comment string `json:"comment"`
33+
}
34+
35+
// 获取数据库表字段及注释
36+
func GetTablesWithComment(ctx context.Context, dbTag string) (tables []*TableComment, err error) {
37+
db := g.DB(dbTag)
38+
dbConf := db.GetConfig()
39+
switch dbConf.Type {
40+
case "mysql":
41+
sql := "SELECT TABLE_NAME as name, TABLE_COMMENT as comment FROM information_schema.`TABLES` WHERE TABLE_SCHEMA = '%s'"
42+
if err = db.Ctx(ctx).Raw(fmt.Sprintf(sql, dbConf.Name)).Scan(&tables); err != nil {
43+
return
44+
}
45+
case "sqlite":
46+
var tableNames []string
47+
tableNames, err = db.Tables(ctx)
48+
if err != nil {
49+
return
50+
}
51+
tables, err = transSqliteTablesComment(ctx, tableNames, db)
52+
}
53+
return
54+
}

server/utility/db/sqlite_example.sql

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
CREATE TABLE IF NOT EXISTS `user1` ( -- 用户管理
2+
`id` INTEGER, -- 编号
3+
`name` VARCHAR(50), -- 名称
4+
`email` VARCHAR(255), -- 邮箱
5+
`address` TEXT, -- 地址
6+
`salt` VARCHAR(50), --
7+
`password` VARCHAR(50), -- 密码
8+
`mark` VARCHAR(255), -- 备注
9+
`permission` TEXT, -- 权限
10+
`created_user_id` INTEGER, -- 创建者编号
11+
`created_at` DATETIME, -- 创建时间
12+
`updated_at` DATETIME, -- 更新时间
13+
`deleted_at` DATETIME, -- 删除时间
14+
PRIMARY KEY(`id`)
15+
);
16+
CREATE TABLE "user2" ( -- 用户管理
17+
"id" INTEGER, -- 编号
18+
"name" VARCHAR(50), -- 名称
19+
"email" VARCHAR(255), -- 邮箱
20+
"address" TEXT, -- 地址
21+
"salt" VARCHAR(50), --
22+
"password" VARCHAR(50), -- 密码
23+
"mark" VARCHAR(255), -- 备注
24+
"permission" TEXT, -- 权限
25+
"created_user_id" INTEGER, -- 创建者编号
26+
"created_at" DATETIME, -- 创建时间
27+
"updated_at" DATETIME, -- 更新时间
28+
"deleted_at" DATETIME, -- 删除时间
29+
PRIMARY KEY("id")
30+
);
31+
CREATE TABLE IF NOT EXISTS user3 ( -- 用户管理
32+
id INTEGER, -- 编号
33+
name VARCHAR(50), -- 名称
34+
email VARCHAR(255), -- 邮箱
35+
address TEXT, -- 地址
36+
salt VARCHAR(50), --
37+
password VARCHAR(50), -- 密码
38+
mark VARCHAR(255), -- 备注
39+
permission TEXT, -- 权限
40+
created_user_id INTEGER, -- 创建者编号
41+
created_at DATETIME, -- 创建时间
42+
updated_at DATETIME, -- 更新时间
43+
deleted_at DATETIME, -- 删除时间
44+
PRIMARY KEY(id)
45+
);

server/utility/db/sqlite_fields.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package db
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/gogf/gf/v2/container/gmap"
9+
"github.com/gogf/gf/v2/database/gdb"
10+
)
11+
12+
// func getSQLiteSchemaByCli(tableName string, db gdb.DB) (string, error) {
13+
// dbConf := db.GetConfig()
14+
// cmd := exec.Command("sqlite3", dbConf.Name, fmt.Sprintf(".schema %s", tableName))
15+
// glog.Info(context.TODO(), "sqlite3", dbConf.Name, fmt.Sprintf("'.schema %s'", tableName))
16+
// output, err := cmd.CombinedOutput()
17+
// if err != nil {
18+
// return "", err
19+
// }
20+
// return strings.TrimSpace(string(output)), nil
21+
// }
22+
23+
func getSQLiteSchemaBySql(ctx context.Context, tableName string, db gdb.DB) (string, error) {
24+
schemaRes, err := db.GetValue(ctx, fmt.Sprintf(`SELECT sql FROM sqlite_master WHERE type='table' AND name='%s';`, tableName))
25+
if err != nil {
26+
return "", err
27+
}
28+
return schemaRes.String(), nil
29+
}
30+
31+
func getSQliteTableComments(createTableSql string) (tableComment, tableName string) {
32+
// 按照换行符分割文本
33+
lines := strings.Split(createTableSql, "\n")
34+
35+
// 循环输出每一行
36+
for _, line := range lines {
37+
// 检查 createTableSql 是否包含 comment
38+
if strings.Contains(line, "--") {
39+
if strings.Contains(line, "CREATE TABLE") {
40+
tableName = getLastWord(strings.Split(line, "(")[0])
41+
tableComment = strings.Split(line, "--")[1]
42+
}
43+
}
44+
}
45+
return
46+
}
47+
48+
func getSQliteFieldsComments(createTableSql string) (fieldCommentMap gmap.Map, tableComment, tableName string) {
49+
// 按照换行符分割文本
50+
lines := strings.Split(createTableSql, "\n")
51+
52+
// 循环输出每一行
53+
for _, line := range lines {
54+
// 检查 createTableSql 是否包含 comment
55+
if strings.Contains(line, "--") {
56+
if strings.Contains(line, "CREATE TABLE") {
57+
tableName = getLastWord(strings.Split(line, "(")[0])
58+
tableComment = strings.Split(line, "--")[1]
59+
} else {
60+
firstWord := getFirstWord(line)
61+
lastWord := getLastWord(line)
62+
fieldCommentMap.Set(firstWord, lastWord)
63+
}
64+
}
65+
}
66+
return
67+
}
68+
69+
func transSqliteTablesComment(ctx context.Context, tableNames []string, db gdb.DB) (tables []*TableComment, err error) {
70+
schemaStr := ""
71+
eleIgnore := "sqlite_sequence"
72+
for _, v := range tableNames {
73+
if v != eleIgnore {
74+
schemaStr, err = getSQLiteSchemaBySql(ctx, v, db)
75+
if err != nil {
76+
return
77+
}
78+
comment, _ := getSQliteTableComments(schemaStr)
79+
tables = append(tables, &TableComment{
80+
Name: v,
81+
Comment: comment,
82+
})
83+
}
84+
}
85+
return
86+
}
87+
88+
func fixSqliteFieldsComment(ctx context.Context, tableName string, db gdb.DB, fields map[string]*gdb.TableField) (map[string]*gdb.TableField, error) {
89+
// 记录: db.DoSelect 无法执行 .开头的命令
90+
// schemaRes, err := db.DoSelect(ctx, dbConf.Link, fmt.Sprintf(`.schema %s`, d.QuoteWord(table)))
91+
// 记录: 查询 sqlite_master 不响应任何结果
92+
// s, err := db.Query(ctx, `select * from sqlite_master WHERE name="%s";`, tableName)
93+
// schemaStr, err := getSQLiteSchemaBySql(tableName, db)
94+
schemaStr, err := getSQLiteSchemaBySql(ctx, tableName, db)
95+
if err != nil {
96+
return fields, err
97+
}
98+
comments, _, _ := getSQliteFieldsComments(schemaStr)
99+
for i := range fields {
100+
if comments.Contains(i) {
101+
fields[i].Comment = comments.Get(i).(string)
102+
}
103+
}
104+
return fields, nil
105+
}

server/utility/db/utils.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package db
2+
3+
import (
4+
"strings"
5+
"unicode"
6+
)
7+
8+
// 判断字符是否为字母、数字或下划线
9+
func isWordChar(r rune) bool {
10+
return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_'
11+
}
12+
13+
// 获取一行文本中的第一个完整单词
14+
func getFirstWord(text string) string {
15+
fields := strings.FieldsFunc(text, func(r rune) bool { return !isWordChar(r) })
16+
if len(fields) > 0 {
17+
return fields[0]
18+
}
19+
return ""
20+
}
21+
22+
// 获取一行文本中的最后一个完整单词
23+
func getLastWord(text string) string {
24+
fields := strings.FieldsFunc(text, func(r rune) bool { return !isWordChar(r) })
25+
if len(fields) > 0 {
26+
return fields[len(fields)-1]
27+
}
28+
return ""
29+
}

web/src/api/base/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export function UploadImage(params) {
1616
const headers = {
1717
Authorization: useUserStore.token,
1818
uploadType: 'default',
19+
'Content-Type': 'multipart/form-data',
1920
};
2021
return http.request({
2122
url: '/upload/file',

web/src/router/generator-routers.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@ export const routerGenerator = (routerMap, parent?): any[] => {
3636

3737
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
3838
currentRouter.path = currentRouter.path.replace('//', '/');
39-
// 重定向
40-
item.redirect && (currentRouter.redirect = item.redirect);
39+
// 重定向 ,菜单类型为目录默认默认跳转
40+
if(item.meta.type === 1){
41+
item.redirect && (currentRouter.redirect = item.redirect);
42+
}
4143
// 是否有子菜单,并递归处理
4244
if (item.children && item.children.length > 0) {
4345
//如果未定义 redirect 默认第一个子路由为 redirect
44-
!item.redirect && (currentRouter.redirect = `${item.path}/${item.children[0].path}`);
46+
if(item.meta.type === 1) {
47+
!item.redirect && (currentRouter.redirect = `${item.path}/${item.children[0].path}`);
48+
}
4549
// Recursion
4650
currentRouter.children = routerGenerator(item.children, currentRouter);
4751
}

web/src/views/permission/role/editDataAuth.vue

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<n-tree-select
2525
multiple
2626
key-field="id"
27+
label-field="name"
2728
:options="deptList"
2829
v-model:value="formValue.customDept"
2930
:default-expand-all="true"

0 commit comments

Comments
 (0)