统一导出模式,使用sql语句的方式进行导出

This commit is contained in:
Mr.Li 2023-04-04 19:04:45 +08:00
parent 485e8cc944
commit fabed6916c
29 changed files with 718 additions and 1382 deletions

View File

@ -1,32 +0,0 @@
package biz
import (
"fmt"
"time"
)
type baseExportBiz struct {
fileSize int
opts *ExportOpts
fileName string
}
func (e *baseExportBiz) FileSize() int {
return e.fileSize
}
func (e *baseExportBiz) FileName() string {
return e.fileName
}
func (e *baseExportBiz) FileNames() []string {
name := make([]string, e.fileSize+1)
for i := 0; i <= e.fileSize; i++ {
name[i] = fmt.Sprintf(e.fileName, i)
}
return name
}
func (e *baseExportBiz) setFileArg(begin time.Time) {
e.fileName = getFielName(e.opts.Path, e.opts.Tag, begin)
}

View File

@ -1,153 +0,0 @@
package biz
import (
"context"
"excel_export/data"
"excel_export/export"
"fmt"
"time"
)
func NewBatchBiz(repo *data.DirectRepo, historyRepo *data.HistoryDirectRepo, opts *ExportOpts) *BatchBiz {
biz := &BatchBiz{
repo: repo,
historyRepo: historyRepo,
}
biz.opts = DefaultOpts
if opts != nil {
biz.opts = opts
}
biz.opts.Tag = "batch"
return biz
}
type BatchBiz struct {
repo *data.DirectRepo
historyRepo *data.HistoryDirectRepo
baseExportBiz
}
func (e *BatchBiz) Export(begin, end time.Time) error {
e.setFileArg(begin)
file := export.NewExport(e.fileName, e.opts.ExcelMaxRow)
if err := file.Open(); err != nil {
return err
}
if err := file.Title(export.Batchs{}.Title()); err != nil {
return err
}
//导出历史订单数据
if err := e.exportHistoryData(file, begin, end); err != nil {
return err
}
//导出最近7天数据
if err := e.exportData(file, begin, end); err != nil {
return err
}
e.fileSize = file.Index()
return nil
}
func (e *BatchBiz) exportHistoryData(export *export.Export, begin, end time.Time) error {
var last string
for true {
//导出最近7天数据
data, err := e.historyRepo.BatchList(context.Background(), begin, end, e.opts.QueryLimit, last)
if err != nil {
return fmt.Errorf("获取导出数据错误:%w", err)
}
if err := export.Export(e.HistoryTransforms(data)); err != nil {
return err
}
if len(data) < e.opts.QueryLimit {
return nil
}
last = data[len(data)-1].SerialNumber
}
return nil
}
func (e *BatchBiz) exportData(export *export.Export, begin, end time.Time) error {
var last string
for true {
//导出最近7天数据
data, err := e.repo.BatchList(context.Background(), begin, end, e.opts.QueryLimit, last)
if err != nil {
return fmt.Errorf("获取导出数据错误:%w", err)
}
if err := export.Export(e.Transforms(data)); err != nil {
return err
}
if len(data) < e.opts.QueryLimit {
return nil
}
last = data[len(data)-1].SerialNumber
}
return nil
}
func (e BatchBiz) Transforms(data []data.Direct) export.Batchs {
result := make(export.Batchs, len(data))
for i, val := range data {
result[i] = e.Transform(val)
}
return result
}
func (e BatchBiz) Transform(data data.Direct) *export.Batch {
return &export.Batch{
ResellerId: data.Batch.ResellerId,
ResellerName: data.Batch.Reseller.Name,
OursProductId: data.OursProductId,
OursProductTitle: data.OrderItem.OursProductTitle,
PlatformName: data.PlatformProduct.Platform.Name,
PlatformProductCode: data.PlatformProduct.Code,
PlatformProductName: data.PlatformProduct.Name,
Status: data.Status,
OrderOrderNumber: data.OrderOrderNumber,
SerialNumber: data.SerialNumber,
TerminalAccount: data.TerminalAccount,
TradePrice: data.TradePrice,
PlatformPrice: data.PlatformPrice,
CreateTime: data.CreateTime.Format("2006-01-02 15:04:05"),
ExecuteTime: data.ExecuteTime.Format("2006-01-02 15:04:05"),
Remark: data.Batch.Remark,
}
}
func (e BatchBiz) HistoryTransforms(data []data.HistoryDirect) export.Batchs {
result := make(export.Batchs, len(data))
for i, val := range data {
result[i] = e.HistoryTransform(val)
}
return result
}
func (e BatchBiz) HistoryTransform(data data.HistoryDirect) *export.Batch {
return &export.Batch{
ResellerId: data.Batch.ResellerId,
ResellerName: data.Batch.Reseller.Name,
OursProductId: data.OursProductId,
OursProductTitle: data.OrderItem.OursProductTitle,
PlatformName: data.PlatformProduct.Platform.Name,
PlatformProductCode: data.PlatformProduct.Code,
PlatformProductName: data.PlatformProduct.Name,
Status: data.Status,
OrderOrderNumber: data.OrderOrderNumber,
SerialNumber: data.SerialNumber,
TerminalAccount: data.TerminalAccount,
TradePrice: data.TradePrice,
PlatformPrice: data.PlatformPrice,
CreateTime: data.CreateTime.Format("2006-01-02 15:04:05"),
ExecuteTime: data.ExecuteTime.Format("2006-01-02 15:04:05"),
Remark: data.Batch.Remark,
}
}

View File

@ -1,36 +0,0 @@
package biz
import (
"excel_export/data"
"fmt"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
func batchBiz() *BatchBiz {
db := data.Conn()
repo := data.NewDirectRepo(db)
historyRepo := data.NewHistoryDirectRepo(db)
return NewBatchBiz(repo, historyRepo, nil)
}
func TestBatchBiz_Export(t *testing.T) {
biz := batchBiz()
begin := time.Date(2021, 10, 1, 0, 0, 0, 0, time.Local)
end := time.Date(2022, 12, 1, 0, 0, 0, 0, time.Local)
err := biz.Export(begin, end)
assert.Nil(t, err)
for i := 0; i <= biz.FileSize(); i++ {
fileName := fmt.Sprintf(biz.FileName(), i)
assert.FileExists(t, fileName)
//清理文件
os.Remove(fileName)
}
}

View File

@ -1,90 +0,0 @@
package biz
import (
"context"
"excel_export/data"
"excel_export/export"
"fmt"
"time"
)
func NewCardBiz(repo *data.CardRepo, opts *ExportOpts) *CardBiz {
biz := &CardBiz{
repo: repo,
}
if opts == nil {
opts = DefaultOpts
}
opts.Tag = "card"
biz.opts = opts
return biz
}
type CardBiz struct {
repo *data.CardRepo
baseExportBiz
}
func (e *CardBiz) Export(begin, end time.Time) error {
e.setFileArg(begin)
file := export.NewExport(e.fileName, e.opts.ExcelMaxRow)
if err := file.Open(); err != nil {
return err
}
if err := file.Title(export.Cards{}.Title()); err != nil {
return err
}
if err := e.exportData(file, begin, end); err != nil {
return err
}
e.fileSize = file.Index()
return nil
}
func (e *CardBiz) exportData(export *export.Export, begin, end time.Time) error {
var last string
for true {
//导出最近7天数据
data, err := e.repo.List(context.Background(), begin, end, e.opts.QueryLimit, last)
if err != nil {
return fmt.Errorf("获取导出数据错误:%w", err)
}
if err := export.Export(e.Transforms(data)); err != nil {
return err
}
if len(data) < e.opts.QueryLimit {
return nil
}
last = data[len(data)-1].OrderNumber
}
return nil
}
func (e CardBiz) Transforms(data []data.Card) export.Cards {
result := make(export.Cards, len(data))
for i, val := range data {
result[i] = e.Transform(val)
}
return result
}
func (e CardBiz) Transform(data data.Card) *export.Card {
return &export.Card{
OrderNumber: data.OrderNumber,
ResellerOrderNumber: data.ResellerOrderNumber,
Amount: data.Amount,
Quantity: data.Quantity,
Status: data.Status,
ResellerId: data.ResellerId,
ResellerName: data.Reseller.Name,
OursProductId: data.OursProductId,
OursProductTitle: data.OurProduct.Name,
CreateTime: time.Unix(data.CreateTime, 0).Format("2006-01-02 15:04:05"),
}
}

View File

@ -1,35 +0,0 @@
package biz
import (
"excel_export/data"
"fmt"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
func cardBiz() *CardBiz {
db := data.Conn()
repo := data.NewCardRepo(db)
return NewCardBiz(repo, nil)
}
func TestCardBiz_Export(t *testing.T) {
biz := cardBiz()
begin := time.Date(2012, 10, 1, 0, 0, 0, 0, time.Local)
end := time.Date(2022, 12, 1, 0, 0, 0, 0, time.Local)
err := biz.Export(begin, end)
assert.Nil(t, err)
for i := 0; i <= biz.FileSize(); i++ {
fileName := fmt.Sprintf(biz.FileName(), i)
assert.FileExists(t, fileName)
//清理文件
os.Remove(fileName)
}
}

View File

@ -1,41 +0,0 @@
package biz
import (
"fmt"
"os"
"time"
)
const (
QueryLImit = 10
ExcelMaxRow = 10
)
func getFielName(path, filename string, time time.Time) string {
if path == "" {
path, _ = os.Getwd()
}
date := time.Format("20060102")
return fmt.Sprintf("%s/%s_%s_%%d.xlsx", path, filename, date)
}
type ExportOpts struct {
QueryLimit int
ExcelMaxRow int
Path string
Tag string
}
var DefaultOpts *ExportOpts = &ExportOpts{
QueryLimit: QueryLImit,
ExcelMaxRow: ExcelMaxRow,
Tag: "export",
}
func NewExportOpts(query, limit int, path string) *ExportOpts {
return &ExportOpts{
QueryLimit: query,
ExcelMaxRow: limit,
Path: path,
}
}

96
biz/config/config.go Normal file
View File

@ -0,0 +1,96 @@
package config
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"time"
)
type (
Config struct {
Systems []System
}
System struct {
Name string
Db string
Jobs []Job
}
Job struct {
Name string
Tasks []Task
File string
}
Task struct {
PK string
Sql string
Timestamp bool
Elt string
Order string
}
)
func (c Config) GetSystem(system string) (System, error) {
for _, s := range c.Systems {
if s.Name == system {
return s, nil
}
}
return System{}, errors.New("没有找到相关配置:" + system)
}
func (s System) GetJob(job string) (Job, error) {
for _, j := range s.Jobs {
if j.Name == job {
return j, nil
}
}
return Job{}, errors.New("没有找到相关配置:" + job)
}
func (t Task) GetSql(params map[string]interface{}) string {
sql := t.Sql
m := regexp.MustCompile("({[a-zA-Z0-9]+})")
if strings.Trim(t.Elt, " ") != "" {
wehre := m.ReplaceAllFunc([]byte(t.Elt), func(b []byte) []byte {
field := string(b[1 : len(b)-1])
if val, ok := params[field]; ok {
return []byte(t.getParam(val))
}
return b
})
sql = sql + " where " + string(wehre)
}
if strings.Trim(t.Order, " ") != "" {
sql = sql + " order by " + t.Order
}
return sql
}
func (t Task) getParam(parm interface{}) string {
switch p := parm.(type) {
case time.Time:
if t.Timestamp {
return strconv.FormatInt(p.Unix(), 10)
} else {
return p.Format("2006-01-02 15:04:05")
}
case string:
return p
case int:
return strconv.Itoa(p)
case int32:
return strconv.FormatInt(int64(p), 10)
default:
return fmt.Sprint(p)
}
}

35
biz/config/task_test.go Normal file
View File

@ -0,0 +1,35 @@
package config
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
"time"
)
func TestTask_GetSql(t *testing.T) {
task := Task{
Sql: "select * from order",
Elt: "create_time between {begin} and {end} and o.order_number > {last}",
Timestamp: true,
Order: " ",
}
begin, _ := time.Parse("2006-01-02 15:04:05", "2023-01-01 00:00:00")
end := begin.Add(time.Hour * 24)
sql := task.GetSql(map[string]interface{}{"begin": begin, "end": end, "last": 0})
assert.Equal(t, fmt.Sprintf("select * from order where create_time between %d and %d and o.order_number > 0", begin.Unix(), end.Unix()), sql)
}
func TestTask_GetSql_OrderBy(t *testing.T) {
task := Task{
Sql: "select * from order",
Elt: "create_time between '{begin}' and '{end}'",
Timestamp: false,
Order: "create_time",
}
begin, _ := time.Parse("2006-01-02 15:04:05", "2023-01-01 00:00:00")
end := begin.Add(time.Hour * 24)
sql := task.GetSql(map[string]interface{}{"begin": begin, "end": end})
assert.Equal(t, fmt.Sprintf("select * from order where create_time between '%s' and '%s' order by create_time", begin.Format("2006-01-02 15:04:05"), end.Format("2006-01-02 15:04:05")), sql)
}

View File

@ -1,159 +0,0 @@
package biz
import (
"context"
"excel_export/data"
"excel_export/export"
"fmt"
"time"
)
func NewDirectBiz(repo *data.DirectRepo, historyRepo *data.HistoryDirectRepo, opts *ExportOpts) *DirectBiz {
biz := &DirectBiz{
repo: repo,
historyRepo: historyRepo,
}
if opts == nil {
opts = DefaultOpts
}
opts.Tag = "direct"
biz.opts = opts
return biz
}
type DirectBiz struct {
repo *data.DirectRepo
historyRepo *data.HistoryDirectRepo
baseExportBiz
}
func (e *DirectBiz) FileSize() int {
return e.fileSize
}
func (e *DirectBiz) Export(begin, end time.Time) error {
e.setFileArg(begin)
file := export.NewExport(e.fileName, e.opts.ExcelMaxRow)
if err := file.Open(); err != nil {
return err
}
if err := file.Title(export.Directs{}.Title()); err != nil {
return err
}
//导出历史订单数据
if err := e.exportHistoryData(file, begin, end); err != nil {
return err
}
//导出最近7天数据
if err := e.exportData(file, begin, end); err != nil {
return err
}
e.fileSize = file.Index()
return nil
}
func (e *DirectBiz) exportHistoryData(export *export.Export, begin, end time.Time) error {
var last string
for true {
//导出最近7天数据
data, err := e.historyRepo.List(context.Background(), begin, end, e.opts.QueryLimit, last)
if err != nil {
return fmt.Errorf("获取导出数据错误:%w", err)
}
if err := export.Export(e.HistoryTransforms(data)); err != nil {
return err
}
if len(data) < e.opts.QueryLimit {
return nil
}
last = data[len(data)-1].SerialNumber
}
return nil
}
func (e *DirectBiz) exportData(export *export.Export, begin, end time.Time) error {
var last string
for true {
//导出最近7天数据
data, err := e.repo.List(context.Background(), begin, end, e.opts.QueryLimit, last)
if err != nil {
return fmt.Errorf("获取导出数据错误:%w", err)
}
if err := export.Export(e.Transforms(data)); err != nil {
return err
}
if len(data) < e.opts.QueryLimit {
return nil
}
last = data[len(data)-1].SerialNumber
}
return nil
}
func (e DirectBiz) Transforms(data []data.Direct) export.Directs {
result := make(export.Directs, len(data))
for i, val := range data {
result[i] = e.Transform(val)
}
return result
}
func (e DirectBiz) Transform(data data.Direct) *export.Direct {
return &export.Direct{
ResellerId: data.Order.ResellerId,
ResellerName: data.Order.Reseller.Name,
ResellerOrderNumber: data.Order.ResellerOrderNumber,
OursProductId: data.OursProductId,
OursProductTitle: data.OrderItem.OursProductTitle,
PlatformName: data.PlatformProduct.Platform.Name,
PlatformProductCode: data.PlatformProduct.Code,
PlatformProductName: data.PlatformProduct.Name,
Status: data.Status,
OrderOrderNumber: data.OrderOrderNumber,
SerialNumber: data.SerialNumber,
TerminalAccount: data.TerminalAccount,
TradePrice: data.TradePrice,
PlatformPrice: data.PlatformPrice,
CreateTime: data.CreateTime.Format("2006-01-02 15:04:05"),
ExecuteTime: data.ExecuteTime.Format("2006-01-02 15:04:05"),
Remark: data.Order.Remark.Remark,
}
}
func (e DirectBiz) HistoryTransforms(data []data.HistoryDirect) export.Directs {
result := make(export.Directs, len(data))
for i, val := range data {
result[i] = e.HistoryTransform(val)
}
return result
}
func (e DirectBiz) HistoryTransform(data data.HistoryDirect) *export.Direct {
return &export.Direct{
ResellerId: data.Order.ResellerId,
ResellerName: data.Order.Reseller.Name,
ResellerOrderNumber: data.Order.ResellerOrderNumber,
OursProductId: data.OursProductId,
OursProductTitle: data.OrderItem.OursProductTitle,
PlatformName: data.PlatformProduct.Platform.Name,
PlatformProductCode: data.PlatformProduct.Code,
PlatformProductName: data.PlatformProduct.Name,
Status: data.Status,
OrderOrderNumber: data.OrderOrderNumber,
SerialNumber: data.SerialNumber,
TerminalAccount: data.TerminalAccount,
TradePrice: data.TradePrice,
PlatformPrice: data.PlatformPrice,
CreateTime: data.CreateTime.Format("2006-01-02 15:04:05"),
ExecuteTime: data.ExecuteTime.Format("2006-01-02 15:04:05"),
Remark: data.Order.Remark.Remark,
}
}

View File

@ -1,36 +0,0 @@
package biz
import (
"excel_export/data"
"fmt"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
func directBiz() *DirectBiz {
db := data.Conn()
repo := data.NewDirectRepo(db)
historyRepo := data.NewHistoryDirectRepo(db)
return NewDirectBiz(repo, historyRepo, nil)
}
func TestDirectBiz_Export(t *testing.T) {
biz := directBiz()
begin := time.Date(2022, 10, 1, 0, 0, 0, 0, time.Local)
end := time.Date(2022, 12, 1, 0, 0, 0, 0, time.Local)
err := biz.Export(begin, end)
assert.Nil(t, err)
for i := 0; i <= biz.FileSize(); i++ {
fileName := fmt.Sprintf(biz.FileName(), i)
assert.FileExists(t, fileName)
//清理文件
os.Remove(fileName)
}
}

96
biz/export/excel.go Normal file
View File

@ -0,0 +1,96 @@
package export
import (
"errors"
"github.com/tealeg/xlsx/v3"
)
const Sheet_Name = "sheet_1"
type Excel struct {
f *File
count int //总数
isNew bool
titles []string
file *xlsx.File
sheet *xlsx.Sheet
}
func NewExcel(fileName string, limit int, param map[string]string) *Excel {
return &Excel{
f: NewFile(fileName, limit, param),
}
}
func (e *Excel) slice() {
if e.f.slice() {
e.reset()
}
}
func (e *Excel) reset() {
e.save()
e.f.NextFile()
e.Open()
e.WriteTitle(nil)
e.slice()
}
func (e *Excel) Open() error {
var err error
if e.f.IsFileExist() {
e.file, err = xlsx.OpenFile(e.f.FileName())
if err != nil {
return err
}
} else {
e.isNew = true
e.file = xlsx.NewFile()
}
var ok bool
e.sheet, ok = e.file.Sheet[Sheet_Name]
if !ok {
e.sheet, err = e.file.AddSheet(Sheet_Name)
if err != nil {
return err
}
} else {
//需要处理偏移数据
e.f.SetRow(e.sheet.MaxRow - 1)
}
return nil
}
func (e *Excel) save() error {
return e.file.Save(e.f.FileName())
}
func (e *Excel) WriteTitle(titles []string) error {
if e.file == nil || e.sheet == nil {
return errors.New("没有执行open方法")
}
if titles != nil {
e.titles = titles
}
if e.titles != nil && e.isNew {
e.Write(e.titles)
}
return nil
}
func (e *Excel) Write(data interface{}) error {
if e.f.slice() {
e.reset()
}
row := e.sheet.AddRow()
row.WriteSlice(data, -1)
return nil
}
func (e *Excel) Close() error {
return e.save()
}

View File

@ -0,0 +1,34 @@
package export
type ExcelExporter struct {
mFetcher DataFetcher
file FileAdapter
}
func NewExcelExporter(fetcher DataFetcher, file FileAdapter) DataExporter {
return &ExcelExporter{
mFetcher: fetcher,
file: file,
}
}
func (ee *ExcelExporter) Fetcher(fetcher DataFetcher) {
ee.mFetcher = fetcher
}
func (ee *ExcelExporter) File(file FileAdapter) {
ee.file = file
}
func (ee *ExcelExporter) Export(sql string) error {
rows := ee.mFetcher.Fetch(sql)
//fmt.Printf("Excel Exporter.Excel, got %v rows\n", len(rows))
ee.file.Open()
ee.file.WriteTitle([]string{"字段1", "字段2", "字段3", "字段4", "字段5"})
for _, v := range rows {
// fmt.Printf(" 行号: %d 值: %s\n", i+1, v)
ee.file.Write(v)
}
ee.file.Close()
return nil
}

View File

@ -0,0 +1,24 @@
package export
import (
"github.com/stretchr/testify/assert"
"os"
"testing"
)
func TestExcelExporter_Export(t *testing.T) {
data := NewMysqlDataFetcher("aaa")
pwd, _ := os.Getwd()
file := NewExcel(pwd+"/aa-{begin}.xlsx", 5, map[string]string{"begin": "202301"})
e := NewExcelExporter(data, file)
err := e.Export("aa")
assert.Nil(t, err)
assert.FileExists(t, pwd+"/aa-202301_0.xlsx")
assert.FileExists(t, pwd+"/aa-202301_1.xlsx")
assert.NoFileExists(t, pwd+"/aa-202301_2.xlsx")
_ = os.Remove(pwd + "/aa-202301_0.xlsx")
_ = os.Remove(pwd + "/aa-202301_1.xlsx")
}

31
biz/export/excel_test.go Normal file
View File

@ -0,0 +1,31 @@
package export
import (
"github.com/stretchr/testify/assert"
"os"
"testing"
)
func TestExcel_Write(t *testing.T) {
pwd, _ := os.Getwd()
e := NewExcel(pwd+"/aa-{begin}.xlsx", 5, map[string]string{"begin": "202301"})
e.Open()
e.WriteTitle([]string{"姓名", "年龄"})
data := make([]interface{}, 2)
data[0] = "张三"
for i := 0; i < 9; i++ {
data[1] = 10 + i
e.Write(data)
}
e.Close()
assert.FileExists(t, pwd+"/aa-202301_0.xlsx")
assert.FileExists(t, pwd+"/aa-202301_1.xlsx")
assert.NoFileExists(t, pwd+"/aa-202301_2.xlsx")
_ = os.Remove(pwd + "/aa-202301_0.xlsx")
_ = os.Remove(pwd + "/aa-202301_1.xlsx")
}

19
biz/export/export.go Normal file
View File

@ -0,0 +1,19 @@
package export
type (
DataExporter interface {
Fetcher(fetcher DataFetcher)
File(file FileAdapter)
Export(sql string) error
}
DataFetcher interface {
Fetch(sql string) []interface{}
}
FileAdapter interface {
Open() error
WriteTitle([]string) error
Write(interface{}) error
Close() error
}
)

88
biz/export/file.go Normal file
View File

@ -0,0 +1,88 @@
package export
import (
"os"
"regexp"
"strconv"
)
type FileOpts struct {
fileName string
limit int
row int
}
func (f *FileOpts) slice() bool {
f.row++
if f.row > f.limit+1 { // +1 排除标题行
f.row = 0
return true
}
return false
}
type File struct {
FileOpts
param map[string]string
index int
}
func NewFile(name string, limit int, param map[string]string) *File {
return &File{
FileOpts: FileOpts{
fileName: name,
limit: limit,
},
param: param,
}
}
func (f *File) SetIndex(index int) *File {
f.index = index
return f
}
func (f *File) SetRow(row int) *File {
f.row = row
return f
}
func (f *File) NextFile() *File {
f.index++
return f
}
func (f *File) FileName() string {
m := regexp.MustCompile("({[a-zA-Z0-9]+})")
name := m.ReplaceAllFunc([]byte(f.fileName), func(b []byte) []byte {
field := string(b[1 : len(b)-1])
if val, ok := f.param[field]; ok {
return []byte(val)
}
return b
})
ex := regexp.MustCompile("(\\..*)")
name = ex.ReplaceAllFunc(name, func(b []byte) []byte {
i := []byte("_" + strconv.Itoa(f.index))
ret := make([]byte, len(b)+len(i))
copy(ret, i)
copy(ret[len(i):], b)
return ret
})
return string(name)
}
func (f *File) IsFileExist() bool {
_, err := os.Stat(f.FileName())
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}

39
biz/export/file_test.go Normal file
View File

@ -0,0 +1,39 @@
package export
import (
"github.com/stretchr/testify/assert"
"os"
"testing"
)
func TestFile_FileName(t *testing.T) {
f := NewFile("/usr/file-{begin}-{end}.xlsx", 10, map[string]string{
"begin": "20230404",
"end": "20230404",
})
assert.Equal(t, "/usr/file-20230404-20230404_0.xlsx", f.FileName())
f.NextFile()
assert.Equal(t, "/usr/file-20230404-20230404_1.xlsx", f.FileName())
}
func TestFile_IsFileExist(t *testing.T) {
gwd, _ := os.Getwd()
f := NewFile(gwd+"/file-{begin}-{end}.xlsx", 10, map[string]string{
"begin": "20230404",
"end": "20230404",
})
assert.False(t, f.IsFileExist())
path := gwd + "/file-20230404-20230404_0.xlsx"
_, err := os.Create(path)
assert.Nil(t, err)
assert.FileExists(t, path)
assert.True(t, f.IsFileExist())
err = os.Remove(path)
assert.Nil(t, err)
}

View File

@ -0,0 +1,22 @@
package export
import (
"math/rand"
)
type MysqlDataFetcher struct {
Config string
}
func (mf *MysqlDataFetcher) Fetch(sql string) []interface{} {
rows := make([]interface{}, 0, 6)
// 插入6个随机数组成的切片模拟查询要返回的数据集
rows = append(rows, rand.Perm(5), rand.Perm(5), rand.Perm(5), rand.Perm(5), rand.Perm(5), rand.Perm(5))
return rows
}
func NewMysqlDataFetcher(configStr string) DataFetcher {
return &MysqlDataFetcher{
Config: configStr,
}
}

View File

@ -1,150 +0,0 @@
package biz
import (
"context"
"excel_export/data"
"excel_export/export"
"fmt"
"time"
)
func NewOrderBiz(repo *data.OrderRepo, historyRepo *data.HistoryOrderRepo, opts *ExportOpts) *OrderBiz {
biz := &OrderBiz{
repo: repo,
historyRepo: historyRepo,
}
biz.opts = DefaultOpts
if opts != nil {
biz.opts = opts
}
return biz
}
type OrderBiz struct {
repo *data.OrderRepo
historyRepo *data.HistoryOrderRepo
baseExportBiz
}
func (e *OrderBiz) Export(begin, end time.Time) error {
e.setFileArg(begin)
file := export.NewExport(e.fileName, e.opts.ExcelMaxRow)
if err := file.Open(); err != nil {
return err
}
if err := file.Title(export.Orders{}.Title()); err != nil {
return err
}
//导出历史订单数据
if err := e.exportHistoryData(file, begin, end); err != nil {
return err
}
//导出最近7天数据
if err := e.exportData(file, begin, end); err != nil {
return err
}
e.fileSize = file.Index()
return nil
}
func (e *OrderBiz) exportHistoryData(export *export.Export, begin, end time.Time) error {
var last string
for true {
//导出最近7天数据
data, err := e.historyRepo.List(context.Background(), begin, end, e.opts.QueryLimit, last)
if err != nil {
return fmt.Errorf("获取导出数据错误:%w", err)
}
if err := export.Export(e.HistoryTransforms(data)); err != nil {
return err
}
if len(data) < e.opts.QueryLimit {
return nil
}
last = data[len(data)-1].OrderNumber
}
return nil
}
func (e *OrderBiz) exportData(export *export.Export, begin, end time.Time) error {
var last string
for true {
//导出最近7天数据
data, err := e.repo.List(context.Background(), begin, end, e.opts.QueryLimit, last)
if err != nil {
return fmt.Errorf("获取导出数据错误:%w", err)
}
if err := export.Export(e.Transforms(data)); err != nil {
return err
}
if len(data) < e.opts.QueryLimit {
return nil
}
last = data[len(data)-1].OrderNumber
}
return nil
}
func (e OrderBiz) Transforms(data []data.Order) export.Orders {
result := make(export.Orders, len(data))
for i, val := range data {
result[i] = e.Transform(val)
}
return result
}
func (e OrderBiz) Transform(order data.Order) *export.Order {
return &export.Order{
OrderNumber: order.OrderNumber,
ResellerId: order.ResellerId,
ResellerName: order.Reseller.Name,
ResellerOrderNumber: order.ResellerOrderNumber,
Amount: order.Amount,
Account: order.Account,
Quantity: order.Quantity,
Status: order.Status,
PayStatus: order.PayStatus,
CreateTime: time.Unix(order.CreateTime, 0).Format("2006-01-02 15:04:05"),
FinishTime: time.Unix(order.FinishTime, 0).Format("2006-01-02 15:04:05"),
OursProductId: order.Item.OursProductId,
OursProductTitle: order.Item.OursProductTitle,
Remark: order.Remark.Remark,
}
}
func (e OrderBiz) HistoryTransforms(data []data.HistoryOrder) export.Orders {
result := make(export.Orders, len(data))
for i, val := range data {
result[i] = e.HistoryTransform(val)
}
return result
}
func (e OrderBiz) HistoryTransform(order data.HistoryOrder) *export.Order {
return &export.Order{
OrderNumber: order.OrderNumber,
ResellerId: order.ResellerId,
ResellerName: order.Reseller.Name,
ResellerOrderNumber: order.ResellerOrderNumber,
Amount: order.Amount,
Account: order.Account,
Quantity: order.Quantity,
Status: order.Status,
PayStatus: order.PayStatus,
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
FinishTime: order.FinishTime.Format("2006-01-02 15:04:05"),
OursProductId: order.Item.OursProductId,
OursProductTitle: order.Item.OursProductTitle,
Remark: order.Remark.Remark,
}
}

View File

@ -1,35 +0,0 @@
package biz
import (
"excel_export/data"
"fmt"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
func orderBiz() *OrderBiz {
db := data.Conn()
repo := data.NewOrderRepo(db)
historyRepo := data.NewHistoryOrderRepo(db)
return NewOrderBiz(repo, historyRepo, nil)
}
func TestOrderBiz_Export(t *testing.T) {
biz := orderBiz()
begin := time.Date(2022, 8, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2022, 8, 2, 10, 0, 0, 0, time.UTC)
err := biz.Export(begin, end)
assert.Nil(t, err)
for i := 0; i <= biz.FileSize(); i++ {
fileName := fmt.Sprintf(biz.FileName(), i)
assert.FileExists(t, fileName)
//清理文件
os.Remove(fileName)
}
}

224
config/config.yaml Normal file
View File

@ -0,0 +1,224 @@
system:
- name: "直连天下"
db: ""
jobs:
- name: "卡密订单"
tasks:
- sql: >-
SELECT
order_number AS `订单号` ,
reseller_order_number AS `分销商订单号`,
order_card.price AS `扣款价格`,
order_card.`quantity` as `数量`,
case order_card.`status`
WHEN 0 THEN '充值中'
WHEN 1 THEN '充值成功'
WHEN -2 THEN '充值失败'
ELSE order_card.`status`
END
AS 充值状态,
r.`name` AS `分销商名称` ,
o.`name` AS `订单商品名称`,
FROM_UNIXTIME(order_card.`create_time`) AS `创建时间`
FROM
order_card
LEFT JOIN reseller r ON order_card.reseller_id = r.id
LEFT JOIN ours_product o ON o.id = order_card.ours_product_id
timestamp: true
elt: "order_card.create_time BETWEEN {begin} AND {end} and order_card.order_number > {last}"
order: "order_card.create_time,order_card.order_number"
file: "直连天下-卡密订单-${month}.xlsx"
- name: "上游订单"
tasks:
- sql: >-
SELECT
o.reseller_id as `分销商id`,
r.`name` as `分销商名称`,
o.reseller_order_number as `商户订单号`,
od.ours_product_id as `我们的商品id`,
oi.ours_product_title as `订单商品名称`,
oi.ours_product_title as `商品名称`,
p.name as `接口平台`,
CASE od.`status`
WHEN -10 THEN '取消充值(失败)'
WHEN -6 THEN '手动失败'
WHEN -5 THEN '手动重试'
WHEN -3 THEN '卡单'
WHEN -2 THEN '失败重试'
WHEN -1 THEN '充值失败'
WHEN 0 THEN '待充值'
WHEN 1 THEN '充值成功'
WHEN 2 THEN '充值中'
else od.`status`
END
AS 充值状态,
pp.`code` as `接口平台产品编码`,
pp.`name` as `接口平台产品名称`,
od.order_order_number as `系统订单号`,
od.serial_number as `流水号`,
od.terminal_account as `充值账号`,
od.trade_price as `成交价格`,
od.platform_price as `接口平台价格`,
od.create_time as `创建时间`,
od.execute_time as `执行时间`,
re.remark as `备注`
FROM
history_order_direct od
right join `history_order` o on o.order_number = od.order_order_number
left join history_order_item oi on o.order_number = oi.order_order_number
left join platform_product pp on pp.id = od.platform_product_id
left join platform p on pp.platform_id = p.id
left join reseller r on r.id = o.reseller_id
left join history_order_remark re on o.order_number = re.order_number
timestamp: false
elt: "od.create_time BETWEEN '{begin}' and '{end}' and od.serial_number > {last}"
order: "od.create_time,od.serial_number"
- sql: >-
SELECT
o.reseller_id as `分销商id`,
r.`name` as `分销商名称`,
o.reseller_order_number as `商户订单号`,
od.ours_product_id as `我们的商品id`,
oi.ours_product_title as `订单商品名称`,
op.NAME as `商品名称`,
p.name as `接口平台`,
CASE od.`status`
WHEN -10 THEN '取掉充值(失败)'
WHEN -6 THEN '手动失败'
WHEN -5 THEN '手动重试'
WHEN -3 THEN '卡单'
WHEN -2 THEN '失败重试'
WHEN -1 THEN '充值失败'
WHEN 0 THEN '待充值'
WHEN 1 THEN '充值成功'
WHEN 2 THEN '充值中'
else od.`status`
END
AS 充值状态,
pp.`code` as `接口平台产品编码`,
pp.`name` as `接口平台产品名称`,
od.order_order_number as `系统订单号`,
od.serial_number as `流水号`,
od.terminal_account as `充值账号`,
od.trade_price as `成交价格`,
od.platform_price as `接口平台价格`,
od.create_time as `创建时间`,
od.execute_time as `执行时间`,
re.remark as `备注`
FROM
order_direct od
right join `order` o on o.order_number = od.order_order_number
left join order_item oi on o.order_number = oi.order_order_number
left JOIN ours_product op ON od.ours_product_id = op.id
left join platform_product pp on pp.id = od.platform_product_id
left join platform p on pp.platform_id = p.id
left join reseller r on r.id = o.reseller_id
left join order_remark re on o.order_number = re.order_number
timestamp: false
elt: "od.create_time BETWEEN '{begin}' and '{end}' and od.serial_number > {last}"
order: "od.create_time,od.serial_number"
file: "直连天下-上游订单-${month}.xlsx"
- name: "下游订单"
tasks:
- sql: >-
SELECT
o.order_number as `订单号`,
o.reseller_id as `分销商id`,
r.`name` as `分销商名称`,
o.reseller_order_number as `商户订单号`,
o.amount as `扣款金额`,
oi.ours_product_id as `我们的商品id`,
oi.ours_product_title as `订单商品名称`,
oi.ours_product_title as `商品名称`,
o.account as `充值账号`,
o.quantity as `数量`,
CASE o.`status`
WHEN -2 THEN '全部失败'
WHEN -1 THEN '关闭订单'
WHEN 0 THEN '待充值'
WHEN 1 THEN '订单完成'
WHEN 2 THEN '部分成功'
END
AS 充值状态,
CASE o.pay_status
WHEN -2 THEN '退款失败'
WHEN -1 THEN '支付失败'
WHEN 0 THEN '未支付'
WHEN 1 THEN '支付中'
WHEN 2 THEN '支付成功'
WHEN 3 THEN '退款中'
WHEN 4 THEN '全部退款'
WHEN 5 THEN '部分退款'
END
AS 支付状态,
o.create_time as `创建时间`,
o.finish_time as `完成时间`,
re.remark as `备注`
FROM
`history_order` o
left join history_order_item oi on oi.order_order_number = o.order_number
left join reseller r on r.id = o.reseller_id
left join history_order_remark re on o.order_number = re.order_number
elt: "o.`create_time` >= '{begin}' and o.create_time < '{end}' and o.order_number > {last}"
timestamp: false
order: "o.create_time,o.order_number"
- sql: >-
SELECT
o.order_number as `订单号`,
o.reseller_id as `分销商id`,
r.`name` as `分销商名称`,
o.reseller_order_number as `商户订单号`,
o.amount as `扣款金额`,
op.id as `我们的商品id`,
oi.ours_product_title as `订单商品名称`,
op.NAME as `商品名称`,
o.account as `充值账号`,
o.quantity as `数量`,
CASE o.`status`
WHEN -2 THEN '全部失败'
WHEN -1 THEN '关闭订单'
WHEN 0 THEN '待充值'
WHEN 1 THEN '订单完成'
WHEN 2 THEN '部分成功'
END
AS 充值状态,
CASE o.pay_status
WHEN -2 THEN '退款失败'
WHEN -1 THEN '支付失败'
WHEN 0 THEN '未支付'
WHEN 1 THEN '支付中'
WHEN 2 THEN '支付成功'
WHEN 3 THEN '退款中'
WHEN 4 THEN '全部退款'
WHEN 5 THEN '部分退款'
END
AS 支付状态,
from_unixtime(o.create_time) as `创建时间`,
from_unixtime(o.finish_time) as `完成时间`,
re.remark as `备注`
FROM
`order` o
left join order_item oi on oi.order_order_number = o.order_number
left JOIN ours_product op ON oi.ours_product_id = op.id
left join reseller r on r.id = o.reseller_id
left join order_remark re on o.order_number = re.order_number
elt: "o.`create_time` >= {begin} and o.create_time < {end} and o.order_number > {last}"
timestamp: true
order: "o.create_time,o.order_number"
file: "直连天下-下游订单-${month}.xlsx"
- name: "营销系统"
db: ""
jobs:
- name: "订单信息"
tasks:
- sql: ""
elt: ""
file: "营销系统-订单信息-${month}.xlsx"

View File

@ -1,57 +0,0 @@
package export
type Batch struct {
ResellerId int
ResellerName string
OursProductId int
OursProductTitle string
PlatformName string
PlatformProductCode string
PlatformProductName string
Status int
OrderOrderNumber string
SerialNumber string
TerminalAccount string
TradePrice float32
PlatformPrice float32
CreateTime string
ExecuteTime string
DingTalkSn string
Remark string
}
type Batchs []*Batch
func (o Batchs) Title() []string {
return []string{
"分销商id",
"分销商名称",
"我们的商品id",
"订单商品名称",
"接口平台",
"接口平台产品编码",
"接口平台产品名称",
"充值状态",
"系统订单号",
"流水号",
"充值账号",
"成交价格",
"接口平台价格",
"创建时间",
"执行时间",
"钉钉审批序号",
"批量备注",
}
}
func (o Batchs) Size() int {
return len(o)
}
func (o Batchs) Data() []interface{} {
d := make([]interface{}, o.Size())
for i, v := range o {
d[i] = v
}
return d
}

View File

@ -1,43 +0,0 @@
package export
type Card struct {
OrderNumber string
ResellerOrderNumber string
Amount float32
Quantity int
Status int
ResellerId int
ResellerName string
OursProductId int
OursProductTitle string
CreateTime string
}
type Cards []*Card
func (o Cards) Title() []string {
return []string{
"订单号",
"分销商订单号",
"扣款价格",
"数量",
"充值状态",
"分销商编号",
"分销商名称",
"订单商品编号",
"订单商品名称",
"创建时间",
}
}
func (o Cards) Size() int {
return len(o)
}
func (o Cards) Data() []interface{} {
d := make([]interface{}, o.Size())
for i, v := range o {
d[i] = v
}
return d
}

View File

@ -1,57 +0,0 @@
package export
type Direct struct {
ResellerId int64
ResellerName string
ResellerOrderNumber string
OursProductId int
OursProductTitle string
PlatformName string
PlatformProductCode string
PlatformProductName string
Status int
OrderOrderNumber string
SerialNumber string
TerminalAccount string
TradePrice float32
PlatformPrice float32
CreateTime string
ExecuteTime string
Remark string
}
type Directs []*Direct
func (o Directs) Title() []string {
return []string{
"分销商id",
"分销商名称",
"商户订单号",
"我们的商品id",
"订单商品名称",
"接口平台",
"接口平台产品编码",
"接口平台产品名称",
"充值状态",
"系统订单号",
"流水号",
"充值账号",
"成交价格",
"接口平台价格",
"创建时间",
"执行时间",
"备注",
}
}
func (o Directs) Size() int {
return len(o)
}
func (o Directs) Data() []interface{} {
d := make([]interface{}, o.Size())
for i, v := range o {
d[i] = v
}
return d
}

View File

@ -1,7 +0,0 @@
package export
type Entity interface {
Title() []string
Size() int
Data() []interface{}
}

View File

@ -1,133 +0,0 @@
package export
import (
"errors"
"fmt"
"github.com/tealeg/xlsx/v3"
"os"
)
const sheet_name = "sheet_1"
type Export struct {
index int
count int //总数
row int //当前执行的数据行
limit int //导出限制
path string
isNew bool
titles []string
file *xlsx.File
sheet *xlsx.Sheet
}
func NewExport(path string, limit int) *Export {
return &Export{
path: path,
limit: limit,
}
}
func (e *Export) Title(titles []string) error {
if e.file == nil || e.sheet == nil {
return errors.New("没有执行open方法")
}
if titles != nil {
e.titles = titles
}
if e.titles != nil && e.isNew {
row := e.sheet.AddRow()
row.WriteSlice(e.titles, -1)
}
return nil
}
func (e *Export) fileName() string {
return fmt.Sprintf(e.path, e.index)
}
func (e *Export) slice() {
e.row++
if e.row > e.limit {
e.row = 0
e.reset()
}
}
func (e *Export) reset() {
e.save()
e.index++
e.Open()
e.Title(nil)
e.slice()
}
func (e Export) Index() int {
return e.index
}
func (e *Export) Export(data Entity) error {
if e.file == nil || e.sheet == nil {
return errors.New("没有执行open方法")
}
e.count = e.count + data.Size()
for _, order := range data.Data() {
e.slice()
row := e.sheet.AddRow()
row.WriteStruct(order, -1)
}
return e.save()
}
func (e *Export) Open() error {
path := e.fileName()
exist, err := e.isFielExist(path)
if err != nil {
return err
}
if exist {
e.isNew = false
if e.file, err = xlsx.OpenFile(path); err != nil {
return err
}
} else {
e.isNew = true
e.file = xlsx.NewFile()
}
var ok bool
e.sheet, ok = e.file.Sheet[sheet_name]
if !ok {
e.sheet, err = e.file.AddSheet(sheet_name)
if err != nil {
return err
}
} else {
//需要处理偏移数据
e.row = e.sheet.MaxRow - 1
}
return nil
}
func (e *Export) isFielExist(file string) (bool, error) {
_, err := os.Stat(file)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
func (e *Export) save() error {
return e.file.Save(e.fileName())
}

View File

@ -1,231 +0,0 @@
package export
import (
"excel_export/data"
"fmt"
"github.com/stretchr/testify/assert"
"os"
"testing"
)
func TestExport_Export(t *testing.T) {
dir, _ := os.Getwd()
path := dir + "/aa_%d.xlsx"
e := NewExport(path, 4)
err := e.Open()
assert.Nil(t, err)
data := NewOrder([]data.Order{
{
OrderNumber: "123451",
Reseller: data.Reseller{
Id: 23456,
Name: "分销商名称",
},
ResellerOrderNumber: "123456448",
Amount: 12.22,
Account: "13512345678",
Quantity: 1,
Status: 1,
PayStatus: 1,
CreateTime: 1672744984,
FinishTime: 1672744984,
Item: data.OrderItem{
OursProductId: 101,
OursProductTitle: "商品名称",
},
Remark: data.OrderRemark{
Remark: "备注信息",
},
},
{
OrderNumber: "123452",
Reseller: data.Reseller{
Id: 23456,
Name: "分销商名称",
},
ResellerOrderNumber: "123456448",
Amount: 12.22,
Account: "13512345678",
Quantity: 1,
Status: 1,
PayStatus: 1,
CreateTime: 1672744984,
FinishTime: 1672744984,
Item: data.OrderItem{
OursProductId: 101,
OursProductTitle: "商品名称",
},
Remark: data.OrderRemark{
Remark: "备注信息",
},
},
{
OrderNumber: "123453",
Reseller: data.Reseller{
Id: 23456,
Name: "分销商名称",
},
ResellerOrderNumber: "123456448",
Amount: 12.22,
Account: "13512345678",
Quantity: 1,
Status: 1,
PayStatus: 1,
CreateTime: 1672744984,
FinishTime: 1672744984,
Item: data.OrderItem{
OursProductId: 101,
OursProductTitle: "商品名称",
},
Remark: data.OrderRemark{
Remark: "备注信息",
},
},
{
OrderNumber: "123454",
Reseller: data.Reseller{
Id: 23456,
Name: "分销商名称",
},
ResellerOrderNumber: "123456448",
Amount: 12.22,
Account: "13512345678",
Quantity: 1,
Status: 1,
PayStatus: 1,
CreateTime: 1672744984,
FinishTime: 1672744984,
Item: data.OrderItem{
OursProductId: 101,
OursProductTitle: "商品名称",
},
Remark: data.OrderRemark{
Remark: "备注信息",
},
},
{
OrderNumber: "123455",
Reseller: data.Reseller{
Id: 23456,
Name: "分销商名称",
},
ResellerOrderNumber: "123456448",
Amount: 12.22,
Account: "13512345678",
Quantity: 1,
Status: 1,
PayStatus: 1,
CreateTime: 1672744984,
FinishTime: 1672744984,
Item: data.OrderItem{
OursProductId: 101,
OursProductTitle: "商品名称",
},
Remark: data.OrderRemark{
Remark: "备注信息",
},
},
{
OrderNumber: "123456",
Reseller: data.Reseller{
Id: 23456,
Name: "分销商名称",
},
ResellerOrderNumber: "123456448",
Amount: 12.22,
Account: "13512345678",
Quantity: 1,
Status: 1,
PayStatus: 1,
CreateTime: 1672744984,
FinishTime: 1672744984,
Item: data.OrderItem{
OursProductId: 101,
OursProductTitle: "商品名称",
},
Remark: data.OrderRemark{
Remark: "备注信息",
},
},
{
OrderNumber: "123457",
Reseller: data.Reseller{
Id: 23456,
Name: "分销商名称",
},
ResellerOrderNumber: "123456448",
Amount: 12.22,
Account: "13512345678",
Quantity: 1,
Status: 1,
PayStatus: 1,
CreateTime: 1672744984,
FinishTime: 1672744984,
Item: data.OrderItem{
OursProductId: 101,
OursProductTitle: "商品名称",
},
Remark: data.OrderRemark{
Remark: "备注信息",
},
},
{
OrderNumber: "123458",
Reseller: data.Reseller{
Id: 23456,
Name: "分销商名称",
},
ResellerOrderNumber: "123456448",
Amount: 12.22,
Account: "13512345678",
Quantity: 1,
Status: 1,
PayStatus: 1,
CreateTime: 1672744984,
FinishTime: 1672744984,
Item: data.OrderItem{
OursProductId: 101,
OursProductTitle: "商品名称",
},
Remark: data.OrderRemark{
Remark: "备注信息",
},
},
{
OrderNumber: "123459",
Reseller: data.Reseller{
Id: 23456,
Name: "分销商名称",
},
ResellerOrderNumber: "123456448",
Amount: 12.22,
Account: "13512345678",
Quantity: 1,
Status: 1,
PayStatus: 1,
CreateTime: 1672744984,
FinishTime: 1672744984,
Item: data.OrderItem{
OursProductId: 101,
OursProductTitle: "商品名称",
},
Remark: data.OrderRemark{
Remark: "备注信息",
},
},
})
err = e.Title(data.Title())
assert.Nil(t, err)
err = e.Export(data)
assert.Nil(t, err)
for i := 0; i <= e.Index(); i++ {
file := fmt.Sprintf(path, i)
assert.FileExists(t, file)
os.Remove(file)
}
}

View File

@ -1,83 +0,0 @@
package export
import (
"excel_export/data"
"time"
)
type Order struct {
OrderNumber string
ResellerId int64
ResellerName string
ResellerOrderNumber string
Amount float32
Account string
Quantity int
Status int
PayStatus int
OursProductId int
OursProductTitle string
Remark string
CreateTime string
FinishTime string
}
func TransformOrder(order data.Order) *Order {
return &Order{
OrderNumber: order.OrderNumber,
ResellerId: order.ResellerId,
ResellerName: order.Reseller.Name,
ResellerOrderNumber: order.ResellerOrderNumber,
Amount: order.Amount,
Account: order.Account,
Quantity: order.Quantity,
Status: order.Status,
PayStatus: order.PayStatus,
CreateTime: time.Unix(order.CreateTime, 0).Format("2006-01-02 15:04:05"),
FinishTime: time.Unix(order.FinishTime, 0).Format("2006-01-02 15:04:05"),
OursProductId: order.Item.OursProductId,
OursProductTitle: order.Item.OursProductTitle,
Remark: order.Remark.Remark,
}
}
func NewOrder(data []data.Order) Orders {
o := make([]*Order, len(data))
for i, v := range data {
o[i] = TransformOrder(v)
}
return o
}
type Orders []*Order
func (o Orders) Title() []string {
return []string{
"订单号",
"分销商ID",
"分销商名称",
"分销商订单号",
"总价",
"充值账号",
"数量",
"状态",
"支付状态",
"商品编号",
"商品名称",
"备注",
"创建时间",
"完成时间",
}
}
func (o Orders) Size() int {
return len(o)
}
func (o Orders) Data() []interface{} {
d := make([]interface{}, o.Size())
for i, v := range o {
d[i] = v
}
return d
}

14
go.mod
View File

@ -3,13 +3,18 @@ module excel_export
go 1.17 go 1.17
require ( require (
github.com/spf13/cobra v1.6.1 // direct
github.com/tealeg/xlsx/v3 v3.2.4 // direct github.com/tealeg/xlsx/v3 v3.2.4 // direct
gorm.io/driver/mysql v1.4.5 // direct gorm.io/driver/mysql v1.4.5 // direct
gorm.io/gorm v1.24.3 // direct gorm.io/gorm v1.24.3 // direct
github.com/spf13/cobra v1.6.1 // direct
) )
require github.com/stretchr/testify v1.8.1 require (
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.1
)
require github.com/smartystreets/goconvey v1.7.2
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
@ -17,17 +22,18 @@ require (
github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/google/btree v1.0.0 // indirect github.com/google/btree v1.0.0 // indirect
github.com/google/go-cmp v0.5.2 // indirect github.com/google/go-cmp v0.5.2 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/kr/pretty v0.2.1 // indirect github.com/kr/pretty v0.2.1 // indirect
github.com/kr/text v0.1.0 // indirect github.com/kr/text v0.1.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/fastuuid v1.2.0 // indirect github.com/rogpeppe/fastuuid v1.2.0 // indirect
github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa // indirect github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa // indirect
github.com/smartystreets/assertions v1.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/text v0.3.3 // indirect golang.org/x/text v0.3.3 // indirect
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect