package imports import ( "fmt" "io" "reflect" "strconv" "strings" "time" "github.com/xuri/excelize/v2" ) type Importer struct { Reader io.Reader Sheets []string xlsxFile *excelize.File Data interface{} } func NewFileImporter(file string, data any) (*Importer, error) { f, err := excelize.OpenFile(file) if err != nil { return nil, err } sheets := f.GetSheetList() return &Importer{ xlsxFile: f, Sheets: sheets, Data: data, }, nil } func NewFileReaderImporter(reader io.Reader, data any) (*Importer, error) { f, err := excelize.OpenReader(reader) if err != nil { return nil, err } sheets := f.GetSheetList() return &Importer{ xlsxFile: f, Sheets: sheets, Data: data, }, nil } func (i *Importer) Import(sheetIndex int) error { v := reflect.ValueOf(i.Data) if v.Kind() != reflect.Pointer { return fmt.Errorf("data must be pointer") } switch v.Elem().Kind() { case reflect.Slice: return i.dealFile(sheetIndex) default: return fmt.Errorf("data must be slice") } } func (i *Importer) dealFile(sheetIndex int) error { sheet := i.Sheets[sheetIndex] rows, err := i.xlsxFile.Rows(sheet) if err != nil { return err } vdata := reflect.ValueOf(i.Data).Elem() if vdata.Type().Elem().Kind() != reflect.Struct { return fmt.Errorf("element must be struct") } testData := reflect.New(vdata.Type().Elem()) tempDic := make(map[int]string) for i := 0; i < testData.Elem().NumField(); i++ { t := testData.Elem().Type().Field(i) tag, ok := t.Tag.Lookup("import") if ok { tagMap := dealTag(tag) index, _ := strconv.Atoi(tagMap["index"]) tempDic[index] = t.Name } } rowIndex := 0 for rows.Next() { rowIndex += 1 // 第一行为表头 if rowIndex > 1 { row, err := rows.Columns() if err != nil { return err } newData := reflect.New(vdata.Type().Elem()) for colIndex := range row { colIndex += 1 cellName, _ := excelize.CoordinatesToCellName(colIndex, rowIndex) value, _ := i.xlsxFile.GetCellValue(sheet, cellName) if fieldName, ok := tempDic[colIndex]; ok { field := newData.Elem().FieldByName(fieldName) switch field.Kind() { case reflect.String: field.SetString(value) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: intValue, _ := strconv.Atoi(value) field.SetInt(int64(intValue)) case reflect.Float32, reflect.Float64: floatValue, _ := strconv.ParseFloat(value, 64) field.SetFloat(floatValue) case reflect.Bool: boolValue, _ := strconv.ParseBool(value) field.SetBool(boolValue) // 如果是结构体,只支持时间类型,并且结构体的字段类型必须是time.Time,不能为自定的时间类型,并且excel里面的时间格式必须是yyyy-mm-dd或者yyyy-mm-dd hh:mm:ss case reflect.Struct: t := time.Time{} if strings.Contains(value, ":") { t, _ = time.Parse(time.DateTime, value) } else { t, _ = time.Parse(time.DateOnly, value) } field.Set(reflect.ValueOf(t)) } } } vdata.Set(reflect.Append(vdata, newData.Elem())) } } if err = rows.Close(); err != nil { return err } return nil } func dealTag(tag string) map[string]string { tag_list := strings.Split(tag, ",") tag_map := make(map[string]string) for _, tag := range tag_list { tag_kv := strings.Split(tag, ":") if len(tag_kv) != 2 { continue } tag_map[tag_kv[0]] = tag_kv[1] } return tag_map }