作者 yangfu

feat: table preview

@@ -69,10 +69,12 @@ @@ -69,10 +69,12 @@
69 "data": { 69 "data": {
70 "dataFields": [ 70 "dataFields": [
71 { 71 {
  72 + "index": 1,
72 "name": "产品名称", 73 "name": "产品名称",
73 "type": "string" 74 "type": "string"
74 }, 75 },
75 { 76 {
  77 + "index": 2,
76 "name": "产品数量", 78 "name": "产品数量",
77 "type": "int" 79 "type": "int"
78 } 80 }
@@ -118,7 +120,8 @@ @@ -118,7 +120,8 @@
118 - [x] 匹配方案主表 /mapping-rule-config/prepare //主表 校验表 主表字段 校验文件表字段 120 - [x] 匹配方案主表 /mapping-rule-config/prepare //主表 校验表 主表字段 校验文件表字段
119 - [x] 匹配方案添加 /mapping-rule-config/ 121 - [x] 匹配方案添加 /mapping-rule-config/
120 - [x] 匹配方案删除 /mapping-rule-config/:id 122 - [x] 匹配方案删除 /mapping-rule-config/:id
121 -- [ ] 追加数据到表格 /append-data-to-table // 验证是否追加过 123 +- [x] 追加数据到表格 /append-data-to-table // 验证是否追加过
  124 +- [ ] 取消校验中的文件 /cancel-verifying-file //
122 125
123 - [x] 表结构更新 /tables/update-table-struct 126 - [x] 表结构更新 /tables/update-table-struct
124 - [x] 表结构添加 /tables/add-table-struct 127 - [x] 表结构添加 /tables/add-table-struct
@@ -141,3 +144,25 @@ @@ -141,3 +144,25 @@
141 ## 数据验证 144 ## 数据验证
142 145
143 - [ ] 文件验证 /data/edit-data-table 146 - [ ] 文件验证 /data/edit-data-table
  147 +
  148 +## 底层字库接口
  149 +
  150 +- [ ] 表格编辑
  151 +
  152 +```json
  153 +{
  154 + "file": {},
  155 + "fields": [],
  156 + "action":"filed_rename",
  157 + "params": ["产品名2"]
  158 +}
  159 +```
  160 +
  161 +- [ ] 保存校验文件 (文件地址)
  162 +- [ ] 生成主表
  163 +- [ ] 表复制
  164 +- [ ] 追加数据
  165 +- [ ] 表删除 (主表、副表、分表)
  166 +- [ ] 表拆分
  167 +- [ ] 更新表结构(分表)
  168 +- [ ] 编辑、添加、删除表数据(副表)
@@ -3,36 +3,52 @@ module gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion @@ -3,36 +3,52 @@ module gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion
3 go 1.16 3 go 1.16
4 4
5 require ( 5 require (
  6 + github.com/Shopify/sarama v1.30.0 // indirect
6 github.com/ajg/form v1.5.1 // indirect 7 github.com/ajg/form v1.5.1 // indirect
  8 + github.com/aswjh/excel v0.0.0-20190302031512-353c59e41f09
7 github.com/beego/beego/v2 v2.0.1 9 github.com/beego/beego/v2 v2.0.1
8 github.com/bwmarrin/snowflake v0.3.0 10 github.com/bwmarrin/snowflake v0.3.0
  11 + github.com/extrame/xls v0.0.1
9 github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect 12 github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
10 github.com/fatih/structs v1.1.0 // indirect 13 github.com/fatih/structs v1.1.0 // indirect
11 github.com/gavv/httpexpect v2.0.0+incompatible 14 github.com/gavv/httpexpect v2.0.0+incompatible
12 - github.com/go-pg/pg/v10 v10.9.0 15 + github.com/go-ole/go-ole v1.2.4 // indirect
  16 + github.com/go-pg/pg/v10 v10.10.6
13 github.com/go-redis/redis v6.15.9+incompatible 17 github.com/go-redis/redis v6.15.9+incompatible
14 - github.com/golang/snappy v0.0.3 // indirect  
15 - github.com/google/go-cmp v0.5.6 // indirect 18 + github.com/google/go-cmp v0.5.7 // indirect
16 github.com/google/go-querystring v1.1.0 // indirect 19 github.com/google/go-querystring v1.1.0 // indirect
17 github.com/google/uuid v1.3.0 20 github.com/google/uuid v1.3.0
18 github.com/imkira/go-interpol v1.1.0 // indirect 21 github.com/imkira/go-interpol v1.1.0 // indirect
19 github.com/linmadan/egglib-go v0.0.0-20210313060205-8b5e456b11f7 22 github.com/linmadan/egglib-go v0.0.0-20210313060205-8b5e456b11f7
  23 + github.com/mattn/go-colorable v0.1.9 // indirect
  24 + github.com/mattn/go-isatty v0.0.14 // indirect
  25 + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
20 github.com/moul/http2curl v1.0.0 // indirect 26 github.com/moul/http2curl v1.0.0 // indirect
21 - github.com/onsi/ginkgo v1.15.2  
22 - github.com/onsi/gomega v1.11.0 27 + github.com/onsi/ginkgo v1.16.5
  28 + github.com/onsi/gomega v1.18.1
  29 + github.com/prometheus/client_golang v1.12.2 // indirect
23 github.com/sergi/go-diff v1.2.0 // indirect 30 github.com/sergi/go-diff v1.2.0 // indirect
24 - github.com/shopspring/decimal v1.2.0 31 + github.com/shopspring/decimal v1.3.1
25 github.com/smartystreets/goconvey v1.7.2 // indirect 32 github.com/smartystreets/goconvey v1.7.2 // indirect
26 - github.com/stretchr/testify v1.7.0 33 + github.com/stretchr/testify v1.7.1
27 github.com/valyala/fasthttp v1.38.0 // indirect 34 github.com/valyala/fasthttp v1.38.0 // indirect
28 github.com/xeipuuv/gojsonschema v1.2.0 // indirect 35 github.com/xeipuuv/gojsonschema v1.2.0 // indirect
  36 + github.com/xuri/excelize/v2 v2.6.0
29 github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect 37 github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
30 github.com/yudai/gojsondiff v1.0.0 // indirect 38 github.com/yudai/gojsondiff v1.0.0 // indirect
31 github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect 39 github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
32 github.com/yudai/pp v2.0.1+incompatible // indirect 40 github.com/yudai/pp v2.0.1+incompatible // indirect
  41 + go.uber.org/automaxprocs v1.5.1 // indirect
  42 + golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 // indirect
  43 + golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32 // indirect
33 golang.org/x/text v0.3.7 44 golang.org/x/text v0.3.7
  45 + golang.org/x/tools v0.1.5 // indirect
  46 + google.golang.org/protobuf v1.28.0 // indirect
34 gorm.io/driver/mysql v1.3.6 47 gorm.io/driver/mysql v1.3.6
35 gorm.io/gorm v1.23.8 48 gorm.io/gorm v1.23.8
36 ) 49 )
37 50
38 -replace github.com/linmadan/egglib-go v0.0.0-20210313060205-8b5e456b11f7 => github.com/tiptok/egglib-go v0.0.0-20220421085958-9682d0ac42c1 51 +replace (
  52 + github.com/extrame/xls v0.0.1 => github.com/tiptok/xls v1.0.1
  53 + github.com/linmadan/egglib-go v0.0.0-20210313060205-8b5e456b11f7 => github.com/tiptok/egglib-go v0.0.0-20220421085958-9682d0ac42c1
  54 +)
@@ -4,6 +4,7 @@ import ( @@ -4,6 +4,7 @@ import (
4 "github.com/linmadan/egglib-go/core/application" 4 "github.com/linmadan/egglib-go/core/application"
5 "github.com/linmadan/egglib-go/transaction/pg" 5 "github.com/linmadan/egglib-go/transaction/pg"
6 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain" 6 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
  7 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain/bytecore"
7 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/domainService" 8 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/domainService"
8 ) 9 )
9 10
@@ -16,6 +17,10 @@ func CreateLoadDataTableService(transactionContext application.TransactionContex @@ -16,6 +17,10 @@ func CreateLoadDataTableService(transactionContext application.TransactionContex
16 return domainService.NewLoadDataTableService(transactionContext.(*pg.TransactionContext)) 17 return domainService.NewLoadDataTableService(transactionContext.(*pg.TransactionContext))
17 } 18 }
18 19
  20 +func CreateEditDataTableService(transactionContext application.TransactionContext) (domain.EditDataTableService, error) {
  21 + return domainService.NewEditDataTableService(transactionContext.(*pg.TransactionContext))
  22 +}
  23 +
19 func CreateFlushDataTableService(transactionContext application.TransactionContext) (domain.FlushDataTableService, error) { 24 func CreateFlushDataTableService(transactionContext application.TransactionContext) (domain.FlushDataTableService, error) {
20 return domainService.NewFlushDataTableService(transactionContext.(*pg.TransactionContext)) 25 return domainService.NewFlushDataTableService(transactionContext.(*pg.TransactionContext))
21 } 26 }
@@ -43,3 +48,12 @@ func CreateUpdateTableStructService(transactionContext application.TransactionCo @@ -43,3 +48,12 @@ func CreateUpdateTableStructService(transactionContext application.TransactionCo
43 func CreateAddTableStructService(transactionContext application.TransactionContext) (domain.AddTableStructService, error) { 48 func CreateAddTableStructService(transactionContext application.TransactionContext) (domain.AddTableStructService, error) {
44 return domainService.NewAddTableStructService(transactionContext.(*pg.TransactionContext)) 49 return domainService.NewAddTableStructService(transactionContext.(*pg.TransactionContext))
45 } 50 }
  51 +
  52 +func CreateAppendDataToTableService(transactionContext application.TransactionContext) (domain.AppendDataToTableService, error) {
  53 + return domainService.NewAppendDataToTableService(transactionContext.(*pg.TransactionContext))
  54 +}
  55 +
  56 +// 字库核心
  57 +func CreateByteCoreService(transactionContext application.TransactionContext) (bytecore.ByteLibService, error) {
  58 + return domainService.ByteCore, nil
  59 +}
  1 +package command
  2 +
  3 +import (
  4 + "fmt"
  5 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
  6 + "reflect"
  7 + "strings"
  8 +
  9 + "github.com/beego/beego/v2/core/validation"
  10 +)
  11 +
  12 +type AppendDataToTableCommand struct {
  13 + // 文件ID
  14 + FileId int `cname:"文件ID" json:"fileId" valid:"Required"`
  15 + // 文件ID
  16 + TableId int `cname:"表ID" json:"tableId" valid:"Required"`
  17 + // 校验文件列
  18 + MappingFields []*domain.MappingField `json:"mappingFields"`
  19 +}
  20 +
  21 +func (cmd *AppendDataToTableCommand) Valid(validation *validation.Validation) {
  22 +
  23 +}
  24 +
  25 +func (cmd *AppendDataToTableCommand) ValidateCommand() error {
  26 + valid := validation.Validation{}
  27 + b, err := valid.Valid(cmd)
  28 + if err != nil {
  29 + return err
  30 + }
  31 + if !b {
  32 + elem := reflect.TypeOf(cmd).Elem()
  33 + for _, validErr := range valid.Errors {
  34 + field, isExist := elem.FieldByName(validErr.Field)
  35 + if isExist {
  36 + return fmt.Errorf(strings.Replace(validErr.Message, validErr.Field, field.Tag.Get("cname"), -1))
  37 + } else {
  38 + return fmt.Errorf(validErr.Message)
  39 + }
  40 + }
  41 + }
  42 + return nil
  43 +}
@@ -2,6 +2,7 @@ package command @@ -2,6 +2,7 @@ package command
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
  5 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
5 "reflect" 6 "reflect"
6 "strings" 7 "strings"
7 8
@@ -11,6 +12,8 @@ import ( @@ -11,6 +12,8 @@ import (
11 type EditDataTableCommand struct { 12 type EditDataTableCommand struct {
12 // 文件ID 13 // 文件ID
13 FileId int `cname:"文件ID" json:"fileId" valid:"Required"` 14 FileId int `cname:"文件ID" json:"fileId" valid:"Required"`
  15 +
  16 + Fields []*domain.Field
14 } 17 }
15 18
16 func (editDataTableCommand *EditDataTableCommand) Valid(validation *validation.Validation) { 19 func (editDataTableCommand *EditDataTableCommand) Valid(validation *validation.Validation) {
@@ -2,6 +2,7 @@ package command @@ -2,6 +2,7 @@ package command
2 2
3 import ( 3 import (
4 "fmt" 4 "fmt"
  5 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
5 "reflect" 6 "reflect"
6 "strings" 7 "strings"
7 8
@@ -12,13 +13,19 @@ type LoadDataTableCommand struct { @@ -12,13 +13,19 @@ type LoadDataTableCommand struct {
12 // 文件ID 13 // 文件ID
13 FileId int `cname:"文件ID" json:"fileId" valid:"Required"` 14 FileId int `cname:"文件ID" json:"fileId" valid:"Required"`
14 // 页号 15 // 页号
15 - PageNumber int `cname:"页号" json:"pageNumber"` 16 + //PageNumber int `cname:"页号" json:"pageNumber"`
16 // 页号 17 // 页号
17 - PageSize int `cname:"数量" json:"pageSize"` 18 + //PageSize int `cname:"数量" json:"pageSize"`
  19 + domain.Where
18 } 20 }
19 21
20 func (loadDataTableCommand *LoadDataTableCommand) Valid(validation *validation.Validation) { 22 func (loadDataTableCommand *LoadDataTableCommand) Valid(validation *validation.Validation) {
21 - 23 + if loadDataTableCommand.PageNumber == 0 {
  24 + loadDataTableCommand.PageNumber = 1
  25 + }
  26 + if loadDataTableCommand.PageSize == 0 {
  27 + loadDataTableCommand.PageSize = 20
  28 + }
22 } 29 }
23 30
24 func (loadDataTableCommand *LoadDataTableCommand) ValidateCommand() error { 31 func (loadDataTableCommand *LoadDataTableCommand) ValidateCommand() error {
@@ -4,7 +4,6 @@ import ( @@ -4,7 +4,6 @@ import (
4 "github.com/linmadan/egglib-go/core/application" 4 "github.com/linmadan/egglib-go/core/application"
5 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/application/factory" 5 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/application/factory"
6 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/application/file/command" 6 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/application/file/command"
7 - "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/application/file/dto"  
8 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain" 7 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
9 ) 8 )
10 9
@@ -25,7 +24,8 @@ func (fileService *FileService) LoadDataTable(ctx *domain.Context, loadDataTable @@ -25,7 +24,8 @@ func (fileService *FileService) LoadDataTable(ctx *domain.Context, loadDataTable
25 }() 24 }()
26 25
27 loadDataTableService, _ := factory.CreateLoadDataTableService(transactionContext) 26 loadDataTableService, _ := factory.CreateLoadDataTableService(transactionContext)
28 - if _, err := loadDataTableService.Load(ctx, loadDataTableCommand.FileId); err != nil { 27 + data, err := loadDataTableService.Load(ctx, loadDataTableCommand.FileId, loadDataTableCommand.Where)
  28 + if err != nil {
29 return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error()) 29 return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
30 } 30 }
31 31
@@ -33,7 +33,8 @@ func (fileService *FileService) LoadDataTable(ctx *domain.Context, loadDataTable @@ -33,7 +33,8 @@ func (fileService *FileService) LoadDataTable(ctx *domain.Context, loadDataTable
33 return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error()) 33 return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
34 } 34 }
35 35
36 - return dto.NewDataTableDtoDemo(loadDataTableService.GetFileId()), nil 36 + //return dto.NewDataTableDtoDemo(loadDataTableService.GetFileId()), nil
  37 + return data, nil
37 } 38 }
38 39
39 // 编辑表格数据 40 // 编辑表格数据
@@ -51,6 +52,7 @@ func (fileService *FileService) EditDataTable(ctx *domain.Context, editDataTable @@ -51,6 +52,7 @@ func (fileService *FileService) EditDataTable(ctx *domain.Context, editDataTable
51 defer func() { 52 defer func() {
52 transactionContext.RollbackTransaction() 53 transactionContext.RollbackTransaction()
53 }() 54 }()
  55 +
54 if err := transactionContext.CommitTransaction(); err != nil { 56 if err := transactionContext.CommitTransaction(); err != nil {
55 return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error()) 57 return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
56 } 58 }
@@ -118,3 +120,30 @@ func (fileService *FileService) GenerateMainTable(ctx *domain.Context, generateM @@ -118,3 +120,30 @@ func (fileService *FileService) GenerateMainTable(ctx *domain.Context, generateM
118 } 120 }
119 return struct{}{}, nil 121 return struct{}{}, nil
120 } 122 }
  123 +
  124 +// 生成主表
  125 +func (fileService *FileService) AppendDataToTable(ctx *domain.Context, cmd *command.AppendDataToTableCommand) (interface{}, error) {
  126 + if err := cmd.ValidateCommand(); err != nil {
  127 + return nil, application.ThrowError(application.ARG_ERROR, err.Error())
  128 + }
  129 + transactionContext, err := factory.CreateTransactionContext(nil)
  130 + if err != nil {
  131 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  132 + }
  133 + if err := transactionContext.StartTransaction(); err != nil {
  134 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  135 + }
  136 + defer func() {
  137 + transactionContext.RollbackTransaction()
  138 + }()
  139 +
  140 + generateMainTableService, _ := factory.CreateAppendDataToTableService(transactionContext)
  141 + result, err := generateMainTableService.AppendData(ctx, cmd.FileId, cmd.TableId, cmd.MappingFields)
  142 + if err != nil {
  143 + return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
  144 + }
  145 + if err := transactionContext.CommitTransaction(); err != nil {
  146 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  147 + }
  148 + return result, nil
  149 +}
@@ -348,6 +348,31 @@ func (tableService *TableService) AddTableStruct(ctx *domain.Context, cmd *comma @@ -348,6 +348,31 @@ func (tableService *TableService) AddTableStruct(ctx *domain.Context, cmd *comma
348 return struct{}{}, nil 348 return struct{}{}, nil
349 } 349 }
350 350
  351 +func (tableService *TableService) PreviewDataTable(ctx *domain.Context, cmd *command.AddTableStructCommand) (interface{}, error) {
  352 + if err := cmd.ValidateCommand(); err != nil {
  353 + return nil, application.ThrowError(application.ARG_ERROR, err.Error())
  354 + }
  355 + transactionContext, err := factory.CreateTransactionContext(nil)
  356 + if err != nil {
  357 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  358 + }
  359 + if err := transactionContext.StartTransaction(); err != nil {
  360 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  361 + }
  362 + defer func() {
  363 + transactionContext.RollbackTransaction()
  364 + }()
  365 +
  366 + AddTableStructService, _ := factory.CreateAddTableStructService(transactionContext)
  367 + if _, err := AddTableStructService.AddTableStruct(ctx, cmd.TableId, cmd.Fields, cmd.Name); err != nil {
  368 + return nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error())
  369 + }
  370 + if err := transactionContext.CommitTransaction(); err != nil {
  371 + return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
  372 + }
  373 + return struct{}{}, nil
  374 +}
  375 +
351 func NewTableService(options map[string]interface{}) *TableService { 376 func NewTableService(options map[string]interface{}) *TableService {
352 newTableService := &TableService{} 377 newTableService := &TableService{}
353 return newTableService 378 return newTableService
  1 +package bytecore
  2 +
  3 +import "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
  4 +
  5 +type ByteLibService interface {
  6 + LoadDataTable(param ReqLoadDataTable) (*DataLoadDataTable, error)
  7 + EditTable(param ReqEditDataTable) (*DataEditDataTable, error)
  8 +}
  9 +
  10 +type (
  11 + ReqLoadDataTable struct {
  12 + FileId int
  13 + FileName string
  14 + Url string
  15 + Ext string
  16 + domain.Where
  17 + }
  18 +
  19 + DataLoadDataTable struct {
  20 + FileId int `json:"fileId"`
  21 + DataFields []*Field `json:"dataFields"`
  22 + DataRows [][]string `json:"dataRows"`
  23 + Total int `json:"total"`
  24 + PageNumber int `json:"pageNumber"`
  25 + InValidCells []InValidCell `json:"inValidCells"`
  26 + }
  27 +
  28 + Field struct {
  29 + // 索引序号
  30 + Index int `json:"index"`
  31 + // 名称
  32 + Name string `json:"name"`
  33 + // 对应数据库类型
  34 + Type string `json:"type"`
  35 + }
  36 +
  37 + InValidCell struct {
  38 + X int `json:"x"`
  39 + Y int `json:"y"`
  40 + Error string `json:"error"`
  41 + }
  42 +)
  43 +
  44 +// https://github.com/go-gota/gota 类似pandas的数据处理包
  45 +// https://github.com/gonum/gonum
  46 +
  47 +func (table DataLoadDataTable) Filter(where domain.Where) *DataLoadDataTable {
  48 + begin := (where.PageNumber - 1) * where.PageSize
  49 + if begin < 0 {
  50 + begin = 0
  51 + }
  52 + end := begin + where.PageSize
  53 + data := make([][]string, 0)
  54 + if begin < table.Total {
  55 + if end < table.Total {
  56 + data = table.DataRows[begin:end]
  57 + } else {
  58 + data = table.DataRows[begin:]
  59 + }
  60 + }
  61 + return &DataLoadDataTable{
  62 + FileId: table.FileId,
  63 + DataFields: table.DataFields,
  64 + DataRows: data,
  65 + Total: table.Total,
  66 + PageNumber: where.PageNumber,
  67 + InValidCells: make([]InValidCell, 0),
  68 + }
  69 +}
  70 +
  71 +type (
  72 + ReqEditDataTable struct {
  73 + FileId int
  74 + FileName string
  75 + Url string
  76 + Ext string
  77 + Fields []*Field
  78 + Action string
  79 + Params []interface{}
  80 + }
  81 +
  82 + DataEditDataTable struct {
  83 + DataLoadDataTable
  84 + }
  85 +)
@@ -5,3 +5,8 @@ type DataTable struct { @@ -5,3 +5,8 @@ type DataTable struct {
5 Data [][]string 5 Data [][]string
6 Total int 6 Total int
7 } 7 }
  8 +
  9 +type Where struct {
  10 + PageNumber int `json:"pageNumber"`
  11 + PageSize int `json:"pageSize"`
  12 +}
1 package domain 1 package domain
2 2
3 type LoadDataTableService interface { 3 type LoadDataTableService interface {
4 - Load(ctx *Context, fileId int) (interface{}, error) 4 + Load(ctx *Context, fileId int, where Where) (interface{}, error)
5 GetFileId() int 5 GetFileId() int
6 } 6 }
7 7
  8 +type EditDataTableService interface {
  9 +}
  10 +
8 type FlushDataTableService interface { 11 type FlushDataTableService interface {
9 Flush(ctx *Context, fileId int, table *Table) (interface{}, error) 12 Flush(ctx *Context, fileId int, table *Table) (interface{}, error)
10 } 13 }
@@ -33,3 +36,7 @@ type UpdateTableStructService interface { @@ -33,3 +36,7 @@ type UpdateTableStructService interface {
33 type AddTableStructService interface { 36 type AddTableStructService interface {
34 AddTableStruct(ctx *Context, parentTableId int, fields []*Field, name string) (interface{}, error) 37 AddTableStruct(ctx *Context, parentTableId int, fields []*Field, name string) (interface{}, error)
35 } 38 }
  39 +
  40 +type AppendDataToTableService interface {
  41 + AppendData(ctx *Context, fileId int, tableId int, mappingFields []*MappingField) (interface{}, error)
  42 +}
  1 +package api
  2 +
  3 +import (
  4 + rawjson "encoding/json"
  5 + "github.com/linmadan/egglib-go/utils/json"
  6 + "time"
  7 +
  8 + "github.com/beego/beego/v2/client/httplib"
  9 +)
  10 +
  11 +type MessageCode struct {
  12 + Code int `json:"code"`
  13 + Msg string `json:"msg"`
  14 +}
  15 +
  16 +//Response 统一消息返回格式
  17 +type Response struct {
  18 + MessageCode
  19 + Data rawjson.RawMessage `json:"data"`
  20 +}
  21 +
  22 +type BaseServiceGateway struct {
  23 + ConnectTimeout time.Duration
  24 + ReadWriteTimeout time.Duration
  25 + host string
  26 +}
  27 +
  28 +type Request struct {
  29 + Url string
  30 + Method string
  31 + Param interface{}
  32 +}
  33 +
  34 +func (gateway BaseServiceGateway) CreateRequest(url string, method string) *httplib.BeegoHTTPRequest {
  35 + var request *httplib.BeegoHTTPRequest
  36 + switch method {
  37 + case "get", "GET":
  38 + request = httplib.Get(url)
  39 + case "post", "POST":
  40 + request = httplib.Post(url)
  41 + case "put", "PUT":
  42 + request = httplib.Put(url)
  43 + case "delete", "DELETE":
  44 + request = httplib.Delete(url)
  45 + case "head", "HEADER":
  46 + request = httplib.Head(url)
  47 + default:
  48 + request = httplib.Get(url)
  49 + }
  50 + return request.SetTimeout(gateway.ConnectTimeout, gateway.ReadWriteTimeout)
  51 +}
  52 +
  53 +func (gateway BaseServiceGateway) GetResponseData(result Response, data interface{}) error {
  54 + err := json.Unmarshal(result.Data, data)
  55 + if err != nil {
  56 + return NewErrCodeMsg(JsonUnMarshError, err.Error())
  57 + }
  58 + return nil
  59 +}
  60 +
  61 +func (gateway BaseServiceGateway) FastDoRequest(url, method string, param interface{}, data interface{}) error {
  62 + err := gateway.DoRequest(Request{
  63 + Url: url,
  64 + Method: method,
  65 + Param: param,
  66 + }, &data)
  67 + if err != nil {
  68 + return err
  69 + }
  70 + return nil
  71 +}
  72 +
  73 +func (gateway BaseServiceGateway) DoRequest(requestParam Request, val interface{}) error {
  74 + r := gateway.CreateRequest(requestParam.Url, requestParam.Method)
  75 + req, err := r.JSONBody(requestParam.Param)
  76 + if err != nil {
  77 + return err
  78 + }
  79 + byteResult, err := req.Bytes()
  80 + if err != nil {
  81 + return err
  82 + }
  83 + var result Response
  84 + err = json.Unmarshal(byteResult, &result)
  85 + if err != nil {
  86 + return err
  87 + }
  88 + if result.Code != 0 && len(result.Msg) > 0 {
  89 + return NewErrCodeMsg(result.Code, result.Msg)
  90 + }
  91 + return gateway.GetResponseData(result, val)
  92 +}
  93 +
  94 +func (gateway BaseServiceGateway) Host() string {
  95 + return gateway.host
  96 +}
  97 +
  98 +func NewBaseServiceGateway(host string) BaseServiceGateway {
  99 + return BaseServiceGateway{
  100 + host: host,
  101 + }
  102 +}
  1 +package bytelib
  2 +
  3 +import (
  4 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain/bytecore"
  5 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/api"
  6 + "time"
  7 +)
  8 +
  9 +// ApiByteLib 字库底层接口
  10 +type ApiByteLib struct {
  11 + api.BaseServiceGateway
  12 + baseUrL string
  13 +}
  14 +
  15 +func NewApiByteLib(host string) *ApiByteLib {
  16 + gt := api.NewBaseServiceGateway(host)
  17 + gt.ConnectTimeout = 10 * time.Second
  18 + gt.ReadWriteTimeout = 10 * time.Second
  19 + return &ApiByteLib{
  20 + BaseServiceGateway: gt,
  21 + }
  22 +}
  23 +
  24 +// 加载数据
  25 +
  26 +func (gateway ApiByteLib) LoadDataTable(param bytecore.ReqLoadDataTable) (*bytecore.DataLoadDataTable, error) {
  27 + return nil, nil
  28 +}
  29 +
  30 +// EditTable 编辑表格
  31 +func (gateway ApiByteLib) EditTable(param bytecore.ReqEditDataTable) (*bytecore.DataEditDataTable, error) {
  32 + url := gateway.Host() + "/table/edit"
  33 + method := "post"
  34 + var data bytecore.DataEditDataTable
  35 + err := gateway.FastDoRequest(url, method, param, &data)
  36 + if err != nil {
  37 + return nil, err
  38 + }
  39 + return &data, nil
  40 +}
  41 +
  42 +// 保存校验文件 (文件地址)
  43 +
  44 +// 生成主表
  45 +
  46 +// 表复制
  47 +
  48 +// 追加数据
  49 +
  50 +// 表删除 (主表、副表、分表)
  51 +
  52 +// 表拆分
  53 +
  54 +// 更新表结构(分表)
  55 +
  56 +// 编辑、添加、删除表数据(副表)
  1 +package bytelib
  2 +
  3 +import "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain/bytecore"
  4 +
  5 +type ApiByteLibService interface {
  6 + EditTable(param bytecore.ReqEditDataTable) (*bytecore.DataEditDataTable, error)
  7 +}
  1 +package api
  2 +
  3 +var (
  4 + JsonUnMarshError int = 1000
  5 +)
  6 +
  7 +type ErrCodeMsg struct {
  8 + Code int
  9 + Msg string
  10 +}
  11 +
  12 +func (e ErrCodeMsg) Error() string {
  13 + return e.Msg
  14 +}
  15 +
  16 +func NewErrCodeMsg(code int, msg string) ErrCodeMsg {
  17 + return ErrCodeMsg{
  18 + Code: code,
  19 + Msg: msg,
  20 + }
  21 +}
  1 +package domainService
  2 +
  3 +import (
  4 + "fmt"
  5 + pgTransaction "github.com/linmadan/egglib-go/transaction/pg"
  6 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
  7 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/repository"
  8 +)
  9 +
  10 +type AppendDataToTableService struct {
  11 + transactionContext *pgTransaction.TransactionContext
  12 +}
  13 +
  14 +func (ptr *AppendDataToTableService) AppendData(ctx *domain.Context, fileId int, tableId int, mappingFields []*domain.MappingField) (interface{}, error) {
  15 + fileRepository, _ := repository.NewFileRepository(ptr.transactionContext)
  16 + file, err := fileRepository.FindOne(map[string]interface{}{"fileId": fileId})
  17 + if err != nil {
  18 + return nil, fmt.Errorf("文件不存在")
  19 + }
  20 +
  21 + tableRepository, _ := repository.NewTableRepository(ptr.transactionContext)
  22 + table, err := tableRepository.FindOne(map[string]interface{}{"tableId": tableId})
  23 + if err != nil {
  24 + return nil, fmt.Errorf("表不存在")
  25 + }
  26 +
  27 + excelTable, err := tableRepository.FindOne(map[string]interface{}{"tableId": file.FileInfo.TableId})
  28 + if err != nil {
  29 + return nil, fmt.Errorf("文件未校验")
  30 + }
  31 +
  32 + if !(table.TableType == domain.MainTable.ToString() || table.TableType == domain.SideTable.ToString()) {
  33 + return nil, fmt.Errorf("只能追加数据到主表或者副表")
  34 + }
  35 + var subTables []*domain.Table
  36 + _, subTables, err = tableRepository.Find(map[string]interface{}{"parentId": tableId, "tableTypes": []string{domain.SubTable.ToString()}})
  37 + if err != nil {
  38 + return nil, err
  39 + }
  40 +
  41 + // 日志
  42 + if err = FastLog(ptr.transactionContext, domain.CommonLog, table.TableId, &AppendDataToTableLog{
  43 + LogEntry: domain.NewLogEntry(table.Name, domain.MainTable.ToString(), domain.AppendData, ctx),
  44 + File: file,
  45 + Table: table,
  46 + SubTables: subTables,
  47 + RowCount: excelTable.RowCount,
  48 + }); err != nil {
  49 + return nil, err
  50 + }
  51 +
  52 + // 通知底层进行追加数据
  53 +
  54 + return map[string]interface{}{
  55 + "result": fmt.Sprintf("源数据%v条,成功追加%v条;", excelTable.RowCount, excelTable.RowCount),
  56 + }, nil
  57 +}
  58 +
  59 +func NewAppendDataToTableService(transactionContext *pgTransaction.TransactionContext) (*AppendDataToTableService, error) {
  60 + if transactionContext == nil {
  61 + return nil, fmt.Errorf("transactionContext参数不能为nil")
  62 + } else {
  63 + return &AppendDataToTableService{
  64 + transactionContext: transactionContext,
  65 + }, nil
  66 + }
  67 +}
  1 +package domainService
  2 +
  3 +import (
  4 + "bytes"
  5 + "github.com/beego/beego/v2/client/httplib"
  6 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
  7 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain/bytecore"
  8 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/excel"
  9 +)
  10 +
  11 +type ByteCoreService struct {
  12 + TempDataTable map[int]*bytecore.DataLoadDataTable
  13 +}
  14 +
  15 +var ByteCore = &ByteCoreService{} //bytecore.ByteLibService
  16 +
  17 +func (ptr *ByteCoreService) LoadDataTable(param bytecore.ReqLoadDataTable) (*bytecore.DataLoadDataTable, error) {
  18 + if v, ok := ptr.load(param.FileId); ok {
  19 + return v.Filter(param.Where), nil
  20 + }
  21 + file, err := httplib.Get(param.Url).Bytes()
  22 + if err != nil {
  23 + return nil, err
  24 + }
  25 + reader := bytes.NewReader(file)
  26 + var importer *excel.Importer
  27 + if param.Ext == domain.XLSX {
  28 + importer = excel.NewExcelImport(&excel.XLXSReader{})
  29 + } else if param.Ext == domain.XLS {
  30 + importer = excel.NewExcelImport(&excel.XLSReader{})
  31 + }
  32 + data, err := importer.OpenExcelFromIoReader(reader)
  33 + if err != nil {
  34 + return nil, err
  35 + }
  36 + cols := importer.Reader().Header().Columns
  37 +
  38 + var response = &bytecore.DataLoadDataTable{
  39 + FileId: param.FileId,
  40 + DataFields: columnToField(cols),
  41 + DataRows: data,
  42 + Total: len(data),
  43 + PageNumber: param.PageNumber,
  44 + InValidCells: make([]bytecore.InValidCell, 0),
  45 + }
  46 +
  47 + ptr.save(param.FileId, response)
  48 + return response.Filter(param.Where), nil
  49 +}
  50 +
  51 +func (ptr *ByteCoreService) EditTable(param bytecore.ReqEditDataTable) (*bytecore.DataEditDataTable, error) {
  52 + return nil, nil
  53 +}
  54 +
  55 +func (ptr *ByteCoreService) save(fileId int, dataTable *bytecore.DataLoadDataTable) {
  56 + // Once
  57 + if ptr.TempDataTable == nil {
  58 + ptr.TempDataTable = make(map[int]*bytecore.DataLoadDataTable)
  59 + }
  60 + ptr.TempDataTable[fileId] = dataTable
  61 +}
  62 +
  63 +func (ptr *ByteCoreService) load(fileId int) (*bytecore.DataLoadDataTable, bool) {
  64 + v, ok := ptr.TempDataTable[fileId]
  65 + return v, ok
  66 +}
  67 +
  68 +func columnToField(cols []string) []*bytecore.Field {
  69 + var fields []*bytecore.Field
  70 + for i, col := range cols {
  71 + fields = append(fields, &bytecore.Field{
  72 + Name: col,
  73 + Index: i + 1,
  74 + Type: "string",
  75 + })
  76 + }
  77 + return fields
  78 +}
  79 +
  80 +//////////////
  81 +// 字库核心
  82 +//////////////
  83 +
  84 +func CreateByteCoreService() (*ByteCoreService, error) {
  85 + return ByteCore, nil
  86 +}
  1 +package domainService
  2 +
  3 +import (
  4 + "fmt"
  5 + pgTransaction "github.com/linmadan/egglib-go/transaction/pg"
  6 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
  7 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/repository"
  8 +)
  9 +
  10 +type EditDataTableService struct {
  11 + transactionContext *pgTransaction.TransactionContext
  12 +}
  13 +
  14 +func (ptr *EditDataTableService) Edit(ctx *domain.Context, fileId int, tableId int, mappingFields []*domain.MappingField) (interface{}, error) {
  15 + fileRepository, _ := repository.NewFileRepository(ptr.transactionContext)
  16 + file, err := fileRepository.FindOne(map[string]interface{}{"fileId": fileId})
  17 + if err != nil {
  18 + return nil, fmt.Errorf("文件不存在")
  19 + }
  20 +
  21 + tableRepository, _ := repository.NewTableRepository(ptr.transactionContext)
  22 + table, err := tableRepository.FindOne(map[string]interface{}{"tableId": tableId})
  23 + if err != nil {
  24 + return nil, fmt.Errorf("表不存在")
  25 + }
  26 +
  27 + excelTable, err := tableRepository.FindOne(map[string]interface{}{"tableId": file.FileInfo.TableId})
  28 + if err != nil {
  29 + return nil, fmt.Errorf("文件未校验")
  30 + }
  31 +
  32 + if !(table.TableType == domain.MainTable.ToString() || table.TableType == domain.SideTable.ToString()) {
  33 + return nil, fmt.Errorf("只能追加数据到主表或者副表")
  34 + }
  35 + var subTables []*domain.Table
  36 + _, subTables, err = tableRepository.Find(map[string]interface{}{"parentId": tableId, "tableTypes": []string{domain.SubTable.ToString()}})
  37 + if err != nil {
  38 + return nil, err
  39 + }
  40 +
  41 + // 日志
  42 + if err = FastLog(ptr.transactionContext, domain.CommonLog, table.TableId, &AppendDataToTableLog{
  43 + LogEntry: domain.NewLogEntry(table.Name, domain.MainTable.ToString(), domain.AppendData, ctx),
  44 + File: file,
  45 + Table: table,
  46 + SubTables: subTables,
  47 + RowCount: excelTable.RowCount,
  48 + }); err != nil {
  49 + return nil, err
  50 + }
  51 +
  52 + // 通知底层进行追加数据
  53 +
  54 + return map[string]interface{}{
  55 + "result": fmt.Sprintf("源数据%v条,成功追加%v条;", excelTable.RowCount, excelTable.RowCount),
  56 + }, nil
  57 +}
  58 +
  59 +func NewEditDataTableService(transactionContext *pgTransaction.TransactionContext) (*EditDataTableService, error) {
  60 + if transactionContext == nil {
  61 + return nil, fmt.Errorf("transactionContext参数不能为nil")
  62 + } else {
  63 + return &EditDataTableService{
  64 + transactionContext: transactionContext,
  65 + }, nil
  66 + }
  67 +}
@@ -4,6 +4,7 @@ import ( @@ -4,6 +4,7 @@ import (
4 "fmt" 4 "fmt"
5 pgTransaction "github.com/linmadan/egglib-go/transaction/pg" 5 pgTransaction "github.com/linmadan/egglib-go/transaction/pg"
6 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain" 6 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
  7 + "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain/bytecore"
7 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/repository" 8 "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/repository"
8 ) 9 )
9 10
@@ -12,7 +13,7 @@ type LoadDataTableService struct { @@ -12,7 +13,7 @@ type LoadDataTableService struct {
12 transactionContext *pgTransaction.TransactionContext 13 transactionContext *pgTransaction.TransactionContext
13 } 14 }
14 15
15 -func (ptr *LoadDataTableService) Load(ctx *domain.Context, fileId int) (interface{}, error) { 16 +func (ptr *LoadDataTableService) Load(ctx *domain.Context, fileId int, where domain.Where) (interface{}, error) {
16 fileRepository, _ := repository.NewFileRepository(ptr.transactionContext) 17 fileRepository, _ := repository.NewFileRepository(ptr.transactionContext)
17 file, err := fileRepository.FindOne(map[string]interface{}{"fileId": fileId}) 18 file, err := fileRepository.FindOne(map[string]interface{}{"fileId": fileId})
18 if err != nil { 19 if err != nil {
@@ -30,10 +31,19 @@ func (ptr *LoadDataTableService) Load(ctx *domain.Context, fileId int) (interfac @@ -30,10 +31,19 @@ func (ptr *LoadDataTableService) Load(ctx *domain.Context, fileId int) (interfac
30 ptr.FileId = file.FileId 31 ptr.FileId = file.FileId
31 32
32 // Load Data From Excel(python api) 33 // Load Data From Excel(python api)
  34 + byteCore, _ := CreateByteCoreService()
  35 + response, err := byteCore.LoadDataTable(bytecore.ReqLoadDataTable{
  36 + FileId: file.FileId,
  37 + FileName: file.FileInfo.Name,
  38 + Url: file.FileInfo.Url,
  39 + Ext: file.FileInfo.Ext,
  40 + Where: where,
  41 + })
  42 + if err != nil {
  43 + return nil, err
  44 + }
33 45
34 - return map[string]interface{}{  
35 - "fileId": file.FileId,  
36 - }, nil 46 + return response, nil
37 } 47 }
38 48
39 func (ptr *LoadDataTableService) GetFileId() int { 49 func (ptr *LoadDataTableService) GetFileId() int {
@@ -207,3 +207,24 @@ func (l *DeleteTableLog) Content() string { @@ -207,3 +207,24 @@ func (l *DeleteTableLog) Content() string {
207 } 207 }
208 return msg 208 return msg
209 } 209 }
  210 +
  211 +// 9.数据追加日志
  212 +type AppendDataToTableLog struct {
  213 + domain.LogEntry
  214 + Table *domain.Table
  215 + File *domain.File
  216 + RowCount int
  217 + SubTables []*domain.Table
  218 +}
  219 +
  220 +func (l *AppendDataToTableLog) Content() string {
  221 + msg := fmt.Sprintf("来源文件:%v校验文件,导入成功%v条,目标表单:%v", l.File.FileInfo.Name, l.RowCount, l.Table.Name)
  222 + var tables []string
  223 + for _, t := range l.SubTables {
  224 + tables = append(tables, t.Name+"分表")
  225 + }
  226 + if len(tables) > 0 {
  227 + msg += fmt.Sprintf(",关联更新%s", strings.Join(tables, "/"))
  228 + }
  229 + return msg
  230 +}
  1 +package excel
  2 +
  3 +import (
  4 + "io"
  5 + "path/filepath"
  6 +)
  7 +
  8 +type Importer struct {
  9 + RowBegin int //第几行开始
  10 + ColumnBegin int //第几列开始,
  11 + ColumnEnd int //第几列结束,
  12 + Sheet string //获取的表格
  13 + reader Reader
  14 + FileName string
  15 +}
  16 +
  17 +func (excelImport Importer) OpenExcelFromIoReader(r io.ReadSeeker) ([][]string, error) {
  18 + return excelImport.reader.Read(&excelImport, r)
  19 +}
  20 +
  21 +func (excelImport Importer) Reader() Reader {
  22 + return excelImport.reader
  23 +}
  24 +
  25 +func NewExcelImport(reader Reader) *Importer {
  26 + return &Importer{
  27 + RowBegin: 1,
  28 + ColumnBegin: 1,
  29 + Sheet: "Sheet1",
  30 + reader: reader,
  31 + }
  32 +}
  33 +
  34 +func NewExcelImportByFile(fileName string) *Importer {
  35 + ext := filepath.Ext(fileName)
  36 + var reader Reader
  37 + if ext == ".xls" {
  38 + reader = &XLSReader{}
  39 + } else if ext == ".csv" {
  40 + reader = &CSVReader{}
  41 + } else {
  42 + reader = &XLXSReader{}
  43 + }
  44 + return &Importer{
  45 + RowBegin: 1,
  46 + ColumnBegin: 1,
  47 + Sheet: "Sheet1",
  48 + reader: reader,
  49 + }
  50 +}
  1 +package excel
  2 +
  3 +import (
  4 + "bytes"
  5 + "encoding/csv"
  6 + "github.com/extrame/xls"
  7 + "github.com/xuri/excelize/v2"
  8 + "golang.org/x/text/encoding/simplifiedchinese"
  9 + "golang.org/x/text/transform"
  10 + "io"
  11 + "unicode/utf8"
  12 +)
  13 +
  14 +type Reader interface {
  15 + Read(importer *Importer, r File) ([][]string, error)
  16 + Header() HeaderInfo
  17 +}
  18 +
  19 +var _ Reader = (*XLXSReader)(nil)
  20 +
  21 +type XLXSReader struct {
  22 + headerInfo HeaderInfo
  23 +}
  24 +
  25 +func (reader *XLXSReader) Read(excelImport *Importer, f File) ([][]string, error) {
  26 + excelFile, err := excelize.OpenReader(f)
  27 + if err != nil {
  28 + return nil, err
  29 + }
  30 + sheets := excelFile.GetSheetList()
  31 + if len(sheets) > 0 && sheets[0] != excelImport.Sheet {
  32 + excelImport.Sheet = sheets[0]
  33 + }
  34 + rows, err := excelFile.Rows(excelImport.Sheet)
  35 + if err != nil {
  36 + return nil, err
  37 + }
  38 + var (
  39 + rowDataList = make([][]string, 0) //数据列表
  40 + rowIndex int //行计数
  41 + lenColumn int
  42 + )
  43 + for rows.Next() {
  44 + rowIndex++
  45 + cols, err := rows.Columns()
  46 + if err != nil {
  47 + return nil, err
  48 + }
  49 + if rowIndex < excelImport.RowBegin {
  50 + continue
  51 + }
  52 + if rowIndex == excelImport.RowBegin {
  53 + reader.headerInfo = NewHeaderInfo(cols)
  54 + lenColumn = len(cols)
  55 + continue
  56 + }
  57 + if len(cols) > 0 {
  58 + if len(cols) < lenColumn {
  59 + padding := make([]string, lenColumn-len(cols))
  60 + cols = append(cols, padding...)
  61 + }
  62 + rowDataList = append(rowDataList, cols)
  63 + }
  64 + }
  65 + return rowDataList, nil
  66 +}
  67 +func (reader *XLXSReader) Header() HeaderInfo {
  68 + return reader.headerInfo
  69 +}
  70 +
  71 +var _ Reader = (*XLSReader)(nil)
  72 +
  73 +type XLSReader struct {
  74 + headerInfo HeaderInfo
  75 +}
  76 +
  77 +func (reader *XLSReader) Read(excelImport *Importer, f File) ([][]string, error) {
  78 + wb, err := xls.OpenReader(f, "utf-8")
  79 + if err != nil {
  80 + return nil, err
  81 + }
  82 + sheet := wb.GetSheet(0)
  83 + var (
  84 + rowDataList = make([][]string, 0) //数据列表
  85 + rowIndex int //行计数
  86 + lenColumn int
  87 + )
  88 + for rowIndex <= int(sheet.MaxRow) {
  89 + row := sheet.Row(rowIndex)
  90 + cols := make([]string, 0)
  91 + if row.LastCol() > 0 {
  92 + for j := 0; j < row.LastCol(); j++ {
  93 + col := row.Col(j)
  94 + cols = append(cols, col)
  95 + }
  96 + }
  97 + rowIndex++
  98 + if rowIndex < excelImport.RowBegin {
  99 + continue
  100 + }
  101 + if rowIndex == excelImport.RowBegin {
  102 + reader.headerInfo = NewHeaderInfo(cols)
  103 + lenColumn = len(cols)
  104 + continue
  105 + }
  106 + if len(cols) == lenColumn {
  107 + rowDataList = append(rowDataList, cols)
  108 + }
  109 + }
  110 + return rowDataList, nil
  111 +}
  112 +func (reader *XLSReader) Header() HeaderInfo {
  113 + return reader.headerInfo
  114 +}
  115 +
  116 +var _ Reader = (*CSVReader)(nil)
  117 +
  118 +type CSVReader struct {
  119 + headerInfo HeaderInfo
  120 +}
  121 +
  122 +func (reader *CSVReader) Read(excelImport *Importer, f File) ([][]string, error) {
  123 + utf8Reader, err := reader.PrepareCheck(f)
  124 + if err != nil {
  125 + return nil, err
  126 + }
  127 + csvReader := csv.NewReader(utf8Reader)
  128 + csvReader.FieldsPerRecord = -1
  129 + csvReader.LazyQuotes = true
  130 +
  131 + records, err := csvReader.ReadAll()
  132 + if err != nil {
  133 + return nil, err
  134 + }
  135 + var (
  136 + rowDataList = make([][]string, 0) //数据列表
  137 + rowIndex int //行计数
  138 + lenColumn int
  139 + )
  140 + for i := 0; i < len(records); i++ {
  141 + rowIndex++
  142 + if rowIndex < excelImport.RowBegin {
  143 + continue
  144 + }
  145 + cols := records[i]
  146 + if rowIndex == excelImport.RowBegin {
  147 + reader.headerInfo = NewHeaderInfo(cols)
  148 + lenColumn = len(cols)
  149 + continue
  150 + }
  151 + if len(cols) > 0 {
  152 + if len(cols) != lenColumn {
  153 + padding := make([]string, lenColumn-len(cols))
  154 + cols = append(cols, padding...)
  155 + }
  156 + rowDataList = append(rowDataList, cols)
  157 + }
  158 + }
  159 + return rowDataList, nil
  160 +}
  161 +func (reader *CSVReader) Header() HeaderInfo {
  162 + return reader.headerInfo
  163 +}
  164 +func (reader *CSVReader) PrepareCheck(r io.Reader) (io.Reader, error) {
  165 + return GBKToUtf8(r)
  166 +}
  167 +func GBKToUtf8(readIn io.Reader) (io.Reader, error) {
  168 + var (
  169 + err error
  170 + fileByte []byte
  171 + )
  172 + fileByte, err = io.ReadAll(readIn)
  173 + if err != nil {
  174 + return nil, err
  175 + }
  176 +
  177 + if utf8.Valid(fileByte) {
  178 + return bytes.NewReader(fileByte), nil
  179 + } else {
  180 + utf8Reader := transform.NewReader(bytes.NewReader(fileByte), simplifiedchinese.GBK.NewDecoder())
  181 + return utf8Reader, nil
  182 + }
  183 +}
  1 +package excel
  2 +
  3 +import (
  4 + "bytes"
  5 + "encoding/csv"
  6 + "fmt"
  7 + "github.com/aswjh/excel"
  8 + "github.com/xuri/excelize/v2"
  9 + "io"
  10 + "os"
  11 +)
  12 +
  13 +type XLXSWriterTo struct {
  14 + data [][]string
  15 + title []string
  16 +}
  17 +
  18 +func (wt *XLXSWriterTo) WriteTo(w io.Writer) (n int64, err error) {
  19 + var file *excelize.File
  20 + file, err = wt.newFile()
  21 + if err != nil {
  22 + return 0, nil
  23 + }
  24 + return file.WriteTo(w)
  25 +}
  26 +
  27 +func (wt *XLXSWriterTo) Save(fileName string) error {
  28 + var file *excelize.File
  29 + var err error
  30 + file, err = wt.newFile()
  31 + if err != nil {
  32 + return nil
  33 + }
  34 + return file.SaveAs(fileName)
  35 +}
  36 +
  37 +func (wt *XLXSWriterTo) newFile() (*excelize.File, error) {
  38 + sheet := "Sheet1"
  39 + file := excelize.NewFile()
  40 + streamWriter, err := file.NewStreamWriter(sheet)
  41 + if err != nil {
  42 + return nil, err
  43 + }
  44 +
  45 + if len(wt.title) == 0 {
  46 + return nil, fmt.Errorf("未设置数据表头")
  47 + }
  48 + if err := streamWriter.SetRow("A1", stringsToInterfaces(wt.title)); err != nil {
  49 + return nil, err
  50 + }
  51 + var rowID = 2
  52 + for i := 0; i < len(wt.data); i++ {
  53 + row := stringsToInterfaces(wt.data[i])
  54 + cell, _ := excelize.CoordinatesToCellName(1, rowID)
  55 + if err := streamWriter.SetRow(cell, row); err != nil {
  56 + return nil, err
  57 + }
  58 + rowID += 1
  59 + }
  60 +
  61 + if err := streamWriter.Flush(); err != nil {
  62 + return nil, err
  63 + }
  64 + return file, nil
  65 +}
  66 +
  67 +func stringsToInterfaces(input []string) []interface{} {
  68 + output := make([]interface{}, len(input))
  69 + for i, v := range input {
  70 + output[i] = v
  71 + }
  72 + return output
  73 +}
  74 +
  75 +func NewXLXSWriterTo(title []string, data [][]string) *XLXSWriterTo {
  76 + return &XLXSWriterTo{
  77 + data: data,
  78 + title: title,
  79 + }
  80 +}
  81 +
  82 +type CSVWriterTo struct {
  83 + data [][]string
  84 + title []string
  85 +}
  86 +
  87 +func (xw *CSVWriterTo) WriteTo(w io.Writer) (n int64, err error) {
  88 + var file = bytes.NewBuffer(nil)
  89 + _, err = xw.write(file)
  90 + if err != nil {
  91 + return 0, nil
  92 + }
  93 + return file.WriteTo(w)
  94 +}
  95 +
  96 +func (xw *CSVWriterTo) Save(fileName string) error {
  97 + csvFile, err := os.Create(fileName)
  98 + if err != nil {
  99 + return err
  100 + }
  101 + defer csvFile.Close()
  102 + if _, err := xw.write(csvFile); err != nil {
  103 + return err
  104 + }
  105 + return nil
  106 +}
  107 +
  108 +func (xw *CSVWriterTo) write(w io.Writer) (*csv.Writer, error) {
  109 + _, err := w.Write([]byte("\xEF\xBB\xBF")) //写入UTF-8 BOM
  110 + if err != nil {
  111 + return nil, err
  112 + }
  113 + csvWriter := csv.NewWriter(w)
  114 + if err := csvWriter.Write(xw.title); err != nil {
  115 + return nil, err
  116 + }
  117 + if err := csvWriter.WriteAll(xw.data); err != nil {
  118 + return nil, err
  119 + }
  120 + csvWriter.Flush()
  121 + return csvWriter, csvWriter.Error()
  122 +}
  123 +
  124 +func NewCSVWriterTo(title []string, data [][]string) *CSVWriterTo {
  125 + return &CSVWriterTo{
  126 + data: data,
  127 + title: title,
  128 + }
  129 +}
  130 +
  131 +type XlSWriterTo struct {
  132 + data [][]string
  133 + title []string
  134 +}
  135 +
  136 +func (xw *XlSWriterTo) WriteTo(w io.Writer) (n int64, err error) {
  137 + var file = bytes.NewBuffer(nil)
  138 + err = xw.write(file)
  139 + if err != nil {
  140 + return 0, nil
  141 + }
  142 + return file.WriteTo(w)
  143 +}
  144 +
  145 +func (xw *XlSWriterTo) Save(fileName string) error {
  146 + option := excel.Option{"Visible": true, "DisplayAlerts": true, "ScreenUpdating": true}
  147 + xl, err := excel.New(option) //xl, _ := excel.Open("test_excel.xls", option)
  148 + if err != nil {
  149 +
  150 + }
  151 + defer xl.Quit()
  152 +
  153 + sheet, _ := xl.Sheet(1) //xl.Sheet("sheet1")
  154 + defer sheet.Release()
  155 +
  156 + index := 1
  157 + sheet.PutRange(excelRange(index, xw.title), toInterface(xw.title)...)
  158 + for _, item := range xw.data {
  159 + row := toInterface(item)
  160 + index += 1
  161 + sheet.PutRange(excelRange(index, item), row)
  162 + }
  163 + errs := xl.SaveAs(fileName)
  164 + if len(errs) > 0 {
  165 + return errs[0]
  166 + }
  167 + return nil
  168 +}
  169 +
  170 +func excelRange(index int, data []string) string {
  171 + var begin byte = 'a'
  172 + r := fmt.Sprintf("%c%d:%c%d", begin, index, begin+byte(len(data)), index)
  173 + return r
  174 +}
  175 +
  176 +func toInterface(data []string) []interface{} {
  177 + row := make([]interface{}, len(data))
  178 + for i := range data {
  179 + row[i] = data[i]
  180 + }
  181 + return row
  182 +}
  183 +
  184 +func (xw *XlSWriterTo) write(w io.Writer) error {
  185 +
  186 + return nil
  187 +}
  188 +
  189 +func NewXlSWriterTo(title []string, data [][]string) *XlSWriterTo {
  190 + return &XlSWriterTo{
  191 + data: data,
  192 + title: title,
  193 + }
  194 +}
  1 +package excel
  2 +
  3 +import "io"
  4 +
  5 +type HeaderInfo struct {
  6 + Columns []string
  7 +}
  8 +
  9 +func NewHeaderInfo(cols []string) HeaderInfo {
  10 + return HeaderInfo{
  11 + Columns: cols,
  12 + }
  13 +}
  14 +
  15 +type File interface {
  16 + io.Reader
  17 + io.Seeker
  18 +}
@@ -26,4 +26,8 @@ type QueryOptions struct { @@ -26,4 +26,8 @@ type QueryOptions struct {
26 26
27 type Condition struct { 27 type Condition struct {
28 Field *domain.Field 28 Field *domain.Field
  29 + In []interface{}
  30 + Ex []interface{}
  31 + Range []interface{}
  32 + Order []interface{}
29 } 33 }
@@ -121,3 +121,11 @@ func (controller *FileController) GenerateMainTable() { @@ -121,3 +121,11 @@ func (controller *FileController) GenerateMainTable() {
121 data, err := fileService.GenerateMainTable(ParseContext(controller.BaseController), generateMainTableCommand) 121 data, err := fileService.GenerateMainTable(ParseContext(controller.BaseController), generateMainTableCommand)
122 controller.Response(data, err) 122 controller.Response(data, err)
123 } 123 }
  124 +
  125 +func (controller *FileController) AppendDataToTable() {
  126 + fileService := service.NewFileService(nil)
  127 + cmdd := &command.AppendDataToTableCommand{}
  128 + controller.Unmarshal(cmdd)
  129 + data, err := fileService.AppendDataToTable(ParseContext(controller.BaseController), cmdd)
  130 + controller.Response(data, err)
  131 +}
@@ -93,6 +93,16 @@ func (controller *TableController) Search() { @@ -93,6 +93,16 @@ func (controller *TableController) Search() {
93 controller.Response(data, err) 93 controller.Response(data, err)
94 } 94 }
95 95
  96 +func (controller *TableController) RelationGraph() {
  97 + tableService := service.NewTableService(nil)
  98 + cmd := &query.SearchTableQuery{}
  99 + Must(controller.Unmarshal(cmd))
  100 + cmd.TableTypes = []string{domain.MainTable.ToString(), domain.SideTable.ToString(), domain.SubTable.ToString()}
  101 + cmd.Context = ParseContext(controller.BaseController)
  102 + data, err := tableService.Search(cmd)
  103 + controller.Response(data, err)
  104 +}
  105 +
96 func (controller *TableController) SearchAppendedList() { 106 func (controller *TableController) SearchAppendedList() {
97 tableService := service.NewTableService(nil) 107 tableService := service.NewTableService(nil)
98 cmd := &query.SearchTableQuery{} 108 cmd := &query.SearchTableQuery{}
@@ -19,4 +19,5 @@ func init() { @@ -19,4 +19,5 @@ func init() {
19 web.Router("/data/edit-data-table", &controllers.FileController{}, "Post:EditDataTable") 19 web.Router("/data/edit-data-table", &controllers.FileController{}, "Post:EditDataTable")
20 web.Router("/data/flush-data-table", &controllers.FileController{}, "Post:FlushDataTable") 20 web.Router("/data/flush-data-table", &controllers.FileController{}, "Post:FlushDataTable")
21 web.Router("/data/generate-main-table", &controllers.FileController{}, "Post:GenerateMainTable") 21 web.Router("/data/generate-main-table", &controllers.FileController{}, "Post:GenerateMainTable")
  22 + web.Router("/data/append-data-to-table", &controllers.FileController{}, "Post:AppendDataToTable")
22 } 23 }
@@ -12,6 +12,7 @@ func init() { @@ -12,6 +12,7 @@ func init() {
12 web.Router("/data/tables/:tableId", &controllers.TableController{}, "Delete:RemoveTable") 12 web.Router("/data/tables/:tableId", &controllers.TableController{}, "Delete:RemoveTable")
13 web.Router("/data/tables/", &controllers.TableController{}, "Get:ListTable") 13 web.Router("/data/tables/", &controllers.TableController{}, "Get:ListTable")
14 web.Router("/data/tables/search", &controllers.TableController{}, "Post:Search") 14 web.Router("/data/tables/search", &controllers.TableController{}, "Post:Search")
  15 + web.Router("/data/tables/relation-graph", &controllers.TableController{}, "Post:RelationGraph")
15 web.Router("/data/tables/search-appended-list", &controllers.TableController{}, "Post:SearchAppendedList") 16 web.Router("/data/tables/search-appended-list", &controllers.TableController{}, "Post:SearchAppendedList")
16 web.Router("/data/tables/search-sub-table-list", &controllers.TableController{}, "Post:SearchSubTableList") 17 web.Router("/data/tables/search-sub-table-list", &controllers.TableController{}, "Post:SearchSubTableList")
17 18
@@ -20,4 +21,5 @@ func init() { @@ -20,4 +21,5 @@ func init() {
20 web.Router("/data/tables/copy-data-table", &controllers.TableController{}, "Post:CopyDataTable") 21 web.Router("/data/tables/copy-data-table", &controllers.TableController{}, "Post:CopyDataTable")
21 web.Router("/data/tables/update-table-struct", &controllers.TableController{}, "Post:UpdateTableStruct") 22 web.Router("/data/tables/update-table-struct", &controllers.TableController{}, "Post:UpdateTableStruct")
22 web.Router("/data/tables/add-table-struct", &controllers.TableController{}, "Post:AddTableStruct") 23 web.Router("/data/tables/add-table-struct", &controllers.TableController{}, "Post:AddTableStruct")
  24 + // web.Router("/data/tables/preview", &controllers.TableController{}, "Post:PreviewDataTable")
23 } 25 }