Подготовка некоторых функций для моделей

This commit is contained in:
Ymnuk 2023-06-26 15:35:24 +03:00
parent 3097759c96
commit 709f842952
13 changed files with 197 additions and 30 deletions

View File

@ -5,4 +5,11 @@ db:
pk: uuid
fields:
- name: f1
- name: test2
pk: int
fields:
- name: f1
- name: f2
fks:
- name: test1

View File

@ -11,8 +11,8 @@ func Backend() {
generateDB()
}
// Генерирование структуры БД
func generateDB() {
// TODO
if err := os.MkdirAll(filepath.Join(AppConfig.OutdirBackend, "db", "model"), 0775); err != nil {
log.Fatal(err)
}
@ -30,6 +30,9 @@ func generateModelBase() {
if err := WriteTmplFile("tmpl/backend/db/transaction.tmpl", filepath.Join(AppConfig.OutdirBackend, "db", "transaction.go")); err != nil {
log.Fatal(err)
}
if err := PrepareTmplFile("tmpl/backend/go.tmpl", Project, filepath.Join(AppConfig.OutdirBackend, "go.mod")); err != nil {
log.Fatal(err)
}
if err := PrepareTmplFile("tmpl/backend/db/db.tmpl", Project, filepath.Join(AppConfig.OutdirBackend, "db", "db.go")); err != nil {
log.Fatal(err)
}

View File

@ -11,6 +11,7 @@ import (
var AppConfig struct {
IsYaml bool
IsJson bool
IsHelp bool
Help bool // Вывести справку на экран
Filename string // Файл с метоописанием в формате YAML или JSON
@ -28,20 +29,13 @@ func parseArgs() {
flag.StringVar(&AppConfig.Filename, "metafile", "", "Путь к файлу с описанием для генерации кода")
flag.StringVar(&AppConfig.OutdirBackend, "outdir-backend", "", "Путь к директории для сохранения генерируемого кода сервера")
flag.StringVar(&AppConfig.OutdirFrontend, "outdir-frontend", "", "Путь к директории для сохранения генерируемого кода клиенат")
help := flag.Bool("help", false, "Справка")
flag.BoolVar(&AppConfig.IsHelp, "help", false, "Справка")
flag.Parse()
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
}
if help != nil && *help {
flag.Usage()
os.Exit(0)
}
if AppConfig.Filename == "" {
flag.Usage()
os.Exit(0)
}
}
func HelpPrint() {

View File

@ -1,5 +1,6 @@
package lib
// Генерирование исходного кода для клиента
func Frontend() {
// TODO
}

31
lib/lib_test.go Normal file
View File

@ -0,0 +1,31 @@
package lib
import (
"testing"
"git.ymnuktech.ru/ymnuk/yt-gen-app/structs"
)
func TestFieldType(t *testing.T) {
if fieldDBName(&structs.Field{Name: "Field"}) != "field" {
t.Fail()
}
if fieldDBName(&structs.Field{Name: "Field_NAME"}) != "field_name" {
t.Fail()
}
if fieldDBName(&structs.Field{Name: "fIELD_nAMe"}) != "field_name" {
t.Fail()
}
}
func TestDBModelAttrib(t *testing.T) {
str := fieldDescript(&structs.Field{
Name: "Field_naME1",
Type: "int",
Description: "Hello world",
})
if str != "`gorm:\"column:field_name1;type:INT;comment:Hello world\"`" {
t.Fail()
}
}

View File

@ -1,7 +1,9 @@
package lib
import (
"fmt"
"log"
"strings"
"git.ymnuktech.ru/ymnuk/yt-gen-app/structs"
uuid "github.com/satori/go.uuid"
@ -24,9 +26,32 @@ func PrepareMetadata(project *structs.Project) {
}
if len(project.DB.Tables[i].Fields) > 0 {
for j := range project.DB.Tables[i].Fields {
project.DB.Tables[i].Fields[j].Name = fieldDBName(&project.DB.Tables[i].Fields[j])
if project.DB.Tables[i].Fields[j].ID == uuid.Nil {
project.DB.Tables[i].Fields[j].ID = uuid.NewV4()
}
project.DB.Tables[i].Fields[j].Type = strings.Trim(project.DB.Tables[i].Fields[j].Type, " ")
project.DB.Tables[i].Fields[j].Type = strings.ToLower(project.DB.Tables[i].Fields[j].Type)
if project.DB.Tables[i].Fields[j].Type == "" {
project.DB.Tables[i].Fields[j].Type = "text"
}
}
}
if project.DB.Tables[i].Recursive {
project.DB.Tables[i].FkFields = append(project.DB.Tables[i].FkFields, structs.Field{
Name: "parent_id",
Description: "Recursive foreign key for self table",
})
switch project.DB.Tables[i].Pk {
case "uuid":
project.DB.Tables[i].FkFields[len(project.DB.Tables[i].FkFields)-1].Type = "uuid"
case "int":
project.DB.Tables[i].FkFields[len(project.DB.Tables[i].FkFields)-1].Type = "bigint"
case "bigintint":
project.DB.Tables[i].FkFields[len(project.DB.Tables[i].FkFields)-1].Type = "bigint"
default:
log.Fatalf("Error primary key type '%s' in table '%s'", project.DB.Tables[i].Pk, project.DB.Tables[i].Name)
}
}
if len(project.DB.Tables[i].FKs) > 0 {
@ -48,6 +73,13 @@ func PrepareMetadata(project *structs.Project) {
}
}
}
if project.DB.Tables[i].FKs[j].TableName == "" {
log.Fatalf("Error foreign key for '%s' table", project.DB.Tables[i].FKs[j].TableID.String())
}
project.DB.Tables[i].FkFields = append(project.DB.Tables[i].FkFields, structs.Field{
Name: fmt.Sprintf("%s_id", strings.ToLower(project.DB.Tables[i].FKs[j].TableName)),
Description: fmt.Sprintf("Foreign key for \"%s\" table", project.DB.Tables[i].FKs[j].TableName),
})
}
}
}

View File

@ -4,39 +4,117 @@ import (
"bufio"
"bytes"
"embed"
"fmt"
"log"
"os"
"strings"
"text/template"
"git.ymnuktech.ru/ymnuk/yt-gen-app/structs"
)
//go:embed tmpl/*
var content embed.FS
var funcMap = template.FuncMap{
"fieldName": fieldName,
"fieldName": fieldName,
"fieldType": fieldType,
"fieldDescript": fieldDescript,
}
func fieldName(name string) (res string) {
res = strings.Trim(name, " ")
if res == "" {
func fieldName(field *structs.Field) string {
if field == nil {
log.Fatal("Field is null")
}
field.Name = strings.Trim(field.Name, " ")
if field.Name == "" {
log.Fatal("Shoudl be set name for field")
}
asRunes := []rune(res)
asRunes := []rune(field.Name)
asRunes[0] = []rune(strings.ToUpper(string([]rune(res)[0])))[0]
res = string(asRunes)
i := strings.Index(res, "_")
asRunes[0] = []rune(strings.ToUpper(string([]rune(field.Name)[0])))[0]
field.Name = string(asRunes)
i := strings.Index(field.Name, "_")
for i != -1 {
asRunes := []rune(res)
if i >= len(res) {
asRunes := []rune(field.Name)
if i >= len(field.Name) {
break
}
asRunes[i+1] = []rune(strings.ToUpper(string(asRunes[i+1])))[0]
asRunes = append(asRunes[:i-1], asRunes[i+1:]...)
res = string(asRunes)
i = strings.Index(res, "_")
field.Name = string(asRunes)
i = strings.Index(field.Name, "_")
}
return field.Name
}
// Возвращает имя в БД
func fieldDBName(field *structs.Field) (res string) {
res = strings.Trim(field.Name, " ")
if res == "" {
log.Fatal("Shoudl be set name for field")
}
res = strings.ToLower(res)
return
}
func fieldType(field *structs.Field) string {
if field == nil {
log.Fatal("field is empty")
}
field.Type = strings.ToLower(strings.Trim(field.Type, " "))
if field.Type == "" {
field.Type = "text"
}
switch field.Type {
case "text":
return "*string"
case "string":
return "*string"
case "int":
return "*int"
case "bigint":
return "*int64"
default:
log.Fatalf("Unknow format %s", field.Type)
}
return ""
}
func fieldTypeDB(field *structs.Field) string {
field.Type = strings.ToLower(strings.Trim(field.Type, " "))
switch field.Type {
case "text":
return "TEXT"
case "string":
tmp := "VARCHAR"
if field.Length != nil && *field.Length == 0 {
tmp += fmt.Sprintf("(%d)", *field.Length)
} else {
tmp += "(255)"
}
//return "TEXT"
return tmp
case "int":
return "INT"
case "bigint":
return "BIGINT"
default:
log.Fatalf("Unknow format %s", field.Type)
}
return ""
}
// Генерирование описания таблиц в БД
func fieldDescript(field *structs.Field) (str string) {
// TODO
str = fmt.Sprintf("`gorm:\"column:%s;type:%s", fieldDBName(field), fieldTypeDB(field))
field.Description = strings.Trim(field.Description, " ")
if field.Description != "" {
str += fmt.Sprintf(";comment:%s", field.Description)
}
str += "\"`"
return
}

View File

@ -1,8 +1,15 @@
// This file generated automatic. Do not change this!
package models
import (
"time"
)
type BaseInt struct {
ID uint64 `gorm:"type:autoIncrement;primaryKey" json:"id,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
UpdatedAt time.Time `json:"updatedAt,omitempty"`
// DeletedAt gorm.DeletedAt `sql:"index" json:"deletedAt"`
DeletedAt gorm.DeletedAt `sql:"index" json:"deletedAt"`
}

View File

@ -1,3 +1,5 @@
// This file generated automatic. Do not change this!
package models
import (
@ -11,7 +13,7 @@ type Base struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;" json:"id,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
UpdatedAt time.Time `json:"updatedAt,omitempty"`
// DeletedAt gorm.DeletedAt `sql:"index" json:"deletedAt"`
DeletedAt gorm.DeletedAt `sql:"index" json:"deletedAt"`
}
// BeforeCreate will set a UUID rather than numeric ID.

View File

@ -1,3 +1,5 @@
// This file generated automatic. Do not change this!
package model
import (
@ -13,6 +15,6 @@ type {{.Name}} struct {
BaseInt
{{ end }}
{{ range $index, $field := .Fields }}
{{ fieldName $field.Name }} {{ $field.Type }}
{{ fieldName $field }} {{ fieldType $field }} {{ fieldDescript $field }}
{{ end }}
}

3
lib/tmpl/backend/go.tmpl Normal file
View File

@ -0,0 +1,3 @@
module {{ .Name }}
go 1.20

14
main.go
View File

@ -2,6 +2,7 @@ package main
import (
"encoding/json"
"flag"
"log"
"os"
@ -10,11 +11,16 @@ import (
"gopkg.in/yaml.v3"
)
func init() {
lib.PrepareParams()
}
func main() {
lib.PrepareParams()
if lib.AppConfig.IsHelp {
flag.Usage()
os.Exit(0)
}
if lib.AppConfig.Filename == "" {
flag.Usage()
os.Exit(0)
}
lib.Project = &structs.Project{}
if _, err := os.Stat(lib.AppConfig.Filename); os.IsNotExist(err) {
log.Fatal(`Метафайл не найден`)

View File

@ -15,14 +15,15 @@ type Table struct {
Pk string `yaml:"pk,omitempty" json:"pk,omitempty"` // Тип первичного ключа. Если uuid, то генерируется V4, int и bigint - используется bigint с автоинкрементом, string - первичный ключ как текстовое поле. Если не указано, то поумолчанию используется UUID
Description string `yaml:"description,omitempty" json:"description,omitempty"` // Описание таблицы
Fields []Field `yaml:"fields,omitempty" json:"fields,omitempty"` // Поля таблицы
Recursive bool `yaml:"recursive,omitempty" json:"recursive,omitempty"` // Рекурсивная таблица
FKs []FK `yaml:"fks,omitempty" json:"fks,omitempty"` // Внешние ключи
FkFields []Field `yaml:"-" json:"-"` // Поля с описанием внешних ключей
}
// Описание поля
type Field struct {
ID uuid.UUID `yaml:"id,omitempty" json:"id,omitempty"` // Идентификатор поля. Если пустой, то генерируется автоматически для дальнейше работы
Name string `yaml:"name,omitempty" json:"name,omitempty"` // Название поля
Recursive bool `yaml:"recursive,omitempty" json:"recursive,omitempty"` // Рекурсивная таблица
Type string `yaml:"type,omitempty" json:"type,omitempty"` // Тип поля. По умолчанию text
Length *int `yaml:"length,omitempty" json:"len,omitempty"` // Размер поля для строковых типов и Decimal
Accuracy *int `yaml:"accuracy,omitempty" json:"accuracy,omitempty"` // Точность поля для Decimal