package main import ( "bytes" "fmt" "os" "path/filepath" "strings" "text/template" "github.com/tealeg/xlsx" ) var XlsxFiles = make(map[string]string) var templates *template.Template type SheetColumnMetaStruct struct { ColIndex int // 列序号 ColName string // 列名称 ColType int // 数据类型 CTString string // proto数据类型 IsArray bool // 是否数组 IsMap bool // 是否map IsIndex bool // 是否建立索引 } type SheetMetaStruct struct { AbsPath string FileName string ProtoName string Cols []*SheetColumnMetaStruct } const ( // xlsx标签 IntType = "(int)" Int64Type = "(int64)" StrType = "(str)" ArrIntType = "(arrint)" ArrInt64Type = "(arrint64)" ArrStrType = "(arrstr)" MapType = "(map)" // xlsx索引类型,带索引 IntTypeIndex = "(int*)" Int64TypeIndex = "(int64*)" StrTypeIndex = "(str*)" // proto类型 IntTypeProto = "int32" Int64TypeProto = "int64" StrTypeProto = "string" MapProto = "map" ) func Init() { var err error wd, err := os.Getwd() if err != nil { return } protoAbsPath := filepath.Join(wd, "..", "..", "protocol") err = os.MkdirAll(protoAbsPath, os.ModePerm) if err != nil { return } xlsxAbsPath := filepath.Join(wd, "..", "..", "xlsx") var fis []os.DirEntry fis, err = os.ReadDir(xlsxAbsPath) if err != nil { return } srvDataAbsPath := filepath.Join(wd, "..", "..", "srvdata") err = os.MkdirAll(srvDataAbsPath, os.ModePerm) if err != nil { return } for _, v := range fis { if !v.IsDir() { if !strings.HasSuffix(v.Name(), ".xlsx") { continue } pfAbs := filepath.Join(xlsxAbsPath, v.Name()) XlsxFiles[v.Name()] = pfAbs } } pattern := filepath.Join(wd, "templ", "*.templ") funcMap := template.FuncMap{ "inc": func(n int) int { n++ return n }, } //fmt.Println("templ目录:", pattern) templates, err = template.Must(template.New("mytempl").Funcs(funcMap).ParseGlob(pattern)).Parse("") } func main() { Init() smsMap := make(map[string]*SheetMetaStruct) for xlsxFileName, xlsxFilePath := range XlsxFiles { xlsxFile, err := xlsx.OpenFile(xlsxFilePath) if err != nil { fmt.Println("excel file open error:", err, " filePath:", xlsxFilePath) continue } for _, sheet := range xlsxFile.Sheets { sms := &SheetMetaStruct{ AbsPath: xlsxFilePath, FileName: xlsxFileName, ProtoName: strings.TrimSuffix(xlsxFileName, ".xlsx"), Cols: make([]*SheetColumnMetaStruct, 0, sheet.MaxCol)} for _, row := range sheet.Rows { for i, cell := range row.Cells { s := cell.String() switch { case strings.HasSuffix(s, IntType): sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, IntType), 1, IntTypeProto, false, false, false}) case strings.HasSuffix(s, StrType): sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, StrType), 2, StrTypeProto, false, false, false}) case strings.HasSuffix(s, ArrIntType): sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, ArrIntType), 3, IntTypeProto, true, false, false}) case strings.HasSuffix(s, Int64Type): sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, Int64Type), 5, Int64TypeProto, false, false, false}) case strings.HasSuffix(s, ArrInt64Type): sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, ArrInt64Type), 6, Int64TypeProto, true, false, false}) case strings.HasSuffix(s, MapType): sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, MapType), 7, MapProto, false, true, false}) case strings.HasSuffix(s, ArrStrType): sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, ArrStrType), 8, StrTypeProto, true, false, false}) case strings.HasSuffix(s, IntTypeIndex): sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, IntTypeIndex), 1, IntTypeProto, false, false, true}) case strings.HasSuffix(s, Int64TypeIndex): sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, Int64TypeIndex), 5, Int64TypeProto, false, false, true}) case strings.HasSuffix(s, StrTypeIndex): sms.Cols = append(sms.Cols, &SheetColumnMetaStruct{i, strings.TrimSuffix(s, StrTypeIndex), 2, StrTypeProto, false, false, true}) } } break //only fetch first row } smsMap[sms.ProtoName] = sms break //only fetch first sheet } } geneProto(smsMap) } func geneProto(xlsxs map[string]*SheetMetaStruct) { if xlsxs == nil { return } geneAgcHelper(xlsxs) //生成go代码 xlsx转二进制文件(.json,.dat) geneDataStructProto(xlsxs) //生成xlsx转protobuf结构文件(pbdata.proto) for _, val := range xlsxs { genGoDataMgr(val) // 生成Go对象文件 } } // geneAgcHelper 生成go代码,读取xlsx数据 func geneAgcHelper(xlsxs map[string]*SheetMetaStruct) { dm := map[string]interface{}{ "data": xlsxs, "opath": filepath.Join("..", "..", "data"), "isMapCol": isMapColumn(xlsxs), } outputHelper := bytes.NewBuffer(nil) err := templates.ExecuteTemplate(outputHelper, "agc", dm) if err != nil { fmt.Println("geneProto ExecuteTemplate error:", err) return } helperGoFile := filepath.Join("..", "xlsx2binary", "agc.go") err = os.WriteFile(helperGoFile, outputHelper.Bytes(), os.ModePerm) if err != nil { fmt.Println("geneProto WriteFile error:", err) return } } // geneDataStructProto 生成xlsx的proto文件 func geneDataStructProto(xlsxs map[string]*SheetMetaStruct) { outputHelper := bytes.NewBuffer(nil) err := templates.ExecuteTemplate(outputHelper, "gpb", xlsxs) if err != nil { fmt.Println("geneDataStructProto ExecuteTemplate error:", err) return } protoFile := "../../protocol/server/pbdata.proto" err = os.WriteFile(protoFile, outputHelper.Bytes(), os.ModePerm) if err != nil { fmt.Println("geneDataStructProto error:", err) } } // genGoDataMgr 生成go代码,读取.dat文件数据 func genGoDataMgr(sms *SheetMetaStruct) { if sms == nil { return } outputHelper := bytes.NewBuffer(nil) err := templates.ExecuteTemplate(outputHelper, "gpb_mgr", sms) if err != nil { fmt.Println("genGoDataMgr ExecuteTemplate error:", err) return } opath := filepath.Join("../../srvdata", strings.ToLower(fmt.Sprintf("%v.go", sms.ProtoName))) err = os.WriteFile(opath, outputHelper.Bytes(), os.ModePerm) if err != nil { fmt.Println("genGoDataMgr WriteFile error:", err) return } } func isMapColumn(xlsxs map[string]*SheetMetaStruct) bool { for _, sheet := range xlsxs { for _, col := range sheet.Cols { if col.IsMap { return true } } } return false }