refactor: optimize DingTalk integration and add new utilities

This commit is contained in:
renzhiyuan 2025-12-12 10:56:42 +08:00
parent 25b55ec1f8
commit afb24987c5
17 changed files with 369 additions and 153 deletions

View File

@ -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,
)) ))
} }

View File

@ -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
} }

View File

@ -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)

View File

@ -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)
}

View File

@ -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
}

View File

@ -0,0 +1,11 @@
package dingtalk
import (
"github.com/google/wire"
)
var ProviderSetDingTalk = wire.NewSet(
NewUser,
NewAuth,
NewDept,
)

View File

@ -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"`
} }

View File

@ -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
} }

View File

@ -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 {

View File

@ -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)),
}
}

View File

@ -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

View File

@ -10,4 +10,7 @@ var ProviderImpl = wire.NewSet(
NewTaskImpl, NewTaskImpl,
NewChatHisImpl, NewChatHisImpl,
NewBotConfigImpl, NewBotConfigImpl,
NewBotDeptImpl,
NewBotUserImpl,
NewBotChatHisImpl,
) )

View File

@ -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

View File

@ -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"`
} }

View File

@ -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 // 文件类型(文件类型,能填最好填,可以跳过一层判断)
} }

View File

@ -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
}

View File

@ -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}
} }