refactor: optimize DingTalk integration and add new utilities
This commit is contained in:
parent
25b55ec1f8
commit
afb24987c5
|
|
@ -5,6 +5,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/biz"
|
"ai_scheduler/internal/biz"
|
||||||
|
"ai_scheduler/internal/biz/handle/dingtalk"
|
||||||
"ai_scheduler/internal/config"
|
"ai_scheduler/internal/config"
|
||||||
"ai_scheduler/internal/data/impl"
|
"ai_scheduler/internal/data/impl"
|
||||||
"ai_scheduler/internal/pkg"
|
"ai_scheduler/internal/pkg"
|
||||||
|
|
@ -29,6 +30,7 @@ func InitializeApp(*config.Config, log.AllLogger) (*server.Servers, func(), erro
|
||||||
impl.ProviderImpl,
|
impl.ProviderImpl,
|
||||||
utils.ProviderUtils,
|
utils.ProviderUtils,
|
||||||
tools_bot.ProviderSetBotTools,
|
tools_bot.ProviderSetBotTools,
|
||||||
|
dingtalk.ProviderSetDingTalk,
|
||||||
))
|
))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package biz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/biz/do"
|
"ai_scheduler/internal/biz/do"
|
||||||
|
"ai_scheduler/internal/biz/handle/dingtalk"
|
||||||
"ai_scheduler/internal/data/constants"
|
"ai_scheduler/internal/data/constants"
|
||||||
"ai_scheduler/internal/data/impl"
|
"ai_scheduler/internal/data/impl"
|
||||||
"ai_scheduler/internal/data/model"
|
"ai_scheduler/internal/data/model"
|
||||||
|
|
@ -22,6 +23,7 @@ type DingTalkBotBiz struct {
|
||||||
botConfigImpl *impl.BotConfigImpl
|
botConfigImpl *impl.BotConfigImpl
|
||||||
replier *chatbot.ChatbotReplier
|
replier *chatbot.ChatbotReplier
|
||||||
log log.Logger
|
log log.Logger
|
||||||
|
dingTalkUser *dingtalk.User
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDingTalkBotBiz
|
// NewDingTalkBotBiz
|
||||||
|
|
@ -29,12 +31,15 @@ func NewDingTalkBotBiz(
|
||||||
do *do.Do,
|
do *do.Do,
|
||||||
handle *do.Handle,
|
handle *do.Handle,
|
||||||
botConfigImpl *impl.BotConfigImpl,
|
botConfigImpl *impl.BotConfigImpl,
|
||||||
|
dingTalkUser *dingtalk.User,
|
||||||
|
|
||||||
) *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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,8 +70,8 @@ func (d *DingTalkBotBiz) InitRequire(ctx context.Context, data *chatbot.BotCallb
|
||||||
}
|
}
|
||||||
entitys.ResLog(requireData.Ch, "recognize_start", "收到消息,正在处理中,请稍等")
|
entitys.ResLog(requireData.Ch, "recognize_start", "收到消息,正在处理中,请稍等")
|
||||||
|
|
||||||
requireData.Sys, err = d.do.GetSysInfoForDingTalkBot(requireData)
|
requireData.UserInfo, err = d.dingTalkUser.GetUserInfo(ctx, data.SenderStaffId, dingtalk.WithId(1))
|
||||||
requireData.Tasks, err = d.do.GetTasks(requireData.Sys.SysID)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ func (d *Do) GetSysInfo(requireData *entitys.RequireData) (sysInfo model.AiSy, e
|
||||||
|
|
||||||
func (d *Do) GetSysInfoForDingTalkBot(requireData *entitys.RequireDataDingTalkBot) (sysInfo model.AiSy, err error) {
|
func (d *Do) GetSysInfoForDingTalkBot(requireData *entitys.RequireDataDingTalkBot) (sysInfo model.AiSy, err error) {
|
||||||
cond := builder.NewCond()
|
cond := builder.NewCond()
|
||||||
cond = cond.And(builder.Eq{"app_key": requireData.Key})
|
cond = cond.And(builder.Eq{"app_key": requireData.Auth})
|
||||||
cond = cond.And(builder.IsNull{"delete_at"})
|
cond = cond.And(builder.IsNull{"delete_at"})
|
||||||
cond = cond.And(builder.Eq{"status": 1})
|
cond = cond.And(builder.Eq{"status": 1})
|
||||||
err = d.sysImpl.GetOneBySearchToStrut(&cond, &sysInfo)
|
err = d.sysImpl.GetOneBySearchToStrut(&cond, &sysInfo)
|
||||||
|
|
|
||||||
|
|
@ -3,25 +3,33 @@ package dingtalk
|
||||||
import (
|
import (
|
||||||
"ai_scheduler/internal/config"
|
"ai_scheduler/internal/config"
|
||||||
"ai_scheduler/internal/data/constants"
|
"ai_scheduler/internal/data/constants"
|
||||||
|
"ai_scheduler/internal/data/impl"
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/internal/entitys"
|
||||||
"ai_scheduler/internal/pkg/l_request"
|
"ai_scheduler/internal/pkg/l_request"
|
||||||
|
"ai_scheduler/utils"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2/log"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
redis *redis.Client
|
redis *redis.Client
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
|
botConfigImpl *impl.BotConfigImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuth(cfg *config.Config, redis *redis.Client) *Auth {
|
func NewAuth(cfg *config.Config, redis *utils.Rdb, botConfigImpl *impl.BotConfigImpl) *Auth {
|
||||||
return &Auth{
|
return &Auth{
|
||||||
redis: redis,
|
redis: redis.Rdb,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
botConfigImpl: botConfigImpl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,13 +37,16 @@ func (a *Auth) GetAccessToken(ctx context.Context, clientId string, clientSecret
|
||||||
if clientId == "" {
|
if clientId == "" {
|
||||||
return "", errors.New("clientId is empty")
|
return "", errors.New("clientId is empty")
|
||||||
}
|
}
|
||||||
token := a.redis.Get(ctx, a.getKey(clientId)).String()
|
accessToken = a.redis.Get(ctx, a.getKey(clientId)).Val()
|
||||||
if token == "" {
|
if accessToken == "" {
|
||||||
authInfo, _err := a.getNewAccessToken(ctx, clientId, clientSecret)
|
authInfo, _err := a.getNewAccessToken(ctx, clientId, clientSecret)
|
||||||
if _err != nil {
|
if _err != nil {
|
||||||
return "", _err
|
return "", _err
|
||||||
}
|
}
|
||||||
a.redis.SetEx(ctx, a.getKey(clientId), authInfo.AccessToken, time.Duration(authInfo.ExpiresIn-3600)*time.Second)
|
err = a.redis.SetEx(ctx, a.getKey(clientId), authInfo.AccessToken, time.Duration(authInfo.ExpireIn-3600)*time.Second).Err()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
accessToken = authInfo.AccessToken
|
accessToken = authInfo.AccessToken
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -53,10 +64,10 @@ func (a *Auth) getNewAccessToken(ctx context.Context, clientId string, clientSec
|
||||||
|
|
||||||
req := l_request.Request{
|
req := l_request.Request{
|
||||||
Method: http.MethodPost,
|
Method: http.MethodPost,
|
||||||
Url: constants.GetDingTalkRequestUrl(constants.RequestUrlGetAccessToken, nil),
|
Url: "https://api.dingtalk.com/v1.0/oauth2/accessToken",
|
||||||
Data: map[string]string{
|
Json: map[string]interface{}{
|
||||||
"appkey": clientId,
|
"appKey": clientId,
|
||||||
"appsecret": clientSecret,
|
"appSecret": clientSecret,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
res, err := req.Send()
|
res, err := req.Send()
|
||||||
|
|
@ -67,3 +78,36 @@ func (a *Auth) getNewAccessToken(ctx context.Context, clientId string, clientSec
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Auth) GetTokenFromBotOption(ctx context.Context, botOption ...BotOption) (token string, err error) {
|
||||||
|
botInfo := &Bot{}
|
||||||
|
for _, option := range botOption {
|
||||||
|
option(botInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if botInfo.id == 0 && botInfo.botConfig == nil {
|
||||||
|
err = errors.New("botInfo is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if botInfo.botConfig == nil {
|
||||||
|
var botConfigDo model.AiBotConfig
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"bot_id": botInfo.id})
|
||||||
|
err = a.botConfigImpl.GetOneBySearchToStrut(&cond, &botConfigDo)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if botConfigDo.BotID == 0 {
|
||||||
|
return "", errors.New("未找到机器人服务配置")
|
||||||
|
}
|
||||||
|
botInfo.botConfig = &botConfigDo
|
||||||
|
}
|
||||||
|
var botConfig entitys.DingTalkBot
|
||||||
|
err = json.Unmarshal([]byte(botInfo.botConfig.BotConfig), &botConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("初始化“%s”失败:%s", botInfo.botConfig.BotName, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return a.GetAccessToken(ctx, botConfig.ClientId, botConfig.ClientSecret)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,111 @@
|
||||||
package dingtalk
|
package dingtalk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"ai_scheduler/internal/data/constants"
|
||||||
"ai_scheduler/internal/data/impl"
|
"ai_scheduler/internal/data/impl"
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
"ai_scheduler/internal/entitys"
|
"ai_scheduler/internal/entitys"
|
||||||
"database/sql"
|
"ai_scheduler/internal/pkg"
|
||||||
"errors"
|
"ai_scheduler/internal/pkg/l_request"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Dept struct {
|
type Dept struct {
|
||||||
dingUserImpl *impl.BotUserImpl
|
dingDeptImpl *impl.BotDeptImpl
|
||||||
|
auth *Auth
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDept(dingUserImpl *impl.BotUserImpl) *User {
|
func NewDept(dingDeptImpl *impl.BotDeptImpl, auth *Auth) *Dept {
|
||||||
return &User{
|
return &Dept{
|
||||||
dingUserImpl: dingUserImpl,
|
dingDeptImpl: dingDeptImpl,
|
||||||
|
auth: auth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) GetDeptInfo(userId string) (userInfo *entitys.DingTalkUserInfo, err error) {
|
func (d *Dept) GetDeptInfoByDeptIds(ctx context.Context, deptIds []int) (depts []*entitys.Dept, err error) {
|
||||||
if len(userId) == 0 {
|
if len(deptIds) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user, err := u.dingUserImpl.GetByStaffId(userId)
|
deptsInfo := make([]model.AiBotDept, 0)
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"dingtalk_dept_id": deptIds})
|
||||||
|
err = d.dingDeptImpl.GetRangeToMapStruct(&cond, deptsInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, sql.ErrNoRows) {
|
return
|
||||||
return
|
}
|
||||||
|
var existDept = make([]int, len(deptsInfo), 0)
|
||||||
|
for _, dept := range deptsInfo {
|
||||||
|
depts = append(depts, &entitys.Dept{
|
||||||
|
DeptId: int(dept.DeptID),
|
||||||
|
Name: dept.Name,
|
||||||
|
})
|
||||||
|
existDept = append(existDept, int(dept.DeptID))
|
||||||
|
}
|
||||||
|
diff := pkg.Difference(deptIds, existDept)
|
||||||
|
if len(diff) > 0 {
|
||||||
|
deptDo := make([]model.AiBotDept, 0)
|
||||||
|
for _, deptId := range diff {
|
||||||
|
deptInfo, err := d.GetDeptInfoFromDingTalk(ctx, deptId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
depts = append(depts, &entitys.Dept{
|
||||||
|
DeptId: deptInfo.DeptId,
|
||||||
|
Name: deptInfo.Name,
|
||||||
|
})
|
||||||
|
deptDo = append(deptDo, model.AiBotDept{
|
||||||
|
DingtalkDeptID: int32(deptInfo.DeptId),
|
||||||
|
Name: deptInfo.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(deptDo) > 0 {
|
||||||
|
_, err = d.dingDeptImpl.Add(deptDo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//如果没有找到,则新增
|
|
||||||
if user == nil {
|
|
||||||
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Dept) GetDeptInfoFromDingTalk(ctx context.Context, deptId int, botOption ...BotOption) (depts DeptResResult, err error) {
|
||||||
|
if deptId == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token, err := d.auth.GetTokenFromBotOption(ctx, botOption...)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := l_request.Request{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Url: constants.GetDingTalkRequestUrl(constants.RequestUrlGetDeptGet, map[string]string{
|
||||||
|
"access_token": token,
|
||||||
|
}),
|
||||||
|
Json: map[string]interface{}{
|
||||||
|
"dept_id": deptId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res, _err := req.Send()
|
||||||
|
if _err != nil {
|
||||||
|
err = _err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var deptInfo DeptRes
|
||||||
|
|
||||||
|
err = json.Unmarshal(res.Content, &deptInfo)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if deptInfo.Errcode != 0 {
|
||||||
|
fmt.Errorf("钉钉请求报错:%s", deptInfo.Errmsg)
|
||||||
|
}
|
||||||
|
return deptInfo.DeptResResult, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package dingtalk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ProviderSetDingTalk = wire.NewSet(
|
||||||
|
NewUser,
|
||||||
|
NewAuth,
|
||||||
|
NewDept,
|
||||||
|
)
|
||||||
|
|
@ -1,59 +1,78 @@
|
||||||
package dingtalk
|
package dingtalk
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type AuthInfo struct {
|
type AuthInfo struct {
|
||||||
AccessToken string `json:"accessToken"`
|
AccessToken string `json:"accessToken"`
|
||||||
ExpiresIn int64 `json:"expiresIn"`
|
ExpireIn int64 `json:"expireIn"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserInfoRes struct {
|
||||||
|
Errcode int `json:"errcode"`
|
||||||
|
Errmsg string `json:"errmsg"`
|
||||||
|
Result UserInfoResResult `json:"result"`
|
||||||
|
RequestId string `json:"request_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserInfoResResult struct {
|
type UserInfoResResult struct {
|
||||||
Errcode string `json:"errcode"`
|
Active bool `json:"active"`
|
||||||
Result Result `json:"result"`
|
Admin bool `json:"admin"`
|
||||||
Errmsg string `json:"errmsg"`
|
Avatar string `json:"avatar"`
|
||||||
}
|
Boss bool `json:"boss"`
|
||||||
type Result struct {
|
CreateTime time.Time `json:"create_time"`
|
||||||
Extension string `json:"extension"`
|
DeptIdList []int `json:"dept_id_list"`
|
||||||
Unionid string `json:"unionid"`
|
DeptOrderList []struct {
|
||||||
Boss string `json:"boss"`
|
DeptId int `json:"dept_id"`
|
||||||
RoleList struct {
|
Order int64 `json:"order"`
|
||||||
GroupName string `json:"group_name"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Id string `json:"id"`
|
|
||||||
} `json:"role_list"`
|
|
||||||
ExclusiveAccount bool `json:"exclusive_account"`
|
|
||||||
ManagerUserid string `json:"manager_userid"`
|
|
||||||
Admin string `json:"admin"`
|
|
||||||
Remark string `json:"remark"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
HiredDate int `json:"hired_date"`
|
|
||||||
Userid string `json:"userid"`
|
|
||||||
WorkPlace string `json:"work_place"`
|
|
||||||
DeptOrderList struct {
|
|
||||||
DeptId string `json:"dept_id"`
|
|
||||||
Order string `json:"order"`
|
|
||||||
} `json:"dept_order_list"`
|
} `json:"dept_order_list"`
|
||||||
RealAuthed string `json:"real_authed"`
|
ExclusiveAccount bool `json:"exclusive_account"`
|
||||||
DeptIdList string `json:"dept_id_list"`
|
HideMobile bool `json:"hide_mobile"`
|
||||||
JobNumber string `json:"job_number"`
|
HiredDate int64 `json:"hired_date"`
|
||||||
Email string `json:"email"`
|
JobNumber string `json:"job_number"`
|
||||||
LeaderInDept struct {
|
LeaderInDept []struct {
|
||||||
Leader string `json:"leader"`
|
DeptId int `json:"dept_id"`
|
||||||
DeptId string `json:"dept_id"`
|
Leader bool `json:"leader"`
|
||||||
} `json:"leader_in_dept"`
|
} `json:"leader_in_dept"`
|
||||||
Mobile string `json:"mobile"`
|
ManagerUserid string `json:"manager_userid"`
|
||||||
Active string `json:"active"`
|
Name string `json:"name"`
|
||||||
OrgEmail string `json:"org_email"`
|
RealAuthed bool `json:"real_authed"`
|
||||||
Telephone string `json:"telephone"`
|
RoleList []struct {
|
||||||
Avatar string `json:"avatar"`
|
GroupName string `json:"group_name"`
|
||||||
HideMobile string `json:"hide_mobile"`
|
Id int `json:"id"`
|
||||||
Senior string `json:"senior"`
|
Name string `json:"name"`
|
||||||
Name string `json:"name"`
|
} `json:"role_list"`
|
||||||
UnionEmpExt struct {
|
Senior bool `json:"senior"`
|
||||||
UnionEmpMapList struct {
|
Title string `json:"title"`
|
||||||
Userid string `json:"userid"`
|
Unionid string `json:"unionid"`
|
||||||
CorpId string `json:"corp_id"`
|
Userid string `json:"userid"`
|
||||||
} `json:"union_emp_map_list"`
|
}
|
||||||
Userid string `json:"userid"`
|
|
||||||
CorpId string `json:"corp_id"`
|
type DeptRes struct {
|
||||||
} `json:"union_emp_ext"`
|
Errcode int `json:"errcode"`
|
||||||
StateCode string `json:"state_code"`
|
Errmsg string `json:"errmsg"`
|
||||||
|
DeptResResult DeptResResult `json:"result"`
|
||||||
|
RequestId string `json:"request_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeptResResult struct {
|
||||||
|
DeptPermits []int `json:"dept_permits"`
|
||||||
|
OuterPermitUsers []string `json:"outer_permit_users"`
|
||||||
|
DeptManagerUseridList []string `json:"dept_manager_userid_list"`
|
||||||
|
OrgDeptOwner string `json:"org_dept_owner"`
|
||||||
|
OuterDept bool `json:"outer_dept"`
|
||||||
|
DeptGroupChatId string `json:"dept_group_chat_id"`
|
||||||
|
GroupContainSubDept bool `json:"group_contain_sub_dept"`
|
||||||
|
AutoAddUser bool `json:"auto_add_user"`
|
||||||
|
HideDept bool `json:"hide_dept"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
OuterPermitDepts []int `json:"outer_permit_depts"`
|
||||||
|
UserPermits []interface{} `json:"user_permits"`
|
||||||
|
DeptId int `json:"dept_id"`
|
||||||
|
CreateDeptGroup bool `json:"create_dept_group"`
|
||||||
|
Order int `json:"order"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
UnionDeptExt struct {
|
||||||
|
CorpId string `json:"corp_id"`
|
||||||
|
DeptId int `json:"dept_id"`
|
||||||
|
} `json:"union_dept_ext"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,29 +15,24 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2/log"
|
|
||||||
"xorm.io/builder"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
dingUserImpl *impl.BotUserImpl
|
dingUserImpl *impl.BotUserImpl
|
||||||
botConfigImpl *impl.BotConfigImpl
|
botConfigImpl *impl.BotConfigImpl
|
||||||
auth *Auth
|
auth *Auth
|
||||||
logger log.Logger
|
dept *Dept
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUser(
|
func NewUser(
|
||||||
dingUserImpl *impl.BotUserImpl,
|
dingUserImpl *impl.BotUserImpl,
|
||||||
botConfig *impl.BotConfigImpl,
|
|
||||||
auth *Auth,
|
auth *Auth,
|
||||||
logger log.Logger,
|
dept *Dept,
|
||||||
) *User {
|
) *User {
|
||||||
return &User{
|
return &User{
|
||||||
dingUserImpl: dingUserImpl,
|
dingUserImpl: dingUserImpl,
|
||||||
botConfigImpl: botConfig,
|
auth: auth,
|
||||||
logger: logger,
|
dept: dept,
|
||||||
auth: auth,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,63 +48,58 @@ func (u *User) GetUserInfo(ctx context.Context, staffId string, botOption ...Bot
|
||||||
}
|
}
|
||||||
//如果没有找到,则新增
|
//如果没有找到,则新增
|
||||||
if user == nil {
|
if user == nil {
|
||||||
DingUserInfo, _err := u.GetUserInfoFromDingTalkWithBot(ctx, staffId, botOption...)
|
DingUserInfo, _err := u.getUserInfoFromDingTalkWithBot(ctx, staffId, botOption...)
|
||||||
if _err != nil {
|
if _err != nil {
|
||||||
return nil, _err
|
return nil, _err
|
||||||
}
|
}
|
||||||
dingUserDo := &model.AiBotUser{
|
user = &model.AiBotUser{
|
||||||
StaffID: DingUserInfo.Userid,
|
StaffID: DingUserInfo.Userid,
|
||||||
Name: DingUserInfo.Name,
|
Name: DingUserInfo.Name,
|
||||||
Title: DingUserInfo.Title,
|
Title: DingUserInfo.Title,
|
||||||
Extension: DingUserInfo.Extension,
|
//Extension: DingUserInfo.Extension,
|
||||||
DeptIDList: DingUserInfo.DeptIdList,
|
DeptIDList: strings.Join(pkg.SliceIntToString(DingUserInfo.DeptIdList), ","),
|
||||||
IsBoss: int32(pkg.Ter(DingUserInfo.Boss == "true", constants.IsBossTrue, constants.IsBossFalse)),
|
IsBoss: int32(pkg.Ter(DingUserInfo.Boss, constants.IsBossTrue, constants.IsBossFalse)),
|
||||||
IsSenior: int32(pkg.Ter(DingUserInfo.Boss == "true", constants.IsSeniorTrue, constants.IsSeniorFalse)),
|
IsSenior: int32(pkg.Ter(DingUserInfo.Senior, constants.IsSeniorTrue, constants.IsSeniorFalse)),
|
||||||
HiredDate: time.Unix(int64(DingUserInfo.HiredDate), 0),
|
HiredDate: time.Unix(DingUserInfo.HiredDate, 0),
|
||||||
}
|
}
|
||||||
//deptIdList, _err := pkg.StringToSlice(DingUserInfo.DeptIdList)
|
|
||||||
//if err != nil {
|
|
||||||
// return nil, _err
|
|
||||||
//}
|
|
||||||
dingUserDo.DeptIDList = strings.Trim(dingUserDo.DeptIDList, "[]")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) GetUserInfoFromDingTalkWithBot(ctx context.Context, staffId string, botOption ...BotOption) (userInfo Result, err error) {
|
_, err = u.dingUserImpl.Add(user)
|
||||||
botInfo := &Bot{}
|
|
||||||
for _, option := range botOption {
|
|
||||||
option(botInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if botInfo.id == 0 && botInfo.botConfig == nil {
|
|
||||||
err = errors.New("botInfo is nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if botInfo.botConfig == nil {
|
|
||||||
cond := builder.NewCond()
|
|
||||||
cond = cond.And(builder.Eq{"bot_id": botInfo.id})
|
|
||||||
err = u.botConfigImpl.GetOneBySearchToStrut(&cond, botInfo.botConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
var config entitys.DingTalkBot
|
userInfo = &entitys.DingTalkUserInfo{
|
||||||
err = json.Unmarshal([]byte(botInfo.botConfig.BotConfig), &config)
|
UserId: int(user.UserID),
|
||||||
if err != nil {
|
StaffId: user.StaffID,
|
||||||
log.Infof("初始化“%s”失败:%s", botInfo.botConfig.BotName, err.Error())
|
Name: user.Name,
|
||||||
return
|
IsBoss: constants.IsBoss(user.IsBoss),
|
||||||
|
IsSenior: constants.IsSenior(user.IsSenior),
|
||||||
|
HiredDate: user.HiredDate,
|
||||||
|
Extension: user.Extension,
|
||||||
}
|
}
|
||||||
token, err := u.auth.GetAccessToken(ctx, config.ClientId, config.ClientSecret)
|
if len(user.DeptIDList) > 0 {
|
||||||
if err != nil {
|
deptIdList := pkg.SliceStringToInt(strings.Split(user.DeptIDList, ","))
|
||||||
return
|
depts, _err := u.dept.GetDeptInfoByDeptIds(ctx, deptIdList)
|
||||||
|
if _err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, dept := range depts {
|
||||||
|
userInfo.Dept = append(userInfo.Dept, dept)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return u.GetUserInfoFromDingTalk(ctx, token, staffId)
|
return userInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) GetUserInfoFromDingTalk(ctx context.Context, token string, staffId string) (user Result, err error) {
|
func (u *User) getUserInfoFromDingTalkWithBot(ctx context.Context, staffId string, botOption ...BotOption) (userInfo UserInfoResResult, err error) {
|
||||||
|
token, err := u.auth.GetTokenFromBotOption(ctx, botOption...)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return u.getUserInfoFromDingTalk(ctx, token, staffId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) getUserInfoFromDingTalk(ctx context.Context, token string, staffId string) (user UserInfoResResult, err error) {
|
||||||
if token == "" && staffId == "" {
|
if token == "" && staffId == "" {
|
||||||
err = errors.New("获取钉钉用户信息的必要参数不足")
|
err = errors.New("获取钉钉用户信息的必要参数不足")
|
||||||
return
|
return
|
||||||
|
|
@ -128,13 +118,13 @@ func (u *User) GetUserInfoFromDingTalk(ctx context.Context, token string, staffI
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var userInfoResResult UserInfoResResult
|
var userInfoRes UserInfoRes
|
||||||
err = json.Unmarshal(res.Content, &userInfoResResult)
|
err = json.Unmarshal(res.Content, &userInfoRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if userInfoResResult.Errcode != "0" {
|
if userInfoRes.Errcode != 0 {
|
||||||
fmt.Errorf("钉钉请求报错:%s", userInfoResResult.Errmsg)
|
fmt.Errorf("钉钉请求报错:%s", userInfoRes.Errmsg)
|
||||||
}
|
}
|
||||||
return userInfoResResult.Result, err
|
return userInfoRes.Result, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ const DingTalkBseUrl = "https://oapi.dingtalk.com"
|
||||||
type RequestUrl string
|
type RequestUrl string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RequestUrlGetAccessToken RequestUrl = "/v1.0/oauth2/accessToken"
|
RequestUrlGetUserGet RequestUrl = "/topapi/v2/user/get"
|
||||||
RequestUrlGetUserGet RequestUrl = "/topapi/v2/user/get"
|
RequestUrlGetDeptGet RequestUrl = "/topapi/v2/department/get"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetDingTalkRequestUrl(path RequestUrl, query map[string]string) string {
|
func GetDingTalkRequestUrl(path RequestUrl, query map[string]string) string {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package impl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/model"
|
||||||
|
"ai_scheduler/tmpl/dataTemp"
|
||||||
|
"ai_scheduler/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BotDeptImpl struct {
|
||||||
|
dataTemp.DataTemp
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBotDeptImpl(db *utils.Db) *BotDeptImpl {
|
||||||
|
return &BotDeptImpl{
|
||||||
|
DataTemp: *dataTemp.NewDataTemp(db, new(model.AiBotDept)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ func NewBotUserImpl(db *utils.Db) *BotUserImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k BotUserImpl) GetByStaffId(staffId string) (data *model.AiBotUser, err error) {
|
func (k BotUserImpl) GetByStaffId(staffId string) (data *model.AiBotUser, err error) {
|
||||||
data = &model.AiBotUser{}
|
|
||||||
err = k.Db.Model(k.Model).Where("staff_id = ?", staffId).Find(data).Error
|
err = k.Db.Model(k.Model).Where("staff_id = ?", staffId).Find(data).Error
|
||||||
if data == nil {
|
if data == nil {
|
||||||
err = sql.ErrNoRows
|
err = sql.ErrNoRows
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,7 @@ var ProviderImpl = wire.NewSet(
|
||||||
NewTaskImpl,
|
NewTaskImpl,
|
||||||
NewChatHisImpl,
|
NewChatHisImpl,
|
||||||
NewBotConfigImpl,
|
NewBotConfigImpl,
|
||||||
|
NewBotDeptImpl,
|
||||||
|
NewBotUserImpl,
|
||||||
|
NewBotChatHisImpl,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
type RequireDataDingTalkBot struct {
|
type RequireDataDingTalkBot struct {
|
||||||
Histories []model.AiChatHi
|
Histories []model.AiChatHi
|
||||||
SessionInfo model.AiSession
|
UserInfo *DingTalkUserInfo
|
||||||
Tasks []model.AiTask
|
Tasks []model.AiTask
|
||||||
Match *Match
|
Match *Match
|
||||||
Req *chatbot.BotCallbackDataModel
|
Req *chatbot.BotCallbackDataModel
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,22 @@
|
||||||
package entitys
|
package entitys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ai_scheduler/internal/data/constants"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type DingTalkUserInfo struct {
|
type DingTalkUserInfo struct {
|
||||||
UserId string `json:"user_id"`
|
UserId int `json:"user_id"`
|
||||||
StaffId string `json:"staff_id"`
|
StaffId string `json:"staff_id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Dept []Dept `json:"dept"`
|
Dept []*Dept `json:"dept"`
|
||||||
IsBoss int `json:"is_boss"`
|
IsBoss constants.IsBoss `json:"is_boss"`
|
||||||
IsSenior int `json:"is_senior"`
|
IsSenior constants.IsSenior `json:"is_senior"`
|
||||||
HiredDate int `json:"hired_date"`
|
HiredDate time.Time `json:"hired_date"`
|
||||||
Extension string `json:"extension"`
|
Extension string `json:"extension"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Dept struct {
|
type Dept struct {
|
||||||
DeptName string `json:"dept_name"`
|
Name string `json:"name"`
|
||||||
DeptId int `json:"dept_id"`
|
DeptId int `json:"dept_id"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,11 @@ type RecognizeUserContent struct {
|
||||||
type FileData []byte
|
type FileData []byte
|
||||||
|
|
||||||
type RecognizeFile struct {
|
type RecognizeFile struct {
|
||||||
|
File Files
|
||||||
|
FileUrl []string // 文件下载链接
|
||||||
|
}
|
||||||
|
|
||||||
|
type Files struct {
|
||||||
File []FileData // 文件数据(二进制格式)
|
File []FileData // 文件数据(二进制格式)
|
||||||
FileUrl string // 文件下载链接
|
|
||||||
FileType constants.Caller // 文件类型(文件类型,能填最好填,可以跳过一层判断)
|
FileType constants.Caller // 文件类型(文件类型,能填最好填,可以跳过一层判断)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,3 +94,43 @@ func StringToSlice(s string) ([]int, error) {
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Difference 差集
|
||||||
|
func Difference[T comparable](a, b []T) []T {
|
||||||
|
// 创建 b 的映射(T 必须是可比较的类型)
|
||||||
|
bMap := make(map[T]struct{}, len(b))
|
||||||
|
for _, item := range b {
|
||||||
|
bMap[item] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var diff []T // 修正为 []T 而非 []int
|
||||||
|
for _, item := range a {
|
||||||
|
if _, found := bMap[item]; !found {
|
||||||
|
diff = append(diff, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diff
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceStringToInt []string=>[]int
|
||||||
|
func SliceStringToInt(strSlice []string) []int {
|
||||||
|
numSlice := make([]int, len(strSlice), 0)
|
||||||
|
for _, str := range strSlice {
|
||||||
|
num, err := strconv.Atoi(str)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
numSlice = append(numSlice, num)
|
||||||
|
}
|
||||||
|
return numSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceIntToString []int=>[]string
|
||||||
|
func SliceIntToString(slice []int) []string {
|
||||||
|
numSlice := make([]string, len(slice), 0)
|
||||||
|
for _, str := range slice {
|
||||||
|
|
||||||
|
numSlice = append(numSlice, strconv.Itoa(str))
|
||||||
|
}
|
||||||
|
return numSlice
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ type Rdb struct {
|
||||||
|
|
||||||
var rdb *Rdb
|
var rdb *Rdb
|
||||||
|
|
||||||
func NewRdb(c *config.Redis) *Rdb {
|
func NewRdb(c *config.Config) *Rdb {
|
||||||
if rdb == nil {
|
if rdb == nil {
|
||||||
//构建 redis
|
//构建 redis
|
||||||
rdbBuild := buildRdb(c)
|
rdbBuild := buildRdb(&c.Redis)
|
||||||
//退出时清理资源
|
//退出时清理资源
|
||||||
rdb = &Rdb{Rdb: rdbBuild}
|
rdb = &Rdb{Rdb: rdbBuild}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue