Files
SamWaf/codetemplete/code_gen.go
T
2025-01-03 13:54:03 +08:00

238 lines
7.5 KiB
Go

package codetemplete
import (
"SamWaf/utils"
"bytes"
"fmt"
"html"
"os"
"path/filepath"
"reflect"
"strings"
"text/template"
"unicode"
)
// getStructFields 拉平解析结构体的字段信息,支持嵌套结构体
func GetStructFields(t interface{}) []map[string]string {
var fields []map[string]string
typ := reflect.TypeOf(t)
// 确保输入的是结构体
if typ.Kind() == reflect.Struct {
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
// 如果字段是嵌套结构体,递归解析子模型
if field.Type.Kind() == reflect.Struct && field.Type.String() != "time.Time" {
//fmt.Printf("field.Type.String():" + field.Type.String())
subFields := GetStructFields(reflect.New(field.Type).Elem().Interface())
fields = append(fields, subFields...)
} else {
// 普通字段
if field.Name == "wall" || field.Name == "ext" || field.Name == "loc" {
continue
}
fields = append(fields, map[string]string{
"Name": field.Name,
"Type": field.Type.String(),
"Tag": string(field.Tag),
})
}
}
}
return fields
}
func initFunc() template.FuncMap {
return template.FuncMap{
"lower": func(s string) string {
return string(bytes.ToLower([]byte(s)))
},
"pascalCase": func(s string) string {
// 将字符串转换为首字母大写格式(PascalCase)
words := strings.Fields(s) // 按空格分割单词
for i := range words {
// 对每个单词的首字母进行大写
words[i] = strings.Title(words[i])
}
// 连接成一个字符串并返回
return strings.Join(words, "")
},
"unescapeHtml": func(s string) string {
return html.UnescapeString(s)
},
"addForm": func(s string) string {
return strings.ReplaceAll(s, "json:", "form:")
},
"snakeCase": func(s string) string {
// 将字符串转换为小写并替换大写字母为下划线连接的小写字母
var result []rune
for i, r := range s {
// 如果是大写字母且不是第一个字符,前面加下划线
if unicode.IsUpper(r) {
if i > 0 {
result = append(result, '_')
}
// 转换为小写
result = append(result, unicode.ToLower(r))
} else {
result = append(result, r)
}
}
return string(result)
},
"removeQuotes": func(s string) string {
// 去掉字符串中的引号
return strings.ReplaceAll(s, `"`, "")
},
}
}
// renderTemplateAndSave 用于渲染模板并保存到指定路径
// templatePath:模板文件的路径
// renderData:传递给模板的数据
// outputPath 路径
// outputPathTemplate:输出文件的保存文件模板
// isDefaultDelim 是否默认模板分隔符
func renderTemplateAndSave(templatePath string, renderData map[string]interface{}, outputPath string, outputPathTemplate string, isDefaultDelim bool) error {
// 读取模板文件
templateContent, err := os.ReadFile(templatePath)
if err != nil {
return fmt.Errorf("读取模板文件失败: %v", err)
}
// 如果 isDefaultDelim 为 false,使用自定义分隔符
var tmpl *template.Template
if isDefaultDelim {
// 使用默认分隔符
tmpl, err = template.New("apiTemplate").Funcs(initFunc()).Parse(string(templateContent))
} else {
// 使用自定义分隔符,防止与 Vue 模板冲突
tmpl, err = template.New("vueTemplate").Delims("[[", "]]").Funcs(initFunc()).Parse(string(templateContent))
}
if err != nil {
return fmt.Errorf("解析模板失败: %v", err)
}
// 渲染模板
var renderedCode bytes.Buffer
if isDefaultDelim {
err = tmpl.Execute(&renderedCode, renderData)
if err != nil {
return fmt.Errorf("渲染模板失败: %v", err)
}
} else {
err = tmpl.ExecuteTemplate(&renderedCode, "vueTemplate", renderData)
if err != nil {
return fmt.Errorf("渲染模板失败: %v", err)
}
}
// 解码HTML实体字符
//decodedCode := html.UnescapeString(renderedCode.String())
/*fmt.Println("Before Rendered Code:")
fmt.Println(renderedCode)
fmt.Println("Decoded Rendered Code:")
fmt.Println(decodedCode)*/
// 渲染输出路径
utils.CheckPathAndCreate(outputPath)
outputPathTemplateParsed, err := template.New("outputPath").Funcs(initFunc()).Parse(outputPathTemplate)
if err != nil {
return fmt.Errorf("解析输出路径模板失败: %v", err)
}
var outputPathBuffer bytes.Buffer
err = outputPathTemplateParsed.Execute(&outputPathBuffer, renderData)
if err != nil {
return fmt.Errorf("渲染输出路径失败: %v", err)
}
// 输出到指定文件
outputPathFinal := filepath.Join(outputPath, outputPathBuffer.String())
err = os.WriteFile(outputPathFinal, renderedCode.Bytes(), 0644)
if err != nil {
return fmt.Errorf("写入输出文件失败: %v", err)
}
fmt.Printf("代码生成成功,已保存到 %s\n", outputPathFinal)
return nil
}
func CodeGeneration(entityName string, fields []map[string]string, uniFields []map[string]string) {
fmt.Printf("%v\n", fields)
fmt.Printf("%v\n", uniFields)
// 打印当前工作目录
/* currentDir, err := os.Getwd()
fmt.Println(currentDir)*/
// 生成api
err := renderTemplateAndSave("./tpl/waf_api.txt", map[string]interface{}{
"EntityName": entityName, // 需要替换的实体名称
"Fields": fields,
"UniFields": uniFields, //唯一识别的字段
}, "./output/"+entityName+"/api/", "waf_{{.EntityName | lower}}_api.go", true)
if err != nil {
fmt.Printf("生成代码失败: %v\n", err)
}
// 生成service
err = renderTemplateAndSave("./tpl/waf_service.txt", map[string]interface{}{
"EntityName": entityName, // 需要替换的实体名称
"Fields": fields,
"UniFields": uniFields, //唯一识别的字段
}, "./output/"+entityName+"/service/", "waf_{{.EntityName | lower}}_service.go", true)
if err != nil {
fmt.Printf("生成代码失败: %v\n", err)
}
// 生成req
err = renderTemplateAndSave("./tpl/waf_req.txt", map[string]interface{}{
"EntityName": entityName, // 需要替换的实体名称
"Fields": fields,
"UniFields": uniFields, //唯一识别的字段
}, "./output/"+entityName+"/request/", "waf_{{.EntityName | lower}}_req.go", true)
if err != nil {
fmt.Printf("生成代码失败: %v\n", err)
}
// 生成router
err = renderTemplateAndSave("./tpl/waf_router.txt", map[string]interface{}{
"EntityName": entityName, // 需要替换的实体名称
"Fields": fields,
"UniFields": uniFields, //唯一识别的字段
}, "./output/"+entityName+"/router/", "waf_{{.EntityName | lower}}_router.go", true)
if err != nil {
fmt.Printf("生成代码失败: %v\n", err)
}
// 生成vue
err = renderTemplateAndSave("./tpl/index_vue.txt", map[string]interface{}{
"EntityName": entityName, // 需要替换的实体名称
"Fields": fields,
"UniFields": uniFields, //唯一识别的字段
}, "./output/"+entityName+"/vue/", "index.vue", false)
if err != nil {
fmt.Printf("生成代码失败: %v\n", err)
}
// 生成js api
err = renderTemplateAndSave("./tpl/index_jsapi.txt", map[string]interface{}{
"EntityName": entityName, // 需要替换的实体名称
"Fields": fields,
"UniFields": uniFields, //唯一识别的字段
}, "./output/"+entityName+"/vue/", "{{.EntityName | snakeCase}}.ts", true)
if err != nil {
fmt.Printf("生成代码失败: %v\n", err)
}
// 生成语言文件
err = renderTemplateAndSave("./tpl/index_zh_en.txt", map[string]interface{}{
"EntityName": entityName, // 需要替换的实体名称
"Fields": fields,
"UniFields": uniFields, //唯一识别的字段
}, "./output/"+entityName+"/vue/", "{{.EntityName | snakeCase}}_zh_en.ts", false)
if err != nil {
fmt.Printf("生成代码失败: %v\n", err)
}
}