From 91b2b52fc8229959c18d048c0c33d49da8b7b4ae Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Mon, 17 Jul 2023 14:36:29 +0800 Subject: [PATCH] =?UTF-8?q?other:=20pce.ce=20=E5=8C=85=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E5=86=85=E7=BD=AE=E7=9A=84=20xlsx=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planner/pce/config.go | 7 +- planner/pce/config_xlsx.go | 133 ------------- planner/pce/config_xlsx_test.go | 25 --- planner/pce/cs/xlsx.go | 183 ++++++++++++++++++ .../{template.xlsx => cs/xlsx_template.xlsx} | Bin planner/pce/cs/xlsx_test.go | 63 ++++++ planner/pce/exporter.go | 45 +---- planner/pce/fields.go | 35 ++++ planner/pce/loader.go | 27 ++- 9 files changed, 309 insertions(+), 209 deletions(-) delete mode 100644 planner/pce/config_xlsx.go delete mode 100644 planner/pce/config_xlsx_test.go create mode 100644 planner/pce/cs/xlsx.go rename planner/pce/{template.xlsx => cs/xlsx_template.xlsx} (100%) create mode 100644 planner/pce/cs/xlsx_test.go diff --git a/planner/pce/config.go b/planner/pce/config.go index 95ac5f2..683ddaa 100644 --- a/planner/pce/config.go +++ b/planner/pce/config.go @@ -1,5 +1,8 @@ package pce +// Config 配置解析接口 +// - 用于将配置文件解析为可供分析的数据结构 +// - 可以在 cs 包中找到内置提供的实现及其模板,例如 cs.XlsxIndexConfig type Config interface { // GetConfigName 配置名称 GetConfigName() string @@ -10,7 +13,7 @@ type Config interface { // GetIndexCount 索引数量 GetIndexCount() int // GetFields 获取字段 - GetFields() []dataField + GetFields() []DataField // GetData 获取数据 - GetData() [][]dataInfo + GetData() [][]DataInfo } diff --git a/planner/pce/config_xlsx.go b/planner/pce/config_xlsx.go deleted file mode 100644 index e3745ef..0000000 --- a/planner/pce/config_xlsx.go +++ /dev/null @@ -1,133 +0,0 @@ -package pce - -import ( - "github.com/kercylan98/minotaur/utils/str" - "github.com/tealeg/xlsx" - "strings" -) - -func NewIndexXlsxConfig(sheet *xlsx.Sheet) *XlsxIndexConfig { - config := &XlsxIndexConfig{ - sheet: sheet, - } - return config -} - -// XlsxIndexConfig 内置的 Xlsx 配置 -type XlsxIndexConfig struct { - sheet *xlsx.Sheet -} - -func (slf *XlsxIndexConfig) GetConfigName() string { - return str.FirstUpper(strings.TrimSpace(slf.sheet.Rows[0].Cells[1].String())) -} - -func (slf *XlsxIndexConfig) GetDisplayName() string { - return slf.sheet.Name -} - -func (slf *XlsxIndexConfig) GetDescription() string { - return "暂无描述" -} - -func (slf *XlsxIndexConfig) GetIndexCount() int { - index, err := slf.sheet.Rows[1].Cells[1].Int() - if err != nil { - panic(err) - } - return index -} - -func (slf *XlsxIndexConfig) GetFields() []dataField { - var fields []dataField - for x := 1; x < slf.getWidth(); x++ { - var ( - desc = slf.get(x, 3) - name = slf.get(x, 4) - fieldType = slf.get(x, 5) - exportType = slf.get(x, 6) - ) - if desc == nil || name == nil || fieldType == nil || exportType == nil { - continue - } - if len(desc.String()) == 0 || len(name.String()) == 0 || len(fieldType.String()) == 0 || len(exportType.String()) == 0 { - continue - } - fields = append(fields, dataField{ - Name: name.String(), - Type: fieldType.String(), - ExportType: exportType.String(), - Desc: desc.String(), - }) - } - return fields -} - -func (slf *XlsxIndexConfig) GetData() [][]dataInfo { - var data [][]dataInfo - var fields = slf.GetFields() - for y := 7; y < slf.getHeight(); y++ { - var line []dataInfo - var stop bool - for x := 0; x < slf.getWidth(); x++ { - if prefixCell := slf.get(x, y); prefixCell != nil { - prefix := prefixCell.String() - if strings.HasPrefix(prefix, "#") { - break - } - } - if x == 0 { - continue - } - var isIndex = x-1 < slf.GetIndexCount() - - var value string - if valueCell := slf.get(x, y); valueCell != nil { - value = valueCell.String() - } else if isIndex { - stop = true - break - } - valueCell := slf.get(x, y) - if valueCell == nil { - break - } - if len(fields) > x-1 { - line = append(line, dataInfo{ - dataField: fields[x-1], - Value: value, - }) - } - } - if len(line) > 0 { - data = append(data, line) - } - if stop { - break - } - } - - return data -} - -// getWidth 获取宽度 -func (slf *XlsxIndexConfig) getWidth() int { - return slf.sheet.MaxCol -} - -// getHeight 获取高度 -func (slf *XlsxIndexConfig) getHeight() int { - return slf.sheet.MaxRow -} - -// get 获取单元格 -func (slf *XlsxIndexConfig) get(x, y int) *xlsx.Cell { - if x < 0 || y < 0 || y >= len(slf.sheet.Rows) { - return nil - } - row := slf.sheet.Rows[y] - if x >= len(row.Cells) { - return nil - } - return row.Cells[x] -} diff --git a/planner/pce/config_xlsx_test.go b/planner/pce/config_xlsx_test.go deleted file mode 100644 index 4056426..0000000 --- a/planner/pce/config_xlsx_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package pce_test - -import ( - "github.com/kercylan98/minotaur/planner/pce" - "github.com/tealeg/xlsx" - "testing" -) - -func TestNewXlsxIndexConfig(t *testing.T) { - f, err := xlsx.OpenFile(`D:\sources\minotaur\planner\ce\template.xlsx`) - if err != nil { - panic(err) - } - xlsxConfig := pce.NewIndexXlsxConfig(f.Sheets[1]) - - loader := pce.NewLoader() - loader.BindField( - new(pce.Int), - new(pce.String), - ) - - loader.LoadStruct(xlsxConfig) - loader.LoadData(xlsxConfig) - -} diff --git a/planner/pce/cs/xlsx.go b/planner/pce/cs/xlsx.go new file mode 100644 index 0000000..40b1931 --- /dev/null +++ b/planner/pce/cs/xlsx.go @@ -0,0 +1,183 @@ +package cs + +import ( + "github.com/kercylan98/minotaur/planner/pce" + "github.com/kercylan98/minotaur/utils/str" + "github.com/tealeg/xlsx" + "regexp" + "strings" +) + +func NewXlsx(sheet *xlsx.Sheet) *Xlsx { + config := &Xlsx{ + sheet: sheet, + } + return config +} + +// Xlsx 内置的 Xlsx 配置 +type Xlsx struct { + sheet *xlsx.Sheet +} + +func (slf *Xlsx) GetConfigName() string { + return str.FirstUpper(strings.TrimSpace(slf.sheet.Rows[0].Cells[1].String())) +} + +func (slf *Xlsx) GetDisplayName() string { + return slf.sheet.Name +} + +func (slf *Xlsx) GetDescription() string { + return "暂无描述" +} + +func (slf *Xlsx) GetIndexCount() int { + index, err := slf.sheet.Rows[1].Cells[1].Int() + if err != nil { + panic(err) + } + return index +} + +func (slf *Xlsx) GetFields() []pce.DataField { + var handle = func(desc, name, fieldType, exportType *xlsx.Cell) (pce.DataField, bool) { + var field pce.DataField + if desc == nil || name == nil || fieldType == nil || exportType == nil { + return field, false + } + field = pce.DataField{ + Name: strings.ReplaceAll(strings.ReplaceAll(str.FirstUpper(name.String()), "\r", ""), "\n", ""), + Type: fieldType.String(), + ExportType: exportType.String(), + Desc: strings.ReplaceAll(strings.ReplaceAll(desc.String(), "\r", ""), "\n", ""), + } + if len(field.Name) == 0 || len(field.Type) == 0 || len(field.ExportType) == 0 { + return field, false + } + + if slf.checkFieldInvalid(field) { + return field, false + } + + return field, true + } + var fields []pce.DataField + if slf.GetIndexCount() > 0 { + for x := 1; x < slf.getWidth(); x++ { + if field, match := handle(slf.get(x, 3), slf.get(x, 4), slf.get(x, 5), slf.get(x, 6)); match { + fields = append(fields, field) + } + } + } else { + for y := 4; y < slf.getHeight(); y++ { + if field, match := handle(slf.get(0, y), slf.get(1, y), slf.get(2, y), slf.get(3, y)); match { + fields = append(fields, field) + } + } + } + return fields +} + +func (slf *Xlsx) GetData() [][]pce.DataInfo { + var data [][]pce.DataInfo + var fields = slf.GetFields() + if slf.GetIndexCount() > 0 { + for y := 7; y < slf.getHeight(); y++ { + var line []pce.DataInfo + var stop bool + for x := 0; x < slf.getWidth(); x++ { + if prefixCell := slf.get(x, y); prefixCell != nil { + prefix := prefixCell.String() + if strings.HasPrefix(prefix, "#") { + break + } + } + if x == 0 { + continue + } + var isIndex = x-1 < slf.GetIndexCount() + + var value string + if valueCell := slf.get(x, y); valueCell != nil { + value = valueCell.String() + } else if isIndex { + stop = true + break + } + valueCell := slf.get(x, y) + if valueCell == nil { + break + } + if len(fields) > x-1 { + line = append(line, pce.DataInfo{ + DataField: fields[x-1], + Value: value, + }) + } + } + if len(line) > 0 { + data = append(data, line) + } + if stop { + break + } + } + } else { + var line []pce.DataInfo + for i, field := range slf.GetFields() { + var value string + if valueCell := slf.get(4, 4+i); valueCell != nil { + value = valueCell.String() + } + line = append(line, pce.DataInfo{ + DataField: field, + Value: value, + }) + } + data = append(data, line) + } + return data +} + +// getWidth 获取宽度 +func (slf *Xlsx) getWidth() int { + return slf.sheet.MaxCol +} + +// getHeight 获取高度 +func (slf *Xlsx) getHeight() int { + return slf.sheet.MaxRow +} + +// get 获取单元格 +func (slf *Xlsx) get(x, y int) *xlsx.Cell { + if x < 0 || y < 0 || y >= len(slf.sheet.Rows) { + return nil + } + row := slf.sheet.Rows[y] + if x >= len(row.Cells) { + return nil + } + return row.Cells[x] +} + +func (slf *Xlsx) checkFieldInvalid(field pce.DataField) bool { + switch strings.ToLower(field.ExportType) { + case "s", "c", "sc", "cs": + default: + return true + } + + pattern := "^[a-zA-Z][a-zA-Z0-9]*$" + reg := regexp.MustCompile(pattern) + if !reg.MatchString(field.Name) { + return true + } + + if strings.HasPrefix(field.Name, "#") || strings.HasPrefix(field.Type, "#") { + return true + } + + return false +} diff --git a/planner/pce/template.xlsx b/planner/pce/cs/xlsx_template.xlsx similarity index 100% rename from planner/pce/template.xlsx rename to planner/pce/cs/xlsx_template.xlsx diff --git a/planner/pce/cs/xlsx_test.go b/planner/pce/cs/xlsx_test.go new file mode 100644 index 0000000..5ae90f3 --- /dev/null +++ b/planner/pce/cs/xlsx_test.go @@ -0,0 +1,63 @@ +package cs_test + +import ( + "github.com/kercylan98/minotaur/planner/pce/cs" + . "github.com/smartystreets/goconvey/convey" + "github.com/tealeg/xlsx" + "testing" +) + +func TestNewIndexXlsxConfig(t *testing.T) { + Convey("TestNewIndexXlsxConfig", t, func() { + f, err := xlsx.OpenFile("./xlsx_template.xlsx") + if err != nil { + panic(err) + } + config := cs.NewXlsx(f.Sheets[1]) + So(config, ShouldNotBeNil) + }) +} + +func TestXlsxIndexConfig_GetConfigName(t *testing.T) { + Convey("TestXlsxIndexConfig_GetConfigName", t, func() { + f, err := xlsx.OpenFile("./xlsx_template.xlsx") + if err != nil { + panic(err) + } + config := cs.NewXlsx(f.Sheets[1]) + So(config.GetConfigName(), ShouldEqual, "IndexConfig") + }) +} + +func TestXlsxIndexConfig_GetDisplayName(t *testing.T) { + Convey("TestXlsxIndexConfig_GetDisplayName", t, func() { + f, err := xlsx.OpenFile("./xlsx_template.xlsx") + if err != nil { + panic(err) + } + config := cs.NewXlsx(f.Sheets[1]) + So(config.GetDisplayName(), ShouldEqual, "有索引") + }) +} + +func TestXlsxIndexConfig_GetDescription(t *testing.T) { + Convey("TestXlsxIndexConfig_GetDescription", t, func() { + f, err := xlsx.OpenFile("./xlsx_template.xlsx") + if err != nil { + panic(err) + } + config := cs.NewXlsx(f.Sheets[1]) + So(config.GetDescription(), ShouldEqual, "暂无描述") + }) +} + +func TestXlsxIndexConfig_GetIndexCount(t *testing.T) { + Convey("TestXlsxIndexConfig_GetIndexCount", t, func() { + f, err := xlsx.OpenFile("./xlsx_template.xlsx") + if err != nil { + panic(err) + } + config := cs.NewXlsx(f.Sheets[1]) + So(config.GetIndexCount(), ShouldEqual, 2) + }) +} diff --git a/planner/pce/exporter.go b/planner/pce/exporter.go index bf5c6a9..25b6581 100644 --- a/planner/pce/exporter.go +++ b/planner/pce/exporter.go @@ -1,53 +1,28 @@ package pce -import ( - "github.com/kercylan98/minotaur/utils/file" - "os/exec" - "path/filepath" -) - // NewExporter 创建导出器 -func NewExporter(filePath string) *Exporter { - return &Exporter{ - filePath: filePath, - } +func NewExporter() *Exporter { + return &Exporter{} } // Exporter 导出器 -type Exporter struct { - filePath string -} +type Exporter struct{} // ExportStruct 导出结构 -func (slf *Exporter) ExportStruct(tmpl Tmpl, tmplStruct ...*TmplStruct) error { - filePath, err := filepath.Abs(slf.filePath) - if err != nil { - return err - } +func (slf *Exporter) ExportStruct(tmpl Tmpl, tmplStruct ...*TmplStruct) ([]byte, error) { raw, err := tmpl.Render(tmplStruct) if err != nil { - return err + return nil, err } - if err = file.WriterFile(filePath, []byte(raw)); err != nil { - return err - } - - cmd := exec.Command("gofmt", "-w", filePath) - return cmd.Run() + return []byte(raw), nil } // ExportData 导出数据 -func (slf *Exporter) ExportData(tmpl DataTmpl, data map[any]any) error { - filePath, err := filepath.Abs(slf.filePath) - if err != nil { - return err - } +func (slf *Exporter) ExportData(tmpl DataTmpl, data map[any]any) ([]byte, error) { raw, err := tmpl.Render(data) if err != nil { - return err + return nil, err } - if err = file.WriterFile(filePath, []byte(raw)); err != nil { - return err - } - return nil + + return []byte(raw), nil } diff --git a/planner/pce/fields.go b/planner/pce/fields.go index 83feec4..cb168c6 100644 --- a/planner/pce/fields.go +++ b/planner/pce/fields.go @@ -6,6 +6,41 @@ import ( "strconv" ) +var fields = []Field{ + new(Int), + new(Int8), + new(Int16), + new(Int32), + new(Int64), + new(Uint), + new(Uint8), + new(Uint16), + new(Uint32), + new(Uint64), + new(Float32), + new(Float64), + new(String), + new(Bool), + new(Byte), + new(Rune), + new(Complex64), + new(Complex128), + new(Uintptr), + new(Double), + new(Float), + new(Long), + new(Short), + new(Char), + new(Number), + new(Integer), + new(Boolean), +} + +// GetFields 获取所有内置支持的字段 +func GetFields() []Field { + return fields +} + type Int int func (slf Int) TypeName() string { diff --git a/planner/pce/loader.go b/planner/pce/loader.go index 4c57328..035c1cf 100644 --- a/planner/pce/loader.go +++ b/planner/pce/loader.go @@ -8,10 +8,14 @@ import ( // NewLoader 创建加载器 // - 加载器被用于加载配置表的数据和结构信息 -func NewLoader() *Loader { - return &Loader{ +func NewLoader(fields []Field) *Loader { + loader := &Loader{ fields: make(map[string]Field), } + for _, f := range fields { + loader.fields[f.TypeName()] = f + } + return loader } // Loader 配置加载器 @@ -19,13 +23,6 @@ type Loader struct { fields map[string]Field } -// BindField 绑定字段 -func (slf *Loader) BindField(fields ...Field) { - for _, field := range fields { - slf.fields[field.TypeName()] = field - } -} - // LoadStruct 加载结构 func (slf *Loader) LoadStruct(config Config) *TmplStruct { var tmpl = &TmplStruct{ @@ -77,6 +74,8 @@ func (slf *Loader) LoadData(config Config) map[any]any { } } else if index == indexCount { action[value] = item + } else if indexCount == -1 { + source = item } } action = source @@ -160,14 +159,14 @@ func (slf *Loader) structInterpreter(fieldType, fieldValue string) any { return data } -// dataInfo 配置数据 -type dataInfo struct { - dataField // 字段 +// DataInfo 配置数据 +type DataInfo struct { + DataField // 字段 Value string // 字段值 } -// dataField 配置数据字段 -type dataField struct { +// DataField 配置数据字段 +type DataField struct { Name string // 字段名称 Desc string // 字段描述 Type string // 字段类型