From 54a000c7d3695171ec3950f8ea7cdb2abe157772 Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Wed, 17 May 2023 20:14:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E8=A1=A8=E5=B7=A5=E5=85=B7=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=89=8D=E5=90=8E=E7=AB=AF=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planner/configexport/configexport.go | 52 ++-- planner/configexport/internal/config.go | 295 ++++++++++++++------ planner/configexport/internal/template.xlsx | Bin 9542 -> 10614 bytes 3 files changed, 228 insertions(+), 119 deletions(-) diff --git a/planner/configexport/configexport.go b/planner/configexport/configexport.go index c192f10..e5c5cb2 100644 --- a/planner/configexport/configexport.go +++ b/planner/configexport/configexport.go @@ -1,18 +1,15 @@ -package main +package configxport import ( "fmt" "github.com/kercylan98/minotaur/planner/configexport/internal" "github.com/kercylan98/minotaur/utils/file" - "github.com/kercylan98/minotaur/utils/log" "github.com/kercylan98/minotaur/utils/str" "github.com/tealeg/xlsx" - "go.uber.org/zap" "os" "os/exec" "path/filepath" - "runtime/debug" - "sync" + "strings" ) func New(xlsxPath string) *ConfigExport { @@ -22,7 +19,9 @@ func New(xlsxPath string) *ConfigExport { panic(err) } for i := 0; i < len(xlsxFile.Sheets); i++ { - ce.configs = append(ce.configs, internal.NewConfig(xlsxFile.Sheets[i])) + if config := internal.NewConfig(xlsxFile.Sheets[i]); config != nil { + ce.configs = append(ce.configs, config) + } } return ce } @@ -33,31 +32,14 @@ type ConfigExport struct { } func (slf *ConfigExport) ExportJSON(outputDir string) { - var errors []func() - var wait sync.WaitGroup for _, config := range slf.configs { config := config - go func() { - wait.Add(1) - defer func() { - if err := recover(); err != nil { - errors = append(errors, func() { - log.Error("导出失败", zap.String("名称", slf.xlsxPath), zap.String("Sheet", config.GetName()), zap.Any("err", err)) - fmt.Println(debug.Stack()) - }) - } - }() - if err := file.WriterFile(filepath.Join(outputDir, fmt.Sprintf("%s.json", config.GetName())), config.GetJSON()); err != nil { - panic(err) - } - wait.Done() - }() - } - - wait.Wait() - - for _, f := range errors { - f() + if err := file.WriterFile(filepath.Join(outputDir, fmt.Sprintf("%s.json", config.GetName())), config.GetJSON()); err != nil { + panic(err) + } + if err := file.WriterFile(filepath.Join(outputDir, fmt.Sprintf("%s.client.json", config.GetName())), config.GetJSONC()); err != nil { + panic(err) + } } } @@ -69,13 +51,21 @@ func (slf *ConfigExport) ExportGo(packageName string, outputDir string) { for _, config := range slf.configs { v := config.GetVariable() vars += fmt.Sprintf("var %s %s\nvar _%sReady %s\n", str.FirstUpper(config.GetName()), v, str.FirstUpper(config.GetName()), v) - varsMake += fmt.Sprintf("_%sReady = make(%s)"+` + if config.GetIndexCount() == 0 { + varsMake += fmt.Sprintf("_%sReady = new(%s)"+` + if err := handle("%s.json", &_%sReady); err != nil { + panic(err) + } +`, str.FirstUpper(config.GetName()), strings.TrimPrefix(v, "*"), str.FirstUpper(config.GetName()), str.FirstUpper(config.GetName())) + } else { + varsMake += fmt.Sprintf("_%sReady = make(%s)"+` if err := handle("%s.json", &_%sReady); err != nil { panic(err) } `, str.FirstUpper(config.GetName()), v, str.FirstUpper(config.GetName()), str.FirstUpper(config.GetName())) + } types += fmt.Sprintf("%s\n", config.GetStruct()) - varsReplace += fmt.Sprintf("%s = _%sReady", str.FirstUpper(config.GetName()), str.FirstUpper(config.GetName())) + varsReplace += fmt.Sprintf("%s = _%sReady\n", str.FirstUpper(config.GetName()), str.FirstUpper(config.GetName())) } _ = os.MkdirAll(outputDir, 0666) diff --git a/planner/configexport/internal/config.go b/planner/configexport/internal/config.go index 0096fdd..898df13 100644 --- a/planner/configexport/internal/config.go +++ b/planner/configexport/internal/config.go @@ -14,105 +14,186 @@ func NewConfig(sheet *xlsx.Sheet) *Config { Sheet: sheet, } - var ( - skipField = make(map[int]bool) - describeLine = config.Sheet.Rows[3] - nameLine = config.Sheet.Rows[4] - typeLine = config.Sheet.Rows[5] - exportParamLine = config.Sheet.Rows[6] - ) - // 分析数据 - { - for i := 1; i < len(describeLine.Cells); i++ { - describe := strings.TrimSpace(nameLine.Cells[i].String()) - if strings.HasPrefix(describe, "#") { - skipField[i] = true - continue - } - typ := strings.TrimSpace(typeLine.Cells[i].String()) - if strings.HasPrefix(typ, "#") || len(typ) == 0 { - skipField[i] = true - continue - } - exportParam := strings.TrimSpace(exportParamLine.Cells[i].String()) - if strings.HasPrefix(exportParam, "#") || len(exportParam) == 0 { - skipField[i] = true - continue - } - } - if len(nameLine.Cells)-1-len(skipField) < config.GetIndexCount() { - panic(errors.New("index count must greater or equal to field count")) - } + if len(config.Sheet.Rows) < 2 || len(config.Sheet.Rows[1].Cells) < 2 { + return nil } - config.skipField = skipField - config.describeLine = describeLine - config.nameLine = nameLine - config.typeLine = typeLine - config.exportParamLine = exportParamLine - - // 整理数据 - var ( - dataLine = make([]map[any]any, len(config.Sheet.Rows)) - ) - for i := 1; i < len(config.describeLine.Cells); i++ { - if skipField[i] { - continue - } + if config.GetIndexCount() > 0 && len(config.Sheet.Rows) < 7 { + return nil + } + if config.GetIndexCount() > 0 { var ( - //describe = strings.TrimSpace(describeLine.Cells[i].String()) - name = strings.TrimSpace(nameLine.Cells[i].String()) - //typ = strings.TrimSpace(typeLine.Cells[i].String()) - //exportParam = strings.TrimSpace(exportParamLine.Cells[i].String()) + skipField = make(map[int]bool) + describeLine = config.Sheet.Rows[3] + nameLine = config.Sheet.Rows[4] + typeLine = config.Sheet.Rows[5] + exportParamLine = config.Sheet.Rows[6] ) - - for row := 7; row < len(config.Sheet.Rows); row++ { - //value := slf.Sheet.Rows[row].Cells[i].String() - var value = getValueWithType(typeLine.Cells[i].String(), config.Sheet.Rows[row].Cells[i].String()) - - line := dataLine[row] - if line == nil { - line = map[any]any{} - dataLine[row] = line + // 分析数据 + { + for i := 1; i < len(describeLine.Cells); i++ { + describe := strings.TrimSpace(nameLine.Cells[i].String()) + if strings.HasPrefix(describe, "#") { + skipField[i] = true + continue + } + typ := strings.TrimSpace(typeLine.Cells[i].String()) + if strings.HasPrefix(typ, "#") || len(typ) == 0 { + skipField[i] = true + continue + } + exportParam := strings.TrimSpace(exportParamLine.Cells[i].String()) + if strings.HasPrefix(exportParam, "#") || len(exportParam) == 0 { + skipField[i] = true + continue + } + } + if len(nameLine.Cells)-1-len(skipField) < config.GetIndexCount() { + panic(errors.New("index count must greater or equal to field count")) } - line[name] = value } - } - // 索引 - var dataSource = make(map[any]any) - var data = dataSource - var index = config.GetIndexCount() - var currentIndex = 0 - for row := 7; row < len(config.Sheet.Rows); row++ { - for i, cell := range config.Sheet.Rows[row].Cells { - if i == 0 || skipField[i] { + config.skipField = skipField + config.describeLine = describeLine + config.nameLine = nameLine + config.typeLine = typeLine + config.exportParamLine = exportParamLine + + // 整理数据 + var ( + dataLine = make([]map[any]any, len(config.Sheet.Rows)) + dataLineC = make([]map[any]any, len(config.Sheet.Rows)) + ) + for i := 1; i < len(config.describeLine.Cells); i++ { + if skipField[i] { continue } - var value = getValueWithType(typeLine.Cells[i].String(), cell.String()) - if currentIndex < index { - currentIndex++ - m, exist := data[value] - if !exist { - if currentIndex == index { - data[value] = dataLine[row] - } else { - m = map[any]any{} - data[value] = m + var ( + //describe = strings.TrimSpace(describeLine.Cells[i].String()) + name = strings.TrimSpace(nameLine.Cells[i].String()) + //typ = strings.TrimSpace(typeLine.Cells[i].String()) + exportParam = strings.TrimSpace(exportParamLine.Cells[i].String()) + ) + + for row := 7; row < len(config.Sheet.Rows); row++ { + //value := slf.Sheet.Rows[row].Cells[i].String() + var value = getValueWithType(typeLine.Cells[i].String(), config.Sheet.Rows[row].Cells[i].String()) + + var needC, needS bool + switch strings.ToLower(exportParam) { + case "c": + needC = true + case "s": + needS = true + case "sc", "cs": + needS = true + needC = true + } + + if needC { + line := dataLineC[row] + if line == nil { + line = map[any]any{} + dataLineC[row] = line + } + line[name] = value + } + + if needS { + line := dataLine[row] + if line == nil { + line = map[any]any{} + dataLine[row] = line + } + line[name] = value + } + + } + } + + // 索引 + var dataSource = make(map[any]any) + var data = dataSource + var dataSourceC = make(map[any]any) + var dataC = dataSourceC + var index = config.GetIndexCount() + var currentIndex = 0 + for row := 7; row < len(config.Sheet.Rows); row++ { + for i, cell := range config.Sheet.Rows[row].Cells { + if i == 0 { + if strings.HasPrefix(typeLine.Cells[i].String(), "#") { + break + } + continue + } + if skipField[i] { + continue + } + var value = getValueWithType(typeLine.Cells[i].String(), cell.String()) + + if currentIndex < index { + currentIndex++ + m, exist := data[value] + if !exist { + if currentIndex == index { + data[value] = dataLine[row] + } else { + m = map[any]any{} + data[value] = m + } + } + if currentIndex < index { + data = m.(map[any]any) + } + + m, exist = dataC[value] + if !exist { + if currentIndex == index { + dataC[value] = dataLineC[row] + } else { + m = map[any]any{} + dataC[value] = m + } + } + if currentIndex < index { + dataC = m.(map[any]any) } } - if currentIndex < index { - data = m.(map[any]any) - } + } + data = dataSource + dataC = dataSourceC + currentIndex = 0 + } + config.data = dataSource + config.dataC = dataSourceC + } else { + config.data = map[any]any{} + config.dataC = map[any]any{} + for i := 4; i < len(config.Rows); i++ { + row := config.Sheet.Rows[i] + + desc := row.Cells[0].String() + if strings.HasPrefix(desc, "#") { + continue + } + name := row.Cells[1].String() + typ := row.Cells[2].String() + exportParam := row.Cells[3].String() + value := row.Cells[4].String() + + switch strings.ToLower(exportParam) { + case "c": + config.dataC[name] = getValueWithType(typ, value) + case "s": + config.data[name] = getValueWithType(typ, value) + case "sc", "cs": + config.dataC[name] = getValueWithType(typ, value) + config.data[name] = getValueWithType(typ, value) } } - data = dataSource - currentIndex = 0 } - config.data = dataSource - config.dataLine = dataLine[len(dataLine)-1] return config } @@ -124,7 +205,7 @@ type Config struct { typeLine *xlsx.Row exportParamLine *xlsx.Row data map[any]any - dataLine map[any]any + dataC map[any]any } // GetDisplayName 获取显示名称 @@ -134,7 +215,7 @@ func (slf *Config) GetDisplayName() string { // GetName 获取配置名称 func (slf *Config) GetName() string { - return slf.Sheet.Rows[0].Cells[1].String() + return str.FirstUpper(slf.Sheet.Rows[0].Cells[1].String()) } // GetIndexCount 获取索引数量 @@ -160,8 +241,20 @@ func (slf *Config) GetJSON() []byte { return bytes } +// GetJSONC 获取JSON类型数据 +func (slf *Config) GetJSONC() []byte { + bytes, err := jsonIter.MarshalIndent(slf.dataC, "", " ") + if err != nil { + panic(err) + } + return bytes +} + func (slf *Config) GetVariable() string { var index = slf.GetIndexCount() + if index <= 0 { + return fmt.Sprintf("*_%s", slf.GetName()) + } var mapStr = "map[%s]%s" for i := 1; i < len(slf.typeLine.Cells); i++ { if slf.skipField[i] { @@ -185,15 +278,41 @@ func (slf *Config) GetVariable() string { func (slf *Config) GetStruct() string { var result string + if slf.GetIndexCount() <= 0 { + for i := 4; i < len(slf.Rows); i++ { + row := slf.Sheet.Rows[i] + + desc := row.Cells[0].String() + if strings.HasPrefix(desc, "#") { + continue + } + name := row.Cells[1].String() + typ := row.Cells[2].String() + exportParam := row.Cells[3].String() + switch strings.ToLower(exportParam) { + case "s", "sc", "cs": + result += fmt.Sprintf("%s %s\n", str.FirstUpper(name), slf.GetType(typ)) + case "c": + continue + } + } + + return fmt.Sprintf("type _%s struct{\n%s}", slf.GetName(), result) + } for i := 1; i < len(slf.typeLine.Cells); i++ { if slf.skipField[i] { continue } typ := slf.typeLine.Cells[i].String() name := slf.nameLine.Cells[i].String() - - name = str.FirstUpper(name) - result += fmt.Sprintf("%s %s\n", name, slf.GetType(typ)) + exportParam := slf.exportParamLine.Cells[i].String() + switch strings.ToLower(exportParam) { + case "s", "sc", "cs": + name = str.FirstUpper(name) + result += fmt.Sprintf("%s %s\n", name, slf.GetType(typ)) + case "c": + continue + } } return fmt.Sprintf("type _%s struct{\n%s}", str.FirstUpper(slf.GetName()), result) } diff --git a/planner/configexport/internal/template.xlsx b/planner/configexport/internal/template.xlsx index b21a7a5bb0996b984d537158e440afa0df1b472e..b423244ccf55b5d8c3d84a7a7cda6653960d8476 100644 GIT binary patch delta 6139 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