fix: 使用额外文件
This commit is contained in:
parent
37a8318814
commit
3a77e0e32b
|
|
@ -3,12 +3,8 @@ package bbxt
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/pkg"
|
"ai_scheduler/internal/pkg"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2/log"
|
|
||||||
"github.com/xuri/excelize/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BbxtTools struct {
|
type BbxtTools struct {
|
||||||
|
|
@ -91,7 +87,18 @@ func (b *BbxtTools) StatisOursProductLossSumTotal(ct []string) (err error) {
|
||||||
reseller.ProductLoss[info.OursProductId] = productLoss
|
reseller.ProductLoss[info.OursProductId] = productLoss
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按经销商总亏损排序
|
||||||
|
resellers := make([]*ResellerLoss, 0, len(resellerMap))
|
||||||
for _, v := range resellerMap {
|
for _, v := range resellerMap {
|
||||||
|
resellers = append(resellers, v)
|
||||||
|
}
|
||||||
|
sort.Slice(resellers, func(i, j int) bool {
|
||||||
|
return resellers[i].Total < resellers[j].Total
|
||||||
|
})
|
||||||
|
|
||||||
|
// 构建分组
|
||||||
|
for _, v := range resellers {
|
||||||
if v.Total <= -100 {
|
if v.Total <= -100 {
|
||||||
total = append(total, []string{
|
total = append(total, []string{
|
||||||
fmt.Sprintf("%s", v.ResellerName),
|
fmt.Sprintf("%s", v.ResellerName),
|
||||||
|
|
@ -114,168 +121,3 @@ func (b *BbxtTools) StatisOursProductLossSumTotal(ct []string) (err error) {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最简单的通用函数
|
|
||||||
func (b *BbxtTools) SimpleFillExcel(templatePath, outputPath string, dataSlice interface{}) error {
|
|
||||||
// 1. 打开模板
|
|
||||||
f, err := excelize.OpenFile(templatePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
sheet := f.GetSheetName(0)
|
|
||||||
|
|
||||||
// 1.1 获取第二行模板样式
|
|
||||||
resellerTplRow := 2
|
|
||||||
styleIDReseller, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", resellerTplRow))
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("获取分销商总计样式失败: %v", err)
|
|
||||||
styleIDReseller = 0
|
|
||||||
}
|
|
||||||
// 1.2 获取分销商总计行高
|
|
||||||
rowHeightReseller, err := f.GetRowHeight(sheet, resellerTplRow)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("获取分销商总计行高失败: %v", err)
|
|
||||||
rowHeightReseller = 31 // 默认高度
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 反射获取切片数据
|
|
||||||
v := reflect.ValueOf(dataSlice)
|
|
||||||
if v.Kind() != reflect.Slice {
|
|
||||||
return fmt.Errorf("dataSlice must be a slice")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 从第2行开始填充
|
|
||||||
row := 2
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
item := v.Index(i).Interface()
|
|
||||||
currentRow := row + i
|
|
||||||
|
|
||||||
// 4. 将item转换为一行数据
|
|
||||||
var rowData []interface{}
|
|
||||||
|
|
||||||
// 如果是切片
|
|
||||||
if reflect.TypeOf(item).Kind() == reflect.Slice {
|
|
||||||
itemV := reflect.ValueOf(item)
|
|
||||||
for j := 0; j < itemV.Len(); j++ {
|
|
||||||
rowData = append(rowData, itemV.Index(j).Interface())
|
|
||||||
}
|
|
||||||
} else if reflect.TypeOf(item).Kind() == reflect.Struct {
|
|
||||||
itemV := reflect.ValueOf(item)
|
|
||||||
for j := 0; j < itemV.NumField(); j++ {
|
|
||||||
if itemV.Field(j).CanInterface() {
|
|
||||||
rowData = append(rowData, itemV.Field(j).Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rowData = []interface{}{item}
|
|
||||||
}
|
|
||||||
// 4.1 设置行高
|
|
||||||
f.SetRowHeight(sheet, currentRow, rowHeightReseller)
|
|
||||||
|
|
||||||
// 5. 填充到Excel
|
|
||||||
for col, value := range rowData {
|
|
||||||
cell := fmt.Sprintf("%c%d", 'A'+col, currentRow)
|
|
||||||
f.SetCellValue(sheet, cell, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5.1 使用第二行模板样式
|
|
||||||
if styleIDReseller != 0 {
|
|
||||||
f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow), styleIDReseller)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. 保存
|
|
||||||
return f.SaveAs(outputPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分销商负利润详情填充excel
|
|
||||||
// 1.使用模板文件作为输出文件
|
|
||||||
// 2.分销商总计使用第二行样式(宽高、背景、颜色等)
|
|
||||||
// 3.商品详情使用第三行样式(宽高、背景、颜色等)
|
|
||||||
// 4.保存为新文件
|
|
||||||
func (b *BbxtTools) resellerDetailFillExcel(templatePath, outputPath string, dataSlice []*ResellerLoss) error {
|
|
||||||
// 1. 读取模板
|
|
||||||
f, err := excelize.OpenFile(templatePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
sheet := f.GetSheetName(0)
|
|
||||||
|
|
||||||
// 获取模板样式1:第二行-分销商总计
|
|
||||||
resellerTplRow := 2
|
|
||||||
styleIDReseller, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", resellerTplRow))
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("获取分销商总计样式失败: %v", err)
|
|
||||||
styleIDReseller = 0
|
|
||||||
}
|
|
||||||
rowHeightReseller, err := f.GetRowHeight(sheet, resellerTplRow)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("获取分销商总计行高失败: %v", err)
|
|
||||||
rowHeightReseller = 31 // 默认高度
|
|
||||||
}
|
|
||||||
// 获取模板样式2:第三行-产品亏损明细
|
|
||||||
productTplRow := 3
|
|
||||||
styleIDProduct, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", productTplRow))
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("获取商品详情样式失败: %v", err)
|
|
||||||
styleIDProduct = 0
|
|
||||||
}
|
|
||||||
rowHeightProduct, err := f.GetRowHeight(sheet, productTplRow)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("获取商品详情行高失败: %v", err)
|
|
||||||
rowHeightProduct = 25 // 默认高度
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRow := 2
|
|
||||||
|
|
||||||
for _, reseller := range dataSlice {
|
|
||||||
// 3. 填充经销商数据 (ResellerName, Total)
|
|
||||||
// 设置行高
|
|
||||||
f.SetRowHeight(sheet, currentRow, rowHeightReseller)
|
|
||||||
|
|
||||||
// 设置单元格值
|
|
||||||
f.SetCellValue(sheet, fmt.Sprintf("A%d", currentRow), reseller.ResellerName)
|
|
||||||
f.SetCellValue(sheet, fmt.Sprintf("B%d", currentRow), reseller.Total)
|
|
||||||
|
|
||||||
// 应用样式
|
|
||||||
if styleIDReseller != 0 {
|
|
||||||
f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow), styleIDReseller)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRow++
|
|
||||||
|
|
||||||
// 4. 填充产品亏损明细
|
|
||||||
// 先对 ProductLoss 进行排序
|
|
||||||
var products []ProductLoss
|
|
||||||
for _, p := range reseller.ProductLoss {
|
|
||||||
products = append(products, p)
|
|
||||||
}
|
|
||||||
// 按 Loss 升序排序 (亏损越多越靠前,负数越小)
|
|
||||||
sort.Slice(products, func(i, j int) bool {
|
|
||||||
return products[i].Loss < products[j].Loss
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, p := range products {
|
|
||||||
// 设置行高
|
|
||||||
f.SetRowHeight(sheet, currentRow, rowHeightProduct)
|
|
||||||
|
|
||||||
// 设置单元格值
|
|
||||||
f.SetCellValue(sheet, fmt.Sprintf("A%d", currentRow), p.ProductName)
|
|
||||||
f.SetCellValue(sheet, fmt.Sprintf("B%d", currentRow), p.Loss)
|
|
||||||
|
|
||||||
// 应用样式
|
|
||||||
if styleIDProduct != 0 {
|
|
||||||
f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow), styleIDProduct)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRow++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. 保存
|
|
||||||
return f.SaveAs(outputPath)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
package bbxt
|
package bbxt
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func Test_StatisOursProductLossSumApiTotal(t *testing.T) {
|
func Test_StatisOursProductLossSumApiTotal(t *testing.T) {
|
||||||
o, err := NewBbxtTools()
|
o, err := NewBbxtTools()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
err = o.StatisOursProductLossSumTotal([]string{"2025-12-28+00:00:00", "2025-12-28+23:59:59.999"})
|
err = o.DailyReport(time.Date(2025, 12, 30, 0, 0, 0, 0, time.Local))
|
||||||
|
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
package bbxt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/go-kratos/kratos/v2/log"
|
||||||
|
"github.com/xuri/excelize/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 最简单的通用函数
|
||||||
|
func (b *BbxtTools) SimpleFillExcel(templatePath, outputPath string, dataSlice interface{}) error {
|
||||||
|
// 1. 打开模板
|
||||||
|
f, err := excelize.OpenFile(templatePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
sheet := f.GetSheetName(0)
|
||||||
|
|
||||||
|
// 1.1 获取第二行模板样式
|
||||||
|
resellerTplRow := 2
|
||||||
|
styleIDReseller, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", resellerTplRow))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取分销商总计样式失败: %v", err)
|
||||||
|
styleIDReseller = 0
|
||||||
|
}
|
||||||
|
// 1.2 获取分销商总计行高
|
||||||
|
rowHeightReseller, err := f.GetRowHeight(sheet, resellerTplRow)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取分销商总计行高失败: %v", err)
|
||||||
|
rowHeightReseller = 31 // 默认高度
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 反射获取切片数据
|
||||||
|
v := reflect.ValueOf(dataSlice)
|
||||||
|
if v.Kind() != reflect.Slice {
|
||||||
|
return fmt.Errorf("dataSlice must be a slice")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 从第2行开始填充
|
||||||
|
row := 2
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
item := v.Index(i).Interface()
|
||||||
|
currentRow := row + i
|
||||||
|
|
||||||
|
// 4. 将item转换为一行数据
|
||||||
|
var rowData []interface{}
|
||||||
|
|
||||||
|
// 如果是切片
|
||||||
|
if reflect.TypeOf(item).Kind() == reflect.Slice {
|
||||||
|
itemV := reflect.ValueOf(item)
|
||||||
|
for j := 0; j < itemV.Len(); j++ {
|
||||||
|
rowData = append(rowData, itemV.Index(j).Interface())
|
||||||
|
}
|
||||||
|
} else if reflect.TypeOf(item).Kind() == reflect.Struct {
|
||||||
|
itemV := reflect.ValueOf(item)
|
||||||
|
for j := 0; j < itemV.NumField(); j++ {
|
||||||
|
if itemV.Field(j).CanInterface() {
|
||||||
|
rowData = append(rowData, itemV.Field(j).Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rowData = []interface{}{item}
|
||||||
|
}
|
||||||
|
// 4.1 设置行高
|
||||||
|
f.SetRowHeight(sheet, currentRow, rowHeightReseller)
|
||||||
|
|
||||||
|
// 5. 填充到Excel
|
||||||
|
for col, value := range rowData {
|
||||||
|
cell := fmt.Sprintf("%c%d", 'A'+col, currentRow)
|
||||||
|
f.SetCellValue(sheet, cell, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.1 使用第二行模板样式
|
||||||
|
if styleIDReseller != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow), styleIDReseller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 保存
|
||||||
|
return f.SaveAs(outputPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分销商负利润详情填充excel
|
||||||
|
// 1.使用模板文件作为输出文件
|
||||||
|
// 2.分销商总计使用第二行样式(宽高、背景、颜色等)
|
||||||
|
// 3.商品详情使用第三行样式(宽高、背景、颜色等)
|
||||||
|
// 4.保存为新文件
|
||||||
|
func (b *BbxtTools) resellerDetailFillExcel(templatePath, outputPath string, dataSlice []*ResellerLoss) error {
|
||||||
|
// 1. 读取模板
|
||||||
|
f, err := excelize.OpenFile(templatePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
sheet := f.GetSheetName(0)
|
||||||
|
|
||||||
|
// 获取模板样式1:第二行-分销商总计
|
||||||
|
resellerTplRow := 2
|
||||||
|
styleIDReseller, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", resellerTplRow))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取分销商总计样式失败: %v", err)
|
||||||
|
styleIDReseller = 0
|
||||||
|
}
|
||||||
|
rowHeightReseller, err := f.GetRowHeight(sheet, resellerTplRow)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取分销商总计行高失败: %v", err)
|
||||||
|
rowHeightReseller = 31 // 默认高度
|
||||||
|
}
|
||||||
|
// 获取模板样式2:第三行-产品亏损明细
|
||||||
|
productTplRow := 3
|
||||||
|
styleIDProduct, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", productTplRow))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取商品详情样式失败: %v", err)
|
||||||
|
styleIDProduct = 0
|
||||||
|
}
|
||||||
|
rowHeightProduct, err := f.GetRowHeight(sheet, productTplRow)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("获取商品详情行高失败: %v", err)
|
||||||
|
rowHeightProduct = 25 // 默认高度
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRow := 2
|
||||||
|
|
||||||
|
for _, reseller := range dataSlice {
|
||||||
|
// 3. 填充经销商数据 (ResellerName, Total)
|
||||||
|
// 设置行高
|
||||||
|
f.SetRowHeight(sheet, currentRow, rowHeightReseller)
|
||||||
|
|
||||||
|
// 设置单元格值
|
||||||
|
f.SetCellValue(sheet, fmt.Sprintf("A%d", currentRow), reseller.ResellerName)
|
||||||
|
f.SetCellValue(sheet, fmt.Sprintf("B%d", currentRow), reseller.Total)
|
||||||
|
|
||||||
|
// 应用样式
|
||||||
|
if styleIDReseller != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow), styleIDReseller)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRow++
|
||||||
|
|
||||||
|
// 4. 填充产品亏损明细
|
||||||
|
// 先对 ProductLoss 进行排序
|
||||||
|
var products []ProductLoss
|
||||||
|
for _, p := range reseller.ProductLoss {
|
||||||
|
products = append(products, p)
|
||||||
|
}
|
||||||
|
// 按 Loss 升序排序 (亏损越多越靠前,负数越小)
|
||||||
|
sort.Slice(products, func(i, j int) bool {
|
||||||
|
return products[i].Loss < products[j].Loss
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, p := range products {
|
||||||
|
// 设置行高
|
||||||
|
f.SetRowHeight(sheet, currentRow, rowHeightProduct)
|
||||||
|
|
||||||
|
// 设置单元格值
|
||||||
|
f.SetCellValue(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("·%s", p.ProductName))
|
||||||
|
f.SetCellValue(sheet, fmt.Sprintf("B%d", currentRow), p.Loss)
|
||||||
|
|
||||||
|
// 应用样式
|
||||||
|
if styleIDProduct != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow), styleIDProduct)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRow++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffer, err := f.WriteToBuffer()
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return buffer.Bytes(), nil
|
||||||
|
|
||||||
|
// 6. 保存
|
||||||
|
return f.SaveAs(outputPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// excel2picPy 将excel转换为图片python
|
||||||
|
// python 接口如下:
|
||||||
|
// curl --location --request POST 'http://192.168.6.109:8010/api/v1/convert' \
|
||||||
|
// --header 'Content-Type: multipart/form-data; boundary=--------------------------952147881043913664015069' \
|
||||||
|
// --form 'file=@"C:\\Users\\Administrator\\Downloads\\销售同比分析2025-12-29 0-12点.xlsx"' \
|
||||||
|
// --form 'sheet_name="销售同比分析"'
|
||||||
|
func (b *BbxtTools) excel2picPy(templatePath string, excelBytes []byte) ([]byte, error) {
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
// return picBytes, nil
|
||||||
|
}
|
||||||
Binary file not shown.
Loading…
Reference in New Issue