fix: 优化excel数据生成方法,支持动态title
This commit is contained in:
parent
f497872d51
commit
ad3c5bc4b1
|
|
@ -124,6 +124,13 @@ eino_tools:
|
|||
# 货易通商品品牌查询
|
||||
hytGoodsBrandSearch:
|
||||
base_url: "https://gateway.dev.cdlsxd.cn/goods-admin/api/v1/goods/brand/list"
|
||||
# == 电商充值系统 ==
|
||||
# 我们的商品统计
|
||||
rechargeStatisticsOursProduct:
|
||||
base_url: "http://admin.1688sup.cn:8001/admin/statistics/oursProduct"
|
||||
api_key: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ1c2VyQ2VudGVyIiwiZXhwIjoxNzY3MDc3NzcwLCJuYmYiOjE3NjcwNzU5NzAsImp0aSI6IjEiLCJQaG9uZSI6IjE4MDAwMDAwMDAwIiwiVXNlck5hbWUiOiJsc3hkIiwiUmVhbE5hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJBY2NvdW50VHlwZSI6MSwiR3JvdXBDb2RlcyI6IlZDTF9DQVNISUVSLFZDTF9PUEVSQVRFLFZDTF9BRE1JTixWQ0xfQUFBLFZDTF9WQ0xfT1BFUkFULFZDTF9JTlZPSUNFLENSTV9BRE1JTixMSUFOTElBTl9BRE1JTixNQVJLRVRNQUcyX0FETUlOLFBIT05FQklMTF9BRE1JTixRSUFOWkhVX1NVUFBFUl9BRE0sTUFSS0VUSU5HU0FBU19TVVBFUkFETUlOLENBUkRfQ09ERSxDQVJEX1BST0NVUkVNRU5ULE1BUktFVElOR1NZU1RFTV9TVVBFUixTVEFUSVNUSUNBTFNZU1RFTV9BRE1JTixaTFRYX0FETUlOLFpMVFhfT1BFUkFURSIsIkRpbmdVc2VySWQiOiIxNjIwMjYxMjMwMjg5MzM4MzQifQ.Nuw_aR6iSPmhhh9E5rhyTxHBsgWtaTZvbnc7SFTnUBJXTQvYahnk0LyZaVpsVw6FB3cU0F5xOdX3rmGyWyaiszWO6yi-o1oxGMXwhf39fMiWT2xUI6pAn9Ync8DmZ4tOMCNUTdEk4CaQFzrTwJs0c-VR4yW6LgoPmNPvUVZ-KwmusUpnPz5j9RsJItzIWE3bpGGsfB54e2UERcZdbo9BXxCZIBbpAYKBKdl73KuI8SNaXgKvGTrJ6hEN4ESpnbisJVwT5pp_kuChJlcfjHTHFsEf4RJDjN9gTrtDbBWZyY3OmO2ukqYAM7tZPs6TXJwvQGJQsFRVZUBGxS1nD_6DzQ"
|
||||
excel2pic:
|
||||
base_url: "http://192.168.6.109:8010/api/v1/convert"
|
||||
|
||||
dingtalk:
|
||||
api_key: "dingsbbntrkeiyazcfdg"
|
||||
|
|
|
|||
|
|
@ -15,19 +15,15 @@ func New() *Client {
|
|||
}
|
||||
|
||||
// Call 根据模板和数据生成 Excel 字节流
|
||||
// templatePath: 模板文件路径
|
||||
// data: 二维字符串数组,不再使用反射
|
||||
// startRow: 数据填充起始行 (默认 2)
|
||||
// styleRow: 样式参考行 (默认 2)
|
||||
func (g *Client) Call(templatePath string, data [][]string, startRow int, styleRow int) ([]byte, error) {
|
||||
if startRow <= 0 {
|
||||
startRow = 2
|
||||
func (g *Client) Call(req *ExcelGeneratorRequest) ([]byte, error) {
|
||||
if req.StartRow <= 0 {
|
||||
req.StartRow = 2
|
||||
}
|
||||
if styleRow <= 0 {
|
||||
styleRow = 2
|
||||
if req.StyleRow <= 0 {
|
||||
req.StyleRow = 2
|
||||
}
|
||||
|
||||
f, err := excelize.OpenFile(templatePath)
|
||||
f, err := excelize.OpenFile(req.TemplatePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -35,20 +31,25 @@ func (g *Client) Call(templatePath string, data [][]string, startRow int, styleR
|
|||
|
||||
sheet := f.GetSheetName(0)
|
||||
|
||||
// 若提供标题,替换第一行表格标题
|
||||
if len(req.Title) > 0 {
|
||||
f.SetCellValue(sheet, "A1", req.Title)
|
||||
}
|
||||
|
||||
// 获取样式和行高
|
||||
styleID, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", styleRow))
|
||||
styleID, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", req.StyleRow))
|
||||
if err != nil {
|
||||
log.Errorf("获取样式失败: %v", err)
|
||||
styleID = 0
|
||||
}
|
||||
rowHeight, err := f.GetRowHeight(sheet, styleRow)
|
||||
rowHeight, err := f.GetRowHeight(sheet, req.StyleRow)
|
||||
if err != nil {
|
||||
log.Errorf("获取行高失败: %v", err)
|
||||
rowHeight = 31 // 默认高度
|
||||
}
|
||||
|
||||
row := startRow
|
||||
for i, item := range data {
|
||||
row := req.StartRow
|
||||
for i, item := range req.ExcelData {
|
||||
currentRow := row + i
|
||||
|
||||
// 设置行高
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package excel_generator
|
||||
|
||||
type ExcelGeneratorRequest struct {
|
||||
TemplatePath string // 模板文件路径
|
||||
ExcelData [][]string // 二维字符串数组
|
||||
StartRow int // 数据填充起始行 (默认 2)
|
||||
StyleRow int // 样式参考行 (默认 2)
|
||||
Title string // 表格标题(仅替换)
|
||||
}
|
||||
|
|
@ -4,16 +4,19 @@ import (
|
|||
"ai_scheduler/internal/config"
|
||||
errorcode "ai_scheduler/internal/data/error"
|
||||
toolManager "ai_scheduler/internal/domain/tools"
|
||||
"ai_scheduler/internal/domain/tools/common/excel_generator"
|
||||
"ai_scheduler/internal/domain/tools/recharge/statistics_ours_product"
|
||||
"ai_scheduler/internal/domain/workflow/runtime"
|
||||
"ai_scheduler/internal/pkg/utils_oss"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/cloudwego/eino/compose"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
)
|
||||
|
||||
const WorkflowIDStatisticsOursProduct = "recharge.statisticsOursProduct"
|
||||
|
|
@ -31,8 +34,7 @@ type statisticsOursProduct struct {
|
|||
}
|
||||
|
||||
type StatisticsOursProductWorkflowInput struct {
|
||||
StartTime string `json:"start_time"`
|
||||
EndTime string `json:"end_time"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
type StatisticsOursProductWorkflowOutput struct {
|
||||
|
|
@ -52,15 +54,10 @@ func (w *statisticsOursProduct) Invoke(ctx context.Context, args *runtime.Workfl
|
|||
}
|
||||
|
||||
// 获取参数时间
|
||||
now := args.Args["now"].(time.Time)
|
||||
input := &StatisticsOursProductWorkflowInput{
|
||||
// 默认值,具体应从 rec 解析
|
||||
StartTime: now.Format("2006010200"),
|
||||
EndTime: now.Format("2006010215"),
|
||||
Time: args.Args["now"].(time.Time),
|
||||
}
|
||||
|
||||
input.StartTime = "2025122300"
|
||||
|
||||
// 工作流过程调用
|
||||
output, err := runnable.Invoke(ctx, input)
|
||||
if err != nil {
|
||||
|
|
@ -75,66 +72,107 @@ func (w *statisticsOursProduct) Invoke(ctx context.Context, args *runtime.Workfl
|
|||
return output, nil
|
||||
}
|
||||
|
||||
type StatisticsOursProductContext struct {
|
||||
Time time.Time
|
||||
StartTime string
|
||||
EndTime string
|
||||
Title string
|
||||
ProductData []statistics_ours_product.StatisticsOursProductItem
|
||||
ImgUrl string
|
||||
ExcelData [][]string
|
||||
}
|
||||
|
||||
func (w *statisticsOursProduct) buildWorkflow(ctx context.Context) (compose.Runnable[*StatisticsOursProductWorkflowInput, map[string]any], error) {
|
||||
c := compose.NewChain[*StatisticsOursProductWorkflowInput, map[string]any]()
|
||||
|
||||
// 1. 调用工具统计我们的商品
|
||||
// 1. 参数整理
|
||||
c.AppendLambda(compose.InvokableLambda(w.formatContext))
|
||||
|
||||
// 2. 调用工具统计我们的商品
|
||||
c.AppendLambda(compose.InvokableLambda(w.callStatisticsTool))
|
||||
|
||||
// 2. 生成 Excel 并转图片上传
|
||||
// 3. 生成 Excel 并转图片上传
|
||||
c.AppendLambda(compose.InvokableLambda(w.generateExcelAndUpload))
|
||||
|
||||
// 3. 转map输出
|
||||
// 4. 转map输出
|
||||
c.AppendLambda(compose.InvokableLambda(w.convertToMap))
|
||||
|
||||
return c.Compile(ctx)
|
||||
}
|
||||
|
||||
func (w *statisticsOursProduct) callStatisticsTool(ctx context.Context, input *StatisticsOursProductWorkflowInput) ([]statistics_ours_product.StatisticsOursProductItem, error) {
|
||||
req := statistics_ours_product.StatisticsOursProductRequest{
|
||||
Page: 1,
|
||||
Limit: 100, // 假设取前100条
|
||||
Serial: []string{input.StartTime, input.EndTime},
|
||||
}
|
||||
// formatContext 整理上下文参数
|
||||
func (w *statisticsOursProduct) formatContext(ctx context.Context, input *StatisticsOursProductWorkflowInput) (*StatisticsOursProductContext, error) {
|
||||
startTime := input.Time.Format("2006010200")
|
||||
endTime := input.Time.Format("2006010215")
|
||||
endTimeStr := input.Time.Format("1.2号15点")
|
||||
|
||||
return w.toolManager.Recharge.StatisticsOursProduct.Call(ctx, req)
|
||||
return &StatisticsOursProductContext{
|
||||
Time: time.Now(),
|
||||
StartTime: startTime,
|
||||
EndTime: endTime,
|
||||
Title: fmt.Sprintf("截止 %s 我们的商品统计", endTimeStr),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *statisticsOursProduct) generateExcelAndUpload(ctx context.Context, data []statistics_ours_product.StatisticsOursProductItem) (*StatisticsOursProductWorkflowOutput, error) {
|
||||
// 2. 获取模板路径 (假设在项目根目录的 assets/templates 下)
|
||||
func (w *statisticsOursProduct) callStatisticsTool(ctx context.Context, state *StatisticsOursProductContext) (*StatisticsOursProductContext, error) {
|
||||
req := statistics_ours_product.StatisticsOursProductRequest{
|
||||
Page: 1,
|
||||
Limit: 100, // 仅取前100条
|
||||
Serial: []string{state.StartTime, state.EndTime},
|
||||
}
|
||||
|
||||
dataList, err := w.toolManager.Recharge.StatisticsOursProduct.Call(ctx, req)
|
||||
if err != nil {
|
||||
log.Errorf("调用统计我们的商品工具失败: %v", err)
|
||||
return nil, fmt.Errorf("获取统计我们的商品数据失败")
|
||||
}
|
||||
if len(dataList) == 0 {
|
||||
return nil, fmt.Errorf("我们的商品数据为空")
|
||||
}
|
||||
|
||||
state.ProductData = dataList
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func (w *statisticsOursProduct) generateExcelAndUpload(ctx context.Context, state *StatisticsOursProductContext) (*StatisticsOursProductContext, error) {
|
||||
// 1. 获取模板路径
|
||||
cwd, _ := filepath.Abs(".")
|
||||
templatePath := filepath.Join(cwd, "tmpl", "excel_temp", "recharge_statistics_ours_product.xlsx")
|
||||
fileName := fmt.Sprintf("statistics_ours_product_%d", time.Now().Unix())
|
||||
templatePath := filepath.Join(cwd, "../../tmpl", "excel_temp", "recharge_statistics_ours_product.xlsx")
|
||||
fileName := fmt.Sprintf("statistics_ours_product_%d%d", time.Now().Unix(), rand.Intn(1000))
|
||||
|
||||
// 3. 转换数据为 [][]string
|
||||
excelData := w.convertDataToExcelFormat(data)
|
||||
// 2. 转换数据为 [][]string
|
||||
excelData := w.convertDataToExcelFormat(state.ProductData)
|
||||
|
||||
// 4. 生成 Excel
|
||||
excelBytes, err := w.toolManager.Common.ExcelGenerator.Call(templatePath, excelData, 4, 3)
|
||||
// 3. 生成 Excel
|
||||
req := &excel_generator.ExcelGeneratorRequest{
|
||||
TemplatePath: templatePath,
|
||||
ExcelData: excelData,
|
||||
StartRow: 4,
|
||||
StyleRow: 3,
|
||||
Title: state.Title,
|
||||
}
|
||||
excelBytes, err := w.toolManager.Common.ExcelGenerator.Call(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("生成 Excel 失败: %v", err)
|
||||
}
|
||||
|
||||
// 5. Excel 转图片
|
||||
// 4. Excel 转图片
|
||||
picBytes, err := w.toolManager.Common.ImageConverter.Call(fileName+".xlsx", excelBytes, 2)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Excel 转图片失败: %v", err)
|
||||
}
|
||||
|
||||
// 6. 上传 OSS
|
||||
// 5. 上传 OSS
|
||||
url, err := w.ossClient.UploadBytes(fileName+".png", picBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("上传 OSS 失败: %v", err)
|
||||
}
|
||||
|
||||
res := &StatisticsOursProductWorkflowOutput{
|
||||
Path: "",
|
||||
Url: url,
|
||||
Data: excelData,
|
||||
Desc: "",
|
||||
}
|
||||
state.ImgUrl = url
|
||||
state.ExcelData = excelData
|
||||
|
||||
return res, nil
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// convertDataToExcelFormat 将业务数据转换为 Excel 生成器需要的二维字符串数组
|
||||
|
|
@ -158,11 +196,11 @@ func (w *statisticsOursProduct) convertDataToExcelFormat(data []statistics_ours_
|
|||
return result
|
||||
}
|
||||
|
||||
func (w *statisticsOursProduct) convertToMap(ctx context.Context, output *StatisticsOursProductWorkflowOutput) (map[string]any, error) {
|
||||
func (w *statisticsOursProduct) convertToMap(ctx context.Context, state *StatisticsOursProductContext) (map[string]any, error) {
|
||||
return map[string]any{
|
||||
"path": output.Path,
|
||||
"url": output.Url,
|
||||
"data": output.Data,
|
||||
"desc": output.Desc,
|
||||
"path": "",
|
||||
"url": state.ImgUrl,
|
||||
"data": state.ExcelData,
|
||||
"desc": state.Title,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue