V2.7.2版本发布 (#1853)

* feat: 自动化代码增加json导出和导入功能

* feat: 自动化代码前端可见分为Table和Form分别选中

* feature: 调整代码预览为左边栏模式的tabs。

* feat: 增加方法自动添加前端api

* feat: 自动化生成前端支持详情功能

* feat: 增加自动创建可控权限按钮功能

* fixed: 顶栏样式菜单样式细节bug修复

* fixed: 修改视频地址

* fixed: 自动获取表结构和数据库表列结构保持一致

* fixed: casbin 设置空权限无需调用 AddPolicies 方法 (#1850)

* feat:对象存储支持配置Cloudflare R2 (#1849)

* fixed:设为首页和菜单勾选互为必选

---------

Co-authored-by: SliverHorn <503551462@qq.com>
Co-authored-by: 千石 <CN_QianShi@hotmail.com>

* feat: 复杂数据类型的查询将不会生成查询语句,会以string形式接收参数,用户自行实现复杂查询逻辑。

* feat: 创建新角色默认携带字典和长传权限。
禁止删除有首页占用的菜单。
不允许切换至无首页的角色。
自动化代码创建失败将返回错误信息。

* feat: 当package或plugin结构异常时候,阻止创建自动化代码。

---------

Co-authored-by: krank <emosick@qq.com>
Co-authored-by: SliverHorn <503551462@qq.com>
Co-authored-by: 千石 <CN_QianShi@hotmail.com>
This commit is contained in:
PiexlMax(奇淼
2024-08-13 21:55:27 +08:00
committed by GitHub
parent 3b5c96d7cb
commit c9477d37fa
40 changed files with 621 additions and 202 deletions

View File

@@ -24,6 +24,34 @@ var AutoCodeTemplate = new(autoCodeTemplate)
type autoCodeTemplate struct{}
func (s *autoCodeTemplate) checkPackage(Pkg string, template string) (err error) {
switch template {
case "package":
apiEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", Pkg, "enter.go")
_, err = os.Stat(apiEnter)
if err != nil {
return fmt.Errorf("package结构异常,缺少api/v1/%s/enter.go", Pkg)
}
serviceEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", Pkg, "enter.go")
_, err = os.Stat(serviceEnter)
if err != nil {
return fmt.Errorf("package结构异常,缺少service/%s/enter.go", Pkg)
}
routerEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", Pkg, "enter.go")
_, err = os.Stat(routerEnter)
if err != nil {
return fmt.Errorf("package结构异常,缺少router/%s/enter.go", Pkg)
}
case "plugin":
pluginEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", Pkg, "plugin.go")
_, err = os.Stat(pluginEnter)
if err != nil {
return fmt.Errorf("plugin结构异常,缺少plugin/%s/plugin.go", Pkg)
}
}
return nil
}
// Create 创建生成自动化代码
func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) error {
history := info.History()
@@ -32,7 +60,10 @@ func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) er
if err != nil {
return errors.Wrap(err, "查询包失败!")
}
err = s.checkPackage(info.Package, autoPkg.Template)
if err != nil {
return err
}
// 增加判断: 重复创建struct
if AutocodeHistory.Repeat(info.BusinessDB, info.StructName, info.Package) {
return errors.New("已经创建过此数据结构,请勿重复创建!")
@@ -87,6 +118,15 @@ func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) er
id = entity.ID
} else {
entity = info.Menu(autoPkg.Template)
if info.AutoCreateBtnAuth {
entity.MenuBtn = []model.SysBaseMenuBtn{
{SysBaseMenuID: entity.ID, Name: "add", Desc: "新增"},
{SysBaseMenuID: entity.ID, Name: "batchDelete", Desc: "批量删除"},
{SysBaseMenuID: entity.ID, Name: "delete", Desc: "删除"},
{SysBaseMenuID: entity.ID, Name: "edit", Desc: "编辑"},
{SysBaseMenuID: entity.ID, Name: "info", Desc: "详情"},
}
}
err = global.GVA_DB.WithContext(ctx).Create(&entity).Error
id = entity.ID
if err != nil {
@@ -190,11 +230,15 @@ func (s *autoCodeTemplate) AddFunc(info request.AutoFunc) error {
if autoPkg.Template != "package" {
info.IsPlugin = true
}
err = s.addTemplateToFile("api", info)
err = s.addTemplateToFile("api.go", info)
if err != nil {
return err
}
err = s.addTemplateToFile("server", info)
err = s.addTemplateToFile("server.go", info)
if err != nil {
return err
}
err = s.addTemplateToFile("api.js", info)
if err != nil {
return err
}
@@ -203,7 +247,7 @@ func (s *autoCodeTemplate) AddFunc(info request.AutoFunc) error {
}
func (s *autoCodeTemplate) getTemplateStr(t string, info request.AutoFunc) (string, error) {
tempPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", "function", t+".go.tpl")
tempPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", "function", t+".tpl")
files, err := template.ParseFiles(tempPath)
if err != nil {
return "", errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", tempPath)
@@ -267,17 +311,21 @@ func (s *autoCodeTemplate) addTemplateToFile(t string, info request.AutoFunc) er
var target string
switch t {
case "api":
case "api.go":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", info.Package, info.HumpPackageName+".go")
case "server":
case "server.go":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", info.Package, info.HumpPackageName+".go")
case "api.js":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "api", info.Package, info.HumpPackageName+".js")
}
if info.IsPlugin {
switch t {
case "api":
case "api.go":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "api", info.HumpPackageName+".go")
case "server":
case "server.go":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "service", info.HumpPackageName+".go")
case "api.js":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", info.Package, "api", info.HumpPackageName+".js")
}
}

View File

@@ -53,7 +53,8 @@ SELECT
CASE
WHEN pk.object_id IS NOT NULL THEN 1
ELSE 0
END AS primary_key
END AS primary_key,
sc.column_id
FROM
%s.sys.columns sc
JOIN
@@ -68,6 +69,8 @@ LEFT JOIN
%s.sys.key_constraints pk ON pk.object_id = si.object_id
WHERE
st.is_user_defined=0 AND sc.object_id = so.object_id
ORDER BY
sc.column_id
`, dbName, dbName, tableName, dbName, dbName, dbName)
if businessDB == "" {

View File

@@ -57,7 +57,8 @@ func (s *autoCodeMysql) GetColumn(businessDB string, tableName string, dbName st
ELSE ''
END AS data_type_long,
c.COLUMN_COMMENT column_comment,
CASE WHEN kcu.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS primary_key
CASE WHEN kcu.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS primary_key,
c.ORDINAL_POSITION
FROM
INFORMATION_SCHEMA.COLUMNS c
LEFT JOIN
@@ -69,7 +70,9 @@ ON
AND kcu.CONSTRAINT_NAME = 'PRIMARY'
WHERE
c.TABLE_NAME = ?
AND c.TABLE_SCHEMA = ?;`
AND c.TABLE_SCHEMA = ?
ORDER BY
c.ORDINAL_POSITION;`
if businessDB == "" {
err = global.GVA_DB.Raw(sql, tableName, dbName).Scan(&entities).Error
} else {

View File

@@ -36,12 +36,13 @@ func (s *autoCodeOracle) GetTables(businessDB string, dbName string) (data []res
func (s *autoCodeOracle) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) {
var entities []response.Column
sql := `
SELECT
SELECT
lower(a.COLUMN_NAME) as "column_name",
(CASE WHEN a.DATA_TYPE = 'NUMBER' AND a.DATA_SCALE=0 THEN 'int' else lower(a.DATA_TYPE) end) as "data_type",
(CASE WHEN a.DATA_TYPE = 'NUMBER' THEN a.DATA_PRECISION else a.DATA_LENGTH end) as "data_type_long",
b.COMMENTS as "column_comment",
(CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END) as "primary_key"
(CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END) as "primary_key",
a.COLUMN_ID
FROM
all_tab_columns a
JOIN
@@ -61,7 +62,9 @@ LEFT JOIN
) pk ON a.OWNER = pk.OWNER AND a.TABLE_NAME = pk.TABLE_NAME AND a.COLUMN_NAME = pk.COLUMN_NAME
WHERE
lower(a.table_name) = ?
AND lower(a.OWNER) = ?;
AND lower(a.OWNER) = ?
ORDER BY
a.COLUMN_ID;
`
err = global.GVA_DBList[businessDB].Raw(sql, tableName, dbName).Scan(&entities).Error

View File

@@ -111,13 +111,16 @@ SELECT
attrelid = conrelid
AND attname = psc.column_name
)]
) > 0 AS primary_key
) > 0 AS primary_key,
psc.ordinal_position
FROM
INFORMATION_SCHEMA.COLUMNS psc
WHERE
table_catalog = ?
AND table_schema = 'public'
AND TABLE_NAME = ?;
AND TABLE_NAME = ?
ORDER BY
psc.ordinal_position;
`
var entities []response.Column
//sql = strings.ReplaceAll(sql, "@table_catalog", dbName)

View File

@@ -20,36 +20,46 @@ var BaseMenuServiceApp = new(BaseMenuService)
func (baseMenuService *BaseMenuService) DeleteBaseMenu(id int) (err error) {
err = global.GVA_DB.First(&system.SysBaseMenu{}, "parent_id = ?", id).Error
if err != nil {
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
err = tx.Delete(&system.SysBaseMenu{}, "id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysBaseMenuBtn{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysAuthorityBtn{}, "sys_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysAuthorityMenu{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
return nil
})
if err == nil {
return errors.New("此菜单存在子菜单不可删除")
}
return errors.New("此菜单存在子菜单不可删除")
var menu system.SysBaseMenu
err = global.GVA_DB.First(&menu, id).Error
if err != nil {
return errors.New("记录不存在")
}
err = global.GVA_DB.First(&system.SysAuthority{}, "default_router = ?", menu.Name).Error
if err == nil {
return errors.New("此菜单有角色正在作为首页,不可删除")
}
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
err = tx.Delete(&system.SysBaseMenu{}, "id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysBaseMenuBtn{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysAuthorityBtn{}, "sys_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysAuthorityMenu{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
return nil
})
}
//@author: [piexlmax](https://github.com/piexlmax)

View File

@@ -104,10 +104,44 @@ func (userService *UserService) GetUserInfoList(info request.PageInfo) (list int
//@return: err error
func (userService *UserService) SetUserAuthority(id uint, authorityId uint) (err error) {
assignErr := global.GVA_DB.Where("sys_user_id = ? AND sys_authority_authority_id = ?", id, authorityId).First(&system.SysUserAuthority{}).Error
if errors.Is(assignErr, gorm.ErrRecordNotFound) {
return errors.New("该用户无此角色")
}
var authority system.SysAuthority
err = global.GVA_DB.Where("authority_id = ?", authorityId).First(&authority).Error
if err != nil {
return err
}
var authorityMenu []system.SysAuthorityMenu
var authorityMenuIDs []string
err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityId).Find(&authorityMenu).Error
if err != nil {
return err
}
for i := range authorityMenu {
authorityMenuIDs = append(authorityMenuIDs, authorityMenu[i].MenuId)
}
var authorityMenus []system.SysBaseMenu
err = global.GVA_DB.Preload("Parameters").Where("id in (?)", authorityMenuIDs).Find(&authorityMenus).Error
if err != nil {
return err
}
hasMenu := false
for i := range authorityMenus {
if authorityMenus[i].Name == authority.DefaultRouter {
hasMenu = true
break
}
}
if !hasMenu {
return errors.New("找不到默认路由,无法切换本角色")
}
err = global.GVA_DB.Model(&system.SysUser{}).Where("id = ?", id).Update("authority_id", authorityId).Error
return err
}