kra-oa/app/system/internal/service/export_excel.go

164 lines
4.3 KiB
Go

package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"strconv"
"strings"
"time"
"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)
lookup := strings.ReplaceAll(strings.ReplaceAll(key, `"`, ""), "`", "")
if len(template.Joins) > 0 {
if parts := strings.Split(lookup, " as "); len(parts) > 1 {
lookup = strings.TrimSpace(parts[1])
} else if parts = strings.Split(lookup, "."); len(parts) > 1 {
lookup = parts[1]
}
}
value := fmt.Sprintf("%v", row[lookup])
if timestamp, ok := row[lookup].(time.Time); ok {
value = timestamp.Format("2006-01-02 15:04:05")
}
if number, parseErr := strconv.ParseFloat(value, 64); parseErr == nil {
_ = file.SetCellValue(sheet, cell, number)
} else {
_ = file.SetCellValue(sheet, cell, value)
}
}
}
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()
rows, err := file.GetRows("Sheet1")
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]
for index := range headers {
headers[index] = strings.TrimSpace(headers[index])
}
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)
}
return s.uc.ImportExportRows(ctx, template, items)
}