fix: 调整代码层级
This commit is contained in:
parent
36db8e7a86
commit
39d2fc1e62
|
|
@ -0,0 +1,290 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"ai_scheduler/internal/config"
|
||||
"ai_scheduler/internal/data/constants"
|
||||
"ai_scheduler/internal/data/impl"
|
||||
"ai_scheduler/internal/domain/tools/common/knowledge_base"
|
||||
"ai_scheduler/internal/pkg/dingtalk"
|
||||
"ai_scheduler/internal/pkg/util"
|
||||
"ai_scheduler/internal/pkg/utils_ollama"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/card"
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
|
||||
"github.com/alibabacloud-go/dingtalk/card_1_0"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"github.com/ollama/ollama/api"
|
||||
)
|
||||
|
||||
type CallbackBiz struct {
|
||||
cfg *config.Config
|
||||
ollamaClient *utils_ollama.Client
|
||||
dingtalkCardClient *dingtalk.CardClient
|
||||
botConfigImpl *impl.BotConfigImpl
|
||||
}
|
||||
|
||||
func NewCallbackBiz(
|
||||
cfg *config.Config,
|
||||
ollamaClient *utils_ollama.Client,
|
||||
dingtalkCardClient *dingtalk.CardClient,
|
||||
botConfigImpl *impl.BotConfigImpl,
|
||||
) *CallbackBiz {
|
||||
return &CallbackBiz{
|
||||
cfg: cfg,
|
||||
ollamaClient: ollamaClient,
|
||||
dingtalkCardClient: dingtalkCardClient,
|
||||
botConfigImpl: botConfigImpl,
|
||||
}
|
||||
}
|
||||
|
||||
// IssueHandlingGroup 问题处理群机器人回调
|
||||
// 能力1: 通过[内容提取] 宏,分析用户QA问题,调出QA表单卡片
|
||||
// 能力2: 通过[QA收集] 宏,收集用户反馈,写入知识库
|
||||
// 能力3: 通过[知识库查询] 宏,查询知识库,返回答案
|
||||
func (c *CallbackBiz) IssueHandlingGroup(data chatbot.BotCallbackDataModel) error {
|
||||
// 能力1、2:分析用户QA问题,写入知识库
|
||||
if strings.Contains(data.Text.Content, "[内容提取]") || strings.Contains(data.Text.Content, "[QA收集]") {
|
||||
c.issueHandlingExtractContent(data)
|
||||
}
|
||||
// 能力3:查询知识库,返回答案
|
||||
if strings.Contains(data.Text.Content, "[知识库查询]") {
|
||||
c.issueHandlingQueryKnowledgeBase(data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 问题处理群机器人内容提取
|
||||
func (c *CallbackBiz) issueHandlingExtractContent(data chatbot.BotCallbackDataModel) {
|
||||
// 1.提取用户输入
|
||||
prompt := fmt.Sprintf(constants.IssueHandlingExtractContentPrompt, data.Text.Content)
|
||||
log.Infof("问题提取提示词: %s", prompt)
|
||||
// LLM 提取
|
||||
generateResp, err := c.ollamaClient.Generation(context.Background(), &api.GenerateRequest{
|
||||
Model: c.cfg.Ollama.GenerateModel,
|
||||
Prompt: prompt,
|
||||
Stream: util.AnyToPoint(false),
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("问题提取失败: %v", err)
|
||||
return
|
||||
}
|
||||
// 解析 JSON 响应
|
||||
var resp struct {
|
||||
Items []struct {
|
||||
Question string `json:"question"`
|
||||
Answer string `json:"answer"`
|
||||
Confidence string `json:"confidence"`
|
||||
} `json:"items"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(generateResp.Response), &resp); err != nil {
|
||||
log.Errorf("解析 JSON 响应失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 2.构建文本域内容
|
||||
cardContentTpl := "问题:%s \n答案:%s"
|
||||
var cardContentList []string
|
||||
for _, item := range resp.Items {
|
||||
cardContentList = append(cardContentList, fmt.Sprintf(cardContentTpl, item.Question, item.Answer))
|
||||
}
|
||||
cardContent := strings.Join(cardContentList, "\n\n")
|
||||
|
||||
// 3.获取应用AppKey
|
||||
appKey, err := c.botConfigImpl.GetRobotAppKey(data.RobotCode)
|
||||
if err != nil {
|
||||
log.Errorf("获取应用配置失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 4.创建并投放卡片
|
||||
outTrackId := constants.BuildCardOutTrackId(data.ConversationId, data.RobotCode) // 构建卡片 OutTrackId
|
||||
_, err = c.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(c.cfg.Dingtalk.Card.Template.ContentCollect),
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CallbackType: tea.String("HTTP"),
|
||||
CallbackRouteKey: tea.String(c.cfg.Dingtalk.Card.CallbackRouteKey),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("QA知识收集"),
|
||||
"button_display": tea.String("true"),
|
||||
"QA_details_now": tea.String(cardContent),
|
||||
"textarea_display": tea.String("normal"),
|
||||
"action_id": tea.String("collect_qa"),
|
||||
"tenant_id": tea.String(constants.KnowledgeTenantIdDefault),
|
||||
"_CARD_DEBUG_TOOL_ENTRY": tea.String(c.cfg.Dingtalk.Card.DebugToolEntryShow), // 调试字段
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + data.ConversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(c.cfg.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
// 问题处理群机器人查询知识库
|
||||
func (c *CallbackBiz) issueHandlingQueryKnowledgeBase(data chatbot.BotCallbackDataModel) {
|
||||
// 获取应用配置
|
||||
appKey, err := c.botConfigImpl.GetRobotAppKey(data.RobotCode)
|
||||
if err != nil {
|
||||
log.Errorf("应用机器人配置不存在: %s, err: %v", data.RobotCode, err)
|
||||
return
|
||||
}
|
||||
// 创建卡片
|
||||
outTrackId := constants.BuildCardOutTrackId(data.ConversationId, data.RobotCode)
|
||||
_, err = c.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(c.cfg.Dingtalk.Card.Template.BaseMsg),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String(data.Text.Content),
|
||||
"markdown": tea.String("知识库检索中..."),
|
||||
},
|
||||
},
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + data.ConversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(data.RobotCode),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// 查询知识库
|
||||
knowledgeBase := knowledge_base.New(c.cfg.KnowledgeConfig)
|
||||
knowledgeResp, err := knowledgeBase.Query(&knowledge_base.QueryRequest{
|
||||
TenantID: constants.KnowledgeTenantIdDefault,
|
||||
Query: data.Text.Content,
|
||||
Mode: constants.KnowledgeModeMix,
|
||||
Stream: false,
|
||||
Think: false,
|
||||
OnlyRAG: true,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("查询知识库失败: %v", err)
|
||||
return
|
||||
}
|
||||
knowledgeRespBytes, err := io.ReadAll(knowledgeResp)
|
||||
if err != nil {
|
||||
log.Errorf("读取知识库响应失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 卡片更新
|
||||
message, isRetrieved, err := knowledge_base.ParseOpenAIHTTPData(string(knowledgeRespBytes))
|
||||
if err != nil {
|
||||
log.Errorf("读取知识库 SSE 数据失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
content := message.Content
|
||||
if !isRetrieved {
|
||||
content = "知识库未检测到匹配信息,请核查知识库数据是否正确。"
|
||||
}
|
||||
|
||||
// 卡片更新
|
||||
_, err = c.dingtalkCardClient.UpdateCard(
|
||||
appKey,
|
||||
&card_1_0.UpdateCardRequest{
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CardData: &card_1_0.UpdateCardRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"markdown": tea.String(content),
|
||||
},
|
||||
},
|
||||
CardUpdateOptions: &card_1_0.UpdateCardRequestCardUpdateOptions{
|
||||
UpdateCardDataByKey: tea.Bool(true),
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("更新卡片失败: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// IssueHandlingCollectQA 问题处理群机器人 QA 收集回调
|
||||
func (c *CallbackBiz) IssueHandlingCollectQA(data card.CardRequest) *card.CardResponse {
|
||||
// 确认提交,文本写入知识库
|
||||
if data.CardActionData.CardPrivateData.Params["submit"] == "submit" {
|
||||
content := data.CardActionData.CardPrivateData.Params["QA_details"].(string)
|
||||
tenantID := data.CardActionData.CardPrivateData.Params["tenant_id"].(string)
|
||||
|
||||
// 协程执行耗时操作,防止阻塞
|
||||
util.SafeGo("inject_knowledge_base", func() {
|
||||
knowledgeBase := knowledge_base.New(c.cfg.KnowledgeConfig)
|
||||
err := knowledgeBase.IngestText(&knowledge_base.IngestTextRequest{
|
||||
TenantID: tenantID,
|
||||
Text: content,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("注入知识库失败: %v", err)
|
||||
} else {
|
||||
log.Infof("注入知识库成功: tenantID=%s", tenantID)
|
||||
}
|
||||
|
||||
// 解析当前卡片的 ConversationId 和 robotCode
|
||||
conversationId, robotCode := constants.ParseCardOutTrackId(data.OutTrackId)
|
||||
|
||||
// 获取应用配置
|
||||
appKey, err := c.botConfigImpl.GetRobotAppKey(robotCode)
|
||||
if err != nil {
|
||||
log.Errorf("获取应用机器人配置失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 发送卡片通知用户注入成功
|
||||
outTrackId := constants.BuildCardOutTrackId(conversationId, robotCode)
|
||||
c.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(c.cfg.Dingtalk.Card.Template.BaseMsg),
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("QA知识收集结果"),
|
||||
"markdown": tea.String("[Get] **成功**"),
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + conversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(robotCode),
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// 取消提交,禁用输入框
|
||||
resp := &card.CardResponse{
|
||||
CardUpdateOptions: &card.CardUpdateOptions{
|
||||
UpdateCardDataByKey: true,
|
||||
},
|
||||
CardData: &card.CardDataDto{
|
||||
CardParamMap: map[string]string{
|
||||
"textarea_display": "disabled",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
|
@ -20,8 +20,13 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/card"
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
|
||||
|
||||
dingtalkPkg "ai_scheduler/internal/pkg/dingtalk"
|
||||
|
||||
"github.com/alibabacloud-go/dingtalk/card_1_0"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
|
@ -45,6 +50,9 @@ type DingTalkBotBiz struct {
|
|||
groupConfigBiz *GroupConfigBiz
|
||||
reportDailyCacheImpl *impl.ReportDailyCacheImpl
|
||||
macro *do.Macro
|
||||
dingtalkOauth2Client *dingtalkPkg.Oauth2Client
|
||||
dingTalkOld *dingtalkPkg.OldClient
|
||||
dingtalkCardClient *dingtalkPkg.CardClient
|
||||
}
|
||||
|
||||
// NewDingTalkBotBiz
|
||||
|
|
@ -61,6 +69,9 @@ func NewDingTalkBotBiz(
|
|||
cardSend *dingtalk.SendCardClient,
|
||||
groupConfigBiz *GroupConfigBiz,
|
||||
macro *do.Macro,
|
||||
dingtalkOauth2Client *dingtalkPkg.Oauth2Client,
|
||||
dingTalkOld *dingtalkPkg.OldClient,
|
||||
dingtalkCardClient *dingtalkPkg.CardClient,
|
||||
) *DingTalkBotBiz {
|
||||
return &DingTalkBotBiz{
|
||||
do: do,
|
||||
|
|
@ -76,6 +87,9 @@ func NewDingTalkBotBiz(
|
|||
cardSend: cardSend,
|
||||
reportDailyCacheImpl: reportDailyCacheImpl,
|
||||
macro: macro,
|
||||
dingtalkOauth2Client: dingtalkOauth2Client,
|
||||
dingTalkOld: dingTalkOld,
|
||||
dingtalkCardClient: dingtalkCardClient,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -408,3 +422,209 @@ func (d *DingTalkBotBiz) defaultPrompt() string {
|
|||
-parameters 必须是 **转义后的 JSON 字符串**(如 "{\"product_name\": \"京东月卡\"}")。
|
||||
当前时间:` + now + `,所有的时间识别精确到秒`
|
||||
}
|
||||
|
||||
// CreateIssueHandlingGroupAndInit 创建问题处理群聊并初始化
|
||||
func (d *DingTalkBotBiz) CreateIssueHandlingGroupAndInit(ctx context.Context, data *card.CardRequest) (resp *card.CardResponse, err error) {
|
||||
|
||||
// 解析 OutTrackId 以获取 SpaceId 和 BotId
|
||||
spaceId, botId := constants.ParseCardOutTrackId(data.OutTrackId)
|
||||
|
||||
// 获取新群聊人员
|
||||
var userIds []string
|
||||
userIds, err = d.buildNewGroupUserIds(spaceId, botId, data.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建群聊及群初始化(异步响应)
|
||||
if data.CardActionData.CardPrivateData.Params["status"] == "confirm" {
|
||||
go func() {
|
||||
err := d.createIssueHandlingGroupAndInit(ctx, data.CardActionData.CardPrivateData.Params, spaceId, botId, userIds)
|
||||
if err != nil {
|
||||
log.Errorf("创建群聊及群初始化失败: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// 构建关闭创建群组卡片按钮的响应
|
||||
return d.buildCreateGroupCardResp(), nil
|
||||
}
|
||||
|
||||
// buildNewGroupUserIds 构建新群聊人员列表
|
||||
func (d *DingTalkBotBiz) buildNewGroupUserIds(spaceId, botId, groupOwner string) ([]string, error) {
|
||||
// 群id+机器人id确认一个群配置
|
||||
botGroup, err := d.botGroupImpl.GetByConversationIdAndRobotCode(spaceId, botId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取群配置
|
||||
var groupConfig model.AiBotGroupConfig
|
||||
cond := builder.NewCond().And(builder.Eq{"config_id": botGroup.ConfigID})
|
||||
err = d.botGroupConfigImpl.GetOneBySearchToStrut(&cond, &groupConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取处理人列表
|
||||
issueOwnerJson := groupConfig.IssueOwner
|
||||
type issueOwnerType struct {
|
||||
UserId string `json:"userid"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
var issueOwner []issueOwnerType
|
||||
if err = json.Unmarshal([]byte(issueOwnerJson), &issueOwner); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 合并所有userid
|
||||
userIds := []string{groupOwner} // 当前用户为群主
|
||||
for _, owner := range issueOwner {
|
||||
userIds = append(userIds, owner.UserId)
|
||||
}
|
||||
|
||||
return userIds, nil
|
||||
}
|
||||
|
||||
// createIssueHandlingGroupAndInit 创建问题处理群聊及群初始化
|
||||
func (d *DingTalkBotBiz) createIssueHandlingGroupAndInit(ctx context.Context, callbackParams map[string]any, spaceId, botId string, userIds []string) error {
|
||||
// 获取应用配置
|
||||
appKey, err := d.botConfigImpl.GetRobotAppKey(botId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取 access_token
|
||||
accessToken, err := d.dingtalkOauth2Client.GetAccessToken(appKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appKey.AccessToken = accessToken
|
||||
|
||||
// 创建群聊
|
||||
_, openConversationId, err := d.createIssueHandlingGroup(ctx, accessToken, userIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 添加当前机器人到新群 - SDK 有问题,后续再考虑使用
|
||||
// _, err = d.dingtalkImClient.AddRobotToConversation(
|
||||
// appKey,
|
||||
// &im_1_0.AddRobotToConversationRequest{
|
||||
// OpenConversationId: tea.String(openConversationId),
|
||||
// RobotCode: tea.String(botId),
|
||||
// })
|
||||
// if err != nil {
|
||||
// fmt.Printf("添加机器人到会话失败: %v", err)
|
||||
// }
|
||||
|
||||
// 返回新群分享链接,直接进群 - SDK 有问题,后续再考虑使用
|
||||
// newGroupShareLink, err = d.dingTalkOld.GetJoinGroupQrcode(ctx, chatId, data.UserId)
|
||||
// if err != nil {
|
||||
// fmt.Printf("获取入群二维码失败: %v", err)
|
||||
// }
|
||||
|
||||
// 初始化群聊
|
||||
groupScope := callbackParams["group_scope"].(string) // 群主题
|
||||
d.initIssueHandlingGroup(appKey, openConversationId, groupScope)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createIssueHandlingGroup 创建问题处理群聊会话
|
||||
func (d *DingTalkBotBiz) createIssueHandlingGroup(ctx context.Context, accessToken string, userIds []string) (chatId, openConversationId string, err error) {
|
||||
// 是否使用模板群开关
|
||||
var useTemplateGroup bool = true
|
||||
|
||||
// 创建内部群会话
|
||||
if !useTemplateGroup {
|
||||
return d.dingTalkOld.CreateInternalGroupConversation(ctx, accessToken, "问题处理群", userIds)
|
||||
}
|
||||
|
||||
// 根据群模板ID创建群
|
||||
if useTemplateGroup {
|
||||
return d.dingTalkOld.CreateSceneGroupConversation(ctx, accessToken, "问题处理群", userIds, d.conf.Dingtalk.SceneGroup.GroupTemplateIDIssueHandling)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// initIssueHandlingGroup 初始化问题处理群聊
|
||||
func (d *DingTalkBotBiz) initIssueHandlingGroup(appKey dingtalkPkg.AppKey, openConversationId, groupScope string) error {
|
||||
// 1.开场白
|
||||
outTrackId := constants.BuildCardOutTrackId(openConversationId, d.conf.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling)
|
||||
_, err := d.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(d.conf.Dingtalk.Card.Template.BaseMsg),
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CallbackType: tea.String("HTTP"),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("当前会话主题"),
|
||||
"markdown": tea.String("问题:" + groupScope),
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + openConversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(d.conf.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling),
|
||||
AtUserIds: map[string]*string{
|
||||
"@ALL": tea.String("@ALL"),
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 机器人能力
|
||||
// 构建卡片 OutTrackId
|
||||
outTrackId = constants.BuildCardOutTrackId(openConversationId, d.conf.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling)
|
||||
_, err = d.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(d.conf.Dingtalk.Card.Template.BaseMsg),
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CallbackType: tea.String("HTTP"),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("当前机器人能力"),
|
||||
"markdown": tea.String("- 聊天内容提取(@机器人 [内容提取]{聊天记录/问答描述}) \n - QA知识收集(卡片信息收集) \n - QA知识问答(@机器人 [知识库查询]{问题描述})"),
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + openConversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(d.conf.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling),
|
||||
AtUserIds: map[string]*string{
|
||||
"@ALL": tea.String("@ALL"),
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildCreateGroupCardResp 构建关闭创建群组卡片按钮
|
||||
func (d *DingTalkBotBiz) buildCreateGroupCardResp() *card.CardResponse {
|
||||
return &card.CardResponse{
|
||||
CardData: &card.CardDataDto{
|
||||
CardParamMap: map[string]string{
|
||||
"button_display": "false",
|
||||
},
|
||||
},
|
||||
CardUpdateOptions: &card.CardUpdateOptions{
|
||||
UpdateCardDataByKey: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -476,7 +476,7 @@ func (g *GroupConfigBiz) GetReportCache(ctx context.Context, day time.Time, tota
|
|||
return nil
|
||||
}
|
||||
|
||||
// handleKnowledgeV2 处理知识库V2版本
|
||||
// handleKnowledge 处理知识库V2版本
|
||||
func (g *GroupConfigBiz) handleKnowledge(ctx context.Context, rec *entitys.Recognize, groupConfig *model.AiBotGroupConfig, callback *chatbot.BotCallbackDataModel) (err error) {
|
||||
// 请求知识库工具
|
||||
knowledgeBase := knowledge_base.New(g.conf.KnowledgeConfig)
|
||||
|
|
|
|||
|
|
@ -6,31 +6,24 @@ import (
|
|||
"ai_scheduler/internal/config"
|
||||
"ai_scheduler/internal/data/constants"
|
||||
errorcode "ai_scheduler/internal/data/error"
|
||||
"ai_scheduler/internal/data/impl"
|
||||
"ai_scheduler/internal/domain/component/callback"
|
||||
"ai_scheduler/internal/domain/tools/common/knowledge_base"
|
||||
"ai_scheduler/internal/entitys"
|
||||
"ai_scheduler/internal/gateway"
|
||||
"ai_scheduler/internal/pkg"
|
||||
"ai_scheduler/internal/pkg/dingtalk"
|
||||
"ai_scheduler/internal/pkg/util"
|
||||
"ai_scheduler/internal/pkg/utils_ollama"
|
||||
"ai_scheduler/internal/tool_callback"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/card"
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
|
||||
"github.com/alibabacloud-go/dingtalk/card_1_0"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"github.com/ollama/ollama/api"
|
||||
)
|
||||
|
||||
// CallbackService 统一回调入口
|
||||
|
|
@ -43,8 +36,9 @@ type CallbackService struct {
|
|||
dingtalkCardClient *dingtalk.CardClient
|
||||
callbackManager callback.Manager
|
||||
dingTalkBotBiz *biz.DingTalkBotBiz
|
||||
ollamaClient *utils_ollama.Client
|
||||
botConfigImpl *impl.BotConfigImpl
|
||||
callbackBiz *biz.CallbackBiz
|
||||
// ollamaClient *utils_ollama.Client
|
||||
// botConfigImpl *impl.BotConfigImpl
|
||||
}
|
||||
|
||||
func NewCallbackService(
|
||||
|
|
@ -56,8 +50,9 @@ func NewCallbackService(
|
|||
dingtalkCardClient *dingtalk.CardClient,
|
||||
callbackManager callback.Manager,
|
||||
dingTalkBotBiz *biz.DingTalkBotBiz,
|
||||
ollamaClient *utils_ollama.Client,
|
||||
botConfigImpl *impl.BotConfigImpl,
|
||||
callbackBiz *biz.CallbackBiz,
|
||||
// ollamaClient *utils_ollama.Client,
|
||||
// botConfigImpl *impl.BotConfigImpl,
|
||||
) *CallbackService {
|
||||
return &CallbackService{
|
||||
cfg: cfg,
|
||||
|
|
@ -68,8 +63,9 @@ func NewCallbackService(
|
|||
dingtalkCardClient: dingtalkCardClient,
|
||||
callbackManager: callbackManager,
|
||||
dingTalkBotBiz: dingTalkBotBiz,
|
||||
ollamaClient: ollamaClient,
|
||||
botConfigImpl: botConfigImpl,
|
||||
callbackBiz: callbackBiz,
|
||||
// ollamaClient: ollamaClient,
|
||||
// botConfigImpl: botConfigImpl,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -411,9 +407,10 @@ func (s *CallbackService) CallbackDingtalkRobot(c *fiber.Ctx) (err error) {
|
|||
switch data.RobotCode {
|
||||
case s.cfg.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling:
|
||||
// 问题处理群机器人
|
||||
err := s.issueHandling(c, data)
|
||||
// err := s.issueHandling(data)
|
||||
err := s.callbackBiz.IssueHandlingGroup(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("issueHandling failed: %v", err)
|
||||
return fmt.Errorf("IssueHandlingGroup failed: %v", err)
|
||||
}
|
||||
default:
|
||||
// 其他机器人
|
||||
|
|
@ -423,190 +420,6 @@ func (s *CallbackService) CallbackDingtalkRobot(c *fiber.Ctx) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// issueHandling 问题处理群机器人回调
|
||||
// 能力1: 通过[内容提取] 宏,分析用户QA问题,调出QA表单卡片
|
||||
// 能力2: 通过[QA收集] 宏,收集用户反馈,写入知识库
|
||||
// 能力3: 通过[知识库查询] 宏,查询知识库,返回答案
|
||||
func (s *CallbackService) issueHandling(c *fiber.Ctx, data chatbot.BotCallbackDataModel) error {
|
||||
// 能力1、2:分析用户QA问题,写入知识库
|
||||
if strings.Contains(data.Text.Content, "[内容提取]") || strings.Contains(data.Text.Content, "[QA收集]") {
|
||||
s.issueHandlingExtractContent(data)
|
||||
}
|
||||
// 能力3:查询知识库,返回答案
|
||||
if strings.Contains(data.Text.Content, "[知识库查询]") {
|
||||
s.issueHandlingQueryKnowledgeBase(data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 问题处理群机器人内容提取
|
||||
func (s *CallbackService) issueHandlingExtractContent(data chatbot.BotCallbackDataModel) {
|
||||
// 1.提取用户输入
|
||||
prompt := fmt.Sprintf(constants.IssueHandlingExtractContentPrompt, data.Text.Content)
|
||||
log.Infof("问题提取提示词: %s", prompt)
|
||||
// LLM 提取
|
||||
generateResp, err := s.ollamaClient.Generation(context.Background(), &api.GenerateRequest{
|
||||
Model: s.cfg.Ollama.GenerateModel,
|
||||
Prompt: prompt,
|
||||
Stream: util.AnyToPoint(false),
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("问题提取失败: %v", err)
|
||||
return
|
||||
}
|
||||
// 解析 JSON 响应
|
||||
var resp struct {
|
||||
Items []struct {
|
||||
Question string `json:"question"`
|
||||
Answer string `json:"answer"`
|
||||
Confidence string `json:"confidence"`
|
||||
} `json:"items"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(generateResp.Response), &resp); err != nil {
|
||||
log.Errorf("解析 JSON 响应失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 2.构建文本域内容
|
||||
cardContentTpl := "问题:%s \n答案:%s"
|
||||
var cardContentList []string
|
||||
for _, item := range resp.Items {
|
||||
cardContentList = append(cardContentList, fmt.Sprintf(cardContentTpl, item.Question, item.Answer))
|
||||
}
|
||||
cardContent := strings.Join(cardContentList, "\n\n")
|
||||
|
||||
// 3.获取应用AppKey
|
||||
appKey, err := s.botConfigImpl.GetRobotAppKey(data.RobotCode)
|
||||
if err != nil {
|
||||
log.Errorf("获取应用配置失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 4.创建并投放卡片
|
||||
outTrackId := constants.BuildCardOutTrackId(data.ConversationId, data.RobotCode) // 构建卡片 OutTrackId
|
||||
_, err = s.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(s.cfg.Dingtalk.Card.Template.ContentCollect),
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CallbackType: tea.String("HTTP"),
|
||||
CallbackRouteKey: tea.String(s.cfg.Dingtalk.Card.CallbackRouteKey),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("QA知识收集"),
|
||||
"button_display": tea.String("true"),
|
||||
"QA_details_now": tea.String(cardContent),
|
||||
"textarea_display": tea.String("normal"),
|
||||
"action_id": tea.String("collect_qa"),
|
||||
"tenant_id": tea.String(constants.KnowledgeTenantIdDefault),
|
||||
"_CARD_DEBUG_TOOL_ENTRY": tea.String(s.cfg.Dingtalk.Card.DebugToolEntryShow), // 调试字段
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + data.ConversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(s.cfg.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
// 问题处理群机器人查询知识库
|
||||
func (s *CallbackService) issueHandlingQueryKnowledgeBase(data chatbot.BotCallbackDataModel) {
|
||||
// // 获取应用主机器人
|
||||
// mainRobotCode := data.RobotCode
|
||||
// if robotCode, ok := constants.GroupTemplateRobotIdMap[data.RobotCode]; ok {
|
||||
// mainRobotCode = robotCode
|
||||
// }
|
||||
|
||||
// 获取应用配置
|
||||
appKey, err := s.botConfigImpl.GetRobotAppKey(data.RobotCode)
|
||||
if err != nil {
|
||||
log.Errorf("应用机器人配置不存在: %s, err: %v", data.RobotCode, err)
|
||||
return
|
||||
}
|
||||
// 创建卡片
|
||||
outTrackId := constants.BuildCardOutTrackId(data.ConversationId, data.RobotCode)
|
||||
_, err = s.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(s.cfg.Dingtalk.Card.Template.BaseMsg),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String(data.Text.Content),
|
||||
"markdown": tea.String("知识库检索中..."),
|
||||
},
|
||||
},
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + data.ConversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(data.RobotCode),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// 查询知识库
|
||||
knowledgeBase := knowledge_base.New(s.cfg.KnowledgeConfig)
|
||||
knowledgeResp, err := knowledgeBase.Query(&knowledge_base.QueryRequest{
|
||||
TenantID: constants.KnowledgeTenantIdDefault,
|
||||
Query: data.Text.Content,
|
||||
Mode: constants.KnowledgeModeMix,
|
||||
Stream: false,
|
||||
Think: false,
|
||||
OnlyRAG: true,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("查询知识库失败: %v", err)
|
||||
return
|
||||
}
|
||||
knowledgeRespBytes, err := io.ReadAll(knowledgeResp)
|
||||
if err != nil {
|
||||
log.Errorf("读取知识库响应失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 卡片更新
|
||||
message, isRetrieved, err := knowledge_base.ParseOpenAIHTTPData(string(knowledgeRespBytes))
|
||||
if err != nil {
|
||||
log.Errorf("读取知识库 SSE 数据失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
content := message.Content
|
||||
if !isRetrieved {
|
||||
content = "知识库未检测到匹配信息,请核查知识库数据是否正确。"
|
||||
}
|
||||
|
||||
// 卡片更新
|
||||
_, err = s.dingtalkCardClient.UpdateCard(
|
||||
appKey,
|
||||
&card_1_0.UpdateCardRequest{
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CardData: &card_1_0.UpdateCardRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"markdown": tea.String(content),
|
||||
},
|
||||
},
|
||||
CardUpdateOptions: &card_1_0.UpdateCardRequestCardUpdateOptions{
|
||||
UpdateCardDataByKey: tea.Bool(true),
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("更新卡片失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CallbackDingtalkCard 处理钉钉卡片回调
|
||||
// 钉钉 callbackRouteKey: gateway.dev.cdlsxd.cn-dingtalk-card
|
||||
// 钉钉 apiSecret: aB3dE7fG9hI2jK4L5M6N7O8P9Q0R1S2T
|
||||
|
|
@ -640,7 +453,7 @@ func (s *CallbackService) CallbackDingtalkCard(c *fiber.Ctx) error {
|
|||
switch actionId {
|
||||
case "collect_qa":
|
||||
// 问题处理群机器人 QA 收集
|
||||
resp = s.issueHandlingCollectQA(data)
|
||||
resp = s.callbackBiz.IssueHandlingCollectQA(data)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -648,79 +461,3 @@ func (s *CallbackService) CallbackDingtalkCard(c *fiber.Ctx) error {
|
|||
c.Locals("skip_response_wrap", true)
|
||||
return c.JSON(resp)
|
||||
}
|
||||
|
||||
// 问题处理群机器人 QA 收集
|
||||
func (s *CallbackService) issueHandlingCollectQA(data card.CardRequest) *card.CardResponse {
|
||||
// 确认提交,文本写入知识库
|
||||
if data.CardActionData.CardPrivateData.Params["submit"] == "submit" {
|
||||
content := data.CardActionData.CardPrivateData.Params["QA_details"].(string)
|
||||
tenantID := data.CardActionData.CardPrivateData.Params["tenant_id"].(string)
|
||||
|
||||
// 协程执行耗时操作,防止阻塞
|
||||
util.SafeGo("inject_knowledge_base", func() {
|
||||
knowledgeBase := knowledge_base.New(s.cfg.KnowledgeConfig)
|
||||
err := knowledgeBase.IngestText(&knowledge_base.IngestTextRequest{
|
||||
TenantID: tenantID,
|
||||
Text: content,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("注入知识库失败: %v", err)
|
||||
} else {
|
||||
log.Infof("注入知识库成功: tenantID=%s", tenantID)
|
||||
}
|
||||
|
||||
// 解析当前卡片的 ConversationId 和 robotCode
|
||||
conversationId, robotCode := constants.ParseCardOutTrackId(data.OutTrackId)
|
||||
|
||||
// 获取主应用机器人(这里可能是群模板机器人)
|
||||
// mainRobotId := robotCode
|
||||
// if robotCode, ok := constants.GroupTemplateRobotIdMap[robotCode]; ok {
|
||||
// mainRobotId = robotCode
|
||||
// }
|
||||
|
||||
// 获取应用配置
|
||||
appKey, err := s.botConfigImpl.GetRobotAppKey(robotCode)
|
||||
if err != nil {
|
||||
log.Errorf("获取应用机器人配置失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 发送卡片通知用户注入成功
|
||||
outTrackId := constants.BuildCardOutTrackId(conversationId, robotCode)
|
||||
s.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(s.cfg.Dingtalk.Card.Template.BaseMsg),
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("QA知识收集结果"),
|
||||
"markdown": tea.String("[Get] **成功**"),
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + conversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(robotCode),
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// 取消提交,禁用输入框
|
||||
resp := &card.CardResponse{
|
||||
CardUpdateOptions: &card.CardUpdateOptions{
|
||||
UpdateCardDataByKey: true,
|
||||
},
|
||||
CardData: &card.CardDataDto{
|
||||
CardParamMap: map[string]string{
|
||||
"textarea_display": "disabled",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,57 +4,29 @@ import (
|
|||
"ai_scheduler/internal/biz"
|
||||
"ai_scheduler/internal/config"
|
||||
"ai_scheduler/internal/data/constants"
|
||||
"ai_scheduler/internal/data/impl"
|
||||
"ai_scheduler/internal/data/model"
|
||||
"ai_scheduler/internal/entitys"
|
||||
"ai_scheduler/internal/pkg/dingtalk"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/card"
|
||||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
|
||||
"github.com/alibabacloud-go/dingtalk/card_1_0"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
type DingBotService struct {
|
||||
config *config.Config
|
||||
dingTalkBotBiz *biz.DingTalkBotBiz
|
||||
dingTalkOld *dingtalk.OldClient
|
||||
dingtalkCardClient *dingtalk.CardClient
|
||||
dingtalkImClient *dingtalk.ImClient
|
||||
dingtalkOauth2Client *dingtalk.Oauth2Client
|
||||
botGroupConfigImpl *impl.BotGroupConfigImpl
|
||||
botGroupImpl *impl.BotGroupImpl
|
||||
botConfigImpl *impl.BotConfigImpl
|
||||
config *config.Config
|
||||
dingTalkBotBiz *biz.DingTalkBotBiz
|
||||
}
|
||||
|
||||
func NewDingBotService(
|
||||
config *config.Config,
|
||||
dingTalkBotBiz *biz.DingTalkBotBiz,
|
||||
dingTalkOld *dingtalk.OldClient,
|
||||
dingtalkCardClient *dingtalk.CardClient,
|
||||
dingtalkImClient *dingtalk.ImClient,
|
||||
dingtalkOauth2Client *dingtalk.Oauth2Client,
|
||||
botGroupConfigImpl *impl.BotGroupConfigImpl,
|
||||
botGroupImpl *impl.BotGroupImpl,
|
||||
botConfigImpl *impl.BotConfigImpl,
|
||||
) *DingBotService {
|
||||
return &DingBotService{
|
||||
config: config,
|
||||
dingTalkBotBiz: dingTalkBotBiz,
|
||||
dingTalkOld: dingTalkOld,
|
||||
dingtalkCardClient: dingtalkCardClient,
|
||||
dingtalkImClient: dingtalkImClient,
|
||||
dingtalkOauth2Client: dingtalkOauth2Client,
|
||||
botGroupConfigImpl: botGroupConfigImpl,
|
||||
botGroupImpl: botGroupImpl,
|
||||
botConfigImpl: botConfigImpl,
|
||||
config: config,
|
||||
dingTalkBotBiz: dingTalkBotBiz,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -174,7 +146,7 @@ func (d *DingBotService) runBackgroundTasks(ctx context.Context, data *chatbot.B
|
|||
return nil
|
||||
}
|
||||
|
||||
// **一把梭先搞,后续规范化**
|
||||
// OnCardMessageReceived 处理卡片回调
|
||||
func (d *DingBotService) OnCardMessageReceived(ctx context.Context, data *card.CardRequest) (resp *card.CardResponse, err error) {
|
||||
// 非回调类型暂不接受
|
||||
if data.Type != constants.CardActionCallbackTypeAction {
|
||||
|
|
@ -185,206 +157,14 @@ func (d *DingBotService) OnCardMessageReceived(ctx context.Context, data *card.C
|
|||
for _, actionId := range data.CardActionData.CardPrivateData.ActionIdList {
|
||||
switch actionId {
|
||||
case constants.CardActionTypeCreateGroup:
|
||||
// 解析 OutTrackId 以获取 SpaceId 和 BotId
|
||||
spaceId, botId := constants.ParseCardOutTrackId(data.OutTrackId)
|
||||
|
||||
// 获取新群聊人员
|
||||
var userIds []string
|
||||
userIds, err = d.buildNewGroupUserIds(ctx, spaceId, botId, data.UserId)
|
||||
resp, err = d.dingTalkBotBiz.CreateIssueHandlingGroupAndInit(ctx, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建群聊及群初始化(ws中,直接协程)
|
||||
if data.CardActionData.CardPrivateData.Params["status"] == "confirm" {
|
||||
go func() {
|
||||
err := d.createIssueHandlingGroupAndInit(ctx, data.CardActionData.CardPrivateData.Params, spaceId, botId, userIds)
|
||||
if err != nil {
|
||||
log.Printf("创建群聊及群初始化失败: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// 构建关闭创建群组卡片按钮的响应
|
||||
resp = d.buildCreateGroupCardResp()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return &card.CardResponse{}, nil
|
||||
}
|
||||
|
||||
// buildNewGroupUserIds 构建新群聊人员列表
|
||||
func (d *DingBotService) buildNewGroupUserIds(ctx context.Context, spaceId, botId, groupOwner string) ([]string, error) {
|
||||
// 群id+机器人id确认一个群配置
|
||||
botGroup, err := d.botGroupImpl.GetByConversationIdAndRobotCode(spaceId, botId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取群配置
|
||||
var groupConfig model.AiBotGroupConfig
|
||||
cond := builder.NewCond().And(builder.Eq{"config_id": botGroup.ConfigID})
|
||||
err = d.botGroupConfigImpl.GetOneBySearchToStrut(&cond, &groupConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取处理人列表
|
||||
issueOwnerJson := groupConfig.IssueOwner
|
||||
type issueOwnerType struct {
|
||||
UserId string `json:"userid"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
var issueOwner []issueOwnerType
|
||||
if err = json.Unmarshal([]byte(issueOwnerJson), &issueOwner); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 合并所有userid
|
||||
userIds := []string{groupOwner} // 当前用户为群主
|
||||
for _, owner := range issueOwner {
|
||||
userIds = append(userIds, owner.UserId)
|
||||
}
|
||||
|
||||
return userIds, nil
|
||||
}
|
||||
|
||||
// createIssueHandlingGroupAndInit 创建问题处理群聊及群初始化
|
||||
func (d *DingBotService) createIssueHandlingGroupAndInit(ctx context.Context, callbackParams map[string]any, spaceId, botId string, userIds []string) error {
|
||||
// 获取机器人配置
|
||||
botConfig, err := d.botConfigImpl.GetRobotConfig(botId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
appKey := dingtalk.AppKey{
|
||||
AppKey: botConfig.ClientId,
|
||||
AppSecret: botConfig.ClientSecret,
|
||||
}
|
||||
|
||||
// 获取 access_token
|
||||
accessToken, err := d.dingtalkOauth2Client.GetAccessToken(appKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appKey.AccessToken = accessToken
|
||||
|
||||
// 创建群聊
|
||||
_, openConversationId, err := d.createIssueHandlingGroup(ctx, accessToken, spaceId, botId, userIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 添加当前机器人到新群 - SDK 有问题,后续再考虑使用
|
||||
// _, err = d.dingtalkImClient.AddRobotToConversation(
|
||||
// appKey,
|
||||
// &im_1_0.AddRobotToConversationRequest{
|
||||
// OpenConversationId: tea.String(openConversationId),
|
||||
// RobotCode: tea.String(botId),
|
||||
// })
|
||||
// if err != nil {
|
||||
// fmt.Printf("添加机器人到会话失败: %v", err)
|
||||
// }
|
||||
|
||||
// 返回新群分享链接,直接进群 - SDK 有问题,后续再考虑使用
|
||||
// newGroupShareLink, err = d.dingTalkOld.GetJoinGroupQrcode(ctx, chatId, data.UserId)
|
||||
// if err != nil {
|
||||
// fmt.Printf("获取入群二维码失败: %v", err)
|
||||
// }
|
||||
|
||||
// 初始化群聊
|
||||
// 1.开场白
|
||||
|
||||
// 群主题
|
||||
groupScope := callbackParams["group_scope"].(string)
|
||||
// 构建卡片 OutTrackId
|
||||
outTrackId := constants.BuildCardOutTrackId(openConversationId, d.config.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling)
|
||||
_, err = d.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(d.config.Dingtalk.Card.Template.BaseMsg),
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CallbackType: tea.String("HTTP"),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("当前会话主题"),
|
||||
"markdown": tea.String("问题:" + groupScope),
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + openConversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(d.config.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling),
|
||||
AtUserIds: map[string]*string{
|
||||
"@ALL": tea.String("@ALL"),
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// 2. 机器人能力
|
||||
// 构建卡片 OutTrackId
|
||||
outTrackId = constants.BuildCardOutTrackId(openConversationId, d.config.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling)
|
||||
_, err = d.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(d.config.Dingtalk.Card.Template.BaseMsg),
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CallbackType: tea.String("HTTP"),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("当前机器人能力"),
|
||||
"markdown": tea.String("- 聊天内容提取(@机器人 [内容提取]{聊天记录/问答描述}) \n - QA知识收集(卡片信息收集) \n - QA知识问答(@机器人 [知识库查询]{问题描述})"),
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + openConversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(d.config.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling),
|
||||
AtUserIds: map[string]*string{
|
||||
"@ALL": tea.String("@ALL"),
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createGroupV1 创建普通内部群会话
|
||||
// 这里用的是“统一登录平台”这个应用的接口加入群聊 - 这里用的是“统一登录平台”这个应用的接口
|
||||
func (d *DingBotService) createIssueHandlingGroup(ctx context.Context, accessToken, spaceId, botId string, userIds []string) (chatId, openConversationId string, err error) {
|
||||
// 是否使用模板群开关
|
||||
var useTemplateGroup bool = true
|
||||
|
||||
// 创建内部群会话
|
||||
if !useTemplateGroup {
|
||||
return d.dingTalkOld.CreateInternalGroupConversation(ctx, accessToken, "问题处理群", userIds)
|
||||
}
|
||||
|
||||
// 根据群模板ID创建群
|
||||
if useTemplateGroup {
|
||||
return d.dingTalkOld.CreateSceneGroupConversation(ctx, accessToken, "问题处理群", userIds, d.config.Dingtalk.SceneGroup.GroupTemplateIDIssueHandling)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// buildCreateGroupCardResp 构建关闭创建群组卡片按钮
|
||||
func (d *DingBotService) buildCreateGroupCardResp() *card.CardResponse {
|
||||
return &card.CardResponse{
|
||||
CardData: &card.CardDataDto{
|
||||
CardParamMap: map[string]string{
|
||||
"button_display": "false",
|
||||
},
|
||||
},
|
||||
CardUpdateOptions: &card.CardUpdateOptions{
|
||||
UpdateCardDataByKey: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ func run() {
|
|||
qywxAppBiz := biz.NewQywxAppBiz(configConfig, botGroupQywxImpl, group, other)
|
||||
groupConfigBiz := biz.NewGroupConfigBiz(toolRegis, utils_ossClient, botGroupConfigImpl, botConfigImpl, registry, configConfig, impl.NewReportDailyCacheImpl(db), rdb, manager, cardClient)
|
||||
macro := do.NewMacro(botGroupImpl, impl.NewReportDailyCacheImpl(db))
|
||||
dingTalkBotBiz := biz.NewDingTalkBotBiz(doDo, handle, botConfigImpl, botGroupImpl, user, botChatHisImpl, impl.NewReportDailyCacheImpl(db), manager, configConfig, sendCardClient, groupConfigBiz, macro)
|
||||
dingTalkBotBiz := biz.NewDingTalkBotBiz(doDo, handle, botConfigImpl, botGroupImpl, user, botChatHisImpl, impl.NewReportDailyCacheImpl(db), manager, configConfig, sendCardClient, groupConfigBiz, macro, oauth2Client, oldClient, cardClient)
|
||||
// 初始化钉钉机器人服务
|
||||
cronService = NewCronService(configConfig, dingTalkBotBiz, qywxAppBiz, groupConfigBiz)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue