fonchain-artistinfo/pkg/util/excel/excelInter.go

254 lines
6.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package excel -----------------------------
// @file : templateInter.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2022/7/23 15:34
// -------------------------------------------
package excel
import (
"errors"
"fmt"
"github.com/xuri/excelize/v2"
"io"
"log"
"path/filepath"
"reflect"
"sync"
)
var (
ErrSheetNotExist = errors.New("sheet does not exist")
ErrSheetDataFrormat = errors.New("sheet data format must be slice or struct")
//ErrSheetNameNotInTemplate = errors.New("sheet name not in emailTemplate")
)
//======================================================================================================================
// Sheet define
// 抽象工作簿
type Sheet interface {
GetData() any
SheetName() string
}
// 实例化工作簿
func NewSheet(sheetName string, datas any) Sheet {
return &newSheetDefine{
Datas: datas,
Name: sheetName,
}
}
// 定义一个能够通用的工作簿结构此结构必须遵循Sheet接口规范
type newSheetDefine struct {
Datas any
Name string
}
func (s *newSheetDefine) GetData() any {
return s.Datas
}
func (s *newSheetDefine) SheetName() string {
return s.Name
}
//======================================================================================================================
// Sheet define
// WriteToExcel 通过模板文件写入数据并另存为
// param fileName : 文件名
// param filesSuffix : 文件后缀名生成函数
// param fileRoot : 导出目录
// param templatePath : 模板文件路径
// param sheets : Sheet类型的数据类型为[]Sheet
// return path : 文件路径
// return exfileName : 导出后的文件名
// return err
func WriteToExcel(fileName string, fileRoot string, templatePath string, sheets ...Sheet) (path string, exFileName string, err error) {
var exc *Excel
exc, err = NewExcelTemplate(fileName, fileRoot, templatePath, sheets...)
if err != nil {
return
}
return exc.WriteToFile()
}
// ReadDataFromExcel 从excel文件读取数据
func ReadDataFromExcel(filepath string, sheetName string, handler func(rowIndex int, rows []string)) error {
ex := Excel{OriginFilePath: filepath}
return ex.ReadSheetData(sheetName, handler)
}
// ReadDataFromBytes 从io口读取数据用户http上传的附件
func ReadDataFromBytes(file io.Reader, sheetName string, handler func(rowIndex int, rows []string)) error {
exce, err := excelize.OpenReader(file)
if err != nil {
return err
}
var ex = Excel{ex: exce}
return ex.ReadSheetData(sheetName, handler)
}
// 新建工作表实例
func NewExcelTemplate(fileName string, fileRoot string, templatePath string, sheets ...Sheet) (exc *Excel, err error) {
exc = &Excel{
SaveRoot: fileRoot,
SaveName: fileName,
OriginFilePath: templatePath,
rwLock: sync.RWMutex{},
}
if sheets != nil {
err = exc.AddSheets(sheets...)
if err != nil {
return
}
}
return exc, nil
}
type Excel struct {
ex *excelize.File
SaveRoot string
SaveName string
OriginFilePath string
Sheets map[string]Sheet
rwLock sync.RWMutex
Opts []Option
}
// UseOption 使用可选项
func (s *Excel) UseOption(opts ...Option) {
if opts != nil {
s.Opts = append(s.Opts, opts...)
}
}
// 添加工作簿
// 注意如果添加相同的工作簿,之前的会被覆盖
func (s *Excel) AddSheets(sheets ...Sheet) (err error) {
if s.Sheets == nil {
s.Sheets = make(map[string]Sheet, 0)
}
for _, sheet := range sheets {
var sheetName = sheet.SheetName()
s.Sheets[sheetName] = sheet
}
return
}
// 删除工作簿
func (s *Excel) DeleteSheets(sheetName string) error {
if s.Sheets == nil {
return nil
} else if s.Sheets[sheetName] != nil {
delete(s.Sheets, sheetName)
} else {
return ErrSheetNotExist
}
return nil
}
// 读取工作簿
func (s *Excel) ReadSheetData(sheetName string, handler func(rowIndex int, rows []string)) (err error) {
if s.ex == nil {
s.ex, err = excelize.OpenFile(s.OriginFilePath)
if err != nil {
return
}
}
datas, err := s.ex.GetRows(sheetName)
for i, rows := range datas {
handler(i, rows)
}
return nil
}
// 写入到文件
func (s *Excel) WriteToFile() (path string, fileName string, err error) {
if s.ex == nil {
s.ex, err = excelize.OpenFile(s.OriginFilePath)
if err != nil {
return
}
}
if s.Opts != nil {
for _, opt := range s.Opts {
opt(s)
}
}
//插入数据
var cellNameList []string
for sheetName, st := range s.Sheets {
var firstRow = s.getFirstEmptyRowIndex(s.ex, sheetName)
var SheetData = reflect.ValueOf(st.GetData())
var SheetType = reflect.TypeOf(st.GetData())
switch SheetData.Kind() {
case reflect.Slice:
cellNameList = s.getJsonFieldList(SheetType)
var rowLen = SheetData.Len()
for i := 0; i < rowLen; i++ {
var dataMap = s.dataToMap(SheetData.Index(i), SheetType)
for column, v := range cellNameList {
var axis = GetCellIndex(i+firstRow+1, column+1)
err = s.ex.SetCellValue(sheetName, axis, dataMap[v])
if err != nil {
fmt.Println(err.Error())
}
}
}
case reflect.Struct:
cellNameList = s.getJsonFieldList(SheetType)
var dataMap = s.dataToMap(SheetData, SheetType)
for column, v := range cellNameList {
var axis = GetCellIndex(firstRow+1, column+1)
err = s.ex.SetCellValue(sheetName, axis, dataMap[v])
if err != nil {
fmt.Println(err.Error())
}
}
default:
return "", "", ErrSheetDataFrormat
}
}
//保存
path = filepath.ToSlash(filepath.Join(s.SaveRoot, s.SaveName))
s.rwLock.Lock()
if err = s.ex.SaveAs(path); err != nil {
log.Println(fmt.Sprintf("save file error :%v", err))
s.rwLock.Unlock()
return
}
s.rwLock.Unlock()
return
}
// getJsonFieldList 获取json字段列表
func (s *Excel) getJsonFieldList(sheetType reflect.Type) (tagList []string) {
t := sheetType.Elem()
for i := 0; i < t.NumField(); i++ {
tagList = append(tagList, t.Field(i).Tag.Get("json"))
}
return
}
// dataToMap 数据转字典
func (s *Excel) dataToMap(sheet reflect.Value, sheetType reflect.Type) (dataMap map[string]any) {
dataMap = make(map[string]any)
t := sheetType.Elem()
for i := 0; i < t.NumField(); i++ {
dataMap[t.Field(i).Tag.Get("json")] = sheet.Field(i).Interface()
}
return dataMap
}
// getFirstEmptyRowIndex 获取首个空行的索引位置
func (s *Excel) getFirstEmptyRowIndex(ex *excelize.File, sheetName string) (index int) {
rows, err := ex.GetRows(sheetName)
if err != nil {
return 1
}
return len(rows)
}