Compare commits
11 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
c7e1bad7ef | |
|
|
c91e7bc95b | |
|
|
289ac1de7a | |
|
|
e538f49bea | |
|
|
c13c601475 | |
|
|
b5312e73f0 | |
|
|
6c987b15db | |
|
|
03652cb588 | |
|
|
44155c058d | |
|
|
b4dddb0635 | |
|
|
89323d3250 |
|
|
@ -27,6 +27,7 @@ lsxd:
|
||||||
login_url: "https://api.user.1688sup.com/v1/login/phone"
|
login_url: "https://api.user.1688sup.com/v1/login/phone"
|
||||||
phone: "ORlviZN7N06W2+WKLe76xg=="
|
phone: "ORlviZN7N06W2+WKLe76xg=="
|
||||||
password: "V5Uh8C4bamEM6UQZh4TCeQ=="
|
password: "V5Uh8C4bamEM6UQZh4TCeQ=="
|
||||||
|
code: "456789"
|
||||||
check_token_url: "https://api.user.1688sup.com/v1/user/welcome"
|
check_token_url: "https://api.user.1688sup.com/v1/user/welcome"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,14 @@ coze:
|
||||||
|
|
||||||
lsxd:
|
lsxd:
|
||||||
# 统一登录
|
# 统一登录
|
||||||
login_url: "https://api.user.1688sup.com/v1/login/phone"
|
login_url: "http://api.test.user.1688sup.com/v1/login/phone"
|
||||||
phone: "ORlviZN7N06W2+WKLe76xg=="
|
phone: "OFJ8UpqOlI7+w3Qklf36ZA=="
|
||||||
password: "V5Uh8C4bamEM6UQZh4TCeQ=="
|
password: "tEbFegH/DRRh6LutFb7o3g=="
|
||||||
check_token_url: "https://api.user.1688sup.com/v1/user/welcome"
|
code: "123456"
|
||||||
|
check_token_url: "http://api.test.user.1688sup.com/v1/user/welcome"
|
||||||
|
|
||||||
|
zltx:
|
||||||
|
req_url: "https://gateway.dev.cdlsxd.cn/zltx_api"
|
||||||
|
|
||||||
sys:
|
sys:
|
||||||
session_len: 6
|
session_len: 6
|
||||||
|
|
@ -41,7 +45,7 @@ redis:
|
||||||
host: 47.97.27.195:6379
|
host: 47.97.27.195:6379
|
||||||
type: node
|
type: node
|
||||||
pass: lansexiongdi@666
|
pass: lansexiongdi@666
|
||||||
key: report-api-test
|
key: ai_scheduler-test
|
||||||
pollSize: 5 #连接池大小,不配置,或配置为0表示不启用连接池
|
pollSize: 5 #连接池大小,不配置,或配置为0表示不启用连接池
|
||||||
minIdleConns: 2 #最小空闲连接数
|
minIdleConns: 2 #最小空闲连接数
|
||||||
maxIdleTime: 30 #每个连接最大空闲时间,如果超过了这个时间会被关闭
|
maxIdleTime: 30 #每个连接最大空闲时间,如果超过了这个时间会被关闭
|
||||||
|
|
@ -138,7 +142,7 @@ eino_tools:
|
||||||
# == 通用工具 ==
|
# == 通用工具 ==
|
||||||
# 表格转图片
|
# 表格转图片
|
||||||
excel2pic:
|
excel2pic:
|
||||||
base_url: "http://192.168.6.109:8010/api/v1/convert"
|
base_url: "http://excel2pic:8000/api/v1/convert"
|
||||||
|
|
||||||
dingtalk:
|
dingtalk:
|
||||||
api_key: "dingsbbntrkeiyazcfdg"
|
api_key: "dingsbbntrkeiyazcfdg"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"ai_scheduler/internal/pkg/util"
|
"ai_scheduler/internal/pkg/util"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -30,7 +31,8 @@ func (s *ChatHistoryBiz) List(ctx context.Context, query *entitys.ChatHistQuery)
|
||||||
con := []impl.CondFunc{
|
con := []impl.CondFunc{
|
||||||
s.chatHiRepo.WithSessionId(query.SessionID),
|
s.chatHiRepo.WithSessionId(query.SessionID),
|
||||||
s.chatHiRepo.PaginateScope(query.Page, query.PageSize),
|
s.chatHiRepo.PaginateScope(query.Page, query.PageSize),
|
||||||
s.chatHiRepo.OrderByDesc("his_id"),
|
// s.chatHiRepo.OrderByDesc("his_id"),
|
||||||
|
s.chatHiRepo.OrderByAsc("his_id"),
|
||||||
}
|
}
|
||||||
if query.HisID > 0 {
|
if query.HisID > 0 {
|
||||||
con = append(con, s.chatHiRepo.WithHisId(query.HisID))
|
con = append(con, s.chatHiRepo.WithHisId(query.HisID))
|
||||||
|
|
@ -127,5 +129,5 @@ func (c *ChatHistoryBiz) UpdateContent(ctx context.Context, chat *entitys.Update
|
||||||
func (s *ChatHistoryBiz) Update(ctx context.Context, chat *entitys.UseFulRequest) error {
|
func (s *ChatHistoryBiz) Update(ctx context.Context, chat *entitys.UseFulRequest) error {
|
||||||
cond := builder.NewCond()
|
cond := builder.NewCond()
|
||||||
cond = cond.And(builder.Eq{"his_id": chat.HisId})
|
cond = cond.And(builder.Eq{"his_id": chat.HisId})
|
||||||
return s.chatHiRepo.UpdateByCond(&cond, &model.AiChatHi{HisID: chat.HisId, Useful: chat.Useful})
|
return s.chatHiRepo.UpdateByCond(&cond, &model.AiChatHi{Useful: chat.Useful})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,21 +29,22 @@ import (
|
||||||
|
|
||||||
// AiRouterBiz 智能路由服务
|
// AiRouterBiz 智能路由服务
|
||||||
type DingTalkBotBiz struct {
|
type DingTalkBotBiz struct {
|
||||||
do *do.Do
|
do *do.Do
|
||||||
handle *do.Handle
|
handle *do.Handle
|
||||||
botConfigImpl *impl.BotConfigImpl
|
botConfigImpl *impl.BotConfigImpl
|
||||||
replier *chatbot.ChatbotReplier
|
replier *chatbot.ChatbotReplier
|
||||||
log log.Logger
|
log log.Logger
|
||||||
dingTalkUser *dingtalk.User
|
dingTalkUser *dingtalk.User
|
||||||
botGroupImpl *impl.BotGroupImpl
|
botGroupImpl *impl.BotGroupImpl
|
||||||
botGroupConfigImpl *impl.BotGroupConfigImpl
|
botGroupConfigImpl *impl.BotGroupConfigImpl
|
||||||
botGroupQywxImpl *impl.BotGroupQywxImpl
|
botGroupQywxImpl *impl.BotGroupQywxImpl
|
||||||
toolManager *tools.Manager
|
toolManager *tools.Manager
|
||||||
chatHis *impl.BotChatHisImpl
|
chatHis *impl.BotChatHisImpl
|
||||||
conf *config.Config
|
conf *config.Config
|
||||||
cardSend *dingtalk.SendCardClient
|
cardSend *dingtalk.SendCardClient
|
||||||
qywxGroupHandle *qywx.Group
|
qywxGroupHandle *qywx.Group
|
||||||
groupConfigBiz *GroupConfigBiz
|
groupConfigBiz *GroupConfigBiz
|
||||||
|
reportDailyCacheImpl *impl.ReportDailyCacheImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDingTalkBotBiz
|
// NewDingTalkBotBiz
|
||||||
|
|
@ -54,23 +55,25 @@ func NewDingTalkBotBiz(
|
||||||
botGroupImpl *impl.BotGroupImpl,
|
botGroupImpl *impl.BotGroupImpl,
|
||||||
dingTalkUser *dingtalk.User,
|
dingTalkUser *dingtalk.User,
|
||||||
chatHis *impl.BotChatHisImpl,
|
chatHis *impl.BotChatHisImpl,
|
||||||
|
reportDailyCacheImpl *impl.ReportDailyCacheImpl,
|
||||||
toolManager *tools.Manager,
|
toolManager *tools.Manager,
|
||||||
conf *config.Config,
|
conf *config.Config,
|
||||||
cardSend *dingtalk.SendCardClient,
|
cardSend *dingtalk.SendCardClient,
|
||||||
groupConfigBiz *GroupConfigBiz,
|
groupConfigBiz *GroupConfigBiz,
|
||||||
) *DingTalkBotBiz {
|
) *DingTalkBotBiz {
|
||||||
return &DingTalkBotBiz{
|
return &DingTalkBotBiz{
|
||||||
do: do,
|
do: do,
|
||||||
handle: handle,
|
handle: handle,
|
||||||
botConfigImpl: botConfigImpl,
|
botConfigImpl: botConfigImpl,
|
||||||
replier: chatbot.NewChatbotReplier(),
|
replier: chatbot.NewChatbotReplier(),
|
||||||
dingTalkUser: dingTalkUser,
|
dingTalkUser: dingTalkUser,
|
||||||
groupConfigBiz: groupConfigBiz,
|
groupConfigBiz: groupConfigBiz,
|
||||||
botGroupImpl: botGroupImpl,
|
botGroupImpl: botGroupImpl,
|
||||||
toolManager: toolManager,
|
toolManager: toolManager,
|
||||||
chatHis: chatHis,
|
chatHis: chatHis,
|
||||||
conf: conf,
|
conf: conf,
|
||||||
cardSend: cardSend,
|
cardSend: cardSend,
|
||||||
|
reportDailyCacheImpl: reportDailyCacheImpl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,6 +200,49 @@ func (d *DingTalkBotBiz) Macro(ctx context.Context, requireData *entitys.Require
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.Contains(content, "[负利润分析]获取") {
|
||||||
|
var (
|
||||||
|
data model.AiReportDailyCache
|
||||||
|
value map[int32]*bbxt.ResellerLossSumProductRelation
|
||||||
|
)
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"`index`": bbxt.IndexLossSumDetail})
|
||||||
|
cond = cond.And(builder.Eq{"`key`": time.Now().Format(time.DateOnly)})
|
||||||
|
err = d.reportDailyCacheImpl.GetOneBySearchToStrut(&cond, &data)
|
||||||
|
if err != nil {
|
||||||
|
entitys.ResText(requireData.Ch, "", "获取失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = json.Unmarshal([]byte(data.Value), &value)
|
||||||
|
if err != nil {
|
||||||
|
entitys.ResText(requireData.Ch, "", "获取失败,格式解析错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(content, "[负利润分析]更新") {
|
||||||
|
// 提取冒号后的内容
|
||||||
|
if len(groupConfig.ProductName) == 0 {
|
||||||
|
entitys.ResText(requireData.Ch, "", "暂未设置")
|
||||||
|
} else {
|
||||||
|
entitys.ResText(requireData.Ch, "", groupConfig.ProductName)
|
||||||
|
isFinish = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(content, "[负利润分析]同步") {
|
||||||
|
// 提取冒号后的内容
|
||||||
|
if len(groupConfig.ProductName) == 0 {
|
||||||
|
entitys.ResText(requireData.Ch, "", "暂未设置")
|
||||||
|
} else {
|
||||||
|
entitys.ResText(requireData.Ch, "", groupConfig.ProductName)
|
||||||
|
isFinish = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -332,9 +378,7 @@ func (d *DingTalkBotBiz) HandleStreamRes(ctx context.Context, data *chatbot.BotC
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DingTalkBotBiz) SendReport(ctx context.Context, groupInfo *model.AiBotGroup, report *bbxt.ReportRes) (err error) {
|
func (d *DingTalkBotBiz) SendReport(ctx context.Context, groupInfo *model.AiBotGroup, report *bbxt.ReportRes) (err error) {
|
||||||
if report == nil {
|
|
||||||
return errors.New("report is nil")
|
|
||||||
}
|
|
||||||
reportChan := make(chan string, 10)
|
reportChan := make(chan string, 10)
|
||||||
defer close(reportChan)
|
defer close(reportChan)
|
||||||
reportChan <- report.Title
|
reportChan <- report.Title
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package do
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/config"
|
"ai_scheduler/internal/config"
|
||||||
|
"ai_scheduler/internal/data/constants"
|
||||||
errors "ai_scheduler/internal/data/error"
|
errors "ai_scheduler/internal/data/error"
|
||||||
"ai_scheduler/internal/data/impl"
|
"ai_scheduler/internal/data/impl"
|
||||||
"ai_scheduler/internal/data/model"
|
"ai_scheduler/internal/data/model"
|
||||||
|
|
@ -32,16 +33,18 @@ type Do struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDo(
|
func NewDo(
|
||||||
|
sessionImpl *impl.SessionImpl,
|
||||||
sysImpl *impl.SysImpl,
|
sysImpl *impl.SysImpl,
|
||||||
taskImpl *impl.TaskImpl,
|
taskImpl *impl.TaskImpl,
|
||||||
hisImpl *impl.ChatHisImpl,
|
hisImpl *impl.ChatHisImpl,
|
||||||
conf *config.Config,
|
conf *config.Config,
|
||||||
) *Do {
|
) *Do {
|
||||||
return &Do{
|
return &Do{
|
||||||
conf: conf,
|
conf: conf,
|
||||||
sysImpl: sysImpl,
|
sessionImpl: sessionImpl,
|
||||||
hisImpl: hisImpl,
|
sysImpl: sysImpl,
|
||||||
taskImpl: taskImpl,
|
hisImpl: hisImpl,
|
||||||
|
taskImpl: taskImpl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,16 +268,27 @@ func (d *Do) startMessageHandler(
|
||||||
if len(chat) > 0 {
|
if len(chat) > 0 {
|
||||||
// 合并所有回答-转json字符串
|
// 合并所有回答-转json字符串
|
||||||
ans, _ := json.Marshal(chat)
|
ans, _ := json.Marshal(chat)
|
||||||
|
// 通过 chat 获取 task_id
|
||||||
|
taskId := d.getTaskIdByChat(chat)
|
||||||
|
|
||||||
AiRes := &model.AiChatHi{
|
AiRes := &model.AiChatHi{
|
||||||
SessionID: requireData.Session,
|
SessionID: requireData.Session,
|
||||||
Ques: requireData.Req.Text,
|
Ques: requireData.Req.Text,
|
||||||
Ans: string(ans),
|
Ans: string(ans),
|
||||||
Files: requireData.Req.Img,
|
Files: requireData.Req.Img,
|
||||||
TaskID: requireData.Task.TaskID,
|
TaskID: taskId,
|
||||||
}
|
}
|
||||||
d.hisImpl.AddWithData(AiRes)
|
d.hisImpl.AddWithData(AiRes)
|
||||||
hisLog.HisId = AiRes.HisID
|
hisLog.HisId = AiRes.HisID
|
||||||
|
|
||||||
|
// 查询当前session
|
||||||
|
cond := builder.NewCond().And(builder.Eq{"session_id": requireData.Session})
|
||||||
|
sessionMap, _ := d.sessionImpl.GetOneBySearch(&cond)
|
||||||
|
requireData.SessionInfo.Title = sessionMap["title"].(string)
|
||||||
|
// 当前 session title为空 ,更新为用户输入
|
||||||
|
if requireData.SessionInfo.Title == "" {
|
||||||
|
d.sessionImpl.UpdateByCond(&cond, &model.AiSession{Title: requireData.Req.Text})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = entitys.MsgSend(client, entitys.Response{
|
_ = entitys.MsgSend(client, entitys.Response{
|
||||||
|
|
@ -399,3 +413,28 @@ func (d *Do) LoadUserPermission(client *gateway.Client, requireData *entitys.Req
|
||||||
|
|
||||||
return respBody.Codes, nil
|
return respBody.Codes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getTaskIdByChat 从 chat 中获取 task_id
|
||||||
|
func (d *Do) getTaskIdByChat(chat []entitys.Response) (taskId int32) {
|
||||||
|
if len(chat) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 taskIndex
|
||||||
|
taskIndex := chat[0].Index
|
||||||
|
|
||||||
|
if _, ok := constants.IrregularTaskToolIndexMap[taskIndex]; ok {
|
||||||
|
taskIndex = constants.IrregularTaskToolIndexMap[taskIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过 taskIndex 获取 taskId
|
||||||
|
cond := builder.NewCond().And(builder.Eq{"`index`": taskIndex})
|
||||||
|
taskMap, _ := d.taskImpl.GetOneBySearch(&cond)
|
||||||
|
if taskMap == nil || taskMap["task_id"] == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
taskId = taskMap["task_id"].(int32)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,15 @@ import (
|
||||||
"ai_scheduler/internal/domain/workflow/recharge"
|
"ai_scheduler/internal/domain/workflow/recharge"
|
||||||
"ai_scheduler/internal/domain/workflow/runtime"
|
"ai_scheduler/internal/domain/workflow/runtime"
|
||||||
"ai_scheduler/internal/entitys"
|
"ai_scheduler/internal/entitys"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
"ai_scheduler/internal/pkg/l_request"
|
"ai_scheduler/internal/pkg/l_request"
|
||||||
|
"ai_scheduler/internal/pkg/lsxd"
|
||||||
"ai_scheduler/internal/pkg/utils_oss"
|
"ai_scheduler/internal/pkg/utils_oss"
|
||||||
"ai_scheduler/internal/tools"
|
"ai_scheduler/internal/tools"
|
||||||
"ai_scheduler/internal/tools/bbxt"
|
"ai_scheduler/internal/tools/bbxt"
|
||||||
|
"ai_scheduler/utils"
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -30,12 +34,14 @@ import (
|
||||||
|
|
||||||
// AiRouterBiz 智能路由服务
|
// AiRouterBiz 智能路由服务
|
||||||
type GroupConfigBiz struct {
|
type GroupConfigBiz struct {
|
||||||
botGroupConfigImpl *impl.BotGroupConfigImpl
|
botGroupConfigImpl *impl.BotGroupConfigImpl
|
||||||
ossClient *utils_oss.Client
|
reportDailyCacheImpl *impl.ReportDailyCacheImpl
|
||||||
workflowManager *runtime.Registry
|
ossClient *utils_oss.Client
|
||||||
botTools []model.AiBotTool
|
workflowManager *runtime.Registry
|
||||||
toolManager *tools.Manager
|
botTools []model.AiBotTool
|
||||||
conf *config.Config
|
toolManager *tools.Manager
|
||||||
|
conf *config.Config
|
||||||
|
rdb *utils.Rdb
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDingTalkBotBiz
|
// NewDingTalkBotBiz
|
||||||
|
|
@ -45,13 +51,17 @@ func NewGroupConfigBiz(
|
||||||
botGroupConfigImpl *impl.BotGroupConfigImpl,
|
botGroupConfigImpl *impl.BotGroupConfigImpl,
|
||||||
workflowManager *runtime.Registry,
|
workflowManager *runtime.Registry,
|
||||||
conf *config.Config,
|
conf *config.Config,
|
||||||
|
reportDailyCacheImpl *impl.ReportDailyCacheImpl,
|
||||||
|
rdb *utils.Rdb,
|
||||||
) *GroupConfigBiz {
|
) *GroupConfigBiz {
|
||||||
return &GroupConfigBiz{
|
return &GroupConfigBiz{
|
||||||
botTools: tools.BootTools,
|
botTools: tools.BootTools,
|
||||||
ossClient: ossClient,
|
ossClient: ossClient,
|
||||||
botGroupConfigImpl: botGroupConfigImpl,
|
botGroupConfigImpl: botGroupConfigImpl,
|
||||||
workflowManager: workflowManager,
|
workflowManager: workflowManager,
|
||||||
conf: conf,
|
conf: conf,
|
||||||
|
reportDailyCacheImpl: reportDailyCacheImpl,
|
||||||
|
rdb: rdb,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,12 +82,12 @@ func (g *GroupConfigBiz) GetReportLists(ctx context.Context, groupConfig *model.
|
||||||
product = strings.Split(groupConfig.ProductName, ",")
|
product = strings.Split(groupConfig.ProductName, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
reportList, err := bbxt.NewBbxtTools(g.conf)
|
reportList, err := bbxt.NewBbxtTools(g.conf, lsxd.NewLogin(g.conf, g.rdb))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reports, err = reportList.DailyReport(time.Now(), bbxt.DownWardValue, product, bbxt.SumFilter, g.ossClient)
|
reports, err = reportList.DailyReport(ctx, time.Now(), bbxt.DownWardValue, product, bbxt.SumFilter, g.ossClient, g.GetReportCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -145,7 +155,8 @@ func (g *GroupConfigBiz) handleReport(ctx context.Context, rec *entitys.Recogniz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rep, err := bbxt.NewBbxtTools(g.conf)
|
|
||||||
|
rep, err := bbxt.NewBbxtTools(g.conf, lsxd.NewLogin(g.conf, g.rdb))
|
||||||
uploader := bbxt.NewUploader(g.ossClient, g.conf)
|
uploader := bbxt.NewUploader(g.ossClient, g.conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -153,7 +164,7 @@ func (g *GroupConfigBiz) handleReport(ctx context.Context, rec *entitys.Recogniz
|
||||||
var reports []*bbxt.ReportRes
|
var reports []*bbxt.ReportRes
|
||||||
switch rec.Match.Index {
|
switch rec.Match.Index {
|
||||||
case "report_loss_analysis":
|
case "report_loss_analysis":
|
||||||
repo, _err := rep.StatisOursProductLossSum(t)
|
repo, _err := rep.StatisOursProductLossSum(ctx, t, g.GetReportCache)
|
||||||
if _err != nil {
|
if _err != nil {
|
||||||
return _err
|
return _err
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +185,7 @@ func (g *GroupConfigBiz) handleReport(ctx context.Context, rec *entitys.Recogniz
|
||||||
reports = append(reports, repo)
|
reports = append(reports, repo)
|
||||||
case "report_daily":
|
case "report_daily":
|
||||||
product := strings.Split(groupConfig.ProductName, ",")
|
product := strings.Split(groupConfig.ProductName, ",")
|
||||||
repo, _err := rep.DailyReport(t, bbxt.DownWardValue, product, bbxt.SumFilter, nil)
|
repo, _err := rep.DailyReport(ctx, t, bbxt.DownWardValue, product, bbxt.SumFilter, nil, g.GetReportCache)
|
||||||
if _err != nil {
|
if _err != nil {
|
||||||
return _err
|
return _err
|
||||||
}
|
}
|
||||||
|
|
@ -404,3 +415,47 @@ func (g *GroupConfigBiz) otherTask(ctx context.Context, rec *entitys.Recognize)
|
||||||
entitys.ResText(rec.Ch, "", rec.Match.Reasoning)
|
entitys.ResText(rec.Ch, "", rec.Match.Reasoning)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GroupConfigBiz) GetReportCache(ctx context.Context, day time.Time, totalDetail []*bbxt.ResellerLoss, bbxtObj *bbxt.BbxtTools) error {
|
||||||
|
var ResellerProductRelation map[int32]*bbxt.ResellerLossSumProductRelation
|
||||||
|
|
||||||
|
dayDate := day.Format(time.DateOnly)
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"index": bbxt.IndexLossSumDetail})
|
||||||
|
cond = cond.And(builder.Eq{"key": dayDate})
|
||||||
|
var cache model.AiReportDailyCache
|
||||||
|
err := g.reportDailyCacheImpl.GetOneBySearchToStrut(&cond, &cache)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(sql.ErrNoRows, err) {
|
||||||
|
ResellerProductRelation, err = bbxtObj.GetResellerLossMannagerAndLossReasonFromApi(ctx, totalDetail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cache = model.AiReportDailyCache{
|
||||||
|
Key: dayDate,
|
||||||
|
Index: bbxt.IndexLossSumDetail,
|
||||||
|
Value: pkg.JsonStringIgonErr(ResellerProductRelation),
|
||||||
|
}
|
||||||
|
_, err = g.reportDailyCacheImpl.Add(&cache)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = json.Unmarshal([]byte(cache.Value), &ResellerProductRelation)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, v := range totalDetail {
|
||||||
|
if _, ex := ResellerProductRelation[v.ResellerId]; !ex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v.Manager = ResellerProductRelation[v.ResellerId].AfterSaleName
|
||||||
|
for _, vv := range v.ProductLoss {
|
||||||
|
if _, ex := ResellerProductRelation[v.ResellerId].Products[vv.ProductId]; !ex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vv.LossReason = ResellerProductRelation[v.ResellerId].Products[vv.ProductId].LossReason
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package biz
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_report(t *testing.T) {
|
|
||||||
run()
|
|
||||||
chatId, err := groupConfigBiz.GetReportLists(context.Background(), nil)
|
|
||||||
t.Log(chatId, err)
|
|
||||||
}
|
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"ai_scheduler/internal/pkg/l_request"
|
"ai_scheduler/internal/pkg/l_request"
|
||||||
"ai_scheduler/internal/tools/bbxt"
|
"ai_scheduler/internal/tools/bbxt"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -112,10 +111,6 @@ func (q *QywxAppBiz) SendReport(ctx context.Context, groupInfo *model.AiBotGroup
|
||||||
|
|
||||||
// SendReportV2 发送到货易通指定的群聊
|
// SendReportV2 发送到货易通指定的群聊
|
||||||
func (q *QywxAppBiz) SendReportHYT(ctx context.Context, groupInfo *model.AiBotGroupQywx, report *bbxt.ReportRes) (err error) {
|
func (q *QywxAppBiz) SendReportHYT(ctx context.Context, groupInfo *model.AiBotGroupQywx, report *bbxt.ReportRes) (err error) {
|
||||||
|
|
||||||
if report == nil {
|
|
||||||
return fmt.Errorf("report is nil")
|
|
||||||
}
|
|
||||||
// 文本消息
|
// 文本消息
|
||||||
err = q.sendReportHYT(groupInfo, &bbxt.ReportRes{
|
err = q.sendReportHYT(groupInfo, &bbxt.ReportRes{
|
||||||
Title: report.Title,
|
Title: report.Title,
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,8 @@ package biz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/biz/handle/qywx"
|
"ai_scheduler/internal/biz/handle/qywx"
|
||||||
"ai_scheduler/internal/biz/tools_regis"
|
|
||||||
"ai_scheduler/internal/config"
|
"ai_scheduler/internal/config"
|
||||||
"ai_scheduler/internal/data/impl"
|
"ai_scheduler/internal/data/impl"
|
||||||
"ai_scheduler/internal/domain/component"
|
|
||||||
"ai_scheduler/internal/domain/component/callback"
|
|
||||||
"ai_scheduler/internal/domain/repo"
|
|
||||||
"ai_scheduler/internal/domain/workflow"
|
|
||||||
"ai_scheduler/internal/pkg"
|
|
||||||
"ai_scheduler/internal/pkg/lsxd"
|
|
||||||
"ai_scheduler/internal/pkg/utils_ollama"
|
|
||||||
"ai_scheduler/internal/pkg/utils_oss"
|
|
||||||
"ai_scheduler/utils"
|
"ai_scheduler/utils"
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -25,9 +16,8 @@ func Test_InitGroup(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configConfig *config.Config
|
configConfig *config.Config
|
||||||
qywxAppBiz *QywxAppBiz
|
qywxAppBiz *QywxAppBiz
|
||||||
groupConfigBiz *GroupConfigBiz
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func run() {
|
func run() {
|
||||||
|
|
@ -38,21 +28,6 @@ func run() {
|
||||||
botGroupQywxImpl := impl.NewBotGroupQywxImpl(db)
|
botGroupQywxImpl := impl.NewBotGroupQywxImpl(db)
|
||||||
qywxAuth := qywx.NewAuth(configConfig, rdb)
|
qywxAuth := qywx.NewAuth(configConfig, rdb)
|
||||||
group := qywx.NewGroup(botGroupQywxImpl, qywxAuth)
|
group := qywx.NewGroup(botGroupQywxImpl, qywxAuth)
|
||||||
sessionImpl := impl.NewSessionImpl(db)
|
|
||||||
other := qywx.NewOther(qywxAuth)
|
other := qywx.NewOther(qywxAuth)
|
||||||
repos := repo.NewRepos(sessionImpl, configConfig, rdb)
|
|
||||||
pkgRdb := pkg.NewRdb(configConfig)
|
|
||||||
redisManager := callback.NewRedisManager(pkgRdb)
|
|
||||||
login := lsxd.NewLogin(configConfig, rdb)
|
|
||||||
components := component.NewComponents(redisManager, login)
|
|
||||||
repos = repo.NewRepos(sessionImpl, configConfig, rdb)
|
|
||||||
botToolsImpl := impl.NewBotToolsImpl(db)
|
|
||||||
toolRegis := tools_regis.NewToolsRegis(botToolsImpl)
|
|
||||||
utils_ossClient, _ := utils_oss.NewClient(configConfig)
|
|
||||||
client, _, _ := utils_ollama.NewClient(configConfig)
|
|
||||||
|
|
||||||
registry := workflow.NewRegistry(configConfig, client, repos, components)
|
|
||||||
botGroupConfigImpl := impl.NewBotGroupConfigImpl(db)
|
|
||||||
qywxAppBiz = NewQywxAppBiz(configConfig, botGroupQywxImpl, group, other)
|
qywxAppBiz = NewQywxAppBiz(configConfig, botGroupQywxImpl, group, other)
|
||||||
groupConfigBiz = NewGroupConfigBiz(toolRegis, utils_ossClient, botGroupConfigImpl, registry, configConfig)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,41 +35,31 @@ func NewSessionBiz(conf *config.Config, sessionImpl *impl.SessionImpl, sysImpl *
|
||||||
func (s *SessionBiz) SessionInit(ctx context.Context, req *entitys.SessionInitRequest) (result *entitys.SessionInitResponse, err error) {
|
func (s *SessionBiz) SessionInit(ctx context.Context, req *entitys.SessionInitRequest) (result *entitys.SessionInitResponse, err error) {
|
||||||
|
|
||||||
// 获取系统配置
|
// 获取系统配置
|
||||||
sysConfig, has, err := s.sysRepo.FindOne(s.sysRepo.WithSysId(req.SysId))
|
sysConfig, err := s.GetSysConfig(req.SysId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
} else if !has {
|
|
||||||
err = errorcode.SysNotFound
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = &entitys.SessionInitResponse{
|
result = &entitys.SessionInitResponse{
|
||||||
Chat: make([]entitys.ChatHistory, 0),
|
Chat: make([]entitys.ChatHistory, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 当天的session
|
// 获取 当天的最新session
|
||||||
t := time.Now().Truncate(24 * time.Hour)
|
t := time.Now().Truncate(24 * time.Hour)
|
||||||
session, has, err := s.sessionRepo.FindOne(
|
session, has, err := s.sessionRepo.Take(
|
||||||
s.sessionRepo.WithUserId(req.UserId), // 条件:用户ID
|
s.sessionRepo.WithUserId(req.UserId), // 条件:用户ID
|
||||||
s.sessionRepo.WithStartTime(t), // 条件:会话开始时间
|
s.sessionRepo.WithStartTime(t), // 条件:会话开始时间
|
||||||
s.sysRepo.WithSysId(sysConfig.SysID), // 条件:系统ID
|
s.sysRepo.WithSysId(sysConfig.SysID),
|
||||||
|
s.sessionRepo.OrderByDesc("create_at"), // 条件:系统ID
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
} else if !has {
|
}
|
||||||
|
if !has {
|
||||||
// 不存在,创建一个
|
// 不存在,创建一个
|
||||||
session = model.AiSession{
|
session, err = s.CreateNew(sysConfig.SysID, req)
|
||||||
SysID: sysConfig.SysID,
|
|
||||||
SessionID: utils.UUID(),
|
|
||||||
UserID: req.UserId,
|
|
||||||
UserName: req.UserName,
|
|
||||||
DingUserId: req.DingUserId,
|
|
||||||
}
|
|
||||||
err = s.sessionRepo.Create(&session)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chat := entitys.ChatHistory{
|
chat := entitys.ChatHistory{
|
||||||
SessionID: session.SessionID,
|
SessionID: session.SessionID,
|
||||||
Role: constants.RoleSystem,
|
Role: constants.RoleSystem,
|
||||||
|
|
@ -122,6 +112,32 @@ func (s *SessionBiz) SessionInit(ctx context.Context, req *entitys.SessionInitRe
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SessionBiz) GetSysConfig(sysId string) (model.AiSy, error) {
|
||||||
|
sysConfig, has, err := s.sysRepo.FindOne(s.sysRepo.WithSysId(sysId))
|
||||||
|
if err != nil {
|
||||||
|
return sysConfig, err
|
||||||
|
} else if !has {
|
||||||
|
err = errorcode.SysNotFound
|
||||||
|
return sysConfig, err
|
||||||
|
}
|
||||||
|
return sysConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SessionBiz) CreateNew(sysID int32, req *entitys.SessionInitRequest) (model.AiSession, error) {
|
||||||
|
session := model.AiSession{
|
||||||
|
SysID: sysID,
|
||||||
|
SessionID: utils.UUID(),
|
||||||
|
UserID: req.UserId,
|
||||||
|
UserName: req.UserName,
|
||||||
|
DingUserId: req.DingUserId,
|
||||||
|
}
|
||||||
|
err := s.sessionRepo.Create(&session)
|
||||||
|
if err != nil {
|
||||||
|
return session, err
|
||||||
|
}
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SessionList 会话列表
|
// SessionList 会话列表
|
||||||
func (s *SessionBiz) SessionList(ctx context.Context, req *entitys.SessionListRequest) (list []model.AiSession, err error) {
|
func (s *SessionBiz) SessionList(ctx context.Context, req *entitys.SessionListRequest) (list []model.AiSession, err error) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,11 @@ type Config struct {
|
||||||
LLM LLM `mapstructure:"llm"`
|
LLM LLM `mapstructure:"llm"`
|
||||||
Dingtalk DingtalkConfig `mapstructure:"dingtalk"`
|
Dingtalk DingtalkConfig `mapstructure:"dingtalk"`
|
||||||
Qywx QywxConfig `mapstructure:"qywx"`
|
Qywx QywxConfig `mapstructure:"qywx"`
|
||||||
|
ZLTX ZLTX `mapstructure:"zltx"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZLTX struct {
|
||||||
|
ReqUrl string `mapstructure:"req_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SysPrompt struct {
|
type SysPrompt struct {
|
||||||
|
|
@ -136,6 +141,7 @@ type LSXDConfig struct {
|
||||||
LoginURL string `mapstructure:"login_url"`
|
LoginURL string `mapstructure:"login_url"`
|
||||||
Phone string `mapstructure:"phone"`
|
Phone string `mapstructure:"phone"`
|
||||||
Password string `mapstructure:"password"`
|
Password string `mapstructure:"password"`
|
||||||
|
Code string `mapstructure:"code"`
|
||||||
CheckTokenURL string `mapstructure:"check_token_url"`
|
CheckTokenURL string `mapstructure:"check_token_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,16 @@ const (
|
||||||
Enable = 1
|
Enable = 1
|
||||||
Disable = 2
|
Disable = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IrregularTaskToolIndexMap 不规则任务工具索引映射
|
||||||
|
var IrregularTaskToolIndexMap = map[string]string{
|
||||||
|
"zltxProduct": "product_diagnosis",
|
||||||
|
"zltxOrderDetail": "order_diagnosis",
|
||||||
|
"knowledgeBase": "knowledge_qa",
|
||||||
|
"zltxOrderStatistics": "account_statistics",
|
||||||
|
"normalChat": "chat",
|
||||||
|
"zltxOrderAfterSaleSupplier": "after_sale_supplier",
|
||||||
|
"zltxOrderAfterSaleReseller": "after_sale_reseller",
|
||||||
|
"zltxOrderAfterSaleResellerBatch": "after_sale_reseller_batch",
|
||||||
|
"zltxLossRiskSearch": "loss_order_direct",
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,19 +43,21 @@ type BaseRepository[P PO] interface {
|
||||||
FindAll(conditions ...CondFunc) ([]P, error) // 查询所有
|
FindAll(conditions ...CondFunc) ([]P, error) // 查询所有
|
||||||
Paginate(page, pageSize int, conditions ...CondFunc) (*PaginationResult[P], error) // 分页查询
|
Paginate(page, pageSize int, conditions ...CondFunc) (*PaginationResult[P], error) // 分页查询
|
||||||
FindOne(conditions ...CondFunc) (P, bool, error) // 查询单条记录,若未找到则返回 has=false, err=nil
|
FindOne(conditions ...CondFunc) (P, bool, error) // 查询单条记录,若未找到则返回 has=false, err=nil
|
||||||
Create(m *P) error // 创建
|
Take(conditions ...CondFunc) (P, bool, error)
|
||||||
BatchCreate(m *[]P) (err error) // 批量创建
|
Create(m *P) error // 创建
|
||||||
Update(m *P, conditions ...CondFunc) (err error) // 更新
|
BatchCreate(m *[]P) (err error) // 批量创建
|
||||||
Delete(conditions ...CondFunc) (err error) // 删除
|
Update(m *P, conditions ...CondFunc) (err error) // 更新
|
||||||
Count(conditions ...CondFunc) (count int64, err error) // 查询条数
|
Delete(conditions ...CondFunc) (err error) // 删除
|
||||||
PaginateScope(page, pageSize int) CondFunc // 分页
|
Count(conditions ...CondFunc) (count int64, err error) // 查询条数
|
||||||
OrderByDesc(field string) CondFunc // 倒序排序
|
PaginateScope(page, pageSize int) CondFunc // 分页
|
||||||
WithId(id interface{}) CondFunc // 查询id
|
OrderByDesc(field string) CondFunc // 倒序排序
|
||||||
WithStatus(status int) CondFunc // 查询status
|
OrderByAsc(field string) CondFunc // 正序排序
|
||||||
GetDb() *gorm.DB // 获取数据库连接
|
WithId(id interface{}) CondFunc // 查询id
|
||||||
WithLimit(limit int) CondFunc // 限制返回条数
|
WithStatus(status int) CondFunc // 查询status
|
||||||
In(field string, values interface{}) CondFunc // 查询字段是否在列表中
|
GetDb() *gorm.DB // 获取数据库连接
|
||||||
Select(fields ...string) CondFunc // 选择字段
|
WithLimit(limit int) CondFunc // 限制返回条数
|
||||||
|
In(field string, values interface{}) CondFunc // 查询字段是否在列表中
|
||||||
|
Select(fields ...string) CondFunc // 选择字段
|
||||||
}
|
}
|
||||||
|
|
||||||
// PaginationResult 分页查询结果
|
// PaginationResult 分页查询结果
|
||||||
|
|
@ -127,6 +129,26 @@ func (this *BaseModel[P]) FindOne(conditions ...CondFunc) (P, bool, error) {
|
||||||
return result, true, err
|
return result, true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *BaseModel[P]) Take(conditions ...CondFunc) (P, bool, error) {
|
||||||
|
var (
|
||||||
|
result P
|
||||||
|
)
|
||||||
|
|
||||||
|
err := this.Db.Model(new(P)).
|
||||||
|
Scopes(conditions...).
|
||||||
|
Take(&result).
|
||||||
|
Error
|
||||||
|
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return result, false, nil // 未找到记录
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return result, false, fmt.Errorf("查询单条记录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, true, err
|
||||||
|
}
|
||||||
|
|
||||||
// 创建
|
// 创建
|
||||||
func (this *BaseModel[P]) Create(m *P) error {
|
func (this *BaseModel[P]) Create(m *P) error {
|
||||||
err := this.Db.Create(m).Error
|
err := this.Db.Create(m).Error
|
||||||
|
|
@ -193,6 +215,13 @@ func (this *BaseModel[P]) OrderByDesc(field string) CondFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 正序排序条件生成器
|
||||||
|
func (this *BaseModel[P]) OrderByAsc(field string) CondFunc {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Order(fmt.Sprintf("%s ASC", field))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ID查询条件生成器
|
// ID查询条件生成器
|
||||||
func (this *BaseModel[P]) WithId(id interface{}) CondFunc {
|
func (this *BaseModel[P]) WithId(id interface{}) CondFunc {
|
||||||
return func(db *gorm.DB) *gorm.DB {
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,5 @@ var ProviderImpl = wire.NewSet(
|
||||||
NewBotGroupImpl,
|
NewBotGroupImpl,
|
||||||
NewBotGroupConfigImpl,
|
NewBotGroupConfigImpl,
|
||||||
NewBotGroupQywxImpl,
|
NewBotGroupQywxImpl,
|
||||||
|
NewReportDailyCacheImpl,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/tmpl/dataTemp"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReportDailyCacheImpl struct {
|
||||||
|
dataTemp.DataTemp
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReportDailyCacheImpl(db *utils.Db) *ReportDailyCacheImpl {
|
||||||
|
return &ReportDailyCacheImpl{
|
||||||
|
DataTemp: *dataTemp.NewDataTemp(db, new(model.AiReportDailyCache)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
const TableNameAiReportDailyCache = "ai_report_daily_cache"
|
||||||
|
|
||||||
|
// AiReportDailyCache mapped from table <ai_report_daily_cache>
|
||||||
|
type AiReportDailyCache struct {
|
||||||
|
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||||
|
Key string `gorm:"column:key;not null;default:1;comment:索引方式,可以是任意类型" json:"key"` // 索引方式,可以是任意类型
|
||||||
|
Value string `gorm:"column:value;comment:类型下所需路由以及参数" json:"value"` // 类型下所需路由以及参数
|
||||||
|
Index string `gorm:"column:index;not null;comment:类型" json:"index"` // 类型
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName AiReportDailyCache's table name
|
||||||
|
func (*AiReportDailyCache) TableName() string {
|
||||||
|
return TableNameAiReportDailyCache
|
||||||
|
}
|
||||||
|
|
@ -33,9 +33,10 @@ type ChatHisQueryResponse struct {
|
||||||
Files string `gorm:"column:files;not null" json:"files"`
|
Files string `gorm:"column:files;not null" json:"files"`
|
||||||
Useful int32 `gorm:"column:useful;not null;comment:0不评价,1有用,其他为无用" json:"useful"` // 0不评价,1有用,其他为无用
|
Useful int32 `gorm:"column:useful;not null;comment:0不评价,1有用,其他为无用" json:"useful"` // 0不评价,1有用,其他为无用
|
||||||
CreateAt string `gorm:"column:create_at;default:CURRENT_TIMESTAMP" json:"create_at"`
|
CreateAt string `gorm:"column:create_at;default:CURRENT_TIMESTAMP" json:"create_at"`
|
||||||
TaskID int32 `gorm:"column:task_id;not null" json:"task_id"` // 任务ID
|
TaskID int32 `gorm:"column:task_id;not null" json:"task_id"` // 任务ID
|
||||||
TaskName string `gorm:"column:task_name;not null" json:"task_name"` // 任务名称
|
TaskName string `gorm:"column:task_name;not null" json:"task_name"` // 任务名称
|
||||||
Contents []string `gorm:"column:contents" json:"contents"` // 前端回传数据
|
Contents []string `gorm:"column:contents" json:"contents"` // 前端回传数据
|
||||||
|
TaskIndex string `gorm:"column:task_index;not null" json:"task_index"` // 任务索引
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ChatHisQueryResponse) FromModel(chat model.AiChatHi, task model.AiTask) {
|
func (c *ChatHisQueryResponse) FromModel(chat model.AiChatHi, task model.AiTask) {
|
||||||
|
|
@ -49,6 +50,7 @@ func (c *ChatHisQueryResponse) FromModel(chat model.AiChatHi, task model.AiTask)
|
||||||
c.TaskID = chat.TaskID
|
c.TaskID = chat.TaskID
|
||||||
c.TaskName = task.Name
|
c.TaskName = task.Name
|
||||||
c.Contents = make([]string, 0)
|
c.Contents = make([]string, 0)
|
||||||
|
c.TaskIndex = c.parseTaskIndex(task.Index)
|
||||||
|
|
||||||
// 解析Content
|
// 解析Content
|
||||||
if "" != chat.Content {
|
if "" != chat.Content {
|
||||||
|
|
@ -60,6 +62,20 @@ func (c *ChatHisQueryResponse) FromModel(chat model.AiChatHi, task model.AiTask)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ChatHisQueryResponse) parseTaskIndex(taskIndex string) string {
|
||||||
|
// 反转 IrregularTaskToolIndexMap
|
||||||
|
reverseMap := make(map[string]string)
|
||||||
|
for k, v := range constants.IrregularTaskToolIndexMap {
|
||||||
|
reverseMap[v] = k
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := reverseMap[taskIndex]; ok {
|
||||||
|
return reverseMap[taskIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
return taskIndex
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateContentRequest struct {
|
type UpdateContentRequest struct {
|
||||||
HisID int64 `json:"his_id" validate:"required"`
|
HisID int64 `json:"his_id" validate:"required"`
|
||||||
Content string `json:"content" validate:"required"`
|
Content string `json:"content" validate:"required"`
|
||||||
|
|
|
||||||
|
|
@ -95,30 +95,18 @@ func (l *Login) checkTokenValid(ctx context.Context, token string) bool {
|
||||||
|
|
||||||
// 调用登录接口获取token
|
// 调用登录接口获取token
|
||||||
func (l *Login) login(ctx context.Context) (string, error) {
|
func (l *Login) login(ctx context.Context) (string, error) {
|
||||||
// 1.获取配置
|
|
||||||
loginURL := l.config.LSXD.LoginURL
|
|
||||||
phone := l.config.LSXD.Phone
|
|
||||||
password := l.config.LSXD.Password
|
|
||||||
|
|
||||||
// 2.调用登录接口获取token
|
|
||||||
if loginURL == "" {
|
|
||||||
return "", errors.New("login url is empty")
|
|
||||||
}
|
|
||||||
if phone == "" || password == "" {
|
|
||||||
return "", errors.New("phone or password is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
reqBody := map[string]any{
|
reqBody := map[string]any{
|
||||||
"phone": phone,
|
"phone": l.config.LSXD.Phone,
|
||||||
"password": password,
|
"password": l.config.LSXD.Password,
|
||||||
"code": "456789",
|
"code": l.config.LSXD.Code,
|
||||||
}
|
}
|
||||||
bodyBytes, err := json.Marshal(reqBody)
|
bodyBytes, err := json.Marshal(reqBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
status, respBody, err := l.doRequestWithBody(ctx, http.MethodPost, loginURL, "", "application/json", bodyBytes)
|
status, respBody, err := l.doRequestWithBody(ctx, http.MethodPost, l.config.LSXD.LoginURL, "", "application/json", bodyBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +146,7 @@ func (l *Login) login(ctx context.Context) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) getCachedToken(ctx context.Context) (string, error) {
|
func (l *Login) getCachedToken(ctx context.Context) (string, error) {
|
||||||
token, err := l.redisCli.Get(ctx, constants.CACHE_KEY_LSXD_TOKEN).Result()
|
token, err := l.redisCli.Get(ctx, l.getCacheKey()).Result()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
@ -168,11 +156,16 @@ func (l *Login) getCachedToken(ctx context.Context) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Login) getCacheKey() string {
|
||||||
|
return l.config.Redis.Key + constants.CACHE_KEY_LSXD_TOKEN + l.config.LSXD.Phone // 1.获取配置
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Login) cacheToken(ctx context.Context, token string) error {
|
func (l *Login) cacheToken(ctx context.Context, token string) error {
|
||||||
if token == "" {
|
if token == "" {
|
||||||
return errors.New("token is empty")
|
return errors.New("token is empty")
|
||||||
}
|
}
|
||||||
return l.redisCli.Set(ctx, constants.CACHE_KEY_LSXD_TOKEN, token, constants.EXPIRE_LSXD_TOKEN).Err()
|
return l.redisCli.Set(ctx, l.getCacheKey(), token, constants.EXPIRE_LSXD_TOKEN).Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) doRequest(ctx context.Context, method string, url string, authorization string, body []byte) (int, error) {
|
func (l *Login) doRequest(ctx context.Context, method string, url string, authorization string, body []byte) (int, error) {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ func SetTaskRecExt(requireData *entitys.RequireData, rec *entitys.Recognize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTaskRecExt(rec *entitys.Recognize) (ext entitys.TaskExt, err error) {
|
func GetTaskRecExt(rec *entitys.Recognize) (ext entitys.TaskExt, err error) {
|
||||||
|
if rec == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
err = json.Unmarshal(rec.Ext, &ext)
|
err = json.Unmarshal(rec.Ext, &ext)
|
||||||
return ext, err
|
return ext, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import "encoding/json"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// StructToMap 将结构体转换为 map[string]any
|
// StructToMap 将结构体转换为 map[string]any
|
||||||
func StructToMap(v any) (map[string]any, error) {
|
func StructToMap(v any) (map[string]any, error) {
|
||||||
|
|
@ -12,3 +16,28 @@ func StructToMap(v any) (map[string]any, error) {
|
||||||
err = json.Unmarshal(b, &m)
|
err = json.Unmarshal(b, &m)
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StructToMapWithReflect(obj interface{}) map[string]interface{} {
|
||||||
|
val := reflect.ValueOf(obj)
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
if val.Kind() != reflect.Struct {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make(map[string]interface{})
|
||||||
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
valueField := val.Field(i)
|
||||||
|
typeField := val.Type().Field(i)
|
||||||
|
jsonTag := typeField.Tag.Get("json")
|
||||||
|
if idx := strings.Index(jsonTag, ","); idx != -1 {
|
||||||
|
jsonTag = jsonTag[:idx]
|
||||||
|
}
|
||||||
|
if !typeField.IsExported() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data[jsonTag] = valueField.Interface()
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,13 +41,13 @@ func (c *CronServer) InitJobs(ctx context.Context) {
|
||||||
c.jobs = []*cronJob{
|
c.jobs = []*cronJob{
|
||||||
{
|
{
|
||||||
Func: c.cronService.CronReportSendDingTalk,
|
Func: c.cronService.CronReportSendDingTalk,
|
||||||
Name: "直连天下报表推送(钉钉)",
|
Name: "直连天下报表推送",
|
||||||
Schedule: "20 12,18,23 * * *",
|
Schedule: "0 12,18,23 * * *",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Func: c.cronService.CronReportSendQywx,
|
Func: c.cronService.CronReportSendQywx,
|
||||||
Name: "直连天下报表推送(微信)",
|
Name: "直连天下报表推送",
|
||||||
Schedule: "20 12,18,23 * * *",
|
Schedule: "0 12,18,23 * * *",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func SetupRoutes(app *fiber.App, ChatService *services.ChatService, sessionServi
|
||||||
c.Response().SetBody([]byte("1"))
|
c.Response().SetBody([]byte("1"))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
r.Post("/session/new", sessionService.NewSession)
|
||||||
r.Post("/session/init", sessionService.SessionInit) // 会话初始化,不存在则创建,存在则返回会话ID和默认条数会话历史
|
r.Post("/session/init", sessionService.SessionInit) // 会话初始化,不存在则创建,存在则返回会话ID和默认条数会话历史
|
||||||
r.Post("/session/list", sessionService.SessionList)
|
r.Post("/session/list", sessionService.SessionList)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ func run() {
|
||||||
botGroupImpl := impl.NewBotGroupImpl(db)
|
botGroupImpl := impl.NewBotGroupImpl(db)
|
||||||
botUserImpl := impl.NewBotUserImpl(db)
|
botUserImpl := impl.NewBotUserImpl(db)
|
||||||
// 初始化Do业务对象
|
// 初始化Do业务对象
|
||||||
doDo := do.NewDo(sysImpl, taskImpl, chatHisImpl, configConfig)
|
doDo := do.NewDo(sessionImpl, sysImpl, taskImpl, chatHisImpl, configConfig)
|
||||||
// 初始化Ollama客户端
|
// 初始化Ollama客户端
|
||||||
client, _, _ := utils_ollama.NewClient(configConfig)
|
client, _, _ := utils_ollama.NewClient(configConfig)
|
||||||
// 初始化vLLM客户端
|
// 初始化vLLM客户端
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,23 @@ func (s *SessionService) SessionInit(c *fiber.Ctx) error {
|
||||||
return c.JSON(result)
|
return c.JSON(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSession 新会话
|
||||||
|
func (s *SessionService) NewSession(c *fiber.Ctx) error {
|
||||||
|
req := &entitys.SessionInitRequest{}
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sysConfig, err := s.sessionBiz.GetSysConfig(req.SysId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
session, err := s.sessionBiz.CreateNew(sysConfig.SysID, req)
|
||||||
|
|
||||||
|
return c.JSON(session)
|
||||||
|
}
|
||||||
|
|
||||||
// SessionList 获取会话列表
|
// SessionList 获取会话列表
|
||||||
func (s *SessionService) SessionList(c *fiber.Ctx) error {
|
func (s *SessionService) SessionList(c *fiber.Ctx) error {
|
||||||
req := &entitys.SessionListRequest{}
|
req := &entitys.SessionListRequest{}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package bbxt
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/pkg"
|
"ai_scheduler/internal/pkg"
|
||||||
"ai_scheduler/internal/pkg/l_request"
|
"ai_scheduler/internal/pkg/l_request"
|
||||||
|
"ai_scheduler/internal/pkg/util"
|
||||||
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -29,12 +31,23 @@ type StatisOursProductLossSumResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Base = "https://reportapi.1688sup.com/api"
|
const Base = "https://reportapi.1688sup.com/api"
|
||||||
|
const AuthUrl = "http://test.analysis.com/api"
|
||||||
|
|
||||||
// StatisOursProductLossSumApi 负利润分析
|
// StatisOursProductLossSumApi 负利润分析
|
||||||
func StatisOursProductLossSumApi(param *StatisOursProductLossSumReq) (*StatisOursProductLossSumRes, error) {
|
func StatisOursProductLossSumApi(param *StatisOursProductLossSumReq) (*StatisOursProductLossSumRes, error) {
|
||||||
url := "/dataanalytics/statisOursProductLossSum"
|
url := "/dataanalytics/statisOursProductLossSum"
|
||||||
var res StatisOursProductLossSumRes
|
var res StatisOursProductLossSumRes
|
||||||
if err := request(url, param, &res); err != nil {
|
if err := request(url, param, &res, ""); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatisOursProductLossSumApi 负利润分析
|
||||||
|
func StatisOursProductLossSumApiWithAuth(param *StatisOursProductLossSumReq, token string) (*StatisOursProductLossSumRes, error) {
|
||||||
|
url := "/dataanalytics/statisOursProductLossSum"
|
||||||
|
var res StatisOursProductLossSumRes
|
||||||
|
if err := request(url, param, &res, token); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &res, nil
|
return &res, nil
|
||||||
|
|
@ -73,7 +86,7 @@ type ProfitRankingSumResponse struct {
|
||||||
func GetProfitRankingSumApi(param *GetProfitRankingSumRequest) (*GetProfitRankingSumResponse, error) {
|
func GetProfitRankingSumApi(param *GetProfitRankingSumRequest) (*GetProfitRankingSumResponse, error) {
|
||||||
url := "/dataanalytics/profitRankingSum"
|
url := "/dataanalytics/profitRankingSum"
|
||||||
var res GetProfitRankingSumResponse
|
var res GetProfitRankingSumResponse
|
||||||
if err := request(url, param, &res); err != nil {
|
if err := request(url, param, &res, ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &res, nil
|
return &res, nil
|
||||||
|
|
@ -106,7 +119,7 @@ type GetStatisOfficialProductSum struct {
|
||||||
func GetStatisOfficialProductSumApi(param *GetStatisOfficialProductSumRequest) (*GetStatisOfficialProductSumResponse, error) {
|
func GetStatisOfficialProductSumApi(param *GetStatisOfficialProductSumRequest) (*GetStatisOfficialProductSumResponse, error) {
|
||||||
url := "/dataanalytics/statisOfficialProduct"
|
url := "/dataanalytics/statisOfficialProduct"
|
||||||
var res GetStatisOfficialProductSumResponse
|
var res GetStatisOfficialProductSumResponse
|
||||||
if err := request(url, param, &res); err != nil {
|
if err := request(url, param, &res, ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &res, nil
|
return &res, nil
|
||||||
|
|
@ -133,7 +146,7 @@ type GetStatisOfficialProductSumDecline struct {
|
||||||
func GetStatisOfficialProductSumDeclineApi(param *GetStatisOfficialProductSumRequest) (*GetStatisOfficialProductSumDeclineResponse, error) {
|
func GetStatisOfficialProductSumDeclineApi(param *GetStatisOfficialProductSumRequest) (*GetStatisOfficialProductSumDeclineResponse, error) {
|
||||||
url := "/dataanalytics/statisOfficialProductDecline"
|
url := "/dataanalytics/statisOfficialProductDecline"
|
||||||
var res GetStatisOfficialProductSumDeclineResponse
|
var res GetStatisOfficialProductSumDeclineResponse
|
||||||
if err := request(url, param, &res); err != nil {
|
if err := request(url, param, &res, ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &res, nil
|
return &res, nil
|
||||||
|
|
@ -162,23 +175,119 @@ type StatisFilterOfficialProductResponse struct {
|
||||||
func GetStatisFilterOfficialProductApi(param *GetStatisFilterOfficialProductRequest) (*GetStatisFilterOfficialProductResponse, error) {
|
func GetStatisFilterOfficialProductApi(param *GetStatisFilterOfficialProductRequest) (*GetStatisFilterOfficialProductResponse, error) {
|
||||||
url := "/dataanalytics/statisFilterOfficialProduct"
|
url := "/dataanalytics/statisFilterOfficialProduct"
|
||||||
var res GetStatisFilterOfficialProductResponse
|
var res GetStatisFilterOfficialProductResponse
|
||||||
if err := request(url, param, &res); err != nil {
|
if err := request(url, param, &res, ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func request(url string, reqData interface{}, resData interface{}) error {
|
//type GetManagerAndDefaultLossReasonRequest struct {
|
||||||
|
// ResellerId int32 ` json:"reseller_id"`
|
||||||
|
// GoodsIds int32 ` json:"reseller_id"`
|
||||||
|
//}
|
||||||
|
|
||||||
|
type GetManagerAndDefaultLossReasonRequest struct {
|
||||||
|
Param map[int32]map[string][]int32 ` json:"param"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetManagerAndDefaultLossReasonResponse struct {
|
||||||
|
Res []*GetManagerAndDefaultLossReasonResponseList `json:"res,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetManagerAndDefaultLossReasonResponseList struct {
|
||||||
|
ResellerInfo *GetManagerAndDefaultLossReasonResponse_ResellerInfo `json:"GetManagerAndDefaultLossReasonResponse_ResellerInfo,omitempty"`
|
||||||
|
ProductList []*GetManagerAndDefaultLossReasonResponse_ProductList `json:"GetManagerAndDefaultLossReasonResponse_ProductList,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetManagerAndDefaultLossReasonResponse_ResellerInfo struct {
|
||||||
|
ResellerId int32 `json:"reseller_id,omitempty"`
|
||||||
|
AfterSaleName string `json:"after_sale_name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetManagerAndDefaultLossReasonResponse_ProductList struct {
|
||||||
|
ProductId int32 `json:"product_id,omitempty"`
|
||||||
|
LossReason string `json:"loss_reason,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatisFilterOfficialProductApi 官方商品列表
|
||||||
|
func GetManagerAndDefaultLossReasonApi(param *GetManagerAndDefaultLossReasonRequest, token string, reqUrl string) ([]*GetManagerAndDefaultLossReasonResponseList, error) {
|
||||||
|
return []*GetManagerAndDefaultLossReasonResponseList{
|
||||||
|
{
|
||||||
|
ResellerInfo: &GetManagerAndDefaultLossReasonResponse_ResellerInfo{
|
||||||
|
ResellerId: 25009,
|
||||||
|
AfterSaleName: "张三",
|
||||||
|
},
|
||||||
|
ProductList: []*GetManagerAndDefaultLossReasonResponse_ProductList{
|
||||||
|
{
|
||||||
|
ProductId: 129,
|
||||||
|
LossReason: "小米钱包h5-QQ音乐绿钻季卡原因",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ProductId: 2218,
|
||||||
|
LossReason: "小米钱包h5-百度网盘新vip会员月卡原因",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ProductId: 3226,
|
||||||
|
LossReason: "小米钱包h5-腾讯视频月卡-小米钱包2024原因",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ProductId: 3364,
|
||||||
|
LossReason: "小米钱包h5-腾讯视频月卡-0元直充原因",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
reqParam, err := util.StructToMap(param)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &l_request.Request{
|
||||||
|
Url: reqUrl + "/admin/reseller/resellerAuthProduct/getManagerAndDefaultLossReason",
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Json: reqParam,
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Authorization": fmt.Sprintf("Bearer %s", token),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res, err := req.Send()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("request failed, status code: %d,resion: %s", res.StatusCode, res.Reason)
|
||||||
|
}
|
||||||
|
var code resCode
|
||||||
|
if err = json.Unmarshal(res.Content, &code); err != nil {
|
||||||
|
return nil, fmt.Errorf("返回结构异常:%s", string(res.Content))
|
||||||
|
}
|
||||||
|
var resData []*GetManagerAndDefaultLossReasonResponseList
|
||||||
|
if err = json.Unmarshal(code.Data, &resData); err != nil {
|
||||||
|
return nil, fmt.Errorf("返回数据异常:%s", string(res.Content))
|
||||||
|
}
|
||||||
|
return resData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func request(url string, reqData interface{}, resData interface{}, token string) error {
|
||||||
|
requestSchema := Base
|
||||||
|
if len(token) > 0 {
|
||||||
|
requestSchema = AuthUrl
|
||||||
|
}
|
||||||
reqParam, err := pkg.StructToURLValues(reqData)
|
reqParam, err := pkg.StructToURLValues(reqData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &l_request.Request{
|
req := &l_request.Request{
|
||||||
Url: FormatPHPURL(Base+url, reqParam),
|
Url: FormatPHPURL(requestSchema+url, reqParam),
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
}
|
}
|
||||||
|
if len(token) > 0 {
|
||||||
|
req.Headers = map[string]string{
|
||||||
|
"Authorization": fmt.Sprintf("Bearer %s", token),
|
||||||
|
}
|
||||||
|
}
|
||||||
res, err := req.Send()
|
res, err := req.Send()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ package bbxt
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/config"
|
"ai_scheduler/internal/config"
|
||||||
pkginner "ai_scheduler/internal/pkg"
|
pkginner "ai_scheduler/internal/pkg"
|
||||||
|
"ai_scheduler/internal/pkg/lsxd"
|
||||||
"ai_scheduler/internal/pkg/utils_oss"
|
"ai_scheduler/internal/pkg/utils_oss"
|
||||||
"ai_scheduler/pkg"
|
"ai_scheduler/pkg"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
@ -18,18 +20,17 @@ const (
|
||||||
GreenStyle = "${color: 008000;horizontal:center;vertical:center;borderColor:#000000}"
|
GreenStyle = "${color: 008000;horizontal:center;vertical:center;borderColor:#000000}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IndexLossSumDetail = "lossSumDetail"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LossSumInitFunc func(ctx context.Context, day time.Time, totalDetail []*ResellerLoss, selfObj *BbxtTools) error
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DownWardValue int32 = 1000
|
DownWardValue int32 = 1000
|
||||||
SumFilter int32 = -150
|
SumFilter int32 = -150
|
||||||
)
|
)
|
||||||
|
|
||||||
var resellerBlackList = []string{
|
|
||||||
"悦跑",
|
|
||||||
"电商-独立",
|
|
||||||
"蓝星严选连续包月",
|
|
||||||
"通钱-2025年12月",
|
|
||||||
}
|
|
||||||
|
|
||||||
var resellerBlackListProduct = []string{
|
var resellerBlackListProduct = []string{
|
||||||
"悦跑",
|
"悦跑",
|
||||||
"电商-独立",
|
"电商-独立",
|
||||||
|
|
@ -43,9 +44,10 @@ type BbxtTools struct {
|
||||||
excelTempDir string
|
excelTempDir string
|
||||||
ossClient *utils_oss.Client
|
ossClient *utils_oss.Client
|
||||||
config *config.Config
|
config *config.Config
|
||||||
|
login *lsxd.Login
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBbxtTools(config *config.Config) (*BbxtTools, error) {
|
func NewBbxtTools(config *config.Config, login *lsxd.Login) (*BbxtTools, error) {
|
||||||
cache, err := pkg.GetCacheDir()
|
cache, err := pkg.GetCacheDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -59,12 +61,21 @@ func NewBbxtTools(config *config.Config) (*BbxtTools, error) {
|
||||||
cacheDir: cache,
|
cacheDir: cache,
|
||||||
excelTempDir: fmt.Sprintf("%s/excel_temp", tempDir),
|
excelTempDir: fmt.Sprintf("%s/excel_temp", tempDir),
|
||||||
config: config,
|
config: config,
|
||||||
|
login: login,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BbxtTools) DailyReport(now time.Time, downWardValue int32, productName []string, sumFilter int32, ossClient *utils_oss.Client) (reports []*ReportRes, err error) {
|
func (b *BbxtTools) DailyReport(
|
||||||
|
ctx context.Context,
|
||||||
|
now time.Time,
|
||||||
|
downWardValue int32,
|
||||||
|
productName []string,
|
||||||
|
sumFilter int32,
|
||||||
|
ossClient *utils_oss.Client,
|
||||||
|
initFunc LossSumInitFunc,
|
||||||
|
) (reports []*ReportRes, err error) {
|
||||||
reports = make([]*ReportRes, 0, 4)
|
reports = make([]*ReportRes, 0, 4)
|
||||||
productLossReport, err := b.StatisOursProductLossSum(now)
|
productLossReport, err := b.StatisOursProductLossSum(ctx, now, initFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -94,7 +105,7 @@ func (b *BbxtTools) DailyReport(now time.Time, downWardValue int32, productName
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatisOursProductLossSum 负利润分析
|
// StatisOursProductLossSum 负利润分析
|
||||||
func (b *BbxtTools) StatisOursProductLossSum(now time.Time) (report []*ReportRes, err error) {
|
func (b *BbxtTools) StatisOursProductLossSum(ctx context.Context, now time.Time, initFunc LossSumInitFunc) (report []*ReportRes, err error) {
|
||||||
ct := []string{
|
ct := []string{
|
||||||
time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Format("2006-01-02 15:04:05"),
|
time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Format("2006-01-02 15:04:05"),
|
||||||
adjustedTime(now), //adjustedTime(time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location())),
|
adjustedTime(now), //adjustedTime(time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location())),
|
||||||
|
|
@ -110,6 +121,7 @@ func (b *BbxtTools) StatisOursProductLossSum(now time.Time) (report []*ReportRes
|
||||||
resellerMap = make(map[int32]*ResellerLoss)
|
resellerMap = make(map[int32]*ResellerLoss)
|
||||||
total [][]string
|
total [][]string
|
||||||
gt []*ResellerLoss
|
gt []*ResellerLoss
|
||||||
|
totalDetail []*ResellerLoss
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, info := range data.List {
|
for _, info := range data.List {
|
||||||
|
|
@ -119,8 +131,8 @@ func (b *BbxtTools) StatisOursProductLossSum(now time.Time) (report []*ReportRes
|
||||||
resellerMap[info.ResellerId] = &ResellerLoss{
|
resellerMap[info.ResellerId] = &ResellerLoss{
|
||||||
ResellerId: info.ResellerId,
|
ResellerId: info.ResellerId,
|
||||||
ResellerName: info.ResellerName,
|
ResellerName: info.ResellerName,
|
||||||
Total: 0, // 初始化为0,后续累加
|
Total: 0, // 初始化为0,后续累加
|
||||||
ProductLoss: make(map[int32]ProductLoss), // 初始化map
|
ProductLoss: make(map[int32]*ProductLoss), // 初始化map
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,7 +145,7 @@ func (b *BbxtTools) StatisOursProductLossSum(now time.Time) (report []*ReportRes
|
||||||
// 检查产品是否已存在
|
// 检查产品是否已存在
|
||||||
if _, ok := reseller.ProductLoss[info.OursProductId]; !ok {
|
if _, ok := reseller.ProductLoss[info.OursProductId]; !ok {
|
||||||
// 创建新的产品亏损记录
|
// 创建新的产品亏损记录
|
||||||
reseller.ProductLoss[info.OursProductId] = ProductLoss{
|
reseller.ProductLoss[info.OursProductId] = &ProductLoss{
|
||||||
ProductId: info.OursProductId,
|
ProductId: info.OursProductId,
|
||||||
ProductName: info.OursProductName,
|
ProductName: info.OursProductName,
|
||||||
Loss: info.Loss, // 初始化为当前产品的亏损
|
Loss: info.Loss, // 初始化为当前产品的亏损
|
||||||
|
|
@ -155,7 +167,8 @@ func (b *BbxtTools) StatisOursProductLossSum(now time.Time) (report []*ReportRes
|
||||||
return resellers[i].Total < resellers[j].Total
|
return resellers[i].Total < resellers[j].Total
|
||||||
})
|
})
|
||||||
var (
|
var (
|
||||||
totalSum float64
|
totalSum float64
|
||||||
|
|
||||||
totalSum500 float64
|
totalSum500 float64
|
||||||
)
|
)
|
||||||
// 构建分组
|
// 构建分组
|
||||||
|
|
@ -166,46 +179,116 @@ func (b *BbxtTools) StatisOursProductLossSum(now time.Time) (report []*ReportRes
|
||||||
fmt.Sprintf("%.2f", v.Total),
|
fmt.Sprintf("%.2f", v.Total),
|
||||||
})
|
})
|
||||||
totalSum += v.Total
|
totalSum += v.Total
|
||||||
|
totalDetail = append(totalDetail, v)
|
||||||
}
|
}
|
||||||
if v.Total <= -500 && !slices.Contains(resellerBlackListProduct, v.ResellerName) {
|
if v.Total <= -500 && !slices.Contains(resellerBlackListProduct, v.ResellerName) {
|
||||||
gt = append(gt, v)
|
gt = append(gt, v)
|
||||||
totalSum500 += v.Total
|
totalSum500 += v.Total
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
report = make([]*ReportRes, 2)
|
report = make([]*ReportRes, 3)
|
||||||
timeCh := now.Format("1月2日15点")
|
timeCh := now.Format("1月2日15点")
|
||||||
//总量生成excel
|
//总量生成excel
|
||||||
if len(total) > 0 {
|
//if len(total) > 0 {
|
||||||
filePath := b.cacheDir + "/kshj_total" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx"
|
// 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, "")
|
// err = b.SimpleFillExcelWithTitle(b.excelTempDir+"/"+"kshj_total.xlsx", filePath, total, "")
|
||||||
report[0] = &ReportRes{
|
// if err != nil {
|
||||||
ReportName: "负利润分析(合计表)",
|
// return
|
||||||
Title: "截至" + timeCh + "利润累计亏损" + fmt.Sprintf("%.2f", totalSum),
|
// }
|
||||||
Path: filePath,
|
// report[0] = &ReportRes{
|
||||||
Data: total,
|
// ReportName: "分销商负利润统计",
|
||||||
}
|
// Title: "截至" + timeCh + "利润累计亏损" + fmt.Sprintf("%.2f", totalSum),
|
||||||
}
|
// Path: filePath,
|
||||||
|
// Data: total,
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
if err != nil {
|
//if len(gt) > 0 {
|
||||||
return
|
// filePath := b.cacheDir + "/kshj_gt" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx"
|
||||||
}
|
// title := "截至" + timeCh + "亏损500以上的分销商和产品"
|
||||||
if len(gt) > 0 {
|
// err = b.resellerDetailFillExcelV2(b.excelTempDir+"/"+"kshj_gt.xlsx", filePath, gt, title)
|
||||||
filePath := b.cacheDir + "/kshj_gt" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx"
|
// if err != nil {
|
||||||
title := "截至" + timeCh + "亏损500以上的分销商和产品"
|
// return
|
||||||
err = b.resellerDetailFillExcelV2(b.excelTempDir+"/"+"kshj_gt.xlsx", filePath, gt, title)
|
// }
|
||||||
report[1] = &ReportRes{
|
// report[1] = &ReportRes{
|
||||||
ReportName: "负利润分析(亏损500以上)",
|
// ReportName: "负利润分析(亏损500以上)",
|
||||||
Title: "截至" + timeCh + "亏损500以上利润累计亏损" + fmt.Sprintf("%.2f", totalSum500),
|
// Title: "截至" + timeCh + "亏损500以上利润累计亏损" + fmt.Sprintf("%.2f", totalSum500),
|
||||||
|
// Path: filePath,
|
||||||
|
// Data: total,
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
if len(totalDetail) > 0 {
|
||||||
|
err = initFunc(ctx, now, totalDetail, b)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filePath := b.cacheDir + "/kshj_total_ana" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx"
|
||||||
|
title := "截至" + timeCh + "亏损100以上的分销商&产品负利润原因"
|
||||||
|
err = b.resellerDetailFillExcelAna(b.excelTempDir+"/"+"kshj_total_ana.xlsx", filePath, totalDetail, title)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
report[2] = &ReportRes{
|
||||||
|
ReportName: "负利润分析(亏损100以上)",
|
||||||
|
Title: "截至" + timeCh + "亏损100以上利润原因",
|
||||||
Path: filePath,
|
Path: filePath,
|
||||||
Data: total,
|
Data: total,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return report, nil
|
return report, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BbxtTools) GetResellerLossMannagerAndLossReasonFromApi(ctx context.Context, resellerLoss []*ResellerLoss) (map[int32]*ResellerLossSumProductRelation, error) {
|
||||||
|
var (
|
||||||
|
resellerMap = make(map[int32]map[string][]int32)
|
||||||
|
resellerLossMap = make(map[int32]*ResellerLoss, len(resellerLoss))
|
||||||
|
relationMap = make(map[int32]*ResellerLossSumProductRelation)
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, v := range resellerLoss {
|
||||||
|
var productSlice = make([]int32, 0, len(v.ProductLoss))
|
||||||
|
for _, vv := range v.ProductLoss {
|
||||||
|
productSlice = append(productSlice, vv.ProductId)
|
||||||
|
}
|
||||||
|
if _, ex := resellerMap[v.ResellerId]; !ex {
|
||||||
|
resellerMap[v.ResellerId] = make(map[string][]int32, 1)
|
||||||
|
}
|
||||||
|
resellerMap[v.ResellerId]["values"] = productSlice
|
||||||
|
resellerLossMap[v.ResellerId] = v
|
||||||
|
relationMap[v.ResellerId] = &ResellerLossSumProductRelation{
|
||||||
|
ResellerName: v.ResellerName,
|
||||||
|
Products: make(map[int32]*LossReason, len(v.ProductLoss)),
|
||||||
|
}
|
||||||
|
for _, product := range v.ProductLoss {
|
||||||
|
relationMap[v.ResellerId].Products[product.ProductId] = &LossReason{
|
||||||
|
ProductName: product.ProductName,
|
||||||
|
LossReason: "未填写", // 初始化为未填写
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res, err := GetManagerAndDefaultLossReasonApi(&GetManagerAndDefaultLossReasonRequest{
|
||||||
|
Param: resellerMap,
|
||||||
|
}, b.login.GetToken(ctx), b.config.ZLTX.ReqUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, v := range res {
|
||||||
|
if v == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := resellerLossMap[v.ResellerInfo.ResellerId]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
relationMap[v.ResellerInfo.ResellerId].AfterSaleName = v.ResellerInfo.AfterSaleName
|
||||||
|
|
||||||
|
for _, vv := range v.ProductList {
|
||||||
|
relationMap[v.ResellerInfo.ResellerId].Products[vv.ProductId].LossReason = vv.LossReason
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return relationMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetProfitRankingSum 利润同比分销商排行榜
|
// GetProfitRankingSum 利润同比分销商排行榜
|
||||||
func (b *BbxtTools) GetProfitRankingSum(now time.Time) (report *ReportRes, err error) {
|
func (b *BbxtTools) GetProfitRankingSum(now time.Time) (report *ReportRes, err error) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,19 @@ package bbxt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/config"
|
"ai_scheduler/internal/config"
|
||||||
|
"ai_scheduler/internal/data/impl"
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/internal/pkg"
|
||||||
|
"ai_scheduler/internal/pkg/lsxd"
|
||||||
"ai_scheduler/internal/pkg/utils_oss"
|
"ai_scheduler/internal/pkg/utils_oss"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_StatisOursProductLossSumApiTotal(t *testing.T) {
|
func Test_StatisOursProductLossSumApiTotal(t *testing.T) {
|
||||||
|
|
@ -23,29 +32,30 @@ func Test_StatisOursProductLossSumApiTotal(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
o, err := NewBbxtTools(nil)
|
o, err := NewBbxtTools(nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
reports, err := o.DailyReport(time.Now(), DownWardValue, []string{"官方-爱奇艺-星钻季卡", "官方-爱奇艺-星钻半年卡", "官方--腾讯-年卡", "官方--爱奇艺-月卡"}, SumFilter, ossClient)
|
reports, err := o.DailyReport(context.Background(), time.Now(), DownWardValue, []string{"官方-爱奇艺-星钻季卡", "官方-爱奇艺-星钻半年卡", "官方--腾讯-年卡", "官方--爱奇艺-月卡"}, SumFilter, ossClient, GetReportCache)
|
||||||
|
|
||||||
t.Log(reports, err)
|
t.Log(reports, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_StatisOursProductLossSum(t *testing.T) {
|
func Test_StatisOursProductLossSum(t *testing.T) {
|
||||||
o, err := NewBbxtTools(nil)
|
run()
|
||||||
|
o, err := NewBbxtTools(configConfig, lsxd.NewLogin(configConfig, utils.NewRdb(configConfig)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
report, err := o.StatisOursProductLossSum(time.Now())
|
report, err := o.StatisOursProductLossSum(context.Background(), time.Now(), GetReportCache)
|
||||||
|
|
||||||
t.Log(report, err)
|
t.Log(report, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_GetProfitRankingSum(t *testing.T) {
|
func Test_GetProfitRankingSum(t *testing.T) {
|
||||||
o, err := NewBbxtTools(nil)
|
o, err := NewBbxtTools(nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -56,21 +66,22 @@ func Test_GetProfitRankingSum(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_GetStatisOfficialProductSumDecline(t *testing.T) {
|
func Test_GetStatisOfficialProductSumDecline(t *testing.T) {
|
||||||
o, err := NewBbxtTools(nil)
|
o, err := NewBbxtTools(nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
s := "官方--腾讯-周卡,官方--腾讯-月卡,官方--腾讯-季卡,官方--腾讯-年卡,官方--优酷周卡,官方--优酷月卡,官方--优酷季卡,官方--优酷年卡,官方--爱奇艺-周卡,官方--爱奇艺-月卡,官方--爱奇艺-季卡,官方--爱奇艺-年卡,官方--芒果-PC周卡,官方--芒果-PC月卡,官方--芒果-PC季卡,官方--美团外卖红包5元,官方--美团外卖红包10元,官方--QQ音乐-绿钻月卡,官方--饿了么超级会员月卡,官方--网易云黑胶vip月卡,官方--喜马拉雅巅峰会员月卡"
|
s := "官方--腾讯-周卡,官方--腾讯-月卡,官方--腾讯-季卡,官方--腾讯-年卡,官方--优酷周卡,官方--优酷月卡,官方--优酷季卡,官方--优酷年卡,官方--爱奇艺-周卡,官方--爱奇艺-月卡,官方--爱奇艺-季卡,官方--爱奇艺-年卡,官方--芒果-PC周卡,官方--芒果-PC月卡,官方--芒果-PC季卡,官方--美团外卖红包5元,官方--美团外卖红包10元,官方--QQ音乐-绿钻月卡,官方--饿了么超级会员月卡,官方--网易云黑胶vip月卡,官方--喜马拉雅巅峰会员月卡"
|
||||||
//s := "官方--QQ音乐-绿钻月卡"
|
//s := "官方--QQ音乐-绿钻月卡"
|
||||||
now := time.Now()
|
report, err := o.GetStatisOfficialProductSumDecline(time.Now(), 1000, strings.Split(s, ","), -150)
|
||||||
report, err := o.GetStatisOfficialProductSumDecline(time.Date(now.Year(), now.Month(), now.Day(), 12, 0, 0, 0, now.Location()), 1000, strings.Split(s, ","), -150)
|
|
||||||
|
|
||||||
t.Log(report, err)
|
t.Log(report, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_GetStatisOfficialProductSum(t *testing.T) {
|
func Test_GetStatisOfficialProductSum(t *testing.T) {
|
||||||
o, err := NewBbxtTools(nil)
|
|
||||||
|
configs := configConfig
|
||||||
|
o, err := NewBbxtTools(nil, lsxd.NewLogin(configs, utils.NewRdb(configConfig)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -80,3 +91,62 @@ func Test_GetStatisOfficialProductSum(t *testing.T) {
|
||||||
t.Log(report, err)
|
t.Log(report, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
reportDailyCacheImpl *impl.ReportDailyCacheImpl
|
||||||
|
configConfig *config.Config
|
||||||
|
)
|
||||||
|
|
||||||
|
func run() {
|
||||||
|
configConfig, _ = config.LoadConfigWithTest()
|
||||||
|
// 初始化数据库连接
|
||||||
|
db, _ := utils.NewGormDb(configConfig)
|
||||||
|
reportDailyCacheImpl = impl.NewReportDailyCacheImpl(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetReportCache(ctx context.Context, day time.Time, totalDetail []*ResellerLoss, bbxtObj *BbxtTools) error {
|
||||||
|
run()
|
||||||
|
|
||||||
|
var ResellerProductRelation map[int32]*ResellerLossSumProductRelation
|
||||||
|
dayDate := day.Format(time.DateOnly)
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"`index`": IndexLossSumDetail})
|
||||||
|
cond = cond.And(builder.Eq{"`key`": dayDate})
|
||||||
|
var cache model.AiReportDailyCache
|
||||||
|
|
||||||
|
err := reportDailyCacheImpl.GetOneBySearchToStrut(&cond, &cache)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cache.Value == "" {
|
||||||
|
ResellerProductRelation, err = bbxtObj.GetResellerLossMannagerAndLossReasonFromApi(ctx, totalDetail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cache = model.AiReportDailyCache{
|
||||||
|
Key: dayDate,
|
||||||
|
Index: IndexLossSumDetail,
|
||||||
|
Value: pkg.JsonStringIgonErr(ResellerProductRelation),
|
||||||
|
}
|
||||||
|
_, err = reportDailyCacheImpl.Add(&cache)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = json.Unmarshal([]byte(cache.Value), &ResellerProductRelation)
|
||||||
|
|
||||||
|
for _, v := range totalDetail {
|
||||||
|
if _, ex := ResellerProductRelation[v.ResellerId]; !ex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v.Manager = ResellerProductRelation[v.ResellerId].AfterSaleName
|
||||||
|
for _, vv := range v.ProductLoss {
|
||||||
|
if _, ex := ResellerProductRelation[v.ResellerId].Products[vv.ProductId]; !ex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vv.LossReason = ResellerProductRelation[v.ResellerId].Products[vv.ProductId].LossReason
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,15 @@ type ResellerLoss struct {
|
||||||
ResellerId int32
|
ResellerId int32
|
||||||
ResellerName string
|
ResellerName string
|
||||||
Total float64
|
Total float64
|
||||||
ProductLoss map[int32]ProductLoss
|
ProductLoss map[int32]*ProductLoss
|
||||||
|
Manager string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductLoss struct {
|
type ProductLoss struct {
|
||||||
ProductId int32
|
ProductId int32
|
||||||
ProductName string
|
ProductName string
|
||||||
Loss float64
|
Loss float64
|
||||||
|
LossReason string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReportRes struct {
|
type ReportRes struct {
|
||||||
|
|
@ -37,3 +39,14 @@ type ProductSumReseller struct {
|
||||||
HistoryTwoNum int32 //上周成功数量
|
HistoryTwoNum int32 //上周成功数量
|
||||||
HistoryTwoDiff int32 //同比上周当前增减量
|
HistoryTwoDiff int32 //同比上周当前增减量
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResellerLossSumProductRelation struct {
|
||||||
|
AfterSaleName string `json:"after_sale_name"`
|
||||||
|
ResellerName string `json:"reseller_name"`
|
||||||
|
Products map[int32]*LossReason
|
||||||
|
}
|
||||||
|
|
||||||
|
type LossReason struct {
|
||||||
|
ProductName string
|
||||||
|
LossReason string
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ func (b *BbxtTools) resellerDetailFillExcelV2(templatePath, outputPath string, d
|
||||||
|
|
||||||
for _, reseller := range dataSlice {
|
for _, reseller := range dataSlice {
|
||||||
// 排序 ProductLoss
|
// 排序 ProductLoss
|
||||||
var products []ProductLoss
|
var products []*ProductLoss
|
||||||
for _, p := range reseller.ProductLoss {
|
for _, p := range reseller.ProductLoss {
|
||||||
products = append(products, p)
|
products = append(products, p)
|
||||||
}
|
}
|
||||||
|
|
@ -311,4 +311,164 @@ func (b *BbxtTools) resellerDetailFillExcelV2(templatePath, outputPath string, d
|
||||||
return f.SaveAs(outputPath)
|
return f.SaveAs(outputPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OfficialProductSumDeclineExcel
|
func (b *BbxtTools) resellerDetailFillExcelAna(templatePath, outputPath string, dataSlice []*ResellerLoss, title string) error {
|
||||||
|
// 1. 读取模板
|
||||||
|
f, err := excelize.OpenFile(templatePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
sheet := f.GetSheetName(0)
|
||||||
|
if len(title) > 0 {
|
||||||
|
// 写入标题
|
||||||
|
f.SetCellValue(sheet, "A1", title)
|
||||||
|
}
|
||||||
|
// ---------------- 样式获取 ----------------
|
||||||
|
// 模板第2行:数据行样式
|
||||||
|
tplRowData := 3
|
||||||
|
styleA3, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", tplRowData))
|
||||||
|
if err != nil {
|
||||||
|
styleA3 = 0
|
||||||
|
}
|
||||||
|
// B2和C2通常样式一致,这里取B2作为明细列样式
|
||||||
|
styleB3, err := f.GetCellStyle(sheet, fmt.Sprintf("B%d", tplRowData))
|
||||||
|
if err != nil {
|
||||||
|
styleB3 = 0
|
||||||
|
}
|
||||||
|
styleC3, err := f.GetCellStyle(sheet, fmt.Sprintf("C%d", tplRowData))
|
||||||
|
if err != nil {
|
||||||
|
styleC3 = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
styleD3, err := f.GetCellStyle(sheet, fmt.Sprintf("D%d", tplRowData))
|
||||||
|
if err != nil {
|
||||||
|
styleC3 = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
styleE3, err := f.GetCellStyle(sheet, fmt.Sprintf("E%d", tplRowData))
|
||||||
|
if err != nil {
|
||||||
|
styleC3 = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
rowHeightData, err := f.GetRowHeight(sheet, tplRowData)
|
||||||
|
if err != nil {
|
||||||
|
rowHeightData = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模板第4行:合计行样式
|
||||||
|
tplRowTotal := 5
|
||||||
|
styleTotalA, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", tplRowTotal))
|
||||||
|
if err != nil {
|
||||||
|
styleTotalA = 0
|
||||||
|
}
|
||||||
|
styleTotalB, err := f.GetCellStyle(sheet, fmt.Sprintf("B%d", tplRowTotal))
|
||||||
|
if err != nil {
|
||||||
|
styleTotalB = 0
|
||||||
|
}
|
||||||
|
styleTotalC, err := f.GetCellStyle(sheet, fmt.Sprintf("C%d", tplRowTotal))
|
||||||
|
if err != nil {
|
||||||
|
styleTotalC = 0
|
||||||
|
}
|
||||||
|
styleTotalD, err := f.GetCellStyle(sheet, fmt.Sprintf("D%d", tplRowTotal))
|
||||||
|
if err != nil {
|
||||||
|
styleTotalC = 0
|
||||||
|
}
|
||||||
|
styleTotalE, err := f.GetCellStyle(sheet, fmt.Sprintf("E%d", tplRowTotal))
|
||||||
|
if err != nil {
|
||||||
|
styleTotalC = 0
|
||||||
|
}
|
||||||
|
rowHeightTotal, err := f.GetRowHeight(sheet, tplRowTotal)
|
||||||
|
if err != nil {
|
||||||
|
rowHeightTotal = 30
|
||||||
|
}
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
currentRow := 3
|
||||||
|
totalLoss := 0.0
|
||||||
|
|
||||||
|
for _, reseller := range dataSlice {
|
||||||
|
// 排序 ProductLoss
|
||||||
|
var products []*ProductLoss
|
||||||
|
for _, p := range reseller.ProductLoss {
|
||||||
|
products = append(products, p)
|
||||||
|
}
|
||||||
|
sort.Slice(products, func(i, j int) bool {
|
||||||
|
return products[i].Loss < products[j].Loss
|
||||||
|
})
|
||||||
|
|
||||||
|
startRow := currentRow
|
||||||
|
|
||||||
|
// 填充该经销商的所有产品
|
||||||
|
for _, p := range products {
|
||||||
|
// 设置行高
|
||||||
|
f.SetRowHeight(sheet, currentRow, rowHeightData)
|
||||||
|
|
||||||
|
// 设置值
|
||||||
|
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)
|
||||||
|
// 设置样式
|
||||||
|
if styleA3 != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("A%d", currentRow), styleA3)
|
||||||
|
}
|
||||||
|
if styleB3 != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("B%d", currentRow), fmt.Sprintf("B%d", currentRow), styleB3)
|
||||||
|
}
|
||||||
|
if styleC3 != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("C%d", currentRow), fmt.Sprintf("C%d", currentRow), styleC3)
|
||||||
|
}
|
||||||
|
if styleD3 != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("D%d", currentRow), fmt.Sprintf("D%d", currentRow), styleD3)
|
||||||
|
}
|
||||||
|
if styleE3 != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("E%d", currentRow), fmt.Sprintf("E%d", currentRow), styleE3)
|
||||||
|
}
|
||||||
|
|
||||||
|
totalLoss += p.Loss
|
||||||
|
currentRow++
|
||||||
|
}
|
||||||
|
|
||||||
|
endRow := currentRow - 1
|
||||||
|
// 合并单元格 (如果多于1行)
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- 填充合计行 ----------------
|
||||||
|
// 四舍五入保留四位小数
|
||||||
|
totalLoss, _ = decimal.NewFromFloat(totalLoss).Round(4).Float64()
|
||||||
|
// 设置行高
|
||||||
|
f.SetRowHeight(sheet, currentRow, rowHeightTotal)
|
||||||
|
|
||||||
|
f.SetCellValue(sheet, fmt.Sprintf("A%d", currentRow), "合计")
|
||||||
|
// B列留空,C列填充总亏损
|
||||||
|
f.SetCellValue(sheet, fmt.Sprintf("C%d", currentRow), totalLoss)
|
||||||
|
|
||||||
|
// 设置合计行样式
|
||||||
|
if styleTotalA != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("A%d", currentRow), styleTotalA)
|
||||||
|
}
|
||||||
|
if styleTotalB != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("B%d", currentRow), fmt.Sprintf("B%d", currentRow), styleTotalB)
|
||||||
|
}
|
||||||
|
if styleTotalC != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("C%d", currentRow), fmt.Sprintf("C%d", currentRow), styleTotalC)
|
||||||
|
}
|
||||||
|
if styleTotalD != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("D%d", currentRow), fmt.Sprintf("D%d", currentRow), styleTotalD)
|
||||||
|
}
|
||||||
|
if styleTotalE != 0 {
|
||||||
|
f.SetCellStyle(sheet, fmt.Sprintf("E%d", currentRow), fmt.Sprintf("E%d", currentRow), styleTotalE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消合并合计行的A、B列
|
||||||
|
// f.MergeCell(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow))
|
||||||
|
|
||||||
|
// 6. 保存
|
||||||
|
return f.SaveAs(outputPath)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,31 @@ import (
|
||||||
config2 "ai_scheduler/internal/config"
|
config2 "ai_scheduler/internal/config"
|
||||||
"ai_scheduler/internal/entitys"
|
"ai_scheduler/internal/entitys"
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_task(t *testing.T) {
|
func Test_task(t *testing.T) {
|
||||||
c := NewZltxOrderDetailTool(config2.ToolConfig{}, nil)
|
c := NewZltxOrderDetailTool(config2.ToolConfig{
|
||||||
|
BaseURL: "https://revcl.1688sup.com/api/admin/direct/ai/%s",
|
||||||
|
AddURL: "https://revcl.1688sup.com/api/admin/direct/log/%s/%s",
|
||||||
|
}, nil)
|
||||||
|
ch := make(chan entitys.Response, 10000)
|
||||||
err :=
|
err :=
|
||||||
c.Execute(context.Background(), &entitys.Recognize{
|
c.Execute(context.Background(), &entitys.Recognize{
|
||||||
Match: &entitys.Match{
|
Match: &entitys.Match{
|
||||||
Parameters: `{"order_number": 859393216068067329}`,
|
Parameters: `{"order_number": 864086822064234497}`,
|
||||||
},
|
},
|
||||||
|
Ext: []byte(`{"auth":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ1c2VyQ2VudGVyIiwiZXhwIjoxNzY3OTM0MTg1LCJuYmYiOjE3Njc5MjMzODUsImp0aSI6IjE3OSIsIlBob25lIjoiMTUwMDA0NTAxNTUiLCJVc2VyTmFtZSI6IjE1MDAwNDUwMTU1IiwiUmVhbE5hbWUiOiLliJjlvanlhpsiLCJBY2NvdW50VHlwZSI6MSwiR3JvdXBDb2RlcyI6IlZDTF9BRE1JTixWQ0xfU1lTVEVNLENSTV9BRE1JTixMU1hERFNfQURNSU4sTFNYRERTX1NZU1RFTSxNQVJLRVRJTkdTQUFTX1NVUEVSQURNSU4sU1RBVElTVElDQUxTWVNURU1fQURNSU4sUEhZU0lDQUxHT09EU19BRE1JTixNQVJLRVRJTkdTWVNURU1fU1VQRVIsUkVQT1JUQ0VOVEVSX0FETUlOLE1PTklUT1JfTUFESU4sWkxUWF9BRE1JTixaTFRYX09QRVJBVEUiLCJEaW5nVXNlcklkIjoiMTcxMTkzNTg3NTAzMjk5MzUifQ.d9z0S1Ia-PFAxhGstT055Amt8PI09bUHxG0_lba4UwvSomiTNCD-5DFdMkbZHwiDTlhVdBjcd1mDYFRRZXWMPoSnanubMBnRnuvTi8csch5nz1L9oWNo-HFyBE3lMw9-UJ5j84gz228_kcBsvRATT1Ixs9bnuaN9CDNz20c524llDt10C3cc8wLGMin4jWEMF4RNrf2oBZOFAahRYSJNeBmutIwRSIP1pMIAaUy_IkMCyOwK8JzgMnHJGLwIH_nxR9XZXlAN0FmrmtWVkRA2YUKvoDX5a5BCYmDVNqUbi_ZNuRPJH87Ia7_-UoyJu8Yq79jX0Qgsm6qJ4rX2nauneg"}`),
|
||||||
|
UserContent: &entitys.RecognizeUserContent{Text: "订单查询:864086822064234497\n"},
|
||||||
})
|
})
|
||||||
t.Log(err)
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
var res strings.Builder
|
||||||
|
for v := range ch {
|
||||||
|
res.WriteString(v.Content)
|
||||||
|
}
|
||||||
|
t.Log(res)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@ type ZltxOrderDetailData struct {
|
||||||
|
|
||||||
// Execute 执行直连天下订单详情查询
|
// Execute 执行直连天下订单详情查询
|
||||||
func (w *ZltxOrderDetailTool) Execute(ctx context.Context, rec *entitys.Recognize) error {
|
func (w *ZltxOrderDetailTool) Execute(ctx context.Context, rec *entitys.Recognize) error {
|
||||||
var req ZltxOrderDetailRequest
|
var req = &ZltxOrderDetailRequest{}
|
||||||
if err := json.Unmarshal([]byte(rec.Match.Parameters), &req); err != nil {
|
if err := req.UnmarshalJSON([]byte(rec.Match.Parameters)); err != nil {
|
||||||
return fmt.Errorf("invalid zltxOrderDetail request: %w", err)
|
return fmt.Errorf("invalid zltxOrderDetail request: %w", err)
|
||||||
}
|
}
|
||||||
if req.OrderNumber == "" {
|
if req.OrderNumber == "" {
|
||||||
|
|
@ -96,6 +96,22 @@ func (w *ZltxOrderDetailTool) Execute(ctx context.Context, rec *entitys.Recogniz
|
||||||
return w.getZltxOrderDetail(rec, req.OrderNumber)
|
return w.getZltxOrderDetail(rec, req.OrderNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ZltxOrderDetailRequest) UnmarshalJSON(data []byte) error {
|
||||||
|
var tmp struct {
|
||||||
|
OrderNumber json.Number `json:"order_number"` // 使用 json.Number 保留原始格式
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 根据需要转换为 int64 或 string
|
||||||
|
if num, err := tmp.OrderNumber.Int64(); err == nil {
|
||||||
|
r.OrderNumber = num
|
||||||
|
} else {
|
||||||
|
r.OrderNumber = tmp.OrderNumber.String()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// getMockZltxOrderDetail 获取模拟直连天下订单详情数据
|
// getMockZltxOrderDetail 获取模拟直连天下订单详情数据
|
||||||
func (w *ZltxOrderDetailTool) getZltxOrderDetail(rec *entitys.Recognize, number interface{}) (err error) {
|
func (w *ZltxOrderDetailTool) getZltxOrderDetail(rec *entitys.Recognize, number interface{}) (err error) {
|
||||||
log.Infof("订单编号:%v,类型:%v")
|
log.Infof("订单编号:%v,类型:%v")
|
||||||
|
|
@ -170,8 +186,13 @@ func (w *ZltxOrderDetailTool) getZltxOrderDetail(rec *entitys.Recognize, number
|
||||||
|
|
||||||
err = w.llm.ChatStream(context.TODO(), rec.Ch, []api.Message{
|
err = w.llm.ChatStream(context.TODO(), rec.Ch, []api.Message{
|
||||||
{
|
{
|
||||||
Role: "system",
|
Role: "system",
|
||||||
Content: "你是一个订单日志助手。用户可能会提供订单日志,你需要分析订单日志,失败订单->分析失败原因,成功订单->找出整个日志的 Base64 编码的 JSON 数据的内容进行转换并反馈给我",
|
Content: "你是一个订单日志助手。用户可能会提供订单日志,你需要按以下规则处理:\n" +
|
||||||
|
"1. **先输出结论**:用<conclusion></conclusion>标签包裹关键结论(如失败原因或Base64解码内容);\n" +
|
||||||
|
"2. **再输出分析过程**:详细解释如何得出结论;分析过程直接输出分析内容,不需要标明是分析过程\n" +
|
||||||
|
"3. **订单类型处理**:\n" +
|
||||||
|
" - 失败订单:分析失败原因(如支付超时、库存不足等);\n" +
|
||||||
|
" - 成功订单:提取日志中的Base64编码JSON数据,解码后转换为用户可读的格式(如表格或JSON)。",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Role: "assistant",
|
Role: "assistant",
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue