作者 yangfu

chore: refactor

... ... @@ -3,7 +3,7 @@
## 源文件管理
- 文件列表 - list
- 上传 - 上传oss / 创建file
- 上传 - 上传 oss / 创建 file
- 加载 - loadDataTable
- 编辑 - editDataTable
- 持久化 - flushDataTable
... ... @@ -11,7 +11,6 @@
- 删除 - delete
- 操作日志 - log
- editDataTable params 列表
### 加载表格数据 loadDataTable - 查询
... ... @@ -20,15 +19,15 @@
{
"fileId": 1,
"where": [
{
"field": {
"index": 1,
"name": "产品名称"
},
"in": ["a","b"],
"ex": ["c","d"],
"sort": ["a","asc"]
}
{
"field": {
"index": 1,
"name": "产品名称"
},
"in": ["a", "b"],
"ex": ["c", "d"],
"sort": ["a", "asc"]
}
]
}
```
... ... @@ -37,153 +36,413 @@
```json
{
"field": {
"index": 1,
"name": "产品名称"
},
"operation": {
"desc": ["拆分","按字符数"],
"code": "split_by_char_number"
},
"params": []
"field": {
"index": 1,
"name": "产品名称"
},
"operation": {
"desc": ["拆分", "按字符数"],
"code": "split_by_char_number"
},
"params": []
}
```
精简
```json
{
"field": "产品名称",
"desc": ["拆分","按字符数"],
"operationCode": "split_by_char_number",
"params": []
"field": "产品名称",
"desc": ["拆分", "按字符数"],
"operationCode": "split_by_char_number",
"params": []
}
```
`params 列表`
### 数据展示
```json
{
"code": 0,
"data": {
"dataFields": [
{
"index": 1,
"name": "产品名称",
"type": "string"
},
{
"index": 2,
"name": "产品数量",
"type": "int"
}
],
"dataRows": [
[
"素面",
200
],
[
"冻豆腐",
400
],
[
"冻豆腐1",
300
],
[
"冻豆2",
"A"
]
],
"total": 100,
"pageNumber": 1,
"inValidCells": [
{
"x": 1,
"y": 3,
"error": "不是一个有效的数值"
}
]
},
"msg": "ok"
"code": 0,
"data": {
"dataFields": [
{
"index": 1,
"name": "产品名称",
"type": "string"
},
{
"index": 2,
"name": "产品数量",
"type": "int"
}
],
"dataRows": [
["素面", 200],
["冻豆腐", 400],
["冻豆腐1", 300],
["冻豆2", "A"]
],
"total": 100,
"pageNumber": 1,
"inValidCells": [
{
"x": 1,
"y": 3,
"error": "不是一个有效的数值"
}
]
},
"msg": "ok"
}
```
## 表关联关系
- [x] 可追加数据的表列表 /tables/search-appended-list
- [x] 校验文件列表 /files/search-verified-file
- [x] 匹配方案列表 /mapping-rule-config/search
- [x] 匹配方案主表 /mapping-rule-config/prepare //主表 校验表 主表字段 校验文件表字段
- [x] 匹配方案添加 /mapping-rule-config/
- [x] 匹配方案删除 /mapping-rule-config/:id
- [x] 追加数据到表格 /append-data-to-table // 验证是否追加过
- [ ] 取消校验中的文件 /cancel-verifying-file //
- [x] 表结构更新 /tables/update-table-struct
- [x] 表结构添加 /tables/add-table-struct
- [x] 分表列表 /tables/search
- [x] 表复制 /tables/copy-data-table
- [x] 表删除 /tables/:id // 若是删除主表,需级联删除关联的分表,删除内容包括表数据及表结构;? 分表的副表是否要删除
- [x] 表详情 /tables/:id // 表结构
- [x] 表更新 /tables/:id // 表结构、分表才可以编辑
- [x] 日志搜索 /log/search
- [x] 校验步骤日志 /log/verified-step-Log
- [x] 可追加数据的表列表 /tables/search-appended-list
- [x] 校验文件列表 /files/search-verified-file
- [x] 匹配方案列表 /mapping-rule-config/search
- [x] 匹配方案主表 /mapping-rule-config/prepare //主表 校验表 主表字段 校验文件表字段
- [x] 匹配方案添加 /mapping-rule-config/
- [x] 匹配方案删除 /mapping-rule-config/:id
- [x] 追加数据到表格 /append-data-to-table // 验证是否追加过
- [ ] 取消校验中的文件 /cancel-verifying-file //
- [x] 表结构更新 /tables/update-table-struct
- [x] 表结构添加 /tables/add-table-struct
- [x] 分表列表 /tables/search
- [x] 表复制 /tables/copy-data-table
- [x] 表删除 /tables/:id // 若是删除主表,需级联删除关联的分表,删除内容包括表数据及表结构;? 分表的副表是否要删除
- [x] 表详情 /tables/:id // 表结构
- [x] 表更新 /tables/:id // 表结构、分表才可以编辑
- [x] 日志搜索 /log/search
- [x] 校验步骤日志 /log/verified-step-Log
## 数据预览
- [x] 表数据预览(格式) /table/preview
- [x] 表数据自定义查询 /table/preview where conditions 升序、降序 包含、不包含
- [x] 表数据字段可选值搜索 /table/field-optional 文本匹配
- [x] 表数据预览(格式) /table/preview
- [x] 表数据自定义查询 /table/preview where conditions 升序、降序 包含、不包含
- [x] 表数据字段可选值搜索 /table/field-optional 文本匹配
- [x] 表数据更新、添加、删除 /table/row-data-mutation
- [x] 表数据导出 /table/export-table
- [x] 表数据导出 /table/export-table
## 数据验证
- [x] 文件验证 /data/edit-data-table
- [x] 文件验证 /data/edit-data-table
## 底层字库接口
```json
{
"file": {},
"fields": [],
"action":"filed_rename",
"params": ["产品名2"]
}
```
- [x] 数据预览 1
- [ ] 表格编辑 1
- [ ] 保存校验文件 (文件地址) 1
- [x] 表格编辑 1
- [x] 保存校验文件 (文件地址) 1
- [x] 生成主表 1
- [x] 表复制 (副表)1
- [x] 追加数据 (主表、副表)
- [ ] 表删除 (主表、副表)~~、分表~~
- [x] 表拆分 1
- [ ] 更新表结构(分表)1
- [ ] 编辑、添加、删除表数据(副表) 1
- [x] 更新表结构(分表)1
- [x] 编辑、添加、删除表数据(副表) 1
- [ ] 取消校验
## 定时作业
- [x] 隔天清理校验中的文件
- [x] 隔天清理public临时文件
- [x] 隔天清理 public 临时文件
## 表数据导出
- [ ] 加锁,只允许当前用户同时只能发起一次导出命令 ,3min过期
- [ ] 加锁,只允许当前用户同时只能发起一次导出命令 ,3min 过期
- [ ] 单次拉取数量 MR
- [ ] 100W ..
- [ ] 50W 120s 读取数据库:30s 保存文件:10s 下载:30M/500K=60S;RAR压缩 24M/500k=50S
- [ ] 20W ..
- [ ] 10W ..
- [ ] 保存单个文件、压缩 | 保存多个文件、压缩
- [ ] 100W ..
- [ ] 50W 120s 读取数据库:30s 保存文件:10s 下载:30M/500K=60S;RAR 压缩 24M/500k=50S
- [ ] 20W ..
- [ ] 10W ..
- [ ] 保存单个文件、压缩 | 保存多个文件、压缩
## 讨论事项
- [ ] 校验动作,参数模型讨论
- [ ] 校验日志错误(标红)
- [ ] 校验完毕应答实体,类型修改即使错误,也要返回修改完毕的表
\ No newline at end of file
- [ ] 校验完毕应答实体,类型修改即使错误,也要返回修改完毕的表
## 参数说明
### 通用格式
```json
{
"objectId": 1,
"processFields": [],
"action": "xx",
"params": {}
}
```
processFields:操作字段
### 常规
1. 删除列
```json
{
"action": "remove-column",
"params": {}
}
```
2. 复制列
```json
{
"action": "copy-column",
"params": {}
}
```
3. 重命名
```json
{
"action": "rename-column",
"params": {
"newColumnName": "新的列名称"
}
}
```
4. 替换值
```json
{
"action": "replace-column",
"params": {
"replaceMethod": "replace",
"searchValue": "搜索值",
"replaceValue": "替换值"
}
}
```
参数说明
```
replaceMethod: 替换方法(1.replace:替换值 2.add-prefix:添加前缀 3.add-postfix:添加后缀 4.remove-prefix:去除前缀 5.remove-postfix:去除后缀 6.remove-chars:去除固定字符 7.clean:清除)
searchValue: 搜索值-replace,remove-prefix,remove-postfix,remove-chars参数
replaceValue: 替换值-replace,add-prefix,add-postfix参数
```
### 格式 formatMethod
1. 大写
```json
{
"action": "format-column",
"params": {
"formatMethod": "upper"
}
}
```
参数说明
```
formatMethod: 格式化方法(1.upper:大写2.lower:小写3.capitalize:首字母大写4.strip:修整)
```
2. 小写
```json
{
"action": "format-column",
"params": {
"formatMethod": "lower"
}
}
```
3. 首字母大写
```json
{
"action": "format-column",
"params": {
"formatMethod": "capitalize"
}
}
```
4. 清除
```json
{
"action": "replace-column",
"params": {
"replaceMethod": "clean"
}
}
```
5. 修整
```json
{
"action": "format-column",
"params": {
"formatMethod": "strip"
}
}
```
6. 添加前缀
```json
{
"action": "replace-column",
"params": {
"replaceMethod": "add-prefix",
"replaceValue": "前缀值"
}
}
```
7. 添加后缀
```json
{
"action": "replace-column",
"params": {
"replaceMethod": "add-postfix",
"replaceValue": "后缀值"
}
}
```
8. 去除前缀
```json
{
"action": "replace-column",
"params": {
"replaceMethod": "remove-prefix",
"searchValue": "前缀值"
}
}
```
9. 去除后最
```json
{
"action": "replace-column",
"params": {
"replaceMethod": "remove-postfix",
"searchValue": "后缀值"
}
}
```
10. 去除固定字符
```json
{
"action": "replace-column",
"params": {
"replaceMethod": "remove-chars",
"searchValue": "字符"
}
}
```
### 拆分 split-column
1. 按分隔符
```json
{
"action": "split-column",
"params": {
"splitMethod": "separator",
"separator": "|",
"splitDirection": "left",
"splitCount": "1"
}
}
```
参数说明
```
separator: 分割符号 ‘|’
splitDirection: 拆分方向(1.left:从左边 2.right:从右边)-separator专属参数
splitCount: 拆分次数-separator专属参数
```
2. 按字符数
```json
{
"action": "split-column",
"params": {
"splitMethod": "char-length",
"charLength": 10
}
}
```
参数说明
```
charLength: 字符长度-char-length专属参数
```
### 提取 extract-column
1. 按日期
```json
{
"action": "extract-column",
"params": {
"extractMethod": "by-date"
}
}
```
参数说明
```
extractMethod: 提取方法(1.by-date:按日期 2.by-number:按数值)
```
2. 按数值 action
```json
{
"action": "extract-column",
"params": {
"extractMethod": "by-number"
}
}
```
### 修改字段类型
```json
{
"action": "convert-column-type",
"params": {
"convertType": "STRING"
}
}
```
参数说明
```
convertType:转换类型 STRING 数值: INT 小数: FLOAT 日期: DATE 时间: DATETIME
```
... ...
... ... @@ -96,7 +96,7 @@ spec:
- name: ERROR_BASE_CODE_MULTIPLE
value: "2000"
- name: ENABLE_KAFKA_LOG
value: "true"
value: "false"
- name: HTTP_PORT
value: "8082"
- name: SERVICE_ENV
... ...
... ... @@ -5,14 +5,12 @@ import (
"github.com/beego/beego/v2/server/web"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/application/crontab"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/constant"
_ "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/constant"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/pg"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/redis"
_ "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/redis"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/starrocks"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/log"
"time"
_ "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/constant"
_ "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/redis"
_ "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/log"
_ "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/port/beego"
)
... ... @@ -37,7 +35,6 @@ func main() {
cron.StartCrontabTask()
defer cron.StopCrontabTask()
time.Sleep(time.Second)
log.Logger.Info("Service:" + constant.SERVICE_NAME)
log.Logger.Info("Version:" + Version)
log.Logger.Info("server start!")
... ...
... ... @@ -18,7 +18,7 @@ type EditDataTableCommand struct {
}
func (editDataTableCommand *EditDataTableCommand) Valid(validation *validation.Validation) {
if len(editDataTableCommand.ProcessFields) == 0 {
if len(editDataTableCommand.ProcessFieldNames) == 0 {
validation.Error("未选择操作列")
return
}
... ...
... ... @@ -10,7 +10,7 @@ import (
type FlushDataTableCommand struct {
// 文件ID
FileId int `cname:"文件ID" json:"objectId" valid:"Required"`
ObjectId int `cname:"文件ID" json:"objectId" valid:"Required"`
// 记录数
//RowCount int `cname:"记录数" json:"rowCount" valid:"Required"`
// 数据列
... ...
package dto
import "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
type EditDataTableDto struct {
Code int `json:"errNo,omitempty"`
Error string `json:"errMsg,omitempty"`
InValidCells []domain.InValidCell `json:"inValidCells,omitempty"`
}
func (d *EditDataTableDto) Load(m *domain.DataEditDataTable) *EditDataTableDto {
d.Error = ""
if len(m.InValidCells) > 0 {
d.Error = "类型转换错误"
d.Code = 1001 // 1001:类型转换错误
}
//d.InValidCells = m.InValidCells
return d
}
... ...
... ... @@ -92,15 +92,16 @@ func (fileService *FileService) EditDataTable(ctx *domain.Context, editDataTable
return nil, factory.FastError(err)
}
editDataTableCommand.Fields = temporaryFile.Fields
editDataTableCommand.ProcessFields = temporaryFile.MatchFields(editDataTableCommand.ProcessFieldNames)
editDataTableService, _ := factory.CreateEditDataTableService(transactionContext)
_, err = editDataTableService.Edit(ctx, editDataTableCommand.EditTableRequest)
response, err := editDataTableService.Edit(ctx, editDataTableCommand.EditTableRequest)
if err != nil {
return nil, factory.FastError(err)
}
if err := transactionContext.CommitTransaction(); err != nil {
return nil, application.ThrowError(application.TRANSACTION_ERROR, err.Error())
}
return struct{}{}, nil
return (&dto.EditDataTableDto{}).Load(response), nil
}
// FlushDataTable 持久化表格数据
... ... @@ -128,12 +129,12 @@ func (fileService *FileService) FlushDataTable(ctx *domain.Context, flushDataTab
//}
cache := redis.NewFileCacheService()
temporaryFile, err := cache.Get(redis.KeyTemporaryFileInfo(flushDataTableCommand.FileId))
temporaryFile, err := cache.Get(redis.KeyTemporaryFileInfo(flushDataTableCommand.ObjectId))
if err != nil {
return nil, factory.FastError(err)
}
if _, err := flushDataTableService.Flush(ctx, flushDataTableCommand.FileId, &domain.Table{
if _, err := flushDataTableService.Flush(ctx, flushDataTableCommand.ObjectId, &domain.Table{
DataFields: temporaryFile.Fields,
RowCount: temporaryFile.Total,
}); err != nil {
... ...
... ... @@ -47,7 +47,7 @@ func (cmd *SearchLogCommand) Valid(validation *validation.Validation) {
}
if cmd.Year > 0 && cmd.Month > 0 && cmd.Day > 0 {
cmd.BeginTime = time.Date(cmd.Year, time.Month(cmd.Month), cmd.Day, 0, 0, 0, 0, time.Local)
cmd.EndTime = cmd.BeginTime.AddDate(0, 0, cmd.Day)
cmd.EndTime = cmd.BeginTime.AddDate(0, 0, 1)
}
}
... ...
... ... @@ -150,6 +150,7 @@ type (
type (
ReqCopyTable struct {
MainTable *Table
Table *Table
CopyToTable *Table
}
... ...
... ... @@ -14,6 +14,9 @@ type Context struct {
}
func (c *Context) WithValue(key string, value interface{}) *Context {
if c.data == nil {
c.data = make(map[string]interface{})
}
if _, ok := c.data[key]; ok {
return c
}
... ... @@ -22,6 +25,9 @@ func (c *Context) WithValue(key string, value interface{}) *Context {
}
func (c *Context) GetValue(key string) (interface{}, bool) {
if c.data == nil {
return nil, false
}
if v, ok := c.data[key]; ok {
return v, true
}
... ...
... ... @@ -24,7 +24,7 @@ type PreviewDataTableService interface {
}
type EditDataTableService interface {
Edit(ctx *Context, param EditTableRequest) (interface{}, error)
Edit(ctx *Context, param EditTableRequest) (*DataEditDataTable, error)
}
type FlushDataTableService interface {
... ... @@ -63,12 +63,13 @@ type AddTableStructService interface {
}
type EditTableRequest struct {
FileId int `json:"objectId"`
Fields []*Field `json:"fields"`
ProcessFields []*Field `json:"processFields"`
Where Where `json:"where"`
Action string `json:"action"`
Params map[string]interface{} `json:"params"`
FileId int `json:"objectId"`
Fields []*Field `json:"fields"`
ProcessFieldNames []string `json:"processFields"`
ProcessFields []*Field `json:"-" `
Where Where `json:"where"`
Action string `json:"action"`
Params map[string]interface{} `json:"params"`
}
type TableEditDataService interface {
... ...
... ... @@ -77,6 +77,7 @@ var (
var (
String SQLType = "STRING"
Int SQLType = "INT"
BigInt SQLType = "BIGINT"
Float SQLType = "FLOAT"
Date SQLType = "DATE"
Datetime SQLType = "DATETIME"
... ... @@ -226,3 +227,98 @@ func (t LogLevel) ToString() string {
const (
DefaultPkField = "id"
)
type Action struct {
// 操作名
Desc string `json:"desc"`
Name string `json:"name"`
}
const (
RemoveColumn = "remove-column"
CopyColumn = "copy-column"
RenameColumn = "rename-column"
ReplaceColumn = "replace-column"
ConvertColumnType = "convert-column-type"
FormatColumn = "format-column"
SplitColumn = "split-column"
ExtractColumn = "extract-column"
)
var MapActionCommon = map[string]Action{
// 常规
RemoveColumn: {
Desc: "删除列", Name: RemoveColumn,
},
CopyColumn: {
Desc: "复制列", Name: CopyColumn,
},
RenameColumn: {
Desc: "重命名", Name: RenameColumn,
},
ConvertColumnType: {
Desc: "数据类型更改", Name: ConvertColumnType,
},
}
var MapActionFormat = map[string]Action{
// 格式
"upper": {
Desc: "大写", Name: "upper",
},
"lower": {
Desc: "小写", Name: "lower",
},
"capitalize": {
Desc: "首字母大写", Name: "capitalize",
},
"f4": {
Desc: "删除列", Name: "cc",
},
"strip": {
Desc: "休整", Name: "strip",
},
}
var MapActionReplaceColumn = map[string]Action{
"add-prefix": {
Desc: "添加前缀", Name: "add-prefix",
},
"add-postfix": {
Desc: "添加后缀", Name: "add-postfix",
},
"remove-prefix": {
Desc: "去除前缀", Name: "remove-prefix",
},
"remove-postfix": {
Desc: "去除后缀", Name: "remove-postfix",
},
"remove-chars": {
Desc: "去除固定字符", Name: "remove-chars",
},
"replace": {
Desc: "替换值", Name: "replace",
},
"clean": {
Desc: "清除", Name: "clean",
},
}
var MapActionSplitColumn = map[string]Action{
"separator": {
Desc: "按按分隔符拆分", Name: "separator",
},
"char-length": {
Desc: "按字符数拆分", Name: "char-length",
},
}
var MapActionExtractColumn = map[string]Action{
"by-date": {
Desc: "按日期提取", Name: "by-date",
},
"by-number": {
Desc: "按数值提取", Name: "by-number",
},
}
... ...
... ... @@ -35,6 +35,16 @@ func (f *Field) Valid() error {
return nil
}
func (f *Field) Copy() *Field {
return &Field{
Index: f.Index,
Name: f.Name,
SQLName: f.SQLName,
SQLType: f.SQLType,
Flag: f.Flag,
}
}
type Fields []*Field
func (fields Fields) ToMap() map[string]*Field {
... ... @@ -163,17 +173,26 @@ func ValueToType(value string, sqlType string) (interface{}, error) {
if err != nil {
err = fmt.Errorf("[%v]不是有效的数值类型", value)
}
case BigInt.ToString():
toTypeVal, err = numberString.Int()
if err != nil {
err = fmt.Errorf("[%v]不是有效的数值类型", value)
}
case Float.ToString():
toTypeVal, err = numberString.Float64()
err = fmt.Errorf("[%v]不是有效的浮点数类型", value)
if err != nil {
err = fmt.Errorf("[%v]不是有效的浮点数类型", value)
}
case Date.ToString():
toTypeVal, err = xtime.Parse(value)
err = fmt.Errorf("[%v]不是有效的日期类型", value)
return nil, nil
if err != nil {
err = fmt.Errorf("[%v]不是有效的日期类型", value)
}
case Datetime.ToString():
toTypeVal, err = xtime.Parse(value)
err = fmt.Errorf("[%v]不是有效的时间类型", value)
return nil, nil
if err != nil {
err = fmt.Errorf("[%v]不是有效的时间类型", value)
}
default:
return nil, fmt.Errorf("unknow sql type :%v", sqlType)
}
... ...
... ... @@ -67,6 +67,16 @@ func (table *Table) WithContext(ctx *Context) *Table {
return table
}
func (table *Table) WithParentId(parentId int) *Table {
table.ParentId = parentId
return table
}
func (table *Table) WithDataFieldIndex(dataFieldIndex int) *Table {
table.DataFieldIndex = dataFieldIndex
return table
}
func (table *Table) Update(data map[string]interface{}) error {
return nil
}
... ...
... ... @@ -109,6 +109,7 @@ func (gateway ApiByteLib) CopyTable(param domain.ReqCopyTable) (*domain.DataCopy
}
url = gateway.Host() + "/master-tables/copy"
case domain.SubTable.ToString():
copyRequest.DatabaseTableName = param.MainTable.SQLName // 分表复制时 传入主表的表名
request = CopyMasterTableRequest{
MasterTableId: intToString(param.Table.TableId),
CopyTableRequest: copyRequest,
... ...
... ... @@ -25,7 +25,7 @@ type DataCheckoutTables struct {
PageSize int `json:"pageSize"`
DataCount int `json:"dataCount"`
ShowData [][]string `json:"showData"`
AbnormalProccessIndexes [][]interface{} `json:"abnormalProccessIndexes"`
AbnormalProccessIndexes [][]string `json:"abnormalProccessIndexes"`
}
func NewRequestCheckoutTablesQuery(param domain.ReqLoadDataTable) RequestCheckoutTablesQuery {
... ... @@ -48,10 +48,10 @@ type RequestCheckoutTablesPreProccess struct {
ColumnSchemas []domain.ColumnSchema `json:"columnSchemas"`
PreProccessColumnNames []string `json:"preProccessColumnNames"`
PreProccessActionName string `json:"preProccessActionName"`
PreProccessActionParameterValues map[string]string `json:"preProccessActionParameterValues"`
PreProccessActionParameterValues map[string]interface{} `json:"preProccessActionParameterValues"`
PageNumber int `json:"pageNumber"`
PageSize int `json:"pageSize"`
QueryParameters map[string]interface{} `json:"queryParameters"`
QueryParameters interface{} `json:"queryParameters"`
SortParameters map[string]interface{} `json:"sortParameters"`
}
... ... @@ -60,13 +60,17 @@ func NewRequestCheckoutTablesPreProccess(param domain.ReqEditDataTable) RequestC
OriginalTableId: fmt.Sprintf("%v", param.FileId),
ColumnSchemas: FieldsToColumnSchemas(param.Fields),
PreProccessActionName: param.Action,
PreProccessActionParameterValues: make(map[string]string),
PreProccessActionParameterValues: param.Params,
PageNumber: param.PageNumber,
PageSize: param.PageSize,
QueryParameters: make(map[string]interface{}),
QueryParameters: []interface{}{},
SortParameters: make(map[string]interface{}),
}
if len(param.Params) == 0 {
req.PreProccessActionParameterValues = make(map[string]interface{})
}
for _, v := range param.ProcessFields {
req.PreProccessColumnNames = append(req.PreProccessColumnNames, v.Name)
}
... ... @@ -120,6 +124,17 @@ func ToDataLoadDataTable(data DataCheckoutTables) *domain.DataLoadDataTable {
SQLType: f.ColumnType,
})
}
for _, invalid := range data.AbnormalProccessIndexes {
if len(invalid) == 3 {
index, _ := strconv.Atoi(invalid[0])
response.InValidCells = append(response.InValidCells, domain.InValidCell{
Index: index,
Col: invalid[1],
Err: invalid[2],
})
}
}
return response
}
... ...
... ... @@ -4,6 +4,7 @@ import (
"fmt"
pgTransaction "github.com/linmadan/egglib-go/transaction/pg"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/redis"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/repository"
)
... ... @@ -12,7 +13,7 @@ type EditDataTableService struct {
}
// Edit 表结构编辑 【data-table】
func (ptr *EditDataTableService) Edit(ctx *domain.Context, req domain.EditTableRequest) (interface{}, error) {
func (ptr *EditDataTableService) Edit(ctx *domain.Context, req domain.EditTableRequest) (*domain.DataEditDataTable, error) {
fileRepository, _ := repository.NewFileRepository(ptr.transactionContext)
file, err := fileRepository.FindOne(map[string]interface{}{"fileId": req.FileId})
if err != nil {
... ... @@ -21,20 +22,7 @@ func (ptr *EditDataTableService) Edit(ctx *domain.Context, req domain.EditTableR
if file.FileType != domain.TemporaryFile.ToString() {
return nil, fmt.Errorf("文件未校验")
}
// TODO:操作名映射
var operateName string = req.Action
// 日志
if err = FastLog(ptr.transactionContext, domain.VerifiedStepLog, file.FileId, &ExcelTableEditLog{
LogEntry: domain.NewLogEntry(file.FileInfo.Name, domain.VerifiedFile.ToString(), domain.FileVerify,
ctx.WithValue(domain.ContextWithLogLevel, domain.LevelInfo).
WithValue(domain.ContextWithLogMsg, "")),
ProcessFields: req.ProcessFields,
OperateName: operateName,
}); err != nil {
return nil, err
}
// 通知底层进行文件表编辑
response, err := ByteCore.EditTable(domain.ReqEditDataTable{
FileId: file.FileId,
PageNumber: req.Where.PageNumber,
... ... @@ -46,13 +34,77 @@ func (ptr *EditDataTableService) Edit(ctx *domain.Context, req domain.EditTableR
})
if err != nil {
return nil, err
//log.Logger.Error(err.Error())
}
// 处理错误
level := domain.LevelInfo
errMsg := ""
if len(response.InValidCells) > 0 {
level = domain.LevelError
errMsg = response.InValidCells[0].Err
}
// 日志
var operateName string = actionName(req.Action, req.Params)
if err = FastLog(ptr.transactionContext, domain.VerifiedStepLog, file.FileId, &ExcelTableEditLog{
LogEntry: domain.NewLogEntry(file.FileInfo.Name, domain.VerifiedFile.ToString(), domain.FileVerify,
ctx.WithValue(domain.ContextWithLogLevel, level).
WithValue(domain.ContextWithLogMsg, errMsg)),
ProcessFields: req.ProcessFields,
OperateName: operateName,
}); err != nil {
return nil, err
}
// 1.有修改表类型的,更新缓存数据列类型
if response != nil && len(response.Fields) > 0 {
// 特殊处理修改类型错误
options := make([]redis.FileCacheOptionsFunc, 0)
if req.Action == "convert-column-type" {
var toType = req.Params["convertType"].(string)
var fieldName = req.ProcessFieldNames[0]
if level == domain.LevelError {
options = append(options, redis.WithAddConvertTypeErrors([]redis.ConvertTypeError{{FieldName: fieldName, ErrMsg: errMsg, ToType: toType}}))
} else {
options = append(options, redis.WithRemoveConvertTypeErrors([]redis.ConvertTypeError{{FieldName: fieldName, ErrMsg: errMsg, ToType: toType}}))
}
}
cacheService := redis.NewFileCacheService()
if _, err := cacheService.Update(redis.KeyTemporaryFileInfo(file.FileId), file, response.Fields, response.Total, options...); err != nil {
return nil, err
}
}
return response, nil
}
func actionName(action string, params map[string]interface{}) string {
if v, ok := domain.MapActionCommon[action]; ok {
return v.Desc
}
if params != nil {
switch action {
case domain.FormatColumn:
if v, ok := domain.MapActionFormat[params["formatMethod"].(string)]; ok {
return v.Desc
}
case domain.SplitColumn:
if v, ok := domain.MapActionSplitColumn[params["splitMethod"].(string)]; ok {
return v.Desc
}
case domain.ExtractColumn:
if v, ok := domain.MapActionExtractColumn[params["extractMethod"].(string)]; ok {
return v.Desc
}
case domain.ReplaceColumn:
if v, ok := domain.MapActionReplaceColumn[params["replaceMethod"].(string)]; ok {
return v.Desc
}
}
}
return action
}
func NewEditDataTableService(transactionContext *pgTransaction.TransactionContext) (*EditDataTableService, error) {
if transactionContext == nil {
return nil, fmt.Errorf("transactionContext参数不能为nil")
... ...
... ... @@ -149,6 +149,22 @@ func NewTable(tableType domain.TableType, fileName string, dataFields []*domain.
return table
}
func NewCopyTable(tableType domain.TableType, fileName string, dataFields []*domain.Field, rowCount int) *domain.Table {
var table = &domain.Table{}
// New Table
table.TableType = tableType.ToString()
table.Name = fileName
table.SQLName = pin(fileName) //SQLTableName()
table.PK = PK()
table.DataFieldIndex = len(dataFields)
table.DataFields = dataFields
table.ManualFields = make([]*domain.Field, 0)
table.CreatedAt = time.Now()
table.UpdatedAt = time.Now()
table.RowCount = rowCount
return table
}
func SQLTableName() string {
id, _ := uuid.NewUUID()
return id.String()
... ...
... ... @@ -21,21 +21,16 @@ func (ptr *PreviewDataTableService) Preview(ctx *domain.Context, fileId int, fie
if err != nil {
return nil, fmt.Errorf("校验文件不存在")
}
isSourceFile := false
isSourceFile := true
fileUrl := file.FileInfo.Url
// Copy to TemporaryFile
if file.FileType != domain.TemporaryFile.ToString() {
//file = file.CopyTo(domain.TemporaryFile, ctx)
//if file, err = fileRepository.Save(file); err != nil {
// return nil, err
//}
//isSourceFile = true
//fileUrl = file.FileInfo.Url
return nil, fmt.Errorf("校验文件不存在")
}
ptr.FileId = file.FileId
if len(fields) == 0 {
isSourceFile = true
fileCache := redis.NewFileCacheService()
if tempFile, _ := fileCache.Get(redis.KeyTemporaryFileInfo(fileId)); tempFile != nil {
isSourceFile = false
fields = tempFile.Fields
}
// Load Data From Excel(python api)
... ... @@ -58,12 +53,12 @@ func (ptr *PreviewDataTableService) Preview(ctx *domain.Context, fileId int, fie
return nil, err
}
cache := redis.NewFileCacheService()
_, err = cache.Update(redis.KeyTemporaryFileInfo(file.FileId), file, response.Fields, response.Total)
tempFile, err := cache.Update(redis.KeyTemporaryFileInfo(file.FileId), file, response.Fields, response.Total)
if err != nil {
return nil, err
}
var responseDto = &FilePreviewDto{}
responseDto.Load(file.FileId, response)
responseDto.Load(file.FileId, response, tempFile)
return responseDto, nil
}
... ... @@ -93,16 +88,27 @@ type FilePreviewDto struct {
InValidCells []domain.InValidCell `json:"inValidCells"`
}
func (d *FilePreviewDto) Load(fileId int, m *domain.DataLoadDataTable) {
func (d *FilePreviewDto) Load(fileId int, m *domain.DataLoadDataTable, file *redis.TemporaryFileInfo) {
d.ObjectId = fileId
d.ObjectType = domain.ObjectFile
d.TableType = domain.ExcelTable.ToString()
d.Fields = m.Fields
var fields []*domain.Field
mapData := domain.ToFieldData(m.Fields, m.Data, true)
d.Data = domain.GripData(mapData, int64(m.Total))
d.PageNumber = m.PageNumber
d.InValidCells = domain.NewInValidCells(m.Fields, mapData)
for _, f := range m.Fields {
copyField := f.Copy()
for _, e := range file.ConvertTypeErrors {
if e.FieldName == copyField.Name {
copyField.SQLType = e.ToType
}
}
fields = append(fields, copyField)
}
d.InValidCells = domain.NewInValidCells(fields, mapData)
}
func (ptr *PreviewDataTableService) GetFileId() int {
... ...
... ... @@ -48,7 +48,7 @@ func (ptr *PGLogService) Log(logType domain.LogType, sourceId int, logEntry Log)
if v, ok := logEntry.Context().GetValue(domain.ContextWithLogLevel); ok {
log.Entry.Level = string(v.(domain.LogLevel))
}
if v, ok := logEntry.Context().GetValue(domain.ContextWithLogLevel); ok {
if v, ok := logEntry.Context().GetValue(domain.ContextWithLogMsg); ok {
log.Entry.Error = v.(string)
}
_, err := logRepository.Save(log)
... ...
... ... @@ -28,8 +28,15 @@ func (ptr *CopyDataTableService) CopyTable(ctx *domain.Context, tableId int, tab
if err != nil {
return nil, err
}
if table.TableType != domain.MainTable.ToString() {
return nil, fmt.Errorf("主表才允许复制")
if !(table.TableType == domain.MainTable.ToString() || table.TableType == domain.SubTable.ToString()) {
return nil, fmt.Errorf("主表、分表才允许复制")
}
var mainTable *domain.Table
if table.TableType == domain.SubTable.ToString() {
mainTable, err = tableRepository.FindOne(map[string]interface{}{"tableId": table.ParentId})
if err != nil {
return nil, err
}
}
// 验证表名是否重复
duplicateTable, err := tableRepository.FindOne(map[string]interface{}{"context": ctx, "tableName": tableName})
... ... @@ -37,8 +44,10 @@ func (ptr *CopyDataTableService) CopyTable(ctx *domain.Context, tableId int, tab
return nil, fmt.Errorf("表名称重复")
}
sideTable := NewTable(domain.SideTable, tableName, table.DataFields, table.RowCount).WithContext(ctx)
sideTable.ParentId = table.TableId
sideTable := NewCopyTable(domain.SideTable, tableName, table.DataFields, table.RowCount).
WithContext(ctx).
WithParentId(table.TableId).
WithDataFieldIndex(table.DataFieldIndex)
if sideTable, err = tableRepository.Save(sideTable); err != nil {
return nil, err
}
... ... @@ -51,7 +60,7 @@ func (ptr *CopyDataTableService) CopyTable(ctx *domain.Context, tableId int, tab
}
// 通知底层
if _, err = ByteCore.CopyTable(domain.ReqCopyTable{Table: table, CopyToTable: sideTable}); err != nil {
if _, err = ByteCore.CopyTable(domain.ReqCopyTable{Table: table, MainTable: mainTable, CopyToTable: sideTable}); err != nil {
return nil, err
}
return nil, nil
... ...
... ... @@ -24,6 +24,18 @@ type TemporaryFileInfo struct {
// 行记录错误
// 列类型有错误
// 表整体错误
ConvertTypeErrors []ConvertTypeError `json:"convertTypeErrors"`
}
func (f *TemporaryFileInfo) MatchFields(columns []string) []*domain.Field {
mapFields := (domain.Fields)(f.Fields).ToMap()
var result = make([]*domain.Field, 0)
for _, c := range columns {
if v, ok := mapFields[c]; ok {
result = append(result, v)
}
}
return result
}
func (f *TemporaryFileInfo) SetFile(file *domain.File) *TemporaryFileInfo {
... ... @@ -42,10 +54,40 @@ func (f *TemporaryFileInfo) SetTotal(total int) *TemporaryFileInfo {
return f
}
func (f *TemporaryFileInfo) AddConvertTypeError(e ConvertTypeError) *TemporaryFileInfo {
f.RemoveConvertTypeError(e)
f.addConvertTypeError(e)
return f
}
func (f *TemporaryFileInfo) addConvertTypeError(e ConvertTypeError) *TemporaryFileInfo {
f.RemoveConvertTypeError(e)
f.ConvertTypeErrors = append(f.ConvertTypeErrors, e)
return f
}
func (f *TemporaryFileInfo) RemoveConvertTypeError(e ConvertTypeError) *TemporaryFileInfo {
var newErrors = make([]ConvertTypeError, 0)
for _, item := range f.ConvertTypeErrors {
if item.FieldName == e.FieldName {
continue
}
newErrors = append(newErrors, item)
}
f.ConvertTypeErrors = newErrors
return f
}
type ConvertTypeError struct {
FieldName string `json:"fieldName"`
ErrMsg string `json:"errMsg"`
ToType string `json:"toType"`
}
type FileCacheService struct {
}
func (s *FileCacheService) Update(key string, file *domain.File, fields []*domain.Field, total int) (*TemporaryFileInfo, error) {
func (s *FileCacheService) Update(key string, file *domain.File, fields []*domain.Field, total int, errors ...FileCacheOptionsFunc) (*TemporaryFileInfo, error) {
ok, err := ZeroCoreRedis.Exists(key)
var response = &TemporaryFileInfo{}
if err != nil {
... ... @@ -64,6 +106,15 @@ func (s *FileCacheService) Update(key string, file *domain.File, fields []*domai
return nil, err
}
response.SetFields(fields)
options := NewFileCacheOptions(errors...)
for i := range options.AddConvertTypeErrors {
response.AddConvertTypeError(options.AddConvertTypeErrors[i])
}
for i := range options.RemoveConvertTypeErrors {
response.RemoveConvertTypeError(options.RemoveConvertTypeErrors[i])
}
err = ZeroCoreRedis.Setex(key, json.MarshalToString(response), TemporaryFileExpire)
if err != nil {
return nil, err
... ... @@ -94,3 +145,30 @@ func (s *FileCacheService) Get(key string) (*TemporaryFileInfo, error) {
func NewFileCacheService() *FileCacheService {
return &FileCacheService{}
}
type FileCacheOptions struct {
RemoveConvertTypeErrors []ConvertTypeError
AddConvertTypeErrors []ConvertTypeError
}
type FileCacheOptionsFunc func(o *FileCacheOptions)
func WithRemoveConvertTypeErrors(errors []ConvertTypeError) FileCacheOptionsFunc {
return func(o *FileCacheOptions) {
o.RemoveConvertTypeErrors = errors
}
}
func WithAddConvertTypeErrors(errors []ConvertTypeError) FileCacheOptionsFunc {
return func(o *FileCacheOptions) {
o.AddConvertTypeErrors = errors
}
}
func NewFileCacheOptions(options ...FileCacheOptionsFunc) *FileCacheOptions {
option := &FileCacheOptions{}
for i := range options {
options[i](option)
}
return option
}
... ...
... ... @@ -118,7 +118,7 @@ func (repository *FileRepository) FindOne(queryOptions map[string]interface{}) (
query.SetWhereByQueryOption("file.file_id = ?", "fileId")
if err := query.First(); err != nil {
if err.Error() == "pg: no rows in result set" {
return nil, fmt.Errorf("没有此资源")
return nil, domain.ErrorNotFound
} else {
return nil, err
}
... ...
... ... @@ -125,7 +125,7 @@ func (repository *LogRepository) FindOne(queryOptions map[string]interface{}) (*
query.SetWhereByQueryOption("log.log_id = ?", "logId")
if err := query.First(); err != nil {
if err.Error() == "pg: no rows in result set" {
return nil, fmt.Errorf("没有此资源")
return nil, domain.ErrorNotFound
} else {
return nil, err
}
... ...
... ... @@ -126,7 +126,7 @@ func (repository *MappingRuleRepository) FindOne(queryOptions map[string]interfa
query.SetWhereByQueryOption("mapping_rule.mapping_rule_id = ?", "mappingRuleId")
if err := query.First(); err != nil {
if err.Error() == "pg: no rows in result set" {
return nil, fmt.Errorf("没有此资源")
return nil, domain.ErrorNotFound
} else {
return nil, err
}
... ...
... ... @@ -147,7 +147,7 @@ func (repository *TableRepository) FindOne(queryOptions map[string]interface{})
query.SetWhereByQueryOption("parent_id = ?", "parentId")
if err := query.First(); err != nil {
if err.Error() == "pg: no rows in result set" {
return nil, fmt.Errorf("没有此资源")
return nil, domain.ErrorNotFound
} else {
return nil, err
}
... ...