package excel import ( "context" "github.com/ayflying/excel2json" "github.com/goccy/go-json" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" "path" "strconv" "strings" "time" ) var ( shadiao = []string{",", ":"} ) type FileItem struct { Name string `json:"name" dc:"配置文件名"` Filename string `json:"filename" dc:"文件名"` Tabs []string `json:"tabs" dc:"页签"` Items []string `json:"items" dc:"道具字段"` ItemsMap []string `json:"items_map" dc:"道具字段map格式"` Slice map[string]string `json:"slice" dc:"切片"` Json []string `json:"json" dc:"json"` } type Excel struct { Header int //表头行数 Key int //key列 } func New(header int, key int) *Excel { return &Excel{ Header: header, Key: key, } } func (s *Excel) ExcelLoad(ctx context.Context, fileItem *FileItem, mainPath string) (runTime time.Duration) { startTime := gtime.Now() filepath := path.Join("manifest/game", fileItem.Name) //如果filepath文件不存在,跳过 if !gfile.Exists(path.Join(mainPath, fileItem.Filename)) { return } //假设我们有一个命令行工具,比如:dir(Windows环境下列出目录内容) var tempJson []interface{} for k, v2 := range fileItem.Tabs { sheet := v2 if k == 0 { sheet = v2 } //导出json excel2json.Excel(path.Join(mainPath, fileItem.Filename), filepath, s.Header, s.Key, sheet) //如果配置了道具字段,则进行转换 //g.Log().Info(ctx, "当前任务表=%v,items=%v", v.Name, v.Items) fileBytes := gfile.GetBytes(filepath) arr, _ := gjson.DecodeToJson(fileBytes) list := arr.Array() //排除注释行 list = s.RemoveComments(list, fileItem.Json) //格式化item格式 if len(fileItem.Items) > 0 { list = s.itemsFormat(list, fileItem.Items) } if len(fileItem.ItemsMap) > 0 { list = s.itemsMapFormat(list, gconv.Strings(fileItem.ItemsMap)) } //格式化切片修改 if len(fileItem.Slice) > 0 { list = s.sliceFormat(list, fileItem.Slice) } //json格式转换 if len(fileItem.Json) > 0 { list = s.jsonFormat(list, fileItem.Json) } //拼接json tempJson = append(tempJson, list...) fileBytes, _ = gjson.MarshalIndent(tempJson, "", "\t") err := gfile.PutBytes(filepath, fileBytes) if err != nil { g.Log().Error(ctx, err) } } runTime = gtime.Now().Sub(startTime) return } //排除配置中注释行 func (s *Excel) RemoveComments(list []interface{}, json []string) []interface{} { var temp = make([]interface{}, 0) // 遍历列表中的每个元素 for _, v2 := range list { var add = true // 遍历当前元素的每个键值对 for _, v3 := range v2.(g.Map) { // 如果字符串中存在//则跳过不写入temp if gstr.Contains(gconv.String(v3), "//") { //delKey = append(delKey, k2) add = false break } } if add { temp = append(temp, v2) } } return temp } // itemsFormat 格式化列表中的道具字段 // 参数: // - list: 待处理的列表,包含多个元素,每个元素是一个 g.Map 类型 // - Items: 包含需要处理的道具字段名称的切片 // 返回值: // - 处理后的列表 func (s *Excel) itemsFormat(list []interface{}, Items []string) []interface{} { // 遍历列表中的每个元素 for k2, v2 := range list { // 遍历当前元素的每个键值对 for k3, v3 := range v2.(g.Map) { // 检查当前键是否在需要处理的道具字段列表中 if gstr.InArray(Items, k3) { // 检查当前值是否为字符串类型 if _, ok := v3.(string); ok { // 如果是字符串类型,调用 Spilt2Item 函数将其转换为 [][]int64 类型,并更新到列表中 list[k2].(g.Map)[k3] = Spilt2Item(v3.(string)) } else { // 如果不是字符串类型,记录错误日志 g.Log().Errorf(gctx.New(), "当前类型断言失败:%v,list=%v", v3, v2) } } } } // 返回处理后的列表 return list } // itemsMapFormat 将列表中指定字段的道具信息转换为映射格式 // 参数: // - list: 待处理的列表,包含多个元素,每个元素是一个 g.Map 类型 // - Items: 包含需要处理的道具字段名称的切片 // 返回值: // - 处理后的列表 func (s *Excel) itemsMapFormat(list []interface{}, Items []string) []interface{} { // 遍历列表中的每个元素 for k2, v2 := range list { // 遍历当前元素的每个键值对 for k3, v3 := range v2.(g.Map) { // 检查当前键是否在需要处理的道具字段列表中 if gstr.InArray(Items, k3) { // 检查当前值是否为字符串类型 if _, ok := v3.(string); ok { // 如果是字符串类型,调用 Spilt2Item 函数将其转换为 [][]int64 类型 get := Spilt2Item(v3.(string)) // 调用 Items2Map 函数将 [][]int64 类型的数据转换为映射格式,并更新到列表中 list[k2].(g.Map)[k3] = s.Items2Map(get) } else { // 如果不是字符串类型,记录错误日志 g.Log().Errorf(gctx.New(), "当前类型断言失败:%v,list=%v", v3, v2) } } } } // 返回处理后的列表 return list } // sliceFormat 格式化列表中指定字段为切片格式 // 参数: // - list: 待处理的列表,包含多个元素,每个元素是一个 g.Map 类型 // - Slice: 一个映射,键为需要处理的字段名,值为目标类型(如 "int", "int64", "float64") // 返回值: // - 处理后的列表 func (s *Excel) sliceFormat(list []interface{}, Slice map[string]string) []interface{} { // 遍历 Slice 映射中的每个键值对 for s1, s2 := range Slice { // 遍历列表中的每个元素 for k2, v2 := range list { // 遍历当前元素的每个键值对 for k3, v3 := range v2.(g.Map) { // 判断当前键是否与 Slice 中的键匹配 if s1 != k3 { // 不匹配则跳过当前循环 continue } // 检查当前值是否为空字符串 if gconv.String(v3) == "" { // 若为空,则将该字段设置为空字符串切片 list[k2].(g.Map)[k3] = []string{} // 跳过当前循环 continue } // 用于存储分割后的字符串切片 var parts []string // 断言当前值是否为字符串类型 if get, ok := v3.(string); !ok { // 若断言失败,将当前值转换为字符串并作为唯一元素存入 parts parts = []string{gconv.String(v3)} } else { // 若为字符串类型,将字符串中的特殊字符替换为 "|" for _, v := range shadiao { get = strings.ReplaceAll(get, v, "|") } // 按 "|" 分割字符串 parts = strings.Split(get, "|") } // 根据 Slice 映射中的值进行类型转换 switch s2 { case "int": // 创建一个长度为 parts 的 int 切片 var temp = make([]int, len(parts)) // 遍历 parts 切片,将每个元素转换为 int 类型 for k, v := range parts { temp[k], _ = strconv.Atoi(v) } // 将转换后的切片存入列表中 list[k2].(g.Map)[k3] = temp case "int64": // 创建一个长度为 parts 的 int64 切片 var temp = make([]int64, len(parts)) // 遍历 parts 切片,将每个元素转换为 int64 类型 for k, v := range parts { temp[k], _ = strconv.ParseInt(v, 10, 64) } case "float64": // 创建一个长度为 parts 的 float64 切片 var temp = make([]float64, len(parts)) // 遍历 parts 切片,将每个元素转换为 float64 类型 for k, v := range parts { temp[k], _ = strconv.ParseFloat(v, 64) } // 将转换后的切片存入列表中 list[k2].(g.Map)[k3] = temp default: // 若未匹配到指定类型,直接将 parts 存入列表中 list[k2].(g.Map)[k3] = parts } } } } // 返回处理后的列表 return list } // jsonFormat 将列表中指定字段的 JSON 字符串解析为 Go 数据结构 // 参数: // - list: 待处理的列表,包含多个元素,每个元素是一个 g.Map 类型 // - Items: 包含需要处理的 JSON 字段名称的切片 // 返回值: // - 处理后的列表 func (s *Excel) jsonFormat(list []interface{}, Items []string) []interface{} { // 遍历列表中的每个元素 for k2, v2 := range list { // 遍历当前元素的每个键值对 for k3, v3 := range v2.(g.Map) { // 检查当前键是否在需要处理的 JSON 字段列表中 if gstr.InArray(Items, k3) { // 检查当前值是否为字符串类型 if _, ok := v3.(string); ok { // 用于存储解析后的 JSON 数据 var get interface{} // 将字符串解析为 JSON 数据 json.Unmarshal([]byte(v3.(string)), &get) // 将解析后的 JSON 数据更新到列表中 list[k2].(g.Map)[k3] = get } else { // 如果不是字符串类型,记录错误日志 g.Log().Errorf(gctx.New(), "当前类型断言失败:%v,list=%v", v3, v2) } } } } // 返回处理后的列表 return list }