Files
gfast/app/system/service/tools_gen_table.go
T

767 lines
22 KiB
Go

/*
* @desc:代码生成功能
* @company:云南省奇讯科技有限公司
* @Author: yixiaohu
* @Date: 2021/7/22 15:30
*/
package service
import (
"bufio"
"context"
"database/sql"
"fmt"
"gfast/app/common/global"
comModel "gfast/app/common/model"
comService "gfast/app/common/service"
"gfast/app/system/dao"
"gfast/app/system/model"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/encoding/gjson"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/os/gview"
"github.com/gogf/gf/text/gregex"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"io"
"os"
"strings"
)
type toolsGenTable struct{}
var ToolsGenTable = new(toolsGenTable)
// SelectListByPage 查询已导入的数据表
func (s *toolsGenTable) SelectListByPage(param *dao.ToolsGenTableSearchReq) (total int, list []*model.ToolsGenTable, err error) {
model := dao.ToolsGenTable.M
if param != nil {
if param.TableName != "" {
model = model.Where(dao.ToolsGenTable.C.TableName+" like ?", "%"+param.TableName+"%")
}
if param.TableComment != "" {
model = model.Where(dao.ToolsGenTable.C.TableComment+"like ?", "%"+param.TableComment+"%")
}
if param.BeginTime != "" {
model = model.Where(dao.ToolsGenTable.C.CreateTime+" >= ", param.BeginTime)
}
if param.EndTime != "" {
model = model.Where(dao.ToolsGenTable.C.CreateTime+" <= ", param.EndTime)
}
total, err = model.Count()
if err != nil {
g.Log().Error(err)
err = gerror.New("获取总行数失败")
return
}
if param.PageNum == 0 {
param.PageNum = 1
}
if param.PageSize == 0 {
param.PageSize = comModel.PageSize
}
err = model.Page(param.PageNum, param.PageSize).Order(dao.ToolsGenTable.C.TableId + " asc").Scan(&list)
if err != nil {
g.Log().Error(err)
err = gerror.New("获取数据失败")
}
}
return
}
// SelectDbTableList 查询据库表
func (s *toolsGenTable) SelectDbTableList(param *dao.ToolsGenTableSearchReq) (total int, list []*model.ToolsGenTable, err error) {
db := g.DB()
if s.getDbDriver() != "pgsql" {
err = gerror.New("代码生成暂时只支持postgres数据库")
return
}
sql := " from pg_tables where schemaname='public'"
//sql := " from information_schema.tables where table_schema = (select database())" +
// " and table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%' and table_name NOT IN (select tablename from " + dao.ToolsGenTable.Table + ") "
if param != nil {
if param.TableName != "" {
sql += gdb.FormatSqlWithArgs(" and lower(tablename) like lower(?)", []interface{}{"%" + param.TableName + "%"})
}
//if param.TableComment != "" {
// sql += gdb.FormatSqlWithArgs(" and lower(table_comment) like lower(?)", []interface{}{"%" + param.TableComment + "%"})
//}
//
//if param.BeginTime != "" {
// sql += gdb.FormatSqlWithArgs(" and date_format(create_time,'%y%m%d') >= date_format(?,'%y%m%d') ", []interface{}{param.BeginTime})
//}
//
//if param.EndTime != "" {
// sql += gdb.FormatSqlWithArgs(" and date_format(create_time,'%y%m%d') <= date_format(?,'%y%m%d') ", []interface{}{param.EndTime})
//}
}
countSql := "select count(1) " + sql
total, err = db.GetCount(countSql)
if err != nil {
g.Log().Error(err)
err = gerror.New("读取总表数失败")
return
}
//sql = "table_name, table_comment, create_time, update_time " + sql
sql = "tablename " + sql
if param.PageNum == 0 {
param.PageNum = 1
}
if param.PageSize == 0 {
param.PageSize = comModel.PageSize
}
page := (param.PageNum - 1) * param.PageSize
sql += " order by tablename asc limit " + gconv.String(param.PageSize) + " offset " + gconv.String(page)
err = db.GetScan(&list, "select "+sql)
if err != nil {
g.Log().Error(err)
err = gerror.New("读取数据失败")
}
return
}
//获取数据库驱动类型
func (s *toolsGenTable) getDbDriver() string {
config := g.DB().GetConfig()
return gstr.ToLower(config.Type)
}
// SelectDbTableListByNames 查询数据库中对应的表数据
func (s *toolsGenTable) SelectDbTableListByNames(tableNames []string) ([]*model.ToolsGenTable, error) {
if s.getDbDriver() != "pgsql" {
return nil, gerror.New("代码生成只支持postgres数据库")
}
db := g.DB()
sql := "select * from information_schema.tables where table_name NOT LIKE 'gleke_%' and table_name NOT LIKE 'gen_%' " +
" and table_schema = 'public' "
if len(tableNames) > 0 {
in := gstr.TrimRight(gstr.Repeat("?,", len(tableNames)), ",")
sql += " and " + gdb.FormatSqlWithArgs("table_name in ("+in+")", gconv.SliceAny(tableNames))
}
var result []*model.ToolsGenTable
err := db.GetScan(&result, sql)
if err != nil {
g.Log().Error(err)
return nil, gerror.New("获取表格信息失败")
}
return result, err
}
// ImportGenTable 导入表结构
func (s *toolsGenTable) ImportGenTable(tableList []*model.ToolsGenTable) error {
if tableList != nil {
tx, err := g.DB().Begin()
if err != nil {
return err
}
for _, table := range tableList {
tableName := table.TableName
s.InitTable(table)
_, err := tx.Model(dao.ToolsGenTable.Table).FieldsEx("table_id").Insert(table)
if err != nil {
g.Log().Error("ImportGenTable", err)
return err
}
tableIDMax, errMax := tx.Model(dao.ToolsGenTable.Table).Max("table_id")
tmpId := int64(tableIDMax)
if errMax != nil || tmpId <= 0 {
tx.Rollback()
return gerror.New("保存数据失败")
}
table.TableId = tmpId
// 保存列信息
genTableColumns, errTable := ToolsGenTableColumn.SelectDbTableColumnsByName(tableName)
if errTable != nil || len(genTableColumns) <= 0 {
tx.Rollback()
return gerror.New("获取列数据失败")
}
for _, column := range genTableColumns {
ToolsGenTableColumn.InitColumnField(column, table)
_, err = tx.Model(dao.ToolsGenTableColumn.Table).FieldsEx("column_id").Insert(column)
if err != nil {
tx.Rollback()
return gerror.New("保存列数据失败")
}
}
}
return tx.Commit()
} else {
return gerror.New("参数错误")
}
}
// InitTable 初始化表信息
func (s *toolsGenTable) InitTable(table *model.ToolsGenTable) {
table.ClassName = s.ConvertClassName(table.TableName)
table.PackageName = g.Cfg().GetString("gen.packageName")
table.ModuleName = g.Cfg().GetString("gen.moduleName")
table.BusinessName = s.GetBusinessName(table.TableName)
table.FunctionName = strings.ReplaceAll(table.TableComment, "表", "")
table.FunctionAuthor = g.Cfg().GetString("gen.author")
table.TplCategory = "crud"
table.CreateTime = gtime.Now()
table.UpdateTime = table.CreateTime
}
// ConvertClassName 表名转换成类名
func (s *toolsGenTable) ConvertClassName(tableName string) string {
return gstr.CaseCamel(s.removeTablePrefix(tableName))
}
// GetBusinessName 获取业务名
func (s *toolsGenTable) GetBusinessName(tableName string) string {
return s.removeTablePrefix(tableName)
}
//删除表前缀
func (s *toolsGenTable) removeTablePrefix(tableName string) string {
autoRemovePre := g.Cfg().GetBool("gen.autoRemovePre")
tablePrefix := g.Cfg().GetString("gen.tablePrefix")
if autoRemovePre && tablePrefix != "" {
searchList := strings.Split(tablePrefix, ",")
for _, str := range searchList {
if strings.HasPrefix(tableName, str) {
tableName = strings.Replace(tableName, str, "", 1) //注意,只替换一次
}
}
}
return tableName
}
// Delete 删除表信息
func (s *toolsGenTable) Delete(ids []int) error {
tx, err := g.DB().Begin()
if err != nil {
g.Log().Error(err)
return gerror.New("开启删除事务出错")
}
_, err = tx.Model(dao.ToolsGenTable.Table).Where(dao.ToolsGenTable.C.TableId+" in(?)", ids).Delete()
if err != nil {
g.Log().Error(err)
tx.Rollback()
return gerror.New("删除表格数据失败")
}
_, err = tx.Model(dao.ToolsGenTableColumn.Table).Where(dao.ToolsGenTableColumn.C.TableId+" in(?)", ids).Delete()
if err != nil {
g.Log().Error(err)
tx.Rollback()
return gerror.New("删除表格字段数据失败")
}
tx.Commit()
return nil
}
// GetTableInfoByTableId 获取表格信息
func (s *toolsGenTable) GetTableInfoByTableId(tableId int64) (info *model.ToolsGenTable, err error) {
err = dao.ToolsGenTable.WherePri(tableId).Scan(&info)
if err != nil {
g.Log().Error(err)
err = gerror.New("获取表格信息出错")
}
return
}
// SaveEdit 更新表及字段生成信息
func (s *toolsGenTable) SaveEdit(req *dao.ToolsGenTableEditReq) (err error) {
err = nil
if req == nil {
err = gerror.New("参数错误")
return
}
var table *model.ToolsGenTable
err = dao.ToolsGenTable.Where("table_id=?", req.TableId).Scan(&table)
g.Log().Info(req.TableId)
g.Log().Info(table)
//g.Log().Error("table_id=?",err)
if err != nil || table == nil {
err = gerror.New("数据不存在")
g.Log().Error(err)
return
}
if req.TableName != "" {
table.TableName = req.TableName
}
if req.TableComment != "" {
table.TableComment = req.TableComment
}
if req.BusinessName != "" {
table.BusinessName = req.BusinessName
}
if req.ClassName != "" {
table.ClassName = req.ClassName
}
if req.FunctionAuthor != "" {
table.FunctionAuthor = req.FunctionAuthor
}
if req.FunctionName != "" {
table.FunctionName = req.FunctionName
}
if req.ModuleName != "" {
table.ModuleName = req.ModuleName
}
if req.PackageName != "" {
table.PackageName = req.PackageName
}
if req.Remark != "" {
table.Remark = req.Remark
}
if req.TplCategory != "" {
table.TplCategory = req.TplCategory
}
if req.Params != "" {
table.Options = req.Params
}
table.UpdateTime = gtime.Now()
var options g.Map
if req.TplCategory == "tree" {
//树表设置options
options = g.Map{
"treeCode": req.TreeCode,
"treeParentCode": req.TreeParentCode,
"treeName": req.TreeName,
}
table.Options = gconv.String(options)
} else {
table.Options = ""
}
var tx *gdb.TX
tx, err = g.DB().Begin()
if err != nil {
return
}
g.Log().Info("tx.Model(dao.ToolsGenTable.Table).Save(table)")
_, err = tx.Model(dao.ToolsGenTable.Table).Where("table_id=?", req.TableId).Update(table)
g.Log().Error("tx.Model(dao.ToolsGenTable.Table).Save(table)", err)
if err != nil {
tx.Rollback()
return err
}
//保存列数据
g.Log().Infof("req.Columns")
if req.Columns != nil {
for _, column := range req.Columns {
if column.ColumnId > 0 {
var dbColumn *model.ToolsGenTableColumn
err = dao.ToolsGenTableColumn.Where("column_id=?", column.ColumnId).Scan(&dbColumn)
g.Log().Error("保存列数据", err)
if dbColumn != nil {
dbColumn.ColumnComment = column.ColumnComment
dbColumn.GoType = column.GoType
dbColumn.HtmlType = column.HtmlType
dbColumn.QueryType = column.QueryType
dbColumn.GoField = column.GoField
dbColumn.DictType = column.DictType
dbColumn.IsInsert = column.IsInsert
dbColumn.IsEdit = column.IsEdit
dbColumn.IsList = column.IsList
dbColumn.IsQuery = column.IsQuery
dbColumn.IsRequired = column.IsRequired
if tc, e := options["treeParentCode"]; options != nil && e && tc != "" && tc == dbColumn.HtmlField {
dbColumn.IsQuery = "0"
dbColumn.IsList = "0"
dbColumn.HtmlType = "select"
}
//获取字段关联表信息
if column.LinkLabelName != "" {
dbColumn.LinkTableName = column.LinkTableName
dbColumn.LinkLabelId = column.LinkLabelId
dbColumn.LinkLabelName = column.LinkLabelName
var linkTable *model.ToolsGenTable
err = dao.ToolsGenTable.Where("table_name =?", column.LinkTableName).Scan(&linkTable)
g.Log().Error("获取字段关联表信息", err)
g.Log().Info("获取字段关联表信息")
if err != nil {
tx.Rollback()
return
}
dbColumn.LinkTableClass = linkTable.ClassName
dbColumn.LinkTablePackage = linkTable.PackageName
} else {
dbColumn.LinkTableName = ""
dbColumn.LinkTableClass = ""
dbColumn.LinkTablePackage = ""
dbColumn.LinkLabelId = ""
dbColumn.LinkLabelName = ""
}
_, err = tx.Model(dao.ToolsGenTableColumn.Table).Where("column_id=?", column.ColumnId).Update(dbColumn)
g.Log().Info("Save(dbColumn)")
g.Log().Error("Save(dbColumn)", err)
if err != nil {
tx.Rollback()
return
}
}
}
}
}
tx.Commit()
return
}
func (s *toolsGenTable) SelectRecordById(tableId int64) (entityExtend *dao.ToolsGenTableExtend, err error) {
var table *model.ToolsGenTable
table, err = s.GetTableInfoByTableId(tableId)
if err != nil {
return
}
m := gconv.Map(table)
gconv.Struct(m, &entityExtend)
if entityExtend.TplCategory == "tree" {
opt := gjson.New(entityExtend.Options)
entityExtend.TreeParentCode = opt.GetString("treeParentCode")
entityExtend.TreeCode = opt.GetString("treeCode")
entityExtend.TreeName = opt.GetString("treeName")
}
//表字段数据
var columns []*model.ToolsGenTableColumn
columns, err = ToolsGenTableColumn.SelectGenTableColumnListByTableId(tableId)
if err != nil {
return
}
entityExtend.Columns = columns
return
}
func (s *toolsGenTable) GenCode(ids []int, ctx context.Context) (err error) {
//获取当前运行时目录
curDir, err := os.Getwd()
if err != nil {
return gerror.New("获取本地路径失败")
}
frontDir := g.Cfg().GetString("gen.frontDir")
if !gfile.IsDir(frontDir) {
err = gerror.New("项目前端路径不存在,请检查是否已在配置文件中配置!")
return
}
for _, id := range ids {
var genData g.MapStrStr
var extendData *dao.ToolsGenTableExtend
genData, extendData, err = s.GenData(gconv.Int64(id), ctx)
packageName := gstr.SubStr(extendData.PackageName, gstr.Pos(extendData.PackageName, "/"))
businessName := gstr.CaseCamelLower(extendData.BusinessName)
for key, code := range genData {
switch key {
case "controller":
path := strings.Join([]string{curDir, packageName, "/api/", extendData.TableName, ".go"}, "")
err = s.createFile(path, code, false)
case "dao":
path := strings.Join([]string{curDir, packageName, "/dao/", extendData.TableName, ".go"}, "")
err = s.createFile(path, code, false)
case "dao_internal":
path := strings.Join([]string{curDir, packageName, "/dao/internal/", extendData.TableName, ".go"}, "")
err = s.createFile(path, code, true)
case "model":
path := strings.Join([]string{curDir, packageName, "/model/", extendData.TableName, ".go"}, "")
err = s.createFile(path, code, true)
case "router":
path := strings.Join([]string{curDir, packageName, "/router/", extendData.TableName, ".go"}, "")
err = s.createFile(path, code, false)
case "service":
path := strings.Join([]string{curDir, packageName, "/service/", extendData.TableName, ".go"}, "")
err = s.createFile(path, code, false)
case "sql":
path := strings.Join([]string{curDir, "/data/gen_sql/", packageName, "/", extendData.TableName, ".sql"}, "")
hasSql := gfile.Exists(path)
err = s.createFile(path, code, false)
if !hasSql {
//第一次生成则向数据库写入菜单数据
err = s.writeDb(path, ctx)
if err != nil {
return
}
//清除菜单缓存
comService.Cache.New().Remove(global.SysAuthMenu)
}
case "vue":
path := strings.Join([]string{frontDir, "/src/views/", extendData.ModuleName, "/", businessName, "/list/index.vue"}, "")
if gstr.ContainsI(extendData.PackageName, "plugins") {
path = strings.Join([]string{frontDir, "/src/views/plugins/", extendData.ModuleName, "/", businessName, "/list/index.vue"}, "")
}
err = s.createFile(path, code, false)
case "jsApi":
path := strings.Join([]string{frontDir, "/src/api/", extendData.ModuleName, "/", businessName, ".js"}, "")
if gstr.ContainsI(extendData.PackageName, "plugins") {
path = strings.Join([]string{frontDir, "/src/api/plugins/", extendData.ModuleName, "/", businessName, ".js"}, "")
}
err = s.createFile(path, code, false)
}
}
//生成对应的模块路由
err = s.genModuleRouter(curDir, extendData.ModuleName, extendData.PackageName)
}
return
}
// createFile 创建文件
func (s *toolsGenTable) createFile(fileName, data string, cover bool) (err error) {
if !gfile.Exists(fileName) || cover {
var f *os.File
f, err = gfile.Create(fileName)
if err == nil {
f.WriteString(data)
}
f.Close()
}
return
}
// GenData 获取生成数据
func (s *toolsGenTable) GenData(tableId int64, ctx context.Context) (data g.MapStrStr, extendData *dao.ToolsGenTableExtend, err error) {
extendData, err = ToolsGenTable.SelectRecordById(tableId)
if err != nil {
return
}
if extendData == nil {
err = gerror.New("表格数据不存在")
return
}
g.Log().Info(extendData)
g.Log().Info(extendData.Columns)
ToolsGenTableColumn.SetPkColumn(extendData, extendData.Columns)
view := gview.New()
view.SetConfigWithMap(g.Map{
"Paths": g.Cfg().GetString("gen.templatePath"),
"Delimiters": []string{"{{", "}}"},
})
view.BindFuncMap(g.Map{
"UcFirst": func(str string) string {
return gstr.UcFirst(str)
},
"Sum": func(a, b int) int {
return a + b
},
"CaseCamelLower": gstr.CaseCamelLower, //首字母小写驼峰
"CaseCamel": gstr.CaseCamel, //首字母大写驼峰
"HasSuffix": gstr.HasSuffix, //是否存在后缀
"ContainsI": gstr.ContainsI, //是否包含子字符串
"VueTag": func(t string) string {
return t
},
})
//树形菜单选项
tplData := g.Map{"table": extendData}
daoKey := "dao"
daoValue := ""
var tmpDao string
if tmpDao, err = view.Parse(ctx, "go/dao.template", tplData); err == nil {
daoValue = tmpDao
daoValue, err = s.trimBreak(daoValue)
} else {
return
}
daoInternalKey := "dao_internal"
daoInternalValue := ""
var tmpInternalDao string
if tmpInternalDao, err = view.Parse(ctx, "go/dao_internal.template", tplData); err == nil {
daoInternalValue = tmpInternalDao
daoInternalValue, err = s.trimBreak(daoInternalValue)
} else {
return
}
modelKey := "model"
modelValue := ""
var tmpModel string
if tmpModel, err = view.Parse(ctx, "go/model.template", tplData); err == nil {
modelValue = tmpModel
modelValue, err = s.trimBreak(modelValue)
} else {
return
}
controllerKey := "controller"
controllerValue := ""
var tmpController string
if tmpController, err = view.Parse(ctx, "go/controller.template", tplData); err == nil {
controllerValue = tmpController
controllerValue, err = s.trimBreak(controllerValue)
} else {
return
}
serviceKey := "service"
serviceValue := ""
var tmpService string
if tmpService, err = view.Parse(ctx, "go/service.template", tplData); err == nil {
serviceValue = tmpService
serviceValue, err = s.trimBreak(serviceValue)
} else {
return
}
routerKey := "router"
routerValue := ""
var tmpRouter string
if tmpRouter, err = view.Parse(ctx, "go/router.template", tplData); err == nil {
routerValue = tmpRouter
routerValue, err = s.trimBreak(routerValue)
} else {
return
}
sqlKey := "sql"
sqlValue := ""
var tmpSql string
if tmpSql, err = view.Parse(ctx, "sql/sql.template", tplData); err == nil {
sqlValue = tmpSql
sqlValue, err = s.trimBreak(sqlValue)
} else {
return
}
jsApiKey := "jsApi"
jsApiValue := ""
var tmpJsApi string
if tmpJsApi, err = view.Parse(ctx, "js/api.template", tplData); err == nil {
jsApiValue = tmpJsApi
jsApiValue, err = s.trimBreak(jsApiValue)
} else {
return
}
vueKey := "vue"
vueValue := ""
var tmpVue string
tmpFile := "vue/list-vue.template"
if extendData.TplCategory == "tree" {
//树表
tmpFile = "vue/tree-vue.template"
}
if tmpVue, err = view.Parse(ctx, tmpFile, tplData); err == nil {
vueValue = tmpVue
vueValue, err = s.trimBreak(vueValue)
} else {
return
}
data = g.MapStrStr{
daoKey: daoValue,
daoInternalKey: daoInternalValue,
modelKey: modelValue,
controllerKey: controllerValue,
serviceKey: serviceValue,
routerKey: routerValue,
sqlKey: sqlValue,
jsApiKey: jsApiValue,
vueKey: vueValue,
}
return
}
//剔除多余的换行
func (s *toolsGenTable) trimBreak(str string) (rStr string, err error) {
var b []byte
if b, err = gregex.Replace("(([\\s\t]*)\r?\n){2,}", []byte("$2\n"), []byte(str)); err != nil {
return
}
if b, err = gregex.Replace("(([\\s\t]*)/{4}\r?\n)", []byte("$2\n\n"), b); err == nil {
rStr = gconv.String(b)
}
return
}
// 写入菜单数据
func (s *toolsGenTable) writeDb(path string, ctx context.Context) (err error) {
isAnnotation := false
var fi *os.File
fi, err = os.Open(path)
if err != nil {
return
}
defer fi.Close()
br := bufio.NewReader(fi)
var sqlStr []string
now := gtime.Now()
var res sql.Result
var id int64
var tx *gdb.TX
tx, err = g.DB().Ctx(ctx).Begin()
if err != nil {
return
}
for {
bytes, e := br.ReadBytes('\n')
if e == io.EOF {
break
}
str := gstr.Trim(string(bytes))
if str == "" {
continue
}
if strings.Contains(str, "/*") {
isAnnotation = true
}
if isAnnotation {
if strings.Contains(str, "*/") {
isAnnotation = false
}
continue
}
if str == "" || strings.HasPrefix(str, "--") || strings.HasPrefix(str, "#") {
continue
}
if strings.HasSuffix(str, ";") {
if gstr.ContainsI(str, "select") {
if gstr.ContainsI(str, "@now") {
continue
}
if gstr.ContainsI(str, "@parentId") {
id, err = res.LastInsertId()
}
}
sqlStr = append(sqlStr, str)
sql := strings.Join(sqlStr, "")
gstr.ReplaceByArray(sql, []string{"@parentId", gconv.String(id), "@now", now.Format("Y-m-d H:i:s")})
//插入业务
g.Log().Info(sql)
res, err = tx.Exec(sql)
if err != nil {
tx.Rollback()
return
}
sqlStr = nil
} else {
sqlStr = []string{str}
}
}
tx.Commit()
return
}
// GenModuleRouter 生成模块路由
func (s *toolsGenTable) genModuleRouter(curDir, moduleName, packageName string) (err error) {
if gstr.CaseSnake(moduleName) != "system" {
routerFilePath := strings.Join([]string{curDir, "/router/", gstr.CaseSnake(moduleName), ".go"}, "")
if gstr.ContainsI(packageName, "plugins") {
routerFilePath = strings.Join([]string{curDir, "/plugins/router/", gstr.CaseSnake(moduleName), ".go"}, "")
}
code := fmt.Sprintf(`package router%simport _ "%s/router"`, "\n", packageName)
err = s.createFile(routerFilePath, code, false)
}
return
}