Compare commits

...

10 Commits

Author SHA1 Message Date
Mr.Li bcc66bad0a fix:调整导出数据拆分模式,按照时间进行分片 2023-05-05 22:05:00 +08:00
Mr.Li 39a0adb091 fix:调整时区信息 2023-05-04 17:10:53 +08:00
Mr.Li f676d63f06 调整输出日志信息 2023-04-23 19:01:31 +08:00
Mr.Li 6d752bdb2f 判断异常信息 2023-04-23 18:36:34 +08:00
Mr.Li 07e3cce332 移出不使用的信息 2023-04-23 18:16:12 +08:00
Mr.Li f07f052c35 Merge remote-tracking branch 'origin/master' 2023-04-23 17:59:59 +08:00
Mr.Li fcf586443e 修正配置文件 2023-04-23 17:59:52 +08:00
Mr.Li 6652ba5d8b 移出不使用的代码逻辑 2023-04-23 17:58:45 +08:00
Mr.Li 7fe66682a9 修正余额信息配置文件 2023-04-23 17:33:14 +08:00
Mr.Li f1d9e7d140 批量充值数据 2023-04-23 17:26:11 +08:00
18 changed files with 291 additions and 415 deletions

View File

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger"
"runtime/trace" "runtime/trace"
"strconv" "strconv"
"time" "time"
@ -23,13 +22,13 @@ func NewDb(str string) (*Db, error) {
db, err := gorm.Open( db, err := gorm.Open(
mysql.Open(str+""), mysql.Open(str+""),
&gorm.Config{ &gorm.Config{
Logger: logger.Discard, //Logger: logger.Discard,
}, },
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
//db = db.Debug() db = db.Debug()
return &Db{ return &Db{
db: db, db: db,
}, nil }, nil

View File

@ -11,7 +11,6 @@ type CsvExporter struct {
mFetcher DataFetcher mFetcher DataFetcher
file FileAdapter file FileAdapter
count int count int
last interface{}
wg *sync.WaitGroup wg *sync.WaitGroup
} }
@ -50,8 +49,8 @@ func (ee *CsvExporter) Export(sql, pk string) error {
if ee.count > 0 { if ee.count > 0 {
//异步导出数据到csv文件中 //异步导出数据到csv文件中
go ee.exportToCsv(data) go ee.exportToCsv(data)
last := data.Data[ee.count-1] } else {
ee.last = last[ee.getPkIndex(data.Title, pk)] ee.wg.Done()
} }
return nil return nil
} }
@ -73,8 +72,8 @@ func (ee *CsvExporter) exportToCsv(data *Data) {
} }
} }
func (ee *CsvExporter) Last() (int, interface{}) { func (ee *CsvExporter) Count() int {
return ee.count, ee.last return ee.count
} }
func (ee *CsvExporter) getPkIndex(titles []string, pk string) int { func (ee *CsvExporter) getPkIndex(titles []string, pk string) int {

View File

@ -1,115 +0,0 @@
package export
import (
"errors"
"github.com/tealeg/xlsx/v3"
"reflect"
)
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)
}
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)
e.isNew = false
}
return nil
}
func (e *Excel) Write(data interface{}) error {
if e.f.slice() {
e.reset()
}
row := e.sheet.AddRow()
v := reflect.ValueOf(data)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Slice {
return errors.New("数据无效,不是切片类型")
}
switch val := data.(type) {
case []string:
row.WriteSlice(val, -1)
case []interface{}:
for _, i := range data.([]interface{}) {
cell := row.AddCell()
cell.SetValue(i)
}
}
return nil
}
func (e *Excel) Close() error {
return e.save()
}

View File

@ -1,67 +0,0 @@
package export
import (
"fmt"
"log"
)
type ExcelExporter struct {
mFetcher DataFetcher
file FileAdapter
count int
last interface{}
}
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, pk string) error {
data, err := ee.mFetcher.Fetch(sql)
if err != nil {
return fmt.Errorf("数据获取错误:%w", err)
}
ee.count = len(data.Data)
//fmt.Printf("Excel Exporter.Excel, got %v data\n", len(data))
//ee.file.Open()
ee.file.WriteTitle(data.Title)
var last []string
for _, val := range data.Data {
last = val
ee.file.Write(last)
}
if len(last) > 0 {
ee.last = last[getPkIndex(data.Title, pk)]
}
//ee.file.Close()
return nil
}
func (ee *ExcelExporter) Last() (int, interface{}) {
return ee.count, ee.last
}
func getPkIndex(titles []string, pk string) int {
if pk == "" {
log.Println("pk is not empty")
return -1
}
for i, title := range titles {
if title == pk {
return i
}
}
return -1
}

View File

@ -1,26 +0,0 @@
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)
file.Open()
err := e.Export("aa", "字段1")
file.Close()
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")
}

View File

@ -1,31 +0,0 @@
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")
}

View File

@ -5,7 +5,7 @@ type (
Fetcher(fetcher DataFetcher) Fetcher(fetcher DataFetcher)
File(file FileAdapter) File(file FileAdapter)
Export(sql, pk string) error Export(sql, pk string) error
Last() (int, interface{}) Count() int
} }
Data struct { Data struct {

View File

@ -1,35 +0,0 @@
package export
import (
"math/rand"
"strconv"
)
type MysqlDataFetcher struct {
Config string
}
func (mf *MysqlDataFetcher) Fetch(sql string) (*Data, error) {
rows := make([][]string, 0, 6)
// 插入6个随机数组成的切片模拟查询要返回的数据集
rows = append(rows, row(), row(), row(), row(), row(), row())
return &Data{
Title: []string{"字段1", "字段2", "字段3", "字段4", "字段5"},
Data: rows,
}, nil
}
func NewMysqlDataFetcher(configStr string) DataFetcher {
return &MysqlDataFetcher{
Config: configStr,
}
}
func row() []string {
strs := make([]string, 5)
nums := rand.Perm(5)
for i, num := range nums {
strs[i] = strconv.Itoa(num)
}
return strs
}

View File

@ -5,6 +5,7 @@ import (
"excel_export/biz/db" "excel_export/biz/db"
"excel_export/biz/export" "excel_export/biz/export"
"fmt" "fmt"
"log"
"os" "os"
"strconv" "strconv"
"sync" "sync"
@ -36,7 +37,6 @@ func (e *Csv) Export(sysName, jobName string, begin, end time.Time, batch int) e
return e.JobHandler(job, d, map[string]interface{}{ return e.JobHandler(job, d, map[string]interface{}{
"begin": begin, "begin": begin,
"end": end, "end": end,
"last": 0,
}, batch) }, batch)
} }
@ -55,40 +55,55 @@ func (e *Csv) JobHandler(job config.Job, d export.DataFetcher, params map[string
func (e *Csv) TaskExport(d export.DataFetcher, t config.Task, params map[string]interface{}, batch int, fileName string) error { func (e *Csv) TaskExport(d export.DataFetcher, t config.Task, params map[string]interface{}, batch int, fileName string) error {
var i int var i int
var wg sync.WaitGroup var wg sync.WaitGroup
for i = 0; i < 1000; i++ { var total int
beginTime := params["begin"].(time.Time)
lastTime := params["end"].(time.Time)
over := false
for i = 0; i < 10000; i++ {
endTime := beginTime.Add(2 * time.Hour)
//结束时间大于最后时间
if endTime.After(lastTime) {
endTime = lastTime
over = true
}
f, err := e.getCsvFile(i) f, err := e.getCsvFile(i)
if err != nil { if err != nil {
return err return err
} }
params["begin"] = beginTime
params["end"] = endTime
sql := t.GetSql(params) sql := t.GetSql(params)
e := export.NewCsvExporter(d, f) e := export.NewCsvExporter(d, f)
e.(*export.CsvExporter).WaitGroup(&wg) e.(*export.CsvExporter).WaitGroup(&wg)
wg.Add(1) wg.Add(1)
e.Export(sql+" limit "+strconv.Itoa(batch), t.PK) e.Export(sql, t.PK)
count, last := e.Last() count := e.Count()
fmt.Printf("已导出 %d 条数据\n", batch*i+count) fmt.Printf("已导出 %d 条数据\n", batch*i+count)
if count == 0 { total = total + count
return nil
}
if count < batch { if over {
if count == 0 {
i = i - 1
}
break break
} }
beginTime = endTime
params["last"] = last
time.Sleep(time.Microsecond * 30) time.Sleep(time.Microsecond * 30)
} }
wg.Wait() wg.Wait()
//fmt.Println("tempDir", e.dirTemp) //fmt.Println("tempDir", e.dirTemp)
//todo 合并csv文件并删除 临时目录 if total > 0 { //查询到数据
err := e.mergeCsvToExcel(e.dirTemp, i, fileName) //合并csv文件并删除 临时目录
fmt.Println(err) if err := e.mergeCsvToExcel(e.dirTemp, i, fileName); err != nil {
log.Printf("合并csv文件异常%s", err.Error())
return err
}
}
//重置临时路径 //重置临时路径
e.dirTemp = "" e.dirTemp = ""
@ -123,5 +138,6 @@ func (e *Csv) mergeCsvToExcel(path string, max int, out string) error {
return err return err
} }
return m.Clear() return nil
//return m.Clear()
} }

View File

@ -1,83 +0,0 @@
package cmd
import (
"excel_export/biz/config"
"excel_export/biz/db"
"excel_export/biz/export"
"fmt"
"strconv"
"time"
)
type Excel struct {
conf *config.Config
}
func NewExcel(conf *config.Config) *Excel {
return &Excel{
conf: conf,
}
}
func (e *Excel) Export(sysName, jobName string, begin, end time.Time, batch int) error {
job, dbStr, err := config.GetJob(e.conf, sysName, jobName)
if err != nil {
return err
}
d, err := db.NewDb(dbStr)
if err != nil {
return err
}
return e.JobHandler(job, d, map[string]interface{}{
"begin": begin,
"end": end,
"last": 0,
}, batch)
}
func (e *Excel) JobHandler(job config.Job, d export.DataFetcher, params map[string]interface{}, batch int) error {
f := export.NewExcel(job.File, job.Size, map[string]string{
"begin": params["begin"].(time.Time).Format("20060102"),
"end": params["end"].(time.Time).Format("20060102"),
})
for i, task := range job.Tasks {
fmt.Printf("执行导出任务:%d\n", i+1)
if err := e.TaskExport(d, task, params, f, batch); err != nil {
return err
}
}
return nil
}
func (e *Excel) TaskExport(d export.DataFetcher, t config.Task, params map[string]interface{}, f export.FileAdapter, batch int) error {
//todo 最多分1000个批次进行处理
f.Open()
defer f.Close()
for i := 0; i < 1000; i++ {
sql := t.GetSql(params)
e := export.NewExcelExporter(d, f)
e.Export(sql+" limit "+strconv.Itoa(batch), t.PK)
count, last := e.Last()
fmt.Printf("已导出 %d 条数据\n", batch*i+count)
if count == 0 {
return nil
}
if count < batch {
break
}
params["last"] = last
time.Sleep(time.Microsecond * 30)
}
return nil
}

View File

@ -2,8 +2,8 @@ package cmd
import ( import (
"excel_export/biz/config" "excel_export/biz/config"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"log"
"strconv" "strconv"
"time" "time"
) )
@ -44,13 +44,17 @@ func exportAllJobRun(cmd *cobra.Command, args []string) {
CmdError(cmd, "无效的参数:%s", err.Error()) CmdError(cmd, "无效的参数:%s", err.Error())
} }
allBegin := time.Now()
for _, job := range sys.Jobs { for _, job := range sys.Jobs {
fmt.Printf("[%s]执行【%s】【%s】导出\n", time.Now().Format("2006-01-02 15:04:05"), sName, job.Name) log.Printf("执行【%s】【%s】导出\n", sName, job.Name)
b := time.Now() b := time.Now()
ee := NewCsv(config.DefaultConfig) ee := NewCsv(config.DefaultConfig)
ee.Export(sName, job.Name, begin, end, batch) if err := ee.Export(sName, job.Name, begin, end, batch); err != nil {
e := time.Now() CmdError(cmd, "【%s】【%s】导出错误%s", sName, job.Name, err.Error())
fmt.Println("导出耗时:" + e.Sub(b).String()) }
log.Println("导出耗时:" + time.Now().Sub(b).String())
} }
log.Println("总耗时:" + time.Now().Sub(allBegin).String())
} }

View File

@ -2,8 +2,8 @@ package cmd
import ( import (
"excel_export/biz/config" "excel_export/biz/config"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"log"
"strconv" "strconv"
"time" "time"
) )
@ -35,15 +35,18 @@ func exportAllRun(cmd *cobra.Command, args []string) {
if err != nil { if err != nil {
CmdError(cmd, "无效的参数:%s", err.Error()) CmdError(cmd, "无效的参数:%s", err.Error())
} }
allBegin := time.Now()
for _, sys := range c.Systems { for _, sys := range c.Systems {
for _, job := range sys.Jobs { for _, job := range sys.Jobs {
fmt.Printf("[%s]执行【%s】【%s】导出\n", time.Now().Format("2006-01-02 15:04:05"), sys.Name, job.Name) log.Printf("执行【%s】【%s】导出\n", sys.Name, job.Name)
b := time.Now() b := time.Now()
ee := NewCsv(config.DefaultConfig) ee := NewCsv(config.DefaultConfig)
ee.Export(sys.Name, job.Name, begin, end, batch) if err := ee.Export(sys.Name, job.Name, begin, end, batch); err != nil {
e := time.Now() CmdError(cmd, "【%s】【%s】导出错误%s", sys.Name, job.Name, err.Error())
fmt.Println("导出耗时:" + e.Sub(b).String()) }
log.Println("导出耗时:" + time.Now().Sub(b).String())
} }
} }
log.Println("总耗时:" + time.Now().Sub(allBegin).String())
} }

View File

@ -2,8 +2,8 @@ package cmd
import ( import (
"excel_export/biz/config" "excel_export/biz/config"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"log"
"strconv" "strconv"
"time" "time"
) )
@ -43,7 +43,7 @@ func exportRun(cmd *cobra.Command, args []string) {
CmdError(cmd, "%s", err.Error()) CmdError(cmd, "%s", err.Error())
} }
fmt.Printf("[%s]执行【%s】【%s】导出\n", time.Now().Format("2006-01-02 15:04:05"), sName, jName) log.Printf("执行【%s】【%s】导出\n", sName, jName)
begin := MustFlagsDateTime(cmd, "begin") begin := MustFlagsDateTime(cmd, "begin")
end := MustFlagsDateTime(cmd, "end") end := MustFlagsDateTime(cmd, "end")
@ -55,7 +55,8 @@ func exportRun(cmd *cobra.Command, args []string) {
} }
b := time.Now() b := time.Now()
ee := NewCsv(config.DefaultConfig) ee := NewCsv(config.DefaultConfig)
ee.Export(sName, jName, begin, end, batch) if err := ee.Export(sName, jName, begin, end, batch); err != nil {
e := time.Now() CmdError(cmd, "【%s】【%s】导出错误%s", sName, jName, err.Error())
fmt.Println("耗时:" + e.Sub(b).String()) }
log.Println("耗时:" + time.Now().Sub(b).String())
} }

View File

@ -1,6 +1,8 @@
package cmd package cmd
import ( import (
"github.com/stretchr/testify/assert"
"os"
"testing" "testing"
) )
@ -17,5 +19,10 @@ func TestMerge_Write(t *testing.T) {
} }
func TestMerge_Save(t *testing.T) { func TestMerge_Save(t *testing.T) {
// m := NewMerge(os.TempDir()+"/3299772411",500000) m := NewMerge(
Reader{Path: os.TempDir() + "/3299772411", Index: 102},
Writer{File: "sss.xlsx", Limit: 1000000},
)
err := m.Merge()
assert.NoError(t, err)
} }

View File

@ -20,7 +20,7 @@ func ParseFlagsDateTime(set *flag.FlagSet, key string) (*time.Time, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("获取参数异常:%w", err) return nil, fmt.Errorf("获取参数异常:%w", err)
} }
beginTime, err := time.Parse("2006-01-02 15:04:05", val) beginTime, err := time.ParseInLocation("2006-01-02 15:04:05", val, time.Local)
if err != nil { if err != nil {
return nil, fmt.Errorf("不是有效的时间格式:%w", err) return nil, fmt.Errorf("不是有效的时间格式:%w", err)
} }

View File

@ -2,26 +2,17 @@ package main
import ( import (
"excel_export/biz/config" "excel_export/biz/config"
"excel_export/biz/util"
"excel_export/cmd/cmd" "excel_export/cmd/cmd"
"github.com/tealeg/xlsx/v3"
"os" "os"
"time"
) )
var Config *config.Config var Config *config.Config
func main() { func main() {
p := util.NewProf() //p := util.NewProf()
defer func() { //defer func() {
p.Close() // p.Close()
}() //}()
//设置默认格式
xlsx.DefaultDateTimeOptions = xlsx.DateTimeOptions{
Location: time.UTC,
ExcelTimeFormat: "yyyy-m-d h:mm:ss",
}
path, _ := os.Getwd() path, _ := os.Getwd()
Config = config.LoadConfig(path + "/config") Config = config.LoadConfig(path + "/config")

View File

@ -223,7 +223,7 @@ system:
size: 1000000 size: 1000000
- name: "余额信息" - name: "余额信息"
tasks: tasks:
- pk: - pk: "分销商编号"
sql: >- sql: >-
SELECT SELECT
b.reseller_id AS `分销商编号`, b.reseller_id AS `分销商编号`,
@ -237,7 +237,7 @@ system:
LEFT JOIN reseller r ON r.id = b.reseller_id LEFT JOIN reseller r ON r.id = b.reseller_id
left join reseller_balance_day rbd on rbd.reseller_id = b.reseller_id and rbd.`day` = DATE_FORMAT(date_add('{end}', interval -1 day),"%Y-%m-%d") left join reseller_balance_day rbd on rbd.reseller_id = b.reseller_id and rbd.`day` = DATE_FORMAT(date_add('{end}', interval -1 day),"%Y-%m-%d")
elt: "r.`status` = 1" elt: "r.`status` = 1"
timestamp: timestamp: false
order: "" order: ""
file: "直连天下-余额信息-{end}.xlsx" file: "直连天下-余额信息-{end}.xlsx"
size: 1000000 size: 1000000
@ -510,6 +510,113 @@ system:
order: "o.create_time,o.order_number" order: "o.create_time,o.order_number"
file: "雅兰芳-下游订单-{begin}-{end}-{task}.xlsx" file: "雅兰芳-下游订单-{begin}-{end}-{task}.xlsx"
size: 1000000 size: 1000000
- name: "余额信息"
tasks:
- pk: "分销商编号"
sql: >-
SELECT
b.reseller_id AS `分销商编号`,
r.`name` AS `简称`,
r.full_name AS `全称`,
balance AS `余额`,
rbd.day_balance as `昨日余额`,
extension AS `授信`
FROM
reseller_balance b
LEFT JOIN reseller r ON r.id = b.reseller_id
left join reseller_balance_day rbd on rbd.reseller_id = b.reseller_id and rbd.`day` = DATE_FORMAT(date_add('{end}', interval -1 day),"%Y-%m-%d")
elt: "r.`status` = 1"
timestamp: false
order: ""
file: "雅兰芳-余额信息-{end}.xlsx"
size: 1000000
- name: "批量充值"
tasks:
- pk: "流水号"
sql: >-
SELECT
bd.customer as `分销商`,
r.`name`,
bd.price as `商品价格`,
od.ours_product_id as `我们的商品id`,
op.NAME as `商品名称`,
p.name as `接口平台`,
CASE od.`status`
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 `接口平台产品编码`,
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 `执行时间`,
bd.ding_talk_sn as `钉钉审批序号`,
bd.remark as `批量备注`
FROM
order_direct od -- FORCE INDEX(idx_direct_create_time)
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
RIGHT JOIN batch_direct bd on bd.batch_id = od.order_order_number
LEFT JOIN reseller r on r.id = bd.reseller_id
elt: "bd.create_time BETWEEN {begin} AND {end} and od.serial_number > '{last}'"
timestamp: true
order: "od.create_time,od.serial_number"
- pk: "流水号"
sql: >-
SELECT
bd.customer as `分销商`,
r.`name`,
bd.price as `商品价格`,
od.ours_product_id as `我们的商品id`,
op.NAME as `商品名称`,
p.name as `接口平台`,
CASE od.`status`
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 `接口平台产品编码`,
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 `执行时间`,
bd.ding_talk_sn as `钉钉审批序号`,
bd.remark as `批量备注`
FROM
history_order_direct od
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
RIGHT JOIN batch_direct bd on bd.batch_id = od.order_order_number
LEFT JOIN reseller r on r.id = bd.reseller_id
elt: "bd.create_time BETWEEN {begin} AND {end} and od.serial_number > '{last}'"
timestamp: true
order: "od.create_time,od.serial_number"
file: "雅兰芳-批量充值-{begin}-{end}-{task}.xlsx"
size: 1000000
- name: "创意择优" - name: "创意择优"
db: "root:lhb767@tcp(120.79.35.82:3307)/new_sys?charset=utf8mb4&parseTime=True" db: "root:lhb767@tcp(120.79.35.82:3307)/new_sys?charset=utf8mb4&parseTime=True"
@ -643,3 +750,110 @@ system:
order: "o.create_time,o.order_number" order: "o.create_time,o.order_number"
file: "创意择优-下游订单-{begin}-{end}-{task}.xlsx" file: "创意择优-下游订单-{begin}-{end}-{task}.xlsx"
size: 1000000 size: 1000000
- name: "余额信息"
tasks:
- pk: "分销商编号"
sql: >-
SELECT
b.reseller_id AS `分销商编号`,
r.`name` AS `简称`,
r.full_name AS `全称`,
balance AS `余额`,
rbd.day_balance as `昨日余额`,
extension AS `授信`
FROM
reseller_balance b
LEFT JOIN reseller r ON r.id = b.reseller_id
left join reseller_balance_day rbd on rbd.reseller_id = b.reseller_id and rbd.`day` = DATE_FORMAT(date_add('{end}', interval -1 day),"%Y-%m-%d")
elt: "r.`status` = 1"
timestamp: false
order: ""
file: "创意择优-余额信息-{end}.xlsx"
size: 1000000
- name: "批量充值"
tasks:
- pk: "流水号"
sql: >-
SELECT
bd.customer as `分销商`,
r.`name`,
bd.price as `商品价格`,
od.ours_product_id as `我们的商品id`,
op.NAME as `商品名称`,
p.name as `接口平台`,
CASE od.`status`
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 `接口平台产品编码`,
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 `执行时间`,
bd.ding_talk_sn as `钉钉审批序号`,
bd.remark as `批量备注`
FROM
order_direct od -- FORCE INDEX(idx_direct_create_time)
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
RIGHT JOIN batch_direct bd on bd.batch_id = od.order_order_number
LEFT JOIN reseller r on r.id = bd.reseller_id
elt: "bd.create_time BETWEEN {begin} AND {end} and od.serial_number > '{last}'"
timestamp: true
order: "od.create_time,od.serial_number"
- pk: "流水号"
sql: >-
SELECT
bd.customer as `分销商`,
r.`name`,
bd.price as `商品价格`,
od.ours_product_id as `我们的商品id`,
op.NAME as `商品名称`,
p.name as `接口平台`,
CASE od.`status`
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 `接口平台产品编码`,
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 `执行时间`,
bd.ding_talk_sn as `钉钉审批序号`,
bd.remark as `批量备注`
FROM
history_order_direct od
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
RIGHT JOIN batch_direct bd on bd.batch_id = od.order_order_number
LEFT JOIN reseller r on r.id = bd.reseller_id
elt: "bd.create_time BETWEEN {begin} AND {end} and od.serial_number > '{last}'"
timestamp: true
order: "od.create_time,od.serial_number"
file: "创意择优-批量充值-{begin}-{end}-{task}.xlsx"
size: 1000000

1
go.mod
View File

@ -4,7 +4,6 @@ go 1.17
require ( require (
github.com/spf13/cobra v1.6.1 // direct github.com/spf13/cobra v1.6.1 // 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/flytam/filenamify v1.1.2 // direct github.com/flytam/filenamify v1.1.2 // direct