fix: 单聊机器人初步开发完成
This commit is contained in:
parent
3b6471a196
commit
847eb8b5db
|
|
@ -25,11 +25,13 @@ import (
|
|||
"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
|
||||
|
||||
dingtalkPkg "ai_scheduler/internal/pkg/dingtalk"
|
||||
"ai_scheduler/internal/pkg/util"
|
||||
|
||||
"github.com/alibabacloud-go/dingtalk/card_1_0"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
|
|
@ -56,7 +58,7 @@ type DingTalkBotBiz struct {
|
|||
dingtalkOauth2Client *dingtalkPkg.Oauth2Client
|
||||
dingTalkOld *dingtalkPkg.OldClient
|
||||
dingtalkCardClient *dingtalkPkg.CardClient
|
||||
rdb *utils.Rdb
|
||||
redisCli *redis.Client
|
||||
issueImpl *impl.IssueImpl
|
||||
sysImpl *impl.SysImpl
|
||||
}
|
||||
|
|
@ -103,7 +105,7 @@ func NewDingTalkBotBiz(
|
|||
dingtalkOauth2Client: dingtalkOauth2Client,
|
||||
dingTalkOld: dingTalkOld,
|
||||
dingtalkCardClient: dingtalkCardClient,
|
||||
rdb: rdb,
|
||||
redisCli: rdb.Rdb,
|
||||
issueImpl: issueImpl,
|
||||
sysImpl: sysImpl,
|
||||
}
|
||||
|
|
@ -167,12 +169,12 @@ func (d *DingTalkBotBiz) handleSingleChat(ctx context.Context, requireData *enti
|
|||
|
||||
// 2. 检查会话状态 (Redis)
|
||||
statusKey := fmt.Sprintf("ai_bot:session:status:%s", requireData.Req.SenderStaffId)
|
||||
status, _ := d.rdb.Rdb.Get(ctx, statusKey).Result()
|
||||
status, _ := d.redisCli.Get(ctx, statusKey).Result()
|
||||
|
||||
if status == "WAITING_FOR_SYS_CONFIRM" {
|
||||
// 用户回复了系统名称
|
||||
sysName := requireData.Req.Text.Content
|
||||
d.rdb.Rdb.Del(ctx, statusKey)
|
||||
d.redisCli.Del(ctx, statusKey)
|
||||
return d.handleWithSpecificSys(ctx, requireData, sysName)
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +285,7 @@ func (d *DingTalkBotBiz) fallbackToGroupCreation(ctx context.Context, requireDat
|
|||
if sys.SysID == 0 {
|
||||
// 无法明确系统,询问用户
|
||||
statusKey := fmt.Sprintf("ai_bot:session:status:%s", requireData.Req.SenderStaffId)
|
||||
d.rdb.Rdb.Set(ctx, statusKey, "WAITING_FOR_SYS_CONFIRM", time.Hour)
|
||||
d.redisCli.Set(ctx, statusKey, "WAITING_FOR_SYS_CONFIRM", time.Hour)
|
||||
entitys.ResText(requireData.Ch, "", "抱歉,我无法确定您咨询的是哪个系统。请告诉我具体系统名称(如:直连天下系统、货易通系统),以便我为您安排对应的技术支持。")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -360,44 +362,30 @@ func (d *DingTalkBotBiz) fallbackToGroupCreationWithSys(ctx context.Context, req
|
|||
// 合并提问者
|
||||
staffIds = append([]string{requireData.Req.SenderStaffId}, staffIds...)
|
||||
|
||||
// 4. 拉群
|
||||
// 4. 发送确认卡片
|
||||
groupName := fmt.Sprintf("[%s]-%s", classification.IssueTypeName, classification.Summary)
|
||||
return d.createIssueHandlingGroupAndInitSingle(ctx, requireData.Req.RobotCode, groupName, staffIds, classification.Summary)
|
||||
return d.SendGroupCreationConfirmCard(ctx, &SendGroupCreationConfirmCardParams{
|
||||
RobotCode: requireData.Req.RobotCode,
|
||||
ConversationId: requireData.Req.ConversationId,
|
||||
SenderStaffId: requireData.Req.SenderStaffId,
|
||||
UserIds: staffIds,
|
||||
GroupName: groupName,
|
||||
Summary: classification.Summary,
|
||||
})
|
||||
}
|
||||
|
||||
// createDefaultGroup 兜底拉群
|
||||
// createDefaultGroup 兜底发送确认卡片
|
||||
func (d *DingTalkBotBiz) createDefaultGroup(ctx context.Context, requireData *entitys.RequireDataDingTalkBot, reason string) error {
|
||||
userIds := []string{requireData.Req.SenderStaffId, "17415698414368678"}
|
||||
groupName := fmt.Sprintf("[未知]-%s", reason)
|
||||
return d.createIssueHandlingGroupAndInitSingle(ctx, requireData.Req.RobotCode, groupName, userIds, "由于无法识别具体问题类型,已为您拉起默认技术支持群。")
|
||||
}
|
||||
|
||||
// createIssueHandlingGroupAndInitSingle 创建群聊并初始化(单聊专用)
|
||||
func (d *DingTalkBotBiz) createIssueHandlingGroupAndInitSingle(ctx context.Context, robotCode string, groupName string, userIds []string, summary string) error {
|
||||
// 获取机器人配置
|
||||
appKey, err := d.botConfigImpl.GetRobotAppKey(robotCode)
|
||||
if err != nil {
|
||||
log.Errorf("get robot app key failed; err: %v", err)
|
||||
return fmt.Errorf("未找到机器人配置")
|
||||
}
|
||||
|
||||
// 获取 access_token
|
||||
accessToken, err := d.dingtalkOauth2Client.GetAccessToken(appKey)
|
||||
if err != nil {
|
||||
log.Errorf("get access token failed; err: %v", err)
|
||||
return fmt.Errorf("获取 access token 失败")
|
||||
}
|
||||
appKey.AccessToken = accessToken
|
||||
|
||||
// 创建群聊
|
||||
_, openConversationId, err := d.createIssueHandlingGroup(ctx, accessToken, groupName, userIds)
|
||||
if err != nil {
|
||||
log.Errorf("create issue handling group failed; err: %v", err)
|
||||
return fmt.Errorf("创建群聊失败")
|
||||
}
|
||||
|
||||
// 初始化群聊
|
||||
return d.initIssueHandlingGroup(appKey, openConversationId, summary)
|
||||
return d.SendGroupCreationConfirmCard(ctx, &SendGroupCreationConfirmCardParams{
|
||||
RobotCode: requireData.Req.RobotCode,
|
||||
ConversationId: requireData.Req.ConversationId,
|
||||
SenderStaffId: requireData.Req.SenderStaffId,
|
||||
UserIds: userIds,
|
||||
GroupName: groupName,
|
||||
Summary: reason,
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DingTalkBotBiz) handleGroupChat(ctx context.Context, requireData *entitys.RequireDataDingTalkBot) (err error) {
|
||||
|
|
@ -675,27 +663,124 @@ func (d *DingTalkBotBiz) CreateIssueHandlingGroupAndInit(ctx context.Context, da
|
|||
// 解析 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
|
||||
}
|
||||
// 获取操作状态
|
||||
status := data.CardActionData.CardPrivateData.Params["status"]
|
||||
if status == "confirm" {
|
||||
// 获取新群聊人员 (从卡片参数中统一解析)
|
||||
targetUserIdsStr := data.CardActionData.CardPrivateData.Params["target_user_ids"].(string)
|
||||
var userIds []string
|
||||
if targetUserIdsStr != "" {
|
||||
userIds = strings.Split(targetUserIdsStr, ",")
|
||||
}
|
||||
|
||||
// 创建群聊及群初始化(异步响应)
|
||||
if data.CardActionData.CardPrivateData.Params["status"] == "confirm" {
|
||||
go func() {
|
||||
if len(userIds) == 0 {
|
||||
return nil, errors.New("target_user_ids 参数不能为空")
|
||||
}
|
||||
|
||||
// 创建群聊及群初始化(异步响应)
|
||||
util.SafeGo("CreateIssueHandlingGroupAndInit", func() {
|
||||
err := d.createIssueHandlingGroupAndInit(ctx, data.CardActionData.CardPrivateData.Params, spaceId, botId, userIds)
|
||||
if err != nil {
|
||||
log.Errorf("创建群聊及群初始化失败: %v", err)
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
// 构建关闭创建群组卡片按钮的响应
|
||||
return d.buildCreateGroupCardResp(), nil
|
||||
}
|
||||
|
||||
type SendGroupCreationConfirmCardParams struct {
|
||||
RobotCode string
|
||||
ConversationId string
|
||||
SenderStaffId string
|
||||
UserIds []string
|
||||
GroupName string
|
||||
Summary string
|
||||
IsGroupChat bool
|
||||
}
|
||||
|
||||
// SendGroupCreationConfirmCard 发送创建群聊确认卡片
|
||||
func (d *DingTalkBotBiz) SendGroupCreationConfirmCard(ctx context.Context, params *SendGroupCreationConfirmCardParams) error {
|
||||
// 获取人员姓名用于展示
|
||||
var userNames []string
|
||||
for _, uid := range params.UserIds {
|
||||
if uid == params.SenderStaffId {
|
||||
continue
|
||||
}
|
||||
user, err := d.botUserImpl.GetByStaffId(uid)
|
||||
if err == nil && user != nil {
|
||||
userNames = append(userNames, "@"+user.Name)
|
||||
} else {
|
||||
userNames = append(userNames, "@"+uid)
|
||||
}
|
||||
}
|
||||
issueOwnerStr := strings.Join(userNames, "、")
|
||||
|
||||
// 获取应用配置
|
||||
appKey, err := d.botConfigImpl.GetRobotAppKey(params.RobotCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 构建卡片 OutTrackId
|
||||
outTrackId := constants.BuildCardOutTrackId(params.ConversationId, params.RobotCode)
|
||||
|
||||
// 准备可见人员列表
|
||||
var recipients []*string
|
||||
if params.IsGroupChat {
|
||||
// 群聊:提问者 + 负责人可见
|
||||
for _, uid := range params.UserIds {
|
||||
recipients = append(recipients, tea.String(uid))
|
||||
}
|
||||
// 确保提问者也在可见列表中
|
||||
foundSender := false
|
||||
for _, uid := range params.UserIds {
|
||||
if uid == params.SenderStaffId {
|
||||
foundSender = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundSender {
|
||||
recipients = append(recipients, tea.String(params.SenderStaffId))
|
||||
}
|
||||
} else {
|
||||
// 单聊:仅提问者可见
|
||||
recipients = append(recipients, tea.String(params.SenderStaffId))
|
||||
}
|
||||
|
||||
// 发送钉钉卡片
|
||||
_, err = d.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
&card_1_0.CreateAndDeliverRequest{
|
||||
CardTemplateId: tea.String(d.conf.Dingtalk.Card.Template.CreateGroupApprove),
|
||||
OutTrackId: tea.String(outTrackId),
|
||||
CallbackType: tea.String("STREAM"),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("创建群聊提醒"),
|
||||
"content": tea.String(fmt.Sprintf("**确认创建群聊?**\n\n将邀请以下成员加入群聊:\n\n%s", issueOwnerStr)),
|
||||
"remark": tea.String("注:如若无需,忽略即可"),
|
||||
"button_left": tea.String("创建群聊"),
|
||||
"button_right": tea.String("忽略"),
|
||||
"action_id": tea.String("create_group"),
|
||||
"button_display": tea.String("true"),
|
||||
"group_scope": tea.String(params.Summary),
|
||||
"target_user_ids": tea.String(strings.Join(params.UserIds, ",")),
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
SupportForward: tea.Bool(false),
|
||||
},
|
||||
OpenSpaceId: tea.String("dtv1.card//im_group." + params.ConversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(params.RobotCode),
|
||||
Recipients: recipients,
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// buildNewGroupUserIds 构建新群聊人员列表
|
||||
func (d *DingTalkBotBiz) buildNewGroupUserIds(spaceId, botId, groupOwner string) ([]string, error) {
|
||||
// 群id+机器人id确认一个群配置
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/alibabacloud-go/dingtalk/card_1_0"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/coze-dev/coze-go"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
|
@ -586,12 +587,16 @@ func (g *GroupConfigBiz) shouldCreateIssueHandlingGroup(ctx context.Context, rec
|
|||
}
|
||||
// 合并所有name、Id
|
||||
userNames := make([]string, 0, len(issueOwner))
|
||||
userIds := make([]*string, 0, len(issueOwner))
|
||||
userIds := make([]string, 0, len(issueOwner))
|
||||
for _, owner := range issueOwner {
|
||||
userNames = append(userNames, "@"+owner.Name)
|
||||
userIds = append(userIds, tea.String(owner.UserId))
|
||||
userIds = append(userIds, owner.UserId)
|
||||
}
|
||||
issueOwnerStr := strings.Join(userNames, "、")
|
||||
targetUserIds := append(userIds, callback.SenderStaffId)
|
||||
recipientsUsers := slice.Map(targetUserIds, func(_ int, item string) *string {
|
||||
return tea.String(item)
|
||||
})
|
||||
|
||||
// 获取应用配置
|
||||
appKey, err := g.botConfigImpl.GetRobotAppKey(callback.RobotCode)
|
||||
|
|
@ -601,7 +606,6 @@ func (g *GroupConfigBiz) shouldCreateIssueHandlingGroup(ctx context.Context, rec
|
|||
|
||||
// 构建卡片 OutTrackId
|
||||
outTrackId := constants.BuildCardOutTrackId(callback.ConversationId, callback.RobotCode)
|
||||
|
||||
// 发送钉钉卡片
|
||||
_, err = g.dingtalkCardClient.CreateAndDeliver(
|
||||
appKey,
|
||||
|
|
@ -611,15 +615,15 @@ func (g *GroupConfigBiz) shouldCreateIssueHandlingGroup(ctx context.Context, rec
|
|||
CallbackType: tea.String("STREAM"),
|
||||
CardData: &card_1_0.CreateAndDeliverRequestCardData{
|
||||
CardParamMap: map[string]*string{
|
||||
"title": tea.String("创建群聊提醒"),
|
||||
"content": tea.String(fmt.Sprintf("**确认创建群聊?**\n\n将邀请以下成员加入群聊:\n\n%s", issueOwnerStr)),
|
||||
"remark": tea.String("注:如若无需,忽略即可"),
|
||||
"button_left": tea.String("创建群聊"),
|
||||
"button_right": tea.String("忽略"),
|
||||
"action_id": tea.String("create_group"),
|
||||
"button_display": tea.String("true"),
|
||||
"group_scope": tea.String(strings.TrimSpace(rec.UserContent.Text)),
|
||||
// "_CARD_DEBUG_TOOL_ENTRY": tea.String(g.conf.Dingtalk.Card.DebugToolEntryShow), // 调试字段
|
||||
"title": tea.String("创建群聊提醒"),
|
||||
"content": tea.String(fmt.Sprintf("**确认创建群聊?**\n\n将邀请以下成员加入群聊:\n\n%s", issueOwnerStr)),
|
||||
"remark": tea.String("注:如若无需,忽略即可"),
|
||||
"button_left": tea.String("创建群聊"),
|
||||
"button_right": tea.String("忽略"),
|
||||
"action_id": tea.String("create_group"),
|
||||
"button_display": tea.String("true"),
|
||||
"group_scope": tea.String(strings.TrimSpace(rec.UserContent.Text)),
|
||||
"target_user_ids": tea.String(strings.Join(targetUserIds, ",")),
|
||||
},
|
||||
},
|
||||
ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
|
||||
|
|
@ -628,7 +632,7 @@ func (g *GroupConfigBiz) shouldCreateIssueHandlingGroup(ctx context.Context, rec
|
|||
OpenSpaceId: tea.String("dtv1.card//im_group." + callback.ConversationId),
|
||||
ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
|
||||
RobotCode: tea.String(callback.RobotCode),
|
||||
Recipients: append(userIds, tea.String(callback.SenderStaffId)),
|
||||
Recipients: recipientsUsers,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -126,7 +126,8 @@ 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, botGroupConfigImpl, user, botChatHisImpl, impl.NewReportDailyCacheImpl(db), manager, configConfig, sendCardClient, groupConfigBiz, macro, oauth2Client, oldClient, cardClient)
|
||||
issueImpl := impl.NewIssueImpl(db)
|
||||
dingTalkBotBiz := biz.NewDingTalkBotBiz(doDo, handle, botConfigImpl, botGroupImpl, botGroupConfigImpl, user, botChatHisImpl, botUserImpl, impl.NewReportDailyCacheImpl(db), manager, configConfig, sendCardClient, groupConfigBiz, macro, oauth2Client, oldClient, cardClient, rdb, issueImpl, sysImpl)
|
||||
// 初始化钉钉机器人服务
|
||||
cronService = NewCronService(configConfig, dingTalkBotBiz, qywxAppBiz, groupConfigBiz)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue