kra-oa/internal/service/export_excel.go

150 lines
3.8 KiB
Go

package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"strings"
"github.com/xuri/excelize/v2"
)
func templateColumns(raw string) ([]string, map[string]string, error) {
labels := map[string]string{}
decoder := json.NewDecoder(strings.NewReader(raw))
token, err := decoder.Token()
if err != nil || token != json.Delim('{') {
return nil, nil, err
}
keys := make([]string, 0)
for decoder.More() {
keyToken, tokenErr := decoder.Token()
if tokenErr != nil {
return nil, nil, tokenErr
}
key := keyToken.(string)
var label string
if tokenErr = decoder.Decode(&label); tokenErr != nil {
return nil, nil, tokenErr
}
keys = append(keys, key)
labels[key] = label
}
if _, err = decoder.Token(); err != nil {
return nil, nil, err
}
return keys, labels, nil
}
func (s *ExportService) Export(ctx context.Context, tid string, params map[string]string) ([]byte, string, error) {
template, err := s.uc.FindExportTemplate(ctx, 0, tid)
if err != nil {
return nil, "", err
}
rows, _, err := s.uc.QueryExport(ctx, template, params)
if err != nil {
return nil, "", err
}
keys, labels, err := templateColumns(template.TemplateInfo)
if err != nil {
return nil, "", err
}
file := excelize.NewFile()
sheet := file.GetSheetName(0)
for column, key := range keys {
cell, _ := excelize.CoordinatesToCellName(column+1, 1)
_ = file.SetCellValue(sheet, cell, labels[key])
}
for rowIndex, row := range rows {
for column, key := range keys {
cell, _ := excelize.CoordinatesToCellName(column+1, rowIndex+2)
_ = file.SetCellValue(sheet, cell, row[key])
}
}
buffer, err := file.WriteToBuffer()
_ = file.Close()
if err != nil {
return nil, "", err
}
return buffer.Bytes(), template.Name + ".xlsx", nil
}
func (s *ExportService) ExportBlankTemplate(ctx context.Context, tid string) ([]byte, string, error) {
template, err := s.uc.FindExportTemplate(ctx, 0, tid)
if err != nil {
return nil, "", err
}
keys, labels, err := templateColumns(template.TemplateInfo)
if err != nil {
return nil, "", err
}
file := excelize.NewFile()
sheet := file.GetSheetName(0)
for column, key := range keys {
cell, _ := excelize.CoordinatesToCellName(column+1, 1)
_ = file.SetCellValue(sheet, cell, labels[key])
}
buffer, err := file.WriteToBuffer()
_ = file.Close()
if err != nil {
return nil, "", err
}
return buffer.Bytes(), template.Name + "_template.xlsx", nil
}
func (s *ExportService) Preview(ctx context.Context, tid string, params map[string]string) (string, error) {
template, err := s.uc.FindExportTemplate(ctx, 0, tid)
if err != nil {
return "", err
}
return s.uc.PreviewExport(ctx, template, params)
}
func (s *ExportService) Import(ctx context.Context, tid string, reader io.Reader) error {
template, err := s.uc.FindExportTemplate(ctx, 0, tid)
if err != nil {
return err
}
file, err := excelize.OpenReader(reader)
if err != nil {
return err
}
defer file.Close()
sheets := file.GetSheetList()
if len(sheets) == 0 {
return errors.New("工作簿没有工作表")
}
rows, err := file.GetRows(sheets[0])
if err != nil {
return err
}
if len(rows) < 2 {
return errors.New("Excel data is not enough.\nIt should contain title row and data")
}
keys, labels, err := templateColumns(template.TemplateInfo)
if err != nil {
return err
}
reverse := map[string]string{}
for _, key := range keys {
reverse[labels[key]] = key
}
headers := rows[0]
items := make([]map[string]any, 0, len(rows)-1)
for _, row := range rows[1:] {
item := map[string]any{}
for column, value := range row {
if column >= len(headers) {
continue
}
key := reverse[headers[column]]
if key != "" {
item[key] = value
}
}
items = append(items, item)
}
if err = s.uc.ImportExportRows(ctx, template, items); err != nil {
return fmt.Errorf("导入失败: %w", err)
}
return nil
}