diff --git a/internal/biz/do/handle.go b/internal/biz/do/handle.go index afc4b2b..3dbdb29 100644 --- a/internal/biz/do/handle.go +++ b/internal/biz/do/handle.go @@ -309,7 +309,7 @@ func (r *Handle) getUserDingtalkUnionId(ctx context.Context, accessToken, sessio creatorName := session.UserName // 获取创建者uid 用户名 -> dingtalk uid - creatorId, err := r.dingtalkContactClient.SearchUserOne(accessToken, creatorName) + creatorId, err := r.dingtalkContactClient.SearchUserOne(dingtalk.AppKey{AccessToken: accessToken}, creatorName) if err != nil { log.Warnf("search dingtalk user one failed: %v", err) return diff --git a/internal/biz/group_config.go b/internal/biz/group_config.go index 8fb3953..0d4739a 100644 --- a/internal/biz/group_config.go +++ b/internal/biz/group_config.go @@ -265,7 +265,7 @@ func (g *GroupConfigBiz) handleMatch(ctx context.Context, rec *entitys.Recognize case constants.TaskTypeCozeWorkflow: return g.handleCozeWorkflow(ctx, rec, pointTask) case constants.TaskTypeKnowle: // 知识库V2版本 - return g.handleKnowledgeV2(ctx, rec, pointTask) + return g.handleKnowledgeV2(ctx, rec, groupConfig) // return g.handleKnowledgeV3(ctx, rec, pointTask) default: return g.otherTask(ctx, rec) @@ -473,7 +473,7 @@ func (g *GroupConfigBiz) GetReportCache(ctx context.Context, day time.Time, tota } // handleKnowledgeV2 处理知识库V2版本 -func (g *GroupConfigBiz) handleKnowledgeV2(ctx context.Context, rec *entitys.Recognize, pointTask *model.AiBotTool) (err error) { +func (g *GroupConfigBiz) handleKnowledgeV2(ctx context.Context, rec *entitys.Recognize, groupConfig *model.AiBotGroupConfig) (err error) { req := l_request.Request{ Method: "POST", Url: "http://127.0.0.1:9600/query", @@ -499,12 +499,24 @@ func (g *GroupConfigBiz) handleKnowledgeV2(ctx context.Context, rec *entitys.Rec return } - // var DingtalkGroupCaseOwner = map[string][]string{ - // "cidwP24PLZhLVOS2dVIkEawLw==": {"付仲云", "贺泽琨", "任志远"}, - // } - // 未检索到匹配信息,询问是否拉群 if !isRetrieved { + // 获取群问题处理人 + type issueOwnerType struct { + UserId string `json:"userid"` + Name string `json:"name"` + } + var issueOwner []issueOwnerType + if err = json.Unmarshal([]byte(groupConfig.IssueOwner), &issueOwner); err != nil { + return fmt.Errorf("解析群问题处理人失败,err: %v", err) + } + // 合并所有name + var userNames []string + for _, owner := range issueOwner { + userNames = append(userNames, "@"+owner.Name) + } + issueOwnerStr := strings.Join(userNames, "、") + // 构建卡片 OutTrackId outTrackId := constants.BuildCardOutTrackId("cidwP24PLZhLVOS2dVIkEawLw==", "ding5wwvnf9hxeyjau7t") @@ -519,7 +531,7 @@ func (g *GroupConfigBiz) handleKnowledgeV2(ctx context.Context, rec *entitys.Rec CardData: &card_1_0.CreateAndDeliverRequestCardData{ CardParamMap: map[string]*string{ "title": tea.String("群创建提醒"), - "content": tea.String("**确认创建群聊?**\n\n将邀请以下成员加入群聊:\n\n@张三、@李四"), + "content": tea.String(fmt.Sprintf("**确认创建群聊?**\n\n将邀请以下成员加入群聊:\n\n%s", issueOwnerStr)), "remark": tea.String("注:如若无需,忽略即可"), "button_left": tea.String("点击进群"), "button_left_link": tea.String(""), diff --git a/internal/pkg/dingtalk/card_client.go b/internal/pkg/dingtalk/card_client.go index a013a04..0862496 100644 --- a/internal/pkg/dingtalk/card_client.go +++ b/internal/pkg/dingtalk/card_client.go @@ -1,7 +1,6 @@ package dingtalk import ( - "ai_scheduler/internal/config" errorcode "ai_scheduler/internal/data/error" openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" @@ -11,30 +10,27 @@ import ( ) type CardClient struct { - config *config.Config cli *card.Client oauth2Client *Oauth2Client } -func NewCardClient(config *config.Config, oauth2Client *Oauth2Client) (*CardClient, error) { +func NewCardClient(oauth2Client *Oauth2Client) (*CardClient, error) { cfg := &openapi.Config{ - AccessKeyId: tea.String(config.Tools.DingTalkBot.APIKey), - AccessKeySecret: tea.String(config.Tools.DingTalkBot.APISecret), - Protocol: tea.String("https"), - RegionId: tea.String("central"), + Protocol: tea.String("https"), + RegionId: tea.String("central"), } c, err := card.NewClient(cfg) if err != nil { return nil, err } - return &CardClient{config: config, cli: c, oauth2Client: oauth2Client}, nil + return &CardClient{cli: c, oauth2Client: oauth2Client}, nil } // 创建并投放卡片 -func (c *CardClient) CreateAndDeliver(req AppKey, cardData *card.CreateAndDeliverRequest) (bool, error) { +func (c *CardClient) CreateAndDeliver(appKey AppKey, cardData *card.CreateAndDeliverRequest) (bool, error) { // 获取token - accessToken, err := c.oauth2Client.GetAccessToken(req) + accessToken, err := c.oauth2Client.GetAccessToken(appKey) if err != nil { return false, err } @@ -60,9 +56,9 @@ func (c *CardClient) CreateAndDeliver(req AppKey, cardData *card.CreateAndDelive } // 更新卡片 -func (c *CardClient) UpdateCard(req AppKey, cardData *card.UpdateCardRequest) (bool, error) { +func (c *CardClient) UpdateCard(appKey AppKey, cardData *card.UpdateCardRequest) (bool, error) { // 获取token - accessToken, err := c.oauth2Client.GetAccessToken(req) + accessToken, err := c.oauth2Client.GetAccessToken(appKey) if err != nil { return false, err } diff --git a/internal/pkg/dingtalk/contact_client.go b/internal/pkg/dingtalk/contact_client.go index ed3e3dd..54a9eb4 100644 --- a/internal/pkg/dingtalk/contact_client.go +++ b/internal/pkg/dingtalk/contact_client.go @@ -1,7 +1,6 @@ package dingtalk import ( - "ai_scheduler/internal/config" errorcode "ai_scheduler/internal/data/error" openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" @@ -11,22 +10,20 @@ import ( ) type ContactClient struct { - config *config.Config - cli *contact.Client + cli *contact.Client + oauth2Client *Oauth2Client } -func NewContactClient(config *config.Config) (*ContactClient, error) { +func NewContactClient(oauth2Client *Oauth2Client) (*ContactClient, error) { cfg := &openapi.Config{ - AccessKeyId: tea.String(config.Tools.DingTalkBot.APIKey), - AccessKeySecret: tea.String(config.Tools.DingTalkBot.APISecret), - Protocol: tea.String("https"), - RegionId: tea.String("central"), + Protocol: tea.String("https"), + RegionId: tea.String("central"), } c, err := contact.NewClient(cfg) if err != nil { return nil, err } - return &ContactClient{config: config, cli: c}, nil + return &ContactClient{cli: c, oauth2Client: oauth2Client}, nil } type SearchUserReq struct { @@ -40,15 +37,23 @@ type SearchUserResp struct { Body interface{} } -func (c *ContactClient) SearchUserOne(accessToken string, name string) (string, error) { - headers := &contact.SearchUserHeaders{} - headers.XAcsDingtalkAccessToken = tea.String(accessToken) - resp, err := c.cli.SearchUserWithOptions(&contact.SearchUserRequest{ - FullMatchField: tea.Int32(1), - QueryWord: tea.String(name), - Offset: tea.Int32(0), - Size: tea.Int32(1), - }, headers, &util.RuntimeOptions{}) +func (c *ContactClient) SearchUserOne(appKey AppKey, name string) (string, error) { + // 获取token + accessToken, err := c.oauth2Client.GetAccessToken(appKey) + if err != nil { + return "", err + } + + resp, err := c.cli.SearchUserWithOptions( + &contact.SearchUserRequest{ + FullMatchField: tea.Int32(1), + QueryWord: tea.String(name), + Offset: tea.Int32(0), + Size: tea.Int32(1), + }, + &contact.SearchUserHeaders{XAcsDingtalkAccessToken: tea.String(accessToken)}, + &util.RuntimeOptions{}, + ) if err != nil { return "", err } diff --git a/internal/pkg/dingtalk/im_client.go b/internal/pkg/dingtalk/im_client.go new file mode 100644 index 0000000..b25e812 --- /dev/null +++ b/internal/pkg/dingtalk/im_client.go @@ -0,0 +1,53 @@ +package dingtalk + +import ( + errorcode "ai_scheduler/internal/data/error" + + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + im "github.com/alibabacloud-go/dingtalk/im_1_0" + util "github.com/alibabacloud-go/tea-utils/v2/service" + "github.com/alibabacloud-go/tea/tea" +) + +type ImClient struct { + cli *im.Client + oauth2Client *Oauth2Client +} + +func NewImClient(oauth2Client *Oauth2Client) (*ImClient, error) { + cfg := &openapi.Config{ + Protocol: tea.String("https"), + RegionId: tea.String("central"), + } + c, err := im.NewClient(cfg) + if err != nil { + return nil, err + } + + return &ImClient{cli: c, oauth2Client: oauth2Client}, nil +} + +// 创建并投放卡片 +func (c *ImClient) AddRobotToConversation(appKey AppKey, imData *im.AddRobotToConversationRequest) (string, error) { + // 获取token + accessToken, err := c.oauth2Client.GetAccessToken(appKey) + if err != nil { + return "", err + } + + // 调用API + resp, err := c.cli.AddRobotToConversationWithOptions( + imData, + &im.AddRobotToConversationHeaders{XAcsDingtalkAccessToken: tea.String(accessToken)}, + &util.RuntimeOptions{}, + ) + if err != nil { + return "", err + } + + if resp.Body == nil { + return "", errorcode.ParamErrf("empty response body") + } + + return *resp.Body.ChatBotUserId, nil +} diff --git a/internal/pkg/dingtalk/notable_client.go b/internal/pkg/dingtalk/notable_client.go index 885e111..d4fb6b0 100644 --- a/internal/pkg/dingtalk/notable_client.go +++ b/internal/pkg/dingtalk/notable_client.go @@ -1,7 +1,6 @@ package dingtalk import ( - "ai_scheduler/internal/config" errorcode "ai_scheduler/internal/data/error" "encoding/json" "time" @@ -13,22 +12,20 @@ import ( ) type NotableClient struct { - config *config.Config - cli *notable.Client + cli *notable.Client + oauth2Client *Oauth2Client } -func NewNotableClient(config *config.Config) (*NotableClient, error) { +func NewNotableClient(oauth2Client *Oauth2Client) (*NotableClient, error) { cfg := &openapi.Config{ - AccessKeyId: tea.String(config.Tools.DingTalkBot.APIKey), - AccessKeySecret: tea.String(config.Tools.DingTalkBot.APISecret), - Protocol: tea.String("https"), - RegionId: tea.String("central"), + Protocol: tea.String("https"), + RegionId: tea.String("central"), } c, err := notable.NewClient(cfg) if err != nil { return nil, err } - return &NotableClient{config: config, cli: c}, nil + return &NotableClient{cli: c, oauth2Client: oauth2Client}, nil } type UpdateRecordReq struct { @@ -43,9 +40,13 @@ type UpdateRecordsserResp struct { Body interface{} } -func (c *NotableClient) UpdateRecord(accessToken string, req *UpdateRecordReq) (bool, error) { - headers := ¬able.UpdateRecordsHeaders{} - headers.XAcsDingtalkAccessToken = tea.String(accessToken) +func (c *NotableClient) UpdateRecord(appKey AppKey, req *UpdateRecordReq) (bool, error) { + // 获取token + accessToken, err := c.oauth2Client.GetAccessToken(appKey) + if err != nil { + return false, err + } + resp, err := c.cli.UpdateRecordsWithOptions( tea.String(req.BaseId), tea.String(req.SheetId), @@ -63,7 +64,10 @@ func (c *NotableClient) UpdateRecord(accessToken string, req *UpdateRecordReq) ( Id: tea.String(req.RecordId), }, }, - }, headers, &util.RuntimeOptions{}) + }, + ¬able.UpdateRecordsHeaders{XAcsDingtalkAccessToken: tea.String(accessToken)}, + &util.RuntimeOptions{}, + ) if err != nil { return false, err } diff --git a/internal/pkg/dingtalk/oauth2_client.go b/internal/pkg/dingtalk/oauth2_client.go index 859db0c..2b1884c 100644 --- a/internal/pkg/dingtalk/oauth2_client.go +++ b/internal/pkg/dingtalk/oauth2_client.go @@ -1,7 +1,6 @@ package dingtalk import ( - "ai_scheduler/internal/config" errorcode "ai_scheduler/internal/data/error" "ai_scheduler/utils" "context" @@ -15,32 +14,35 @@ import ( ) type Oauth2Client struct { - config *config.Config cli *oauth2.Client redisCli *redis.Client } -func NewOauth2Client(config *config.Config, rds *utils.Rdb) (*Oauth2Client, error) { +func NewOauth2Client(rds *utils.Rdb) (*Oauth2Client, error) { cfg := &openapi.Config{ - AccessKeyId: tea.String(config.Tools.DingTalkBot.APIKey), - AccessKeySecret: tea.String(config.Tools.DingTalkBot.APISecret), - Protocol: tea.String("https"), - RegionId: tea.String("central"), + Protocol: tea.String("https"), + RegionId: tea.String("central"), } c, err := oauth2.NewClient(cfg) if err != nil { return nil, err } - return &Oauth2Client{config: config, cli: c, redisCli: rds.Rdb}, nil + return &Oauth2Client{cli: c, redisCli: rds.Rdb}, nil } type AppKey struct { - AppKey string `json:"appKey"` - AppSecret string `json:"appSecret"` + AppKey string `json:"appKey"` + AppSecret string `json:"appSecret"` + AccessToken string `json:"accessToken"` } // GetAccessToken 获取access token func (c *Oauth2Client) GetAccessToken(req AppKey) (string, error) { + // 兼容直接传入 access token 场景 + if req.AccessToken != "" { + return req.AccessToken, nil + } + // 取cache ctx := context.Background() accessToken, err := c.redisCli.Get(ctx, fmt.Sprintf("dingtalk:oauth2:%s:access_token", req.AppKey)).Result() diff --git a/internal/pkg/dingtalk/old_client.go b/internal/pkg/dingtalk/old_client.go index 6fb7a49..8d1877c 100644 --- a/internal/pkg/dingtalk/old_client.go +++ b/internal/pkg/dingtalk/old_client.go @@ -113,7 +113,7 @@ func (c *OldClient) GetAccessToken() (string, error) { } // CreateInternalGroupConversation 创建企业内部群聊 -func (c *OldClient) CreateInternalGroupConversation(ctx context.Context, groupName string, userIds []string) (string, error) { +func (c *OldClient) CreateInternalGroupConversation(ctx context.Context, groupName string, userIds []string) (chatId, openConversationId string, err error) { body := struct { Name string `json:"name"` Owner string `json:"owner"` @@ -130,9 +130,10 @@ func (c *OldClient) CreateInternalGroupConversation(ctx context.Context, groupNa UserIds: userIds, } b, _ := json.Marshal(body) - res, err := c.do(ctx, http.MethodPost, "/chat/create", b) + var res []byte + res, err = c.do(ctx, http.MethodPost, "/chat/create", b) if err != nil { - return "", err + return } var resp struct { Code int `json:"errcode"` @@ -141,11 +142,37 @@ func (c *OldClient) CreateInternalGroupConversation(ctx context.Context, groupNa OpenConversationId string `json:"openConversationId"` ConversationTag int `json:"conversationTag"` } + if err = json.Unmarshal(res, &resp); err != nil { + return + } + if resp.Code != 0 { + return "", "", errors.New(resp.Msg) + } + + return resp.ChatId, resp.OpenConversationId, nil +} + +// 获取入群二维码链接 +func (c *OldClient) GetJoinGroupQrcode(ctx context.Context, chatId, userId string) (string, error) { + body := struct { + ChatId string `json:"chatid"` + UserId string `json:"userid"` + }{ChatId: chatId, UserId: userId} + b, _ := json.Marshal(body) + res, err := c.do(ctx, http.MethodPost, "/topapi/chat/qrcode/get", b) + if err != nil { + return "", err + } + var resp struct { + Code int `json:"errcode"` + Msg string `json:"errmsg"` + Result string `json:"result"` + } if err := json.Unmarshal(res, &resp); err != nil { return "", err } if resp.Code != 0 { return "", errors.New(resp.Msg) } - return resp.OpenConversationId, nil + return resp.Result, nil } diff --git a/internal/pkg/dingtalk/robot_client.go b/internal/pkg/dingtalk/robot_client.go index e81f3ac..886b389 100644 --- a/internal/pkg/dingtalk/robot_client.go +++ b/internal/pkg/dingtalk/robot_client.go @@ -1,7 +1,6 @@ package dingtalk import ( - "ai_scheduler/internal/config" errorcode "ai_scheduler/internal/data/error" "encoding/json" @@ -12,22 +11,19 @@ import ( ) type RobotClient struct { - config *config.Config - cli *robot.Client + cli *robot.Client } -func NewRobotClient(config *config.Config) (*RobotClient, error) { +func NewRobotClient() (*RobotClient, error) { cfg := &openapi.Config{ - AccessKeyId: tea.String(config.Tools.DingTalkBot.APIKey), - AccessKeySecret: tea.String(config.Tools.DingTalkBot.APISecret), - Protocol: tea.String("https"), - RegionId: tea.String("central"), + Protocol: tea.String("https"), + RegionId: tea.String("central"), } c, err := robot.NewClient(cfg) if err != nil { return nil, err } - return &RobotClient{config: config, cli: c}, nil + return &RobotClient{cli: c}, nil } type SendGroupMessagesReq struct { @@ -37,17 +33,19 @@ type SendGroupMessagesReq struct { RobotCode string } -func (c *RobotClient) SendGroupMessages(accessToken string, req *SendGroupMessagesReq) (string, error) { - headers := &robot.OrgGroupSendHeaders{} - headers.XAcsDingtalkAccessToken = tea.String(accessToken) +func (c *RobotClient) SendGroupMessages(appKey AppKey, req *SendGroupMessagesReq) (string, error) { msgParamBytes, _ := json.Marshal(req.MsgParam) msgParamJson := string(msgParamBytes) - resp, err := c.cli.OrgGroupSendWithOptions(&robot.OrgGroupSendRequest{ - MsgKey: tea.String(req.MsgKey), - MsgParam: tea.String(msgParamJson), - OpenConversationId: tea.String(req.OpenConversationId), - RobotCode: tea.String(req.RobotCode), - }, headers, &util.RuntimeOptions{}) + resp, err := c.cli.OrgGroupSendWithOptions( + &robot.OrgGroupSendRequest{ + MsgKey: tea.String(req.MsgKey), + MsgParam: tea.String(msgParamJson), + OpenConversationId: tea.String(req.OpenConversationId), + RobotCode: tea.String(req.RobotCode), + }, + &robot.OrgGroupSendHeaders{XAcsDingtalkAccessToken: tea.String(appKey.AccessToken)}, + &util.RuntimeOptions{}, + ) if err != nil { return "", err } diff --git a/internal/pkg/provider_set.go b/internal/pkg/provider_set.go index 76bab52..a49c8bf 100644 --- a/internal/pkg/provider_set.go +++ b/internal/pkg/provider_set.go @@ -24,6 +24,7 @@ var ProviderSetClient = wire.NewSet( // dingtalk.NewRobotClient, dingtalk.NewOauth2Client, dingtalk.NewCardClient, + dingtalk.NewImClient, utils_oss.NewClient, lsxd.NewLogin, diff --git a/internal/services/callback.go b/internal/services/callback.go index 0cbb877..fe46028 100644 --- a/internal/services/callback.go +++ b/internal/services/callback.go @@ -270,7 +270,7 @@ func (s *CallbackService) handleBugOptimizationSubmitUpdate(ctx context.Context, // 获取创建者uid accessToken, _ := s.dingtalkOldClient.GetAccessToken() - creatorId, err := s.dingtalkContactClient.SearchUserOne(accessToken, data.Creator) + creatorId, err := s.dingtalkContactClient.SearchUserOne(dingtalk.AppKey{AccessToken: accessToken}, data.Creator) if err != nil { return "", errorcode.ParamErrf("invalid data type: %v", err) } @@ -286,7 +286,7 @@ func (s *CallbackService) handleBugOptimizationSubmitUpdate(ctx context.Context, unionId := userDetails.UnionID // 更新记录 - ok, err := s.dingtalkNotableClient.UpdateRecord(accessToken, &dingtalk.UpdateRecordReq{ + ok, err := s.dingtalkNotableClient.UpdateRecord(dingtalk.AppKey{AccessToken: accessToken}, &dingtalk.UpdateRecordReq{ BaseId: data.BaseId, SheetId: data.SheetId, RecordId: data.RecordId, diff --git a/internal/services/dtalk_bot.go b/internal/services/dtalk_bot.go index cd59c80..9e46bf5 100644 --- a/internal/services/dtalk_bot.go +++ b/internal/services/dtalk_bot.go @@ -10,12 +10,16 @@ import ( "ai_scheduler/internal/pkg/dingtalk" "context" "encoding/json" + "fmt" "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/dingtalk/im_1_0" + "github.com/alibabacloud-go/tea/tea" "golang.org/x/sync/errgroup" "xorm.io/builder" ) @@ -25,8 +29,10 @@ type DingBotService struct { dingTalkBotBiz *biz.DingTalkBotBiz dingTalkOld *dingtalk.OldClient dingtalkCardClient *dingtalk.CardClient + dingtalkImClient *dingtalk.ImClient botGroupConfigImpl *impl.BotGroupConfigImpl botGroupImpl *impl.BotGroupImpl + botConfigImpl *impl.BotConfigImpl } func NewDingBotService( @@ -34,16 +40,20 @@ func NewDingBotService( dingTalkBotBiz *biz.DingTalkBotBiz, dingTalkOld *dingtalk.OldClient, dingtalkCardClient *dingtalk.CardClient, + dingtalkImClient *dingtalk.ImClient, botGroupConfigImpl *impl.BotGroupConfigImpl, botGroupImpl *impl.BotGroupImpl, + botConfigImpl *impl.BotConfigImpl, ) *DingBotService { return &DingBotService{ config: config, dingTalkBotBiz: dingTalkBotBiz, dingTalkOld: dingTalkOld, dingtalkCardClient: dingtalkCardClient, + dingtalkImClient: dingtalkImClient, botGroupConfigImpl: botGroupConfigImpl, botGroupImpl: botGroupImpl, + botConfigImpl: botConfigImpl, } } @@ -170,51 +180,95 @@ func (d *DingBotService) OnCardMessageReceived(ctx context.Context, data *card.C return nil, nil } + // 卡片同步回调超时时间为2s,2s内同步返回,2s后异步更新卡片 + startTime := time.Now() + // action 处理 - 这里先只处理第一个匹配的actionId for _, actionId := range data.CardActionData.CardPrivateData.ActionIdList { switch actionId { case constants.CardActionTypeCreateGroup: // 解析 OutTrackId 以获取 SpaceId 和 BotId spaceId, botId := constants.ParseCardOutTrackId(data.OutTrackId) - // 群id+机器人id确认一个群配置 - var botGroup *model.AiBotGroup - botGroup, err = d.botGroupImpl.GetByConversationIdAndRobotCode(spaceId, botId) + + // 获取新群聊人员 + var userIds []string + userIds, err = d.buildNewGroupUserIds(ctx, spaceId, botId, data.UserId) 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{data.UserId} // 当前用户为群主 - for _, owner := range issueOwner { - userIds = append(userIds, owner.UserId) - } + + // 新群分享链接 + newGroupShareLink := "" + timeOutLimit := 1500 * time.Millisecond + + // 钉钉appKey + appKey := dingtalk.AppKey{} if data.CardActionData.CardPrivateData.Params["status"] == "confirm" { // 创建群聊 - 这里用的是“统一登录平台”这个应用的接口 // 不是很关心成功失败,ws中,后续考虑协程去创建 - if _, err = d.dingTalkOld.CreateInternalGroupConversation(ctx, "问题处理群", userIds); err != nil { + chatId, openConversationId, err := d.dingTalkOld.CreateInternalGroupConversation(ctx, "问题处理群", userIds) + if err != nil { + fmt.Printf("创建群聊失败: %v", err) + } + + _ = openConversationId + // 获取机器人配置 + var botConfig model.AiBotConfig + cond := builder.NewCond().And(builder.Eq{"robot_code": botId}) + err = d.botConfigImpl.GetOneBySearchToStrut(&cond, &botConfig) + if err != nil { return nil, err } + // 解出 config + var config entitys.DingTalkBot + err = json.Unmarshal([]byte(botConfig.BotConfig), &config) + if err != nil { + log.Printf("配置解析失败 %v", err.Error()) + } + appKey = dingtalk.AppKey{ + AppKey: config.ClientId, + AppSecret: config.ClientSecret, + } + + // 添加当前机器人到新群 + _, err = d.dingtalkImClient.AddRobotToConversation( + appKey, + &im_1_0.AddRobotToConversationRequest{ + OpenConversationId: tea.String(openConversationId), + RobotCode: tea.String(botId), + }) + if err != nil { + fmt.Printf("添加机器人到会话失败: %v", err) + } + + // 返回新群分享链接,直接进群 + newGroupShareLink, err = d.dingTalkOld.GetJoinGroupQrcode(ctx, chatId, data.UserId) + if err != nil { + fmt.Printf("获取入群二维码失败: %v", err) + } + } + + endTime := time.Now() + if endTime.Sub(startTime) > timeOutLimit { + // 异步更新卡片 + d.dingtalkCardClient.UpdateCard(appKey, &card_1_0.UpdateCardRequest{ + OutTrackId: tea.String(data.OutTrackId), + CardData: &card_1_0.UpdateCardRequestCardData{ + CardParamMap: map[string]*string{ + "button_display": tea.String("false"), + "new_group_share_link": tea.String(newGroupShareLink), + }, + }, + CardUpdateOptions: &card_1_0.UpdateCardRequestCardUpdateOptions{ + UpdateCardDataByKey: tea.Bool(true), + }, + }) + return } // 构建关闭创建群组卡片按钮的响应 - resp = d.buildCreateGroupCardResp() + resp = d.buildCreateGroupCardResp(newGroupShareLink) return } } @@ -222,12 +276,49 @@ func (d *DingBotService) OnCardMessageReceived(ctx context.Context, data *card.C return &card.CardResponse{}, nil } -// 关闭创建群组卡片按钮 -func (d *DingBotService) buildCreateGroupCardResp() *card.CardResponse { +// 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 +} + +// buildCreateGroupCardResp 构建关闭创建群组卡片按钮 +func (d *DingBotService) buildCreateGroupCardResp(newGroupShareLink string) *card.CardResponse { return &card.CardResponse{ CardData: &card.CardDataDto{ CardParamMap: map[string]string{ - "button_display": "false", + "button_display": "false", + "new_group_share_link": newGroupShareLink, }, }, CardUpdateOptions: &card.CardUpdateOptions{