From b41e68fd098dfeec41bc4ad7489c3b596bdd16e3 Mon Sep 17 00:00:00 2001 From: fuzhongyun <15339891972@163.com> Date: Sun, 8 Feb 2026 15:56:36 +0800 Subject: [PATCH 01/15] =?UTF-8?q?fix:=201.=E8=A7=A3=E5=86=B3=E6=97=A5?= =?UTF-8?q?=E6=8A=A5=E5=8F=91=E9=80=81=E6=97=B6=E9=97=B4=E9=97=B4=E9=9A=94?= =?UTF-8?q?=E6=9E=81=E9=95=BF=E9=97=AE=E9=A2=98=202.=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=8D=8F=E7=A8=8B=E8=B6=85=E6=97=B6=E6=97=B6=E9=95=BF=201000s?= =?UTF-8?q?=20->=20100s=203.=E4=BF=AE=E6=94=B9=E9=83=A8=E5=88=86=E6=97=A0?= =?UTF-8?q?=E5=85=B3=E9=85=8D=E7=BD=AE=E3=80=81=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config_env.yaml | 2 ++ internal/biz/ding_talk_bot.go | 32 +++++++++++++++++++++++ internal/biz/handle/dingtalk/send_card.go | 2 +- internal/services/cron.go | 18 ++++++++----- internal/services/dtalk_bot_test.go | 8 +++--- 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/config/config_env.yaml b/config/config_env.yaml index 1b1a8bd..532191a 100644 --- a/config/config_env.yaml +++ b/config/config_env.yaml @@ -88,6 +88,8 @@ tools: zltxOrderAfterSaleResellerBatch: enabled: true base_url: "https://gateway.dev.cdlsxd.cn/zltx_api/admin/afterSales/reseller_pre_ai" + zltxResellerAuthProductToManagerAndDefaultLossReason: + base_url: "https://revcl.1688sup.com/api/admin/reseller/resellerAuthProduct/getManagerAndDefaultLossReason" # eino tool 配置 eino_tools: diff --git a/internal/biz/ding_talk_bot.go b/internal/biz/ding_talk_bot.go index 6773353..8aaa4cd 100644 --- a/internal/biz/ding_talk_bot.go +++ b/internal/biz/ding_talk_bot.go @@ -312,6 +312,38 @@ func (d *DingTalkBotBiz) SendReport(ctx context.Context, groupInfo *model.AiBotG return } +// SendReports 发送多个报告 +func (d *DingTalkBotBiz) SendReports(ctx context.Context, groupInfo *model.AiBotGroup, reports []*bbxt.ReportRes) (err error) { + if len(reports) == 0 { + return errors.New("report is empty") + } + + title := fmt.Sprintf("截止%s日报", time.Now().Format("1月2日15点")) + reportChan := make(chan string, len(reports)*2) + writeCount := 0 + for _, v := range reports { + if v == nil { + continue + } + reportChan <- fmt.Sprintf("**%s**", v.Title) + reportChan <- fmt.Sprintf("![图片](%s)", v.Url) + writeCount += 2 + } + close(reportChan) + if writeCount == 0 { + return errors.New("report is empty") + } + err = d.HandleStreamRes(ctx, &chatbot.BotCallbackDataModel{ + RobotCode: groupInfo.RobotCode, + ConversationType: constants.ConversationTypeGroup, + ConversationId: groupInfo.ConversationID, + Text: chatbot.BotCallbackDataTextModel{ + Content: title, + }, + }, reportChan) + return +} + func (d *DingTalkBotBiz) GetGroupInfo(ctx context.Context, groupId int) (group model.AiBotGroup, err error) { cond := builder.NewCond() diff --git a/internal/biz/handle/dingtalk/send_card.go b/internal/biz/handle/dingtalk/send_card.go index 4660f33..d2e5cb7 100644 --- a/internal/biz/handle/dingtalk/send_card.go +++ b/internal/biz/handle/dingtalk/send_card.go @@ -20,7 +20,7 @@ import ( ) const DefaultInterval = 100 * time.Millisecond -const HeardBeatX = 1000 +const HeardBeatX = 100 type SendCardClient struct { Auth *Auth diff --git a/internal/services/cron.go b/internal/services/cron.go index e3c5604..b1f1023 100644 --- a/internal/services/cron.go +++ b/internal/services/cron.go @@ -50,12 +50,18 @@ func (d *CronService) CronReportSendDingTalk(ctx context.Context) error { return err } - for _, report := range reports { - err = d.dingTalkBotBiz.SendReport(ctx, &groupInfo, report) - if err != nil { - log.Error(err) - continue - } + // for _, report := range reports { + // err = d.dingTalkBotBiz.SendReport(ctx, &groupInfo, report) + // if err != nil { + // log.Error(err) + // continue + // } + // } + + err = d.dingTalkBotBiz.SendReports(ctx, &groupInfo, reports) + if err != nil { + log.Error(err) + return err } return nil } diff --git a/internal/services/dtalk_bot_test.go b/internal/services/dtalk_bot_test.go index 37c206e..8e9e477 100644 --- a/internal/services/dtalk_bot_test.go +++ b/internal/services/dtalk_bot_test.go @@ -92,7 +92,7 @@ func run() { // 初始化Ollama服务 ollamaService := llm_service.NewOllamaGenerate(client, utils_vllmClient, configConfig, chatHisImpl) // 初始化工具管理器 - manager := tools.NewManager(configConfig, client) + manager := tools.NewManager(configConfig, client, rdb) // 初始化钉钉联系人客户端 contactClient, _ := dingtalk.NewContactClient(configConfig) // 初始化钉钉记事本客户端 @@ -120,8 +120,10 @@ func run() { group := qywx.NewGroup(botGroupQywxImpl, qywxAuth) other := qywx.NewOther(qywxAuth) qywxAppBiz := biz.NewQywxAppBiz(configConfig, botGroupQywxImpl, group, other) - groupConfigBiz := biz.NewGroupConfigBiz(toolRegis, utils_ossClient, botGroupConfigImpl, registry, configConfig, impl.NewReportDailyCacheImpl(db), rdb) - dingTalkBotBiz := biz.NewDingTalkBotBiz(doDo, handle, botConfigImpl, botGroupImpl, user, botChatHisImpl, impl.NewReportDailyCacheImpl(db), manager, configConfig, sendCardClient, groupConfigBiz) + reportDailyCacheImpl := impl.NewReportDailyCacheImpl(db) + macro := do.NewMacro(botGroupImpl, reportDailyCacheImpl, configConfig, rdb) + groupConfigBiz := biz.NewGroupConfigBiz(toolRegis, utils_ossClient, botGroupConfigImpl, registry, configConfig, impl.NewReportDailyCacheImpl(db), rdb, macro, manager, handle) + dingTalkBotBiz := biz.NewDingTalkBotBiz(doDo, handle, botConfigImpl, botGroupImpl, user, botChatHisImpl, reportDailyCacheImpl, manager, configConfig, sendCardClient, groupConfigBiz, macro) // 初始化钉钉机器人服务 cronService = NewCronService(configConfig, dingTalkBotBiz, qywxAppBiz, groupConfigBiz) } From a7c9d7ed3bc60c88d6695f2d22df842a960277a1 Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Mon, 9 Feb 2026 13:38:57 +0800 Subject: [PATCH 02/15] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=84=E7=90=86=E5=92=8C=E6=A8=A1=E5=9E=8B=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tmpl/excel_temp/kshj_total_ana.xlsx | Bin 10270 -> 10282 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tmpl/excel_temp/kshj_total_ana.xlsx b/tmpl/excel_temp/kshj_total_ana.xlsx index a2b81786c4c87d82fd6fd5dcb54ccff4b2c98411..fc64998506660abf972ba3af79d5868868071444 100644 GIT binary patch delta 2070 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 Date: Mon, 9 Feb 2026 15:28:35 +0800 Subject: [PATCH 03/15] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E6=8A=A5?= =?UTF-8?q?=E8=A1=A8=E7=94=9F=E6=88=90=E9=80=BB=E8=BE=91=E5=92=8C=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config_test.yaml | 13 ++-- internal/biz/group_config.go | 7 +- internal/tools/bbxt/bbxt.go | 96 +++++++++++++++------------- internal/tools/bbxt/excel.go | 33 +++++++--- tmpl/excel_temp/kshj_total_ana.xlsx | Bin 10282 -> 10363 bytes 5 files changed, 82 insertions(+), 67 deletions(-) diff --git a/config/config_test.yaml b/config/config_test.yaml index a6f07b9..563cd0e 100644 --- a/config/config_test.yaml +++ b/config/config_test.yaml @@ -26,12 +26,11 @@ coze: lsxd: # 统一登录 - login_url: "http://api.test.user.1688sup.com/v1/login/phone" - phone: "OFJ8UpqOlI7+w3Qklf36ZA==" - password: "tEbFegH/DRRh6LutFb7o3g==" - code: "123456" - check_token_url: "http://api.test.user.1688sup.com/v1/user/welcome" - + login_url: "https://api.user.1688sup.com/v1/login/phone" + phone: "ORlviZN7N06W2+WKLe76xg==" + password: "V5Uh8C4bamEM6UQZh4TCeQ==" + code: "456789" + check_token_url: "https://api.user.1688sup.com/v1/user/welcome" sys: session_len: 6 @@ -142,7 +141,7 @@ eino_tools: # == 通用工具 == # 表格转图片 excel2pic: - base_url: "http://192.168.6.109:8010/api/v1/convert" + base_url: "http://192.168.6.115:8010/api/v1/convert" dingtalk: api_key: "dingsbbntrkeiyazcfdg" diff --git a/internal/biz/group_config.go b/internal/biz/group_config.go index 0c6dbbc..38cbc01 100644 --- a/internal/biz/group_config.go +++ b/internal/biz/group_config.go @@ -98,13 +98,11 @@ func (g *GroupConfigBiz) GetReportLists(ctx context.Context, groupConfig *model. if err != nil { return } - //追加电商充值系统统计 - 返回统一使用[]*bbxt.ReportRes rechargeReports, err := g.rechargeDailyReport(ctx, time.Now(), nil, g.ossClient) if err != nil || len(rechargeReports) == 0 { return } - reports = append(reports, rechargeReports...) return @@ -175,7 +173,7 @@ func (g *GroupConfigBiz) handleReport(ctx context.Context, rec *entitys.Recogniz if _err != nil { return _err } - reports = append(reports, repo...) + reports = append(reports, repo) case "report_sales_analysis": product := strings.Split(groupConfig.ProductName, ",") repo, _err := rep.GetStatisOfficialProductSum(t, product) @@ -200,8 +198,9 @@ func (g *GroupConfigBiz) handleReport(ctx context.Context, rec *entitys.Recogniz if _err != nil { return _err } - reports = append(reports, repo...) reports = append(reports, rechargeReport...) + reports = append(reports, repo...) + case "report_daily_recharge": product := strings.Split(groupConfig.ProductName, ",") repo, _err := g.rechargeDailyReport(ctx, t, product, nil) diff --git a/internal/tools/bbxt/bbxt.go b/internal/tools/bbxt/bbxt.go index 7a03cce..1f9d2b6 100644 --- a/internal/tools/bbxt/bbxt.go +++ b/internal/tools/bbxt/bbxt.go @@ -13,6 +13,8 @@ import ( "sort" "time" + + "github.com/shopspring/decimal" ) const ( @@ -91,9 +93,7 @@ func (b *BbxtTools) DailyReport( if err != nil { return } - reports = append(reports, productLossReport...) - reports = append(reports, profitRankingSum, statisOfficialProductSum, statisOfficialProductSumDecline) - + reports = append(reports, profitRankingSum, statisOfficialProductSum, statisOfficialProductSumDecline, productLossReport) if ossClient != nil { uploader := NewUploader(ossClient, b.config) for _, report := range reports { @@ -135,7 +135,9 @@ func (b *BbxtTools) ResellerLossSort(ctx context.Context, now time.Time) ([]*Res reseller := resellerMap[info.ResellerId] // 累加经销商总亏损 - reseller.Total += info.Loss + num1 := decimal.NewFromFloat(reseller.Total) + num2 := decimal.NewFromFloat(info.Loss) + reseller.Total, _ = num1.Add(num2).Round(2).Float64() // 检查产品是否已存在 if _, ok := reseller.ProductLoss[info.OursProductId]; !ok { @@ -209,14 +211,14 @@ func (b *BbxtTools) ResellerLossToMapResellerLossSumProductRelation(totalDetail } // StatisOursProductLossSum 负利润分析 -func (b *BbxtTools) StatisOursProductLossSum(ctx context.Context, now time.Time, initFunc LossSumInitFunc) (report []*ReportRes, err error) { +func (b *BbxtTools) StatisOursProductLossSum(ctx context.Context, now time.Time, initFunc LossSumInitFunc) (report *ReportRes, err error) { resellers, err := b.ResellerLossSort(ctx, now) var ( - total [][]string - gt []*ResellerLoss + total [][]string + //gt []*ResellerLoss totalDetail []*ResellerLoss - totalSum float64 - totalSum500 float64 + totalSum = decimal.NewFromFloat(0) + //totalSum500 float64 ) // 构建分组 for _, v := range resellers { @@ -225,45 +227,46 @@ func (b *BbxtTools) StatisOursProductLossSum(ctx context.Context, now time.Time, fmt.Sprintf("%s", v.ResellerName), fmt.Sprintf("%.2f", v.Total), }) - totalSum += v.Total + num := decimal.NewFromFloat(v.Total) + totalSum = totalSum.Add(num).Round(2) totalDetail = append(totalDetail, v) } - if v.Total <= -500 && !slices.Contains(ResellerBlackListProduct, v.ResellerName) { - gt = append(gt, v) - totalSum500 += v.Total - } + //if v.Total <= -500 && !slices.Contains(ResellerBlackListProduct, v.ResellerName) { + // gt = append(gt, v) + // totalSum500 += v.Total + //} } - report = make([]*ReportRes, 3) + //report = make([]*ReportRes, 3) timeCh := now.Format("1月2日15点") //总量生成excel - if len(total) > 0 { - filePath := b.cacheDir + "/kshj_total" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx" - err = b.SimpleFillExcelWithTitle(b.excelTempDir+"/"+"kshj_total.xlsx", filePath, total, "") - if err != nil { - return - } - report[0] = &ReportRes{ - ReportName: "分销商负利润统计", - Title: "截至" + timeCh + "利润累计亏损" + fmt.Sprintf("%.2f", totalSum), - Path: filePath, - Data: total, - } - } - - if len(gt) > 0 { - filePath := b.cacheDir + "/kshj_gt" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx" - title := "截至" + timeCh + "亏损500以上的分销商和产品" - err = b.resellerDetailFillExcelV2(b.excelTempDir+"/"+"kshj_gt.xlsx", filePath, gt, title) - if err != nil { - return - } - report[1] = &ReportRes{ - ReportName: "负利润分析(亏损500以上)", - Title: "截至" + timeCh + "亏损500以上利润累计亏损" + fmt.Sprintf("%.2f", totalSum500), - Path: filePath, - Data: total, - } - } + //if len(total) > 0 { + // filePath := b.cacheDir + "/kshj_total" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx" + // err = b.SimpleFillExcelWithTitle(b.excelTempDir+"/"+"kshj_total.xlsx", filePath, total, "") + // if err != nil { + // return + // } + // report[0] = &ReportRes{ + // ReportName: "分销商负利润统计", + // Title: "截至" + timeCh + "利润累计亏损" + fmt.Sprintf("%.2f", totalSum), + // Path: filePath, + // Data: total, + // } + //} + // + //if len(gt) > 0 { + // filePath := b.cacheDir + "/kshj_gt" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx" + // title := "截至" + timeCh + "亏损500以上的分销商和产品" + // err = b.resellerDetailFillExcelV2(b.excelTempDir+"/"+"kshj_gt.xlsx", filePath, gt, title) + // if err != nil { + // return + // } + // report[1] = &ReportRes{ + // ReportName: "负利润分析(亏损500以上)", + // Title: "截至" + timeCh + "亏损500以上利润累计亏损" + fmt.Sprintf("%.2f", totalSum500), + // Path: filePath, + // Data: total, + // } + //} if len(totalDetail) > 0 { err = initFunc(ctx, now, totalDetail, b) @@ -271,14 +274,15 @@ func (b *BbxtTools) StatisOursProductLossSum(ctx context.Context, now time.Time, return } filePath := b.cacheDir + "/kshj_total_ana" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx" - title := "截至" + timeCh + "亏损100以上的分销商&产品负利润原因" + totalSumFloat64, _ := totalSum.Float64() + title := "截至" + timeCh + "利润累计亏损" + fmt.Sprintf("%.2f", totalSumFloat64) + ",亏损100以上利润原因如下" err = b.resellerDetailFillExcelAna(b.excelTempDir+"/"+"kshj_total_ana.xlsx", filePath, totalDetail, title) if err != nil { return } - report[2] = &ReportRes{ + report = &ReportRes{ ReportName: "负利润分析(亏损100以上)", - Title: "截至" + timeCh + "亏损100以上利润原因", + Title: title, Path: filePath, Data: total, } diff --git a/internal/tools/bbxt/excel.go b/internal/tools/bbxt/excel.go index 91915dc..c10906d 100644 --- a/internal/tools/bbxt/excel.go +++ b/internal/tools/bbxt/excel.go @@ -343,14 +343,17 @@ func (b *BbxtTools) resellerDetailFillExcelAna(templatePath, outputPath string, styleD3, err := f.GetCellStyle(sheet, fmt.Sprintf("D%d", tplRowData)) if err != nil { - styleC3 = 0 + styleD3 = 0 } styleE3, err := f.GetCellStyle(sheet, fmt.Sprintf("E%d", tplRowData)) if err != nil { - styleC3 = 0 + styleE3 = 0 + } + styleF3, err := f.GetCellStyle(sheet, fmt.Sprintf("F%d", tplRowData)) + if err != nil { + styleF3 = 0 } - rowHeightData, err := f.GetRowHeight(sheet, tplRowData) if err != nil { rowHeightData = 20 @@ -372,11 +375,15 @@ func (b *BbxtTools) resellerDetailFillExcelAna(templatePath, outputPath string, } styleTotalD, err := f.GetCellStyle(sheet, fmt.Sprintf("D%d", tplRowTotal)) if err != nil { - styleTotalC = 0 + styleTotalD = 0 } styleTotalE, err := f.GetCellStyle(sheet, fmt.Sprintf("E%d", tplRowTotal)) if err != nil { - styleTotalC = 0 + styleTotalE = 0 + } + styleTotalF, err := f.GetCellStyle(sheet, fmt.Sprintf("F%d", tplRowTotal)) + if err != nil { + styleTotalF = 0 } rowHeightTotal, err := f.GetRowHeight(sheet, tplRowTotal) if err != nil { @@ -408,8 +415,9 @@ func (b *BbxtTools) resellerDetailFillExcelAna(templatePath, outputPath string, f.SetCellValue(sheet, fmt.Sprintf("A%d", currentRow), reseller.ResellerName) f.SetCellValue(sheet, fmt.Sprintf("B%d", currentRow), p.ProductName) f.SetCellValue(sheet, fmt.Sprintf("C%d", currentRow), p.Loss) - f.SetCellValue(sheet, fmt.Sprintf("D%d", currentRow), reseller.Manager) - f.SetCellValue(sheet, fmt.Sprintf("E%d", currentRow), p.LossReason) + f.SetCellValue(sheet, fmt.Sprintf("D%d", currentRow), reseller.Total) + f.SetCellValue(sheet, fmt.Sprintf("E%d", currentRow), reseller.Manager) + f.SetCellValue(sheet, fmt.Sprintf("F%d", currentRow), p.LossReason) // 设置样式 if styleA3 != 0 { f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("A%d", currentRow), styleA3) @@ -426,7 +434,9 @@ func (b *BbxtTools) resellerDetailFillExcelAna(templatePath, outputPath string, if styleE3 != 0 { f.SetCellStyle(sheet, fmt.Sprintf("E%d", currentRow), fmt.Sprintf("E%d", currentRow), styleE3) } - + if styleF3 != 0 { + f.SetCellStyle(sheet, fmt.Sprintf("F%d", currentRow), fmt.Sprintf("F%d", currentRow), styleF3) + } totalLoss += p.Loss currentRow++ } @@ -436,6 +446,7 @@ func (b *BbxtTools) resellerDetailFillExcelAna(templatePath, outputPath string, if endRow > startRow { f.MergeCell(sheet, fmt.Sprintf("A%d", startRow), fmt.Sprintf("A%d", endRow)) f.MergeCell(sheet, fmt.Sprintf("D%d", startRow), fmt.Sprintf("D%d", endRow)) + f.MergeCell(sheet, fmt.Sprintf("E%d", startRow), fmt.Sprintf("E%d", endRow)) } } @@ -447,7 +458,7 @@ func (b *BbxtTools) resellerDetailFillExcelAna(templatePath, outputPath string, f.SetCellValue(sheet, fmt.Sprintf("A%d", currentRow), "合计") // B列留空,C列填充总亏损 - f.SetCellValue(sheet, fmt.Sprintf("C%d", currentRow), totalLoss) + f.SetCellValue(sheet, fmt.Sprintf("D%d", currentRow), totalLoss) // 设置合计行样式 if styleTotalA != 0 { @@ -465,7 +476,9 @@ func (b *BbxtTools) resellerDetailFillExcelAna(templatePath, outputPath string, if styleTotalE != 0 { f.SetCellStyle(sheet, fmt.Sprintf("E%d", currentRow), fmt.Sprintf("E%d", currentRow), styleTotalE) } - + if styleTotalF != 0 { + f.SetCellStyle(sheet, fmt.Sprintf("F%d", currentRow), fmt.Sprintf("F%d", currentRow), styleTotalF) + } // 取消合并合计行的A、B列 // f.MergeCell(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow)) diff --git a/tmpl/excel_temp/kshj_total_ana.xlsx b/tmpl/excel_temp/kshj_total_ana.xlsx index fc64998506660abf972ba3af79d5868868071444..73cb4683bde31b53d2a388e343d4419f23077034 100644 GIT binary patch delta 5386 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 delta 5324 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 From 90e89d927e78cd857c512fb3929ef1d08ac534a6 Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Mon, 9 Feb 2026 15:54:10 +0800 Subject: [PATCH 04/15] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E7=BB=8F?= =?UTF-8?q?=E9=94=80=E5=95=86=E7=AD=9B=E9=80=89=E9=80=BB=E8=BE=91=E5=B9=B6?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=BB=91=E5=90=8D=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/tools/bbxt/bbxt.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/internal/tools/bbxt/bbxt.go b/internal/tools/bbxt/bbxt.go index 1f9d2b6..be068bc 100644 --- a/internal/tools/bbxt/bbxt.go +++ b/internal/tools/bbxt/bbxt.go @@ -39,6 +39,8 @@ var ResellerBlackListProduct = []string{ "蓝星严选连续包月", "通钱-2025年12月", "彦浩同行", + "运营部测试专用", + "彦浩直客商户", } type BbxtTools struct { @@ -222,15 +224,19 @@ func (b *BbxtTools) StatisOursProductLossSum(ctx context.Context, now time.Time, ) // 构建分组 for _, v := range resellers { - if v.Total <= -100 && !slices.Contains(ResellerBlackListProduct, v.ResellerName) { + if slices.Contains(ResellerBlackListProduct, v.ResellerName) { + continue + } + if v.Total <= -100 { total = append(total, []string{ fmt.Sprintf("%s", v.ResellerName), fmt.Sprintf("%.2f", v.Total), }) - num := decimal.NewFromFloat(v.Total) - totalSum = totalSum.Add(num).Round(2) + totalDetail = append(totalDetail, v) } + num := decimal.NewFromFloat(v.Total) + totalSum = totalSum.Add(num).Round(2) //if v.Total <= -500 && !slices.Contains(ResellerBlackListProduct, v.ResellerName) { // gt = append(gt, v) // totalSum500 += v.Total From 229d3585ba55356e7e2945559a75cf31c4647b43 Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Mon, 9 Feb 2026 16:10:15 +0800 Subject: [PATCH 05/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84=E5=92=8C=E6=8A=A5?= =?UTF-8?q?=E8=A1=A8=E5=90=88=E5=B9=B6=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.yaml | 2 +- internal/biz/group_config.go | 2 +- internal/config/config.go | 2 +- tmpl/excel_temp/kshj_total_ana.xlsx | Bin 10363 -> 10362 bytes 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index c41dac1..99cd2b7 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -149,7 +149,7 @@ eino_tools: # == 通用工具 == # 表格转图片 excel2pic: - base_url: "http://excel2pic:8000/api/v1/convert" + base_url: "http://192.168.6.115:8010/api/v1/convert" dingtalk: api_key: "dingsbbntrkeiyazcfdg" diff --git a/internal/biz/group_config.go b/internal/biz/group_config.go index 38cbc01..ca0ccc5 100644 --- a/internal/biz/group_config.go +++ b/internal/biz/group_config.go @@ -103,7 +103,7 @@ func (g *GroupConfigBiz) GetReportLists(ctx context.Context, groupConfig *model. if err != nil || len(rechargeReports) == 0 { return } - reports = append(reports, rechargeReports...) + reports = append(rechargeReports, reports...) return } diff --git a/internal/config/config.go b/internal/config/config.go index 64857a9..6e9a2e6 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -309,7 +309,7 @@ func LoadConfigWithEnv() (*Config, error) { if err != nil { return nil, err } - viper.SetConfigFile(modularDir + "/config/config_env.yaml") + viper.SetConfigFile(modularDir + "/config/config.yaml") viper.SetConfigType("yaml") // 读取配置文件 if err := viper.ReadInConfig(); err != nil { diff --git a/tmpl/excel_temp/kshj_total_ana.xlsx b/tmpl/excel_temp/kshj_total_ana.xlsx index 73cb4683bde31b53d2a388e343d4419f23077034..e4e7277df86919133fdd87e74fc9817578319383 100644 GIT binary patch delta 4735 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 CFd-5E From c7baaa8514a790ab78a9d206e69e2208fc540e06 Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Mon, 9 Feb 2026 17:07:14 +0800 Subject: [PATCH 06/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84=E5=92=8C=E6=8A=A5?= =?UTF-8?q?=E8=A1=A8=E5=90=88=E5=B9=B6=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/services/dtalk_bot.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/services/dtalk_bot.go b/internal/services/dtalk_bot.go index 177406b..28eacb1 100644 --- a/internal/services/dtalk_bot.go +++ b/internal/services/dtalk_bot.go @@ -64,6 +64,7 @@ func (d *DingBotService) runBackgroundTasks(ctx context.Context, data *chatbot.B g.Go(func() error { defer func() { // 确保通道最终关闭 + log.Println("流式处理协程关闭") close(resChan) }() return d.dingTalkBotBiz.HandleStreamRes(ctx, data, resChan) From 02acb714baf671b9b7d835cb03b35f20366cb6a8 Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Mon, 9 Feb 2026 17:09:45 +0800 Subject: [PATCH 07/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84=E5=92=8C=E6=8A=A5?= =?UTF-8?q?=E8=A1=A8=E5=90=88=E5=B9=B6=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/services/dtalk_bot.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/services/dtalk_bot.go b/internal/services/dtalk_bot.go index 28eacb1..c0c29fe 100644 --- a/internal/services/dtalk_bot.go +++ b/internal/services/dtalk_bot.go @@ -65,9 +65,14 @@ func (d *DingBotService) runBackgroundTasks(ctx context.Context, data *chatbot.B defer func() { // 确保通道最终关闭 log.Println("流式处理协程关闭") + close(resChan) }() - return d.dingTalkBotBiz.HandleStreamRes(ctx, data, resChan) + err := d.dingTalkBotBiz.HandleStreamRes(ctx, data, resChan) + if err != nil { + log.Println("流式回复产生错误,错误:", err.Error()) + } + return err }) // 2. 业务处理协程(负责关闭requireData.Ch) From ce5597f4dd88e365c7cc7982d5a07a87ca78183f Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Mon, 9 Feb 2026 17:22:02 +0800 Subject: [PATCH 08/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84=E5=92=8C=E6=8A=A5?= =?UTF-8?q?=E8=A1=A8=E5=90=88=E5=B9=B6=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/handle/dingtalk/send_card.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/biz/handle/dingtalk/send_card.go b/internal/biz/handle/dingtalk/send_card.go index d2e5cb7..fd18c78 100644 --- a/internal/biz/handle/dingtalk/send_card.go +++ b/internal/biz/handle/dingtalk/send_card.go @@ -115,6 +115,7 @@ func (s *SendCardClient) NewCard(ctx context.Context, cardSend *CardSend) error s.processContentChannel(ctx, cardSend, cardInstanceId.String(), client) }() wg.Wait() + log.Info("处理通道结束") } return nil From 52689749bf14791e711cff16db5739132a5173cf Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Mon, 9 Feb 2026 17:28:07 +0800 Subject: [PATCH 09/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84=E5=92=8C=E6=8A=A5?= =?UTF-8?q?=E8=A1=A8=E5=90=88=E5=B9=B6=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/handle/dingtalk/send_card.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/biz/handle/dingtalk/send_card.go b/internal/biz/handle/dingtalk/send_card.go index fd18c78..31ade89 100644 --- a/internal/biz/handle/dingtalk/send_card.go +++ b/internal/biz/handle/dingtalk/send_card.go @@ -189,10 +189,12 @@ func (s *SendCardClient) processContentChannel(ctx context.Context, cardSend *Ca case <-heartbeatTicker.C: if time.Now().Unix()-lastUpdate.Unix() >= HeardBeatX { + log.Info("心跳超时") return } case <-ctx.Done(): + log.Info("send_card上下文失效") s.logger.Info("context canceled, stop channel processing") return } From 6b3ef527389a1fed6ba522fe0acdd2d46e2e3ca6 Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Mon, 9 Feb 2026 17:28:55 +0800 Subject: [PATCH 10/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84=E5=92=8C=E6=8A=A5?= =?UTF-8?q?=E8=A1=A8=E5=90=88=E5=B9=B6=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/handle/dingtalk/send_card.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/biz/handle/dingtalk/send_card.go b/internal/biz/handle/dingtalk/send_card.go index 31ade89..2b86313 100644 --- a/internal/biz/handle/dingtalk/send_card.go +++ b/internal/biz/handle/dingtalk/send_card.go @@ -174,6 +174,7 @@ func (s *SendCardClient) processContentChannel(ctx context.Context, cardSend *Ca // 通道关闭,发送最终内容 if contentBuilder.Len() > 0 { if err := s.updateCardContent(ctx, cardSend, cardInstanceId, contentBuilder.String(), client); err != nil { + log.Info("contentBuilder.Len()修改失败1") s.logger.Errorf("更新卡片失败1:%s", err.Error()) } } @@ -182,6 +183,7 @@ func (s *SendCardClient) processContentChannel(ctx context.Context, cardSend *Ca contentBuilder.WriteString(content) if contentBuilder.Len() > 0 { if err := s.updateCardContent(ctx, cardSend, cardInstanceId, contentBuilder.String(), client); err != nil { + log.Info("contentBuilder.Len()修改失败2") s.logger.Errorf("更新卡片失败2:%s", err.Error()) } } From bc9801d55314d0fbe01375c99daf78354ec8fefb Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Mon, 9 Feb 2026 17:31:16 +0800 Subject: [PATCH 11/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84=E5=92=8C=E6=8A=A5?= =?UTF-8?q?=E8=A1=A8=E5=90=88=E5=B9=B6=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/handle/dingtalk/send_card.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/biz/handle/dingtalk/send_card.go b/internal/biz/handle/dingtalk/send_card.go index 2b86313..71de592 100644 --- a/internal/biz/handle/dingtalk/send_card.go +++ b/internal/biz/handle/dingtalk/send_card.go @@ -164,7 +164,7 @@ func (s *SendCardClient) processContentChannel(ctx context.Context, cardSend *Ca var ( contentBuilder strings.Builder - lastUpdate time.Time + lastUpdate = time.Now() ) for { @@ -191,7 +191,7 @@ func (s *SendCardClient) processContentChannel(ctx context.Context, cardSend *Ca case <-heartbeatTicker.C: if time.Now().Unix()-lastUpdate.Unix() >= HeardBeatX { - log.Info("心跳超时") + log.Infof("心跳超时,当前时间:%d,最后时间:%d", time.Now().Unix(), lastUpdate.Unix()) return } From d85df95a65005bf17c90de424f9cf86535a0928b Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Fri, 27 Feb 2026 17:13:36 +0800 Subject: [PATCH 12/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8A=A0=E8=BD=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.yaml | 12 ++++++------ internal/biz/do/handle.go | 2 +- internal/biz/do/macro.go | 5 ++++- internal/biz/do/macro_test.go | 32 ++++++++++++++++++++++++++++++++ internal/tools/bbxt/bbxt_test.go | 2 +- 5 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 internal/biz/do/macro_test.go diff --git a/config/config.yaml b/config/config.yaml index 99cd2b7..37e1d40 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -5,12 +5,12 @@ server: ollama: base_url: "http://172.17.0.1:11434" - # model: "qwen3:8b" - # generate_model: "qwen3:8b" - # mapping_model: "qwen3:8b" - model: "qwen3-coder:480b-cloud" - generate_model: "qwen3-coder:480b-cloud" - mapping_model: "deepseek-v3.2:cloud" + model: "qwen3:8b" + generate_model: "qwen3:8b" + mapping_model: "qwen3:8b" +# model: "qwen3-coder:480b-cloud" +# generate_model: "qwen3-coder:480b-cloud" +# mapping_model: "deepseek-v3.2:cloud" vl_model: "qwen2.5vl:3b" timeout: "120s" level: "info" diff --git a/internal/biz/do/handle.go b/internal/biz/do/handle.go index 81c70b7..e312d04 100644 --- a/internal/biz/do/handle.go +++ b/internal/biz/do/handle.go @@ -506,7 +506,7 @@ func handleCozeWorkflowEvents(ctx context.Context, resp coze.Stream[coze.Workflo case coze.WorkflowEventTypeMessage: entitys.ResStream(ch, index, event.Message.Content) case coze.WorkflowEventTypeError: - entitys.ResError(ch, index, fmt.Sprintf("工作流执行错误: %s", event.Error)) + entitys.ResError(ch, index, fmt.Sprintf("工作流执行错误: %v", event.Error)) case coze.WorkflowEventTypeDone: entitys.ResEnd(ch, index, "工作流执行完成") case coze.WorkflowEventTypeInterrupt: diff --git a/internal/biz/do/macro.go b/internal/biz/do/macro.go index fe1ffc5..0d99485 100644 --- a/internal/biz/do/macro.go +++ b/internal/biz/do/macro.go @@ -26,6 +26,7 @@ import ( type Macro struct { botGroupImpl *impl.BotGroupImpl + botGroupConfigImpl *impl.BotGroupConfigImpl reportDailyCacheImpl *impl.ReportDailyCacheImpl config *config.Config rdb *utils.Rdb @@ -36,12 +37,14 @@ func NewMacro( reportDailyCacheImpl *impl.ReportDailyCacheImpl, config *config.Config, rdb *utils.Rdb, + botGroupConfigImpl *impl.BotGroupConfigImpl, ) *Macro { return &Macro{ botGroupImpl: botGroupImpl, reportDailyCacheImpl: reportDailyCacheImpl, config: config, rdb: rdb, + botGroupConfigImpl: botGroupConfigImpl, } } @@ -193,7 +196,7 @@ func (m *Macro) ProductModify(ctx context.Context, content string, groupConfig * groupConfig.ProductName = itemInfo cond := builder.NewCond() cond = cond.And(builder.Eq{"config_id": groupConfig.ConfigID}) - err = m.botGroupImpl.UpdateByCond(&cond, groupConfig) + err = m.botGroupConfigImpl.UpdateByCond(&cond, groupConfig) if err != nil { err = fmt.Errorf("修改失败:%v", err) return diff --git a/internal/biz/do/macro_test.go b/internal/biz/do/macro_test.go new file mode 100644 index 0000000..80445be --- /dev/null +++ b/internal/biz/do/macro_test.go @@ -0,0 +1,32 @@ +package do + +import ( + "ai_scheduler/internal/config" + "ai_scheduler/internal/data/impl" + "ai_scheduler/internal/data/model" + + "ai_scheduler/utils" + "context" + "testing" +) + +func Test_report(t *testing.T) { + con := "[利润同比报表]商品修改:官方–优酷周卡,官方–优酷月卡,官方–优酷季卡,官方–优酷年卡,,官方–爱奇艺-月卡,官方–爱奇艺-季卡,官方–爱奇艺-年卡,官方–芒果-PC周卡,官方–芒果-PC月卡,官方–芒果-PC季卡,官方–QQ音乐-绿钻月卡,官方–饿了么超级会员月卡,官方–网易云黑胶vip月卡,官方–喜马拉雅巅峰会员月卡,剪映会员7天卡,剪映会员月卡,剪映会员年卡,剪映SVIP会员7天卡,剪映SVIP会员月卡,剪映SVIP会员年卡" + run() + + chatId, err, i := ma.ProductModify(context.Background(), con, &model.AiBotGroupConfig{ConfigID: 1, ToolList: "8,9,10,11,12,13,16"}) + t.Log(chatId, err, i) +} + +var ma *Macro + +func run() { + configConfig, _ := config.LoadConfigWithTest() + db, _ := utils.NewGormDb(configConfig) + + rdb := utils.NewRdb(configConfig) + reportDailyCacheImpl := impl.NewReportDailyCacheImpl(db) + botGroupImpl := impl.NewBotGroupImpl(db) + botGroupConfigImpl := impl.NewBotGroupConfigImpl(db) + ma = NewMacro(botGroupImpl, reportDailyCacheImpl, configConfig, rdb, botGroupConfigImpl) +} diff --git a/internal/tools/bbxt/bbxt_test.go b/internal/tools/bbxt/bbxt_test.go index 3d0f623..b2e58b1 100644 --- a/internal/tools/bbxt/bbxt_test.go +++ b/internal/tools/bbxt/bbxt_test.go @@ -106,7 +106,7 @@ var ( ) func run() { - configConfig, _ = config.LoadConfigWithTest() + configConfig, _ = config.LoadConfigWithEnv() // 初始化数据库连接 db, _ := utils.NewGormDb(configConfig) reportDailyCacheImpl = impl.NewReportDailyCacheImpl(db) From 376c08e83650d5d50f262cc029a455320a8e530e Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Fri, 27 Feb 2026 17:28:00 +0800 Subject: [PATCH 13/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8A=A0=E8=BD=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index 37e1d40..57ce0fc 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -5,12 +5,12 @@ server: ollama: base_url: "http://172.17.0.1:11434" - model: "qwen3:8b" - generate_model: "qwen3:8b" - mapping_model: "qwen3:8b" -# model: "qwen3-coder:480b-cloud" -# generate_model: "qwen3-coder:480b-cloud" -# mapping_model: "deepseek-v3.2:cloud" +# model: "qwen3:8b" +# generate_model: "qwen3:8b" +# mapping_model: "qwen3:8b" + model: "qwen3-coder:480b-cloud" + generate_model: "qwen3-coder:480b-cloud" + mapping_model: "deepseek-v3.2:cloud" vl_model: "qwen2.5vl:3b" timeout: "120s" level: "info" From f7c2b5f855ec14cb3e8796fc7aa16626d3e12c38 Mon Sep 17 00:00:00 2001 From: renzhiyuan <465386466@qq.com> Date: Fri, 27 Feb 2026 18:08:57 +0800 Subject: [PATCH 14/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8A=A0=E8=BD=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/server/main.go | 2 +- config/config.yaml | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 806c43d..bd6b49b 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -10,7 +10,7 @@ import ( ) func main() { - configPath := flag.String("config", "./config/config_test.yaml", "Path to configuration file") + configPath := flag.String("config", "./config/config.yaml", "Path to configuration file") onBot := flag.String("bot", "", "bot start") cron := flag.String("cron", "", "close") flag.Parse() diff --git a/config/config.yaml b/config/config.yaml index 57ce0fc..721ff60 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -4,13 +4,13 @@ server: host: "0.0.0.0" ollama: - base_url: "http://172.17.0.1:11434" -# model: "qwen3:8b" -# generate_model: "qwen3:8b" -# mapping_model: "qwen3:8b" - model: "qwen3-coder:480b-cloud" - generate_model: "qwen3-coder:480b-cloud" - mapping_model: "deepseek-v3.2:cloud" + base_url: "http://192.168.6.115:11434" + model: "qwen3:8b" + generate_model: "qwen3:8b" + mapping_model: "qwen3:8b" +# model: "qwen3-coder:480b-cloud" +# generate_model: "qwen3-coder:480b-cloud" +# mapping_model: "deepseek-v3.2:cloud" vl_model: "qwen2.5vl:3b" timeout: "120s" level: "info" From ca671694f95cf20416743ee90ef01e7e351e5021 Mon Sep 17 00:00:00 2001 From: fuzhongyun <15339891972@163.com> Date: Sat, 28 Feb 2026 14:16:53 +0800 Subject: [PATCH 15/15] =?UTF-8?q?fix:=201.=20=E8=B0=83=E6=95=B4vllm?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=8F=8A=E5=85=B6=E7=9B=B8=E5=85=B3=202.=20?= =?UTF-8?q?=E6=84=8F=E5=9B=BE=E8=AF=86=E5=88=AB=E6=A8=A1=E5=9E=8B=E5=88=87?= =?UTF-8?q?=E6=8D=A2=20ollama=20->=20vllm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.yaml | 14 ++- config/config_env.yaml | 14 ++- config/config_test.yaml | 14 ++- internal/biz/do/handle.go | 6 +- internal/biz/llm_service/vllm.go | 153 ++++++++++++++++++++++++++++++ internal/biz/provider_set.go | 1 + internal/config/config.go | 7 +- internal/pkg/utils_vllm/client.go | 52 ++++++++-- 8 files changed, 237 insertions(+), 24 deletions(-) create mode 100644 internal/biz/llm_service/vllm.go diff --git a/config/config.yaml b/config/config.yaml index 57ce0fc..0636216 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -17,10 +17,16 @@ ollama: format: "json" vllm: - base_url: "http://172.17.0.1:8001/v1" - vl_model: "qwen2.5-vl-3b-awq" - timeout: "120s" - level: "info" + vl_model: + base_url: "http://192.168.6.115:8001/v1" + model: "qwen2.5-vl-3b-awq" + timeout: "120s" + level: "info" + text_model: + base_url: "http://192.168.6.115:8002/v1" + model: "qwen3-8b-fp8" + timeout: "120s" + level: "info" coze: base_url: "https://api.coze.cn" diff --git a/config/config_env.yaml b/config/config_env.yaml index 532191a..00c5349 100644 --- a/config/config_env.yaml +++ b/config/config_env.yaml @@ -14,10 +14,16 @@ ollama: format: "json" vllm: - base_url: "http://117.175.169.61:16001/v1" - vl_model: "qwen2.5-vl-3b-awq" - timeout: "120s" - level: "info" + vl_model: + base_url: "http://192.168.6.115:8001/v1" + model: "qwen2.5-vl-3b-awq" + timeout: "120s" + level: "info" + text_model: + base_url: "http://192.168.6.115:8002/v1" + model: "qwen3-8b-fp8" + timeout: "120s" + level: "info" coze: base_url: "https://api.coze.cn" diff --git a/config/config_test.yaml b/config/config_test.yaml index 563cd0e..82e7df1 100644 --- a/config/config_test.yaml +++ b/config/config_test.yaml @@ -14,10 +14,16 @@ ollama: format: "json" vllm: - base_url: "http://host.docker.internal:8001/v1" - vl_model: "qwen2.5-vl-3b-awq" - timeout: "120s" - level: "info" + vl_model: + base_url: "http://192.168.6.115:8001/v1" + model: "qwen2.5-vl-3b-awq" + timeout: "120s" + level: "info" + text_model: + base_url: "http://192.168.6.115:8002/v1" + model: "qwen3-8b-fp8" + timeout: "120s" + level: "info" coze: base_url: "https://api.coze.cn" diff --git a/internal/biz/do/handle.go b/internal/biz/do/handle.go index e312d04..828c059 100644 --- a/internal/biz/do/handle.go +++ b/internal/biz/do/handle.go @@ -36,6 +36,7 @@ import ( type Handle struct { Ollama *llm_service.OllamaService + Vllm *llm_service.VllmService toolManager *tools.Manager conf *config.Config sessionImpl *impl.SessionImpl @@ -47,6 +48,7 @@ type Handle struct { func NewHandle( Ollama *llm_service.OllamaService, + Vllm *llm_service.VllmService, toolManager *tools.Manager, conf *config.Config, sessionImpl *impl.SessionImpl, @@ -57,6 +59,7 @@ func NewHandle( ) *Handle { return &Handle{ Ollama: Ollama, + Vllm: Vllm, toolManager: toolManager, conf: conf, sessionImpl: sessionImpl, @@ -72,7 +75,8 @@ func (r *Handle) Recognize(ctx context.Context, rec *entitys.Recognize, promptPr prompt, err := promptProcessor.CreatePrompt(ctx, rec) //意图识别 - recognizeMsg, err := r.Ollama.IntentRecognize(ctx, &entitys.ToolSelect{ + // recognizeMsg, err := r.Ollama.IntentRecognize(ctx, &entitys.ToolSelect{ + recognizeMsg, err := r.Vllm.IntentRecognize(ctx, &entitys.ToolSelect{ Prompt: prompt, Tools: rec.Tasks, }) diff --git a/internal/biz/llm_service/vllm.go b/internal/biz/llm_service/vllm.go new file mode 100644 index 0000000..c054f27 --- /dev/null +++ b/internal/biz/llm_service/vllm.go @@ -0,0 +1,153 @@ +package llm_service + +import ( + "ai_scheduler/internal/config" + "ai_scheduler/internal/entitys" + "ai_scheduler/internal/pkg" + "ai_scheduler/internal/pkg/utils_vllm" + "context" + "encoding/base64" + "errors" + "fmt" + "strings" + + "github.com/cloudwego/eino/schema" + "github.com/ollama/ollama/api" +) + +type VllmService struct { + client *utils_vllm.Client + config *config.Config +} + +func NewVllmService( + client *utils_vllm.Client, + config *config.Config, +) *VllmService { + return &VllmService{ + client: client, + config: config, + } +} + +func (s *VllmService) IntentRecognize(ctx context.Context, req *entitys.ToolSelect) (msg string, err error) { + msgs := s.convertMessages(req.Prompt) + tools := s.convertTools(req.Tools) + + resp, err := s.client.ToolSelect(ctx, msgs, tools) + if err != nil { + return + } + + if resp.Content == "" { + if len(resp.ToolCalls) > 0 { + call := resp.ToolCalls[0] + var matchFromTools = &entitys.Match{ + Confidence: 1, + Index: call.Function.Name, + Parameters: call.Function.Arguments, + IsMatch: true, + } + msg = pkg.JsonStringIgonErr(matchFromTools) + } else { + err = errors.New("不太明白你想表达的意思呢,可以在仔细描述一下您所需要的内容吗,感谢感谢") + return + } + } else { + msg = resp.Content + } + return +} + +func (s *VllmService) convertMessages(prompts []api.Message) []*schema.Message { + msgs := make([]*schema.Message, 0, len(prompts)) + for _, p := range prompts { + msg := &schema.Message{ + Role: schema.RoleType(p.Role), + Content: p.Content, + } + + // 这里实际应该不会走进来 + if len(p.Images) > 0 { + parts := []schema.MessageInputPart{ + {Type: schema.ChatMessagePartTypeText, Text: p.Content}, + } + for _, imgData := range p.Images { + b64 := base64.StdEncoding.EncodeToString(imgData) + mimeType := "image/jpeg" + parts = append(parts, schema.MessageInputPart{ + Type: schema.ChatMessagePartTypeImageURL, + Image: &schema.MessageInputImage{ + MessagePartCommon: schema.MessagePartCommon{ + MIMEType: mimeType, + Base64Data: &b64, + }, + }, + }) + } + msg.UserInputMultiContent = parts + } + msgs = append(msgs, msg) + } + return msgs +} + +func (s *VllmService) convertTools(tasks []entitys.RegistrationTask) []*schema.ToolInfo { + tools := make([]*schema.ToolInfo, 0, len(tasks)) + for _, task := range tasks { + params := make(map[string]*schema.ParameterInfo) + for k, v := range task.TaskConfigDetail.Param.Properties { + dt := schema.String + + // Handle v.Type dynamically to support both string and []string (compiler suggests []string) + // Using fmt.Sprint handles both cases safely without knowing exact type structure + typeStr := fmt.Sprintf("%v", v.Type) + typeStr = strings.Trim(typeStr, "[]") // normalize "[string]" -> "string" + + switch typeStr { + case "string": + dt = schema.String + case "integer", "int": + dt = schema.Integer + case "number", "float": + dt = schema.Number + case "boolean", "bool": + dt = schema.Boolean + case "object": + dt = schema.Object + case "array": + dt = schema.Array + } + + required := false + for _, r := range task.TaskConfigDetail.Param.Required { + if r == k { + required = true + break + } + } + + desc := v.Description + if len(v.Enum) > 0 { + var enumStrs []string + for _, e := range v.Enum { + enumStrs = append(enumStrs, fmt.Sprintf("%v", e)) + } + desc += " Enum: " + strings.Join(enumStrs, ", ") + } + + params[k] = &schema.ParameterInfo{ + Type: dt, + Desc: desc, + Required: required, + } + } + + tools = append(tools, &schema.ToolInfo{ + Name: task.Name, + Desc: task.Desc, + ParamsOneOf: schema.NewParamsOneOfByParams(params), + }) + } + return tools +} diff --git a/internal/biz/provider_set.go b/internal/biz/provider_set.go index 6f95898..2054c54 100644 --- a/internal/biz/provider_set.go +++ b/internal/biz/provider_set.go @@ -13,6 +13,7 @@ var ProviderSetBiz = wire.NewSet( NewChatHistoryBiz, //llm_service.NewLangChainGenerate, llm_service.NewOllamaGenerate, + llm_service.NewVllmService, //handle.NewHandle, do.NewDo, do.NewHandle, diff --git a/internal/config/config.go b/internal/config/config.go index 6e9a2e6..52932bb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -122,8 +122,13 @@ type OllamaConfig struct { } type VllmConfig struct { + VLModel VllmModel `mapstructure:"vl_model"` + TextModel VllmModel `mapstructure:"text_model"` +} + +type VllmModel struct { BaseURL string `mapstructure:"base_url"` - VlModel string `mapstructure:"vl_model"` + Model string `mapstructure:"model"` Timeout time.Duration `mapstructure:"timeout"` Level string `mapstructure:"level"` } diff --git a/internal/pkg/utils_vllm/client.go b/internal/pkg/utils_vllm/client.go index c8c4aec..6926b21 100644 --- a/internal/pkg/utils_vllm/client.go +++ b/internal/pkg/utils_vllm/client.go @@ -7,33 +7,63 @@ import ( "encoding/base64" "github.com/cloudwego/eino-ext/components/model/openai" + "github.com/cloudwego/eino/components/model" "github.com/cloudwego/eino/schema" ) type Client struct { - model *openai.ChatModel - config *config.Config + vlModel *openai.ChatModel + generateModel *openai.ChatModel + config *config.Config } func NewClient(config *config.Config) (*Client, func(), error) { - m, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{ - BaseURL: config.Vllm.BaseURL, - Model: config.Vllm.VlModel, - Timeout: config.Vllm.Timeout, + // 初始化视觉模型 + vl, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{ + BaseURL: config.Vllm.VLModel.BaseURL, + Model: config.Vllm.VLModel.Model, + Timeout: config.Vllm.VLModel.Timeout, }) if err != nil { return nil, nil, err } - c := &Client{model: m, config: config} + + // 初始化生成模型 + gen, err := openai.NewChatModel(context.Background(), &openai.ChatModelConfig{ + BaseURL: config.Vllm.TextModel.BaseURL, + Model: config.Vllm.TextModel.Model, + Timeout: config.Vllm.TextModel.Timeout, + ExtraFields: map[string]any{ + "chat_template_kwargs": map[string]any{ + "enable_thinking": false, + }, + }, + }) + if err != nil { + return nil, nil, err + } + + c := &Client{ + vlModel: vl, + generateModel: gen, + config: config, + } cleanup := func() {} return c, cleanup, nil } func (c *Client) Chat(ctx context.Context, msgs []*schema.Message) (*schema.Message, error) { - return c.model.Generate(ctx, msgs) + // 默认聊天使用生成模型 + return c.generateModel.Generate(ctx, msgs) +} + +func (c *Client) ToolSelect(ctx context.Context, msgs []*schema.Message, tools []*schema.ToolInfo) (*schema.Message, error) { + // 工具选择使用生成模型 + return c.generateModel.Generate(ctx, msgs, model.WithTools(tools)) } func (c *Client) RecognizeWithImg(ctx context.Context, systemPrompt, userPrompt string, imgURLs []string) (*schema.Message, error) { + // 图片识别使用视觉模型 in := []*schema.Message{ { Role: schema.System, @@ -58,11 +88,12 @@ func (c *Client) RecognizeWithImg(ctx context.Context, systemPrompt, userPrompt } in[1].UserInputMultiContent = parts - return c.model.Generate(ctx, in) + return c.vlModel.Generate(ctx, in) } // 识别图片by二进制文件 func (c *Client) RecognizeWithImgBytes(ctx context.Context, systemPrompt, userPrompt string, imgBytes []byte, imgType string) (*schema.Message, error) { + // 图片识别使用视觉模型 in := []*schema.Message{ { Role: schema.System, @@ -82,9 +113,10 @@ func (c *Client) RecognizeWithImgBytes(ctx context.Context, systemPrompt, userPr MIMEType: imgType, Base64Data: util.AnyToPoint(base64.StdEncoding.EncodeToString(imgBytes)), }, + Detail: schema.ImageURLDetailHigh, }, }) in[1].UserInputMultiContent = parts - return c.model.Generate(ctx, in) + return c.vlModel.Generate(ctx, in) }