From 4c65753f695fa218f09ac35c1410104bb4b08352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B8=AB=E8=AE=B2=E6=A2=B5?= Date: Tue, 21 Jun 2022 20:50:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A3=9E=E4=B9=A6=E7=9A=84?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=83=BD=E5=8A=9B=20(#34)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 4 +- controller/group_controller.go | 8 + controller/user_controller.go | 8 + go.mod | 7 +- go.sum | 11 +- logic/a_logic.go | 1 + logic/feishu_logic.go | 263 +++++++++++++++++++++++++++++ model/request/group_req.go | 4 + model/request/user_req.go | 4 + public/client/dingtalk/dingtalk.go | 1 + public/client/feishu/client.go | 13 ++ public/client/feishu/feishu.go | 79 +++++++++ public/client/wechat/wecom.go | 2 +- public/common/init_mysql_data.go | 18 +- routes/group_routes.go | 1 + routes/user_routes.go | 1 + 16 files changed, 415 insertions(+), 10 deletions(-) create mode 100644 logic/feishu_logic.go create mode 100644 public/client/feishu/client.go create mode 100644 public/client/feishu/feishu.go diff --git a/config/config.go b/config/config.go index 980c790..b23c3e5 100644 --- a/config/config.go +++ b/config/config.go @@ -168,5 +168,7 @@ type WeComConfig struct { } type FeiShuConfig struct { - Flag string `mapstructure:"flag" json:"flag"` + Flag string `mapstructure:"flag" json:"flag"` + AppID string `mapstructure:"app-id" json:"appId"` + AppSecret string `mapstructure:"app-secret" json:"appSecret"` } diff --git a/controller/group_controller.go b/controller/group_controller.go index 6623ab4..53b800e 100644 --- a/controller/group_controller.go +++ b/controller/group_controller.go @@ -96,3 +96,11 @@ func (m *GroupController) SyncWeComDepts(c *gin.Context) { return logic.WeCom.SyncWeComDepts(c, req) }) } + +//同步飞书部门信息 +func (m *GroupController) SyncFeiShuDepts(c *gin.Context) { + req := new(request.SyncFeiShuDeptsReq) + Run(c, req, func() (interface{}, interface{}) { + return logic.FeiShu.SyncFeiShuDepts(c, req) + }) +} diff --git a/controller/user_controller.go b/controller/user_controller.go index 3e56f01..97a2f8a 100644 --- a/controller/user_controller.go +++ b/controller/user_controller.go @@ -80,3 +80,11 @@ func (uc UserController) SyncWeComUsers(c *gin.Context) { return logic.WeCom.SyncWeComUsers(c, req) }) } + +// 同步飞书用户信息 +func (uc UserController) SyncFeiShuUsers(c *gin.Context) { + req := new(request.SyncFeiShuUserReq) + Run(c, req, func() (interface{}, interface{}) { + return logic.FeiShu.SyncFeiShuUsers(c, req) + }) +} diff --git a/go.mod b/go.mod index 75da4f4..c8f57f5 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,10 @@ require ( gorm.io/gorm v1.20.12 ) -require github.com/wenerme/go-wecom v0.0.0-20220617125121-2ee950da3e63 +require ( + github.com/chyroc/lark v0.0.96 + github.com/wenerme/go-wecom v0.0.0-20220617125121-2ee950da3e63 +) require ( github.com/BurntSushi/toml v1.1.0 // indirect @@ -36,7 +39,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/wenerme/go-req v0.0.0-20210907160348-d822e81276bb // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( diff --git a/go.sum b/go.sum index 66932aa..a0cc318 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,10 @@ github.com/casbin/gorm-adapter/v3 v3.1.0/go.mod h1:kaMBsBHluoYwudSbVnism8LhJeVyu github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chyroc/go-ptr v1.6.0 h1:4GwCNrNfk4806eQKbHO2A/N/YOLW6jHIrBPWKfMe6F0= +github.com/chyroc/go-ptr v1.6.0/go.mod h1:FKNjNg3sCLx7VhQGwuml6sITX1mvhKS0Je9uN9tt65Q= +github.com/chyroc/lark v0.0.96 h1:3G977xmpktiIoposLjAEO8VOfrIfqWtzBhX9/Z/JSHc= +github.com/chyroc/lark v0.0.96/go.mod h1:ihmf5fJFY8yneFOORvtJhdYWoNqp1OI3DSrCOlvK9Lw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -493,14 +497,16 @@ github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUs github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= +github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/thoas/go-funk v0.7.0 h1:GmirKrs6j6zJbhJIficOsz2aAI7700KsU/5YrdHRM1Y= @@ -1032,8 +1038,9 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.0.1/go.mod h1:KtqSthtg55lFp3S5kUXqlGaelnWpKitn4k1xZTnoiPw= gorm.io/driver/mysql v1.0.4 h1:TATTzt+kR+IV0+h3iUB3dHUe8omCvQ0rOkmfCsUBohk= gorm.io/driver/mysql v1.0.4/go.mod h1:MEgp8tk2n60cSBCq5iTcPDw3ns8Gs+zOva9EUhkknTs= diff --git a/logic/a_logic.go b/logic/a_logic.go index 543d9d0..e184402 100644 --- a/logic/a_logic.go +++ b/logic/a_logic.go @@ -17,5 +17,6 @@ var ( OperationLog = &OperationLogLogic{} DingTalk = &DingTalkLogic{} WeCom = &WeComLogic{} + FeiShu = &FeiShuLogic{} Base = &BaseLogic{} ) diff --git a/logic/feishu_logic.go b/logic/feishu_logic.go new file mode 100644 index 0000000..edd17af --- /dev/null +++ b/logic/feishu_logic.go @@ -0,0 +1,263 @@ +package logic + +import ( + "fmt" + "strings" + + "github.com/chyroc/lark" + "github.com/eryajf/go-ldap-admin/config" + "github.com/eryajf/go-ldap-admin/model" + "github.com/eryajf/go-ldap-admin/model/request" + "github.com/eryajf/go-ldap-admin/public/client/feishu" + "github.com/mozillazg/go-pinyin" + + "github.com/eryajf/go-ldap-admin/public/tools" + "github.com/eryajf/go-ldap-admin/service/ildap" + "github.com/eryajf/go-ldap-admin/service/isql" + "github.com/gin-gonic/gin" +) + +type FeiShuLogic struct { +} + +//通过飞书获取部门信息 +func (d *FeiShuLogic) SyncFeiShuDepts(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) { + // 1.获取所有部门 + depts, err := feishu.GetAllDepts() + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("获取飞书部门列表失败:%s", err.Error())) + } + // 2.将部门这个数组进行拆分,一组是父ID为根的,一组是父ID不为根的 + var firstDepts []*lark.GetDepartmentListRespItem // 父ID为根的部门 + var otherDepts []*lark.GetDepartmentListRespItem // 父ID不为根的部门 + for _, dept := range depts { + if dept.ParentDepartmentID == "0" { + firstDepts = append(firstDepts, dept) + } else { + otherDepts = append(otherDepts, dept) + } + } + // 3.先写父ID为根的,再写父ID不为根的 + for _, dept := range firstDepts { + err := d.AddDepts(&request.WeComGroupAddReq{ + GroupType: "cn", + GroupName: strings.Join(pinyin.LazyConvert(dept.Name, nil), ""), + Remark: dept.Name, + SourceDeptId: fmt.Sprintf("%s_%s", config.Conf.FeiShu.Flag, dept.OpenDepartmentID), + Source: config.Conf.FeiShu.Flag, + SourceDeptParentId: fmt.Sprintf("%s_%d", config.Conf.FeiShu.Flag, 1), + }) + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("SyncFeiShuDepts添加根部门失败:%s", err.Error())) + } + } + + for _, dept := range otherDepts { + err := d.AddDepts(&request.WeComGroupAddReq{ + GroupType: "cn", + GroupName: strings.Join(pinyin.LazyConvert(dept.Name, nil), ""), + Remark: dept.Name, + SourceDeptId: fmt.Sprintf("%s_%s", config.Conf.FeiShu.Flag, dept.OpenDepartmentID), + Source: config.Conf.FeiShu.Flag, + SourceDeptParentId: fmt.Sprintf("%s_%s", config.Conf.FeiShu.Flag, dept.ParentDepartmentID), + }) + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("SyncFeiShuDepts添加根部门失败:%s", err.Error())) + } + } + return nil, nil +} + +// AddGroup 添加部门数据 +func (d FeiShuLogic) AddDepts(r *request.WeComGroupAddReq) error { + // 判断部门名称是否存在 + parentGroup := new(model.Group) + err := isql.Group.Find(tools.H{"source_dept_id": r.SourceDeptParentId}, parentGroup) + if err != nil { + return tools.NewMySqlError(fmt.Errorf("查询父级部门失败:%s", err.Error())) + } + if !isql.Group.Exist(tools.H{"source_dept_id": r.SourceDeptId}) { + groupTmp := model.Group{ + GroupName: r.GroupName, + Remark: r.Remark, + Creator: "system", + GroupType: "cn", + ParentId: parentGroup.ID, + SourceDeptId: r.SourceDeptId, + Source: r.Source, + SourceDeptParentId: r.SourceDeptParentId, + GroupDN: fmt.Sprintf("cn=%s,%s", r.GroupName, parentGroup.GroupDN), + } + err = CommonAddGroup(&groupTmp) + if err != nil { + return tools.NewOperationError(fmt.Errorf("添加部门失败:%s", err.Error())) + } + } + // todo: 分组存在,但是信息有变更的情况,需要考量,但是这种组织架构的调整,通常是比较复杂的情况,这里并不好与之一一对应同步,暂时不做支持 + return nil +} + +//根据现有数据库同步到的部门信息,开启用户同步 +func (d FeiShuLogic) SyncFeiShuUsers(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) { + // 1.获取飞书用户列表 + users, err := feishu.GetAllUsers() + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("SyncFeiShuUsers获取飞书用户列表失败:%s", err.Error())) + } + // 2.遍历用户,开始写入 + for _, detail := range users { + // 用户名的几种情况 + var userName string + if detail.Email != "" { + userName = strings.Split(detail.Email, "@")[0] + } + if userName == "" && detail.Name != "" { + userName = strings.Join(pinyin.LazyConvert(detail.Name, nil), "") + } + if userName == "" && detail.Mobile != "" { + userName = detail.Mobile + } + if userName == "" && detail.Email != "" { + userName = strings.Split(detail.Email, "@")[0] + } + + // 如果企业内没有工号,则工号用名字占位 + if detail.EmployeeNo == "" { + detail.EmployeeNo = detail.Mobile + } + + //飞书部门ids,转换为内部部门id + var sourceDeptIds []string + for _, deptId := range detail.DepartmentIDs { + sourceDeptIds = append(sourceDeptIds, fmt.Sprintf("%s_%s", config.Conf.FeiShu.Flag, deptId)) + } + groupIds, err := isql.Group.DeptIdsToGroupIds(sourceDeptIds) + if err != nil { + return nil, tools.NewMySqlError(fmt.Errorf("SyncFeiShuUsers获取飞书部门ids转换为内部部门id失败:%s", err.Error())) + } + + // 写入用户 + user := request.WeComUserAddReq{ + Username: userName, + Password: config.Conf.Ldap.UserInitPassword, + Nickname: detail.Name, + GivenName: detail.Name, + Mail: detail.Email, + JobNumber: detail.Name, // 工号暂用名字替代 + Mobile: detail.Mobile[3:], + Avatar: detail.Avatar.AvatarOrigin, + PostalAddress: detail.City, + Position: detail.JobTitle, + Introduction: detail.Name, + Status: 1, + DepartmentId: groupIds, + Source: config.Conf.FeiShu.Flag, + SourceUserId: fmt.Sprintf("%s_%s", config.Conf.FeiShu.Flag, detail.UserID), + SourceUnionId: fmt.Sprintf("%s_%s", config.Conf.FeiShu.Flag, detail.UnionID), + } + // 入库 + err = d.AddUsers(&user) + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("SyncFeiShuUsers写入用户失败:%s", err.Error())) + } + } + + // 3.获取飞书已离职用户id列表 + userIds, err := feishu.GetLeaveUserIds() + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("SyncFeiShuUsers获取飞书离职用户列表失败:%s", err.Error())) + } + // 4.遍历id,开始处理 + for _, uid := range userIds { + user := new(model.User) + err = isql.User.Find(tools.H{"source_union_id": fmt.Sprintf("%s_%s", config.Conf.FeiShu.Flag, uid)}, user) + if err != nil { + return nil, tools.NewMySqlError(fmt.Errorf("在MySQL查询用户失败: " + err.Error())) + } + // 先从ldap删除用户 + err = ildap.User.Delete(user.UserDN) + if err != nil { + return nil, tools.NewLdapError(fmt.Errorf("在LDAP删除用户失败" + err.Error())) + } + // 然后更新MySQL中用户状态 + err = isql.User.ChangeStatus(int(user.ID), 2) + if err != nil { + return nil, tools.NewMySqlError(fmt.Errorf("在MySQL更新用户状态失败: " + err.Error())) + } + } + + return nil, nil +} + +// AddUser 添加用户数据 +func (d FeiShuLogic) AddUsers(r *request.WeComUserAddReq) error { + // 根据 unionid 查询用户,不存在则创建 + if !isql.User.Exist(tools.H{"source_union_id": r.SourceUnionId}) { + // 根据角色id获取角色 + r.RoleIds = []uint{2} // 默认添加为普通用户角色 + roles, err := isql.Role.GetRolesByIds(r.RoleIds) + if err != nil { + return tools.NewValidatorError(fmt.Errorf("根据角色ID获取角色信息失败:%s", err.Error())) + } + + deptIds := tools.SliceToString(r.DepartmentId, ",") + user := model.User{ + Username: r.Username, + Password: r.Password, + Nickname: r.Nickname, + GivenName: r.GivenName, + Mail: r.Mail, + JobNumber: r.JobNumber, + Mobile: r.Mobile, + Avatar: r.Avatar, + PostalAddress: r.PostalAddress, + Departments: r.Departments, + Position: r.Position, + Introduction: r.Introduction, + Status: r.Status, + Creator: "system", + DepartmentId: deptIds, + Roles: roles, + Source: r.Source, + SourceUserId: r.SourceUserId, + SourceUnionId: r.SourceUnionId, + UserDN: fmt.Sprintf("uid=%s,%s", r.Username, config.Conf.Ldap.UserDN), + } + err = CommonAddUser(&user, r.DepartmentId) + if err != nil { + return err + } + } + // todo: 用户如果存在,则暂时跳过,目前用户名取自邮箱等内容,因为这个不确定性,可能会造成一些逻辑上的问题,因为默认情况下,用户名是无法在ldap中更改的,所以暂时跳过,如果用户有这里的需求,可以根据自己的情况固定用户名的字段,也就可以打开如下的注释了 + // else { + // oldData := new(model.User) + // if err := isql.User.Find(tools.H{"source_union_id": r.SourceUnionId}, oldData); err != nil { + // return err + // } + // if r.Username != oldData.Username || r.Mail != oldData.Mail || r.Mobile != oldData.Mobile { + // user := model.User{ + // Model: oldData.Model, + // Username: r.Username, + // Nickname: r.Nickname, + // GivenName: r.GivenName, + // Mail: r.Mail, + // JobNumber: r.JobNumber, + // Mobile: r.Mobile, + // Avatar: r.Avatar, + // PostalAddress: r.PostalAddress, + // Departments: r.Departments, + // Position: r.Position, + // Introduction: r.Introduction, + // Creator: oldData.Creator, + // DepartmentId: tools.SliceToString(r.DepartmentId, ","), + // Source: oldData.Source, + // Roles: oldData.Roles, + // UserDN: oldData.UserDN, + // } + // if err := CommonUpdateUser(oldData, &user, r.DepartmentId); err != nil { + // return err + // } + // } + // } + return nil +} diff --git a/model/request/group_req.go b/model/request/group_req.go index bd740ae..208702e 100644 --- a/model/request/group_req.go +++ b/model/request/group_req.go @@ -102,3 +102,7 @@ type SyncDingTalkDeptsReq struct { // SyncWeComDeptsReq 同步企业微信部门信息 type SyncWeComDeptsReq struct { } + +// SyncFeiShuDeptsReq 同步飞书部门信息 +type SyncFeiShuDeptsReq struct { +} diff --git a/model/request/user_req.go b/model/request/user_req.go index 3e878ce..94b2943 100644 --- a/model/request/user_req.go +++ b/model/request/user_req.go @@ -112,6 +112,10 @@ type SyncDingUserReq struct { type SyncWeComUserReq struct { } +// SyncFeiShuUserReq 同步飞书用户信息 +type SyncFeiShuUserReq struct { +} + // UserListReq 获取用户列表结构体 type UserListReq struct { Username string `json:"username" form:"username"` diff --git a/public/client/dingtalk/dingtalk.go b/public/client/dingtalk/dingtalk.go index e968ae0..9cb033d 100644 --- a/public/client/dingtalk/dingtalk.go +++ b/public/client/dingtalk/dingtalk.go @@ -82,6 +82,7 @@ func GetAllUsers() (result []*DingTalkUser, err error) { return } +// GetLeaveUserIds 获取离职人员ID列表 func GetLeaveUserIds() ([]string, error) { var ids []string ReqParm := struct { diff --git a/public/client/feishu/client.go b/public/client/feishu/client.go new file mode 100644 index 0000000..96d7ee8 --- /dev/null +++ b/public/client/feishu/client.go @@ -0,0 +1,13 @@ +package feishu + +import ( + "github.com/chyroc/lark" + "github.com/eryajf/go-ldap-admin/config" +) + +func InitFeiShuClient() *lark.Lark { + return lark.New(lark.WithAppCredential( + config.Conf.FeiShu.AppID, + config.Conf.FeiShu.AppSecret, + )) +} diff --git a/public/client/feishu/feishu.go b/public/client/feishu/feishu.go new file mode 100644 index 0000000..0ea745c --- /dev/null +++ b/public/client/feishu/feishu.go @@ -0,0 +1,79 @@ +package feishu + +import ( + "context" + + "github.com/chyroc/lark" +) + +// GetAllDepts 获取所有部门 +func GetAllDepts() (depts []*lark.GetDepartmentListRespItem, err error) { + var ( + fetchChild bool = true + pageSize int64 = 50 + ) + + req := lark.GetDepartmentListReq{ + FetchChild: &fetchChild, + PageSize: &pageSize, + DepartmentID: "0"} + + for { + res, _, err := InitFeiShuClient().Contact.GetDepartmentList(context.TODO(), &req) + if err != nil { + return nil, err + } + depts = append(depts, res.Items...) + if !res.HasMore { + break + } + req.PageToken = &res.PageToken + } + return +} + +// GetAllUsers 获取所有员工信息 +func GetAllUsers() (users []*lark.GetUserListRespItem, err error) { + var ( + pageSize int64 = 50 + ) + depts, err := GetAllDepts() + if err != nil { + return nil, err + } + for _, dept := range depts { + req := lark.GetUserListReq{ + PageSize: &pageSize, + PageToken: new(string), + DepartmentID: dept.OpenDepartmentID, + } + for { + res, _, err := InitFeiShuClient().Contact.GetUserList(context.Background(), &req) + if err != nil { + return nil, err + } + users = append(users, res.Items...) + if !res.HasMore { + break + } + req.PageToken = &res.PageToken + } + } + return +} + +// GetLeaveUserIds 获取离职人员ID列表 +func GetLeaveUserIds() ([]string, error) { + var ids []string + users, _, err := InitFeiShuClient().EHR.GetEHREmployeeList(context.TODO(), &lark.GetEHREmployeeListReq{ + Status: []int64{5}, + UserIDType: lark.IDTypePtr(lark.IDTypeUnionID), // 只查询unionID + }) + if err != nil { + return nil, err + } + for _, user := range users.Items { + ids = append(ids, user.UserID) + } + return ids, nil +} diff --git a/public/client/wechat/wecom.go b/public/client/wechat/wecom.go index e81e1ba..8d5096a 100644 --- a/public/client/wechat/wecom.go +++ b/public/client/wechat/wecom.go @@ -17,7 +17,7 @@ func GetAllDepts() ([]wecom.ListDepartmentResponseItem, error) { return depts.Department, nil } -// GetAllDepts 获取所有部门 +// GetAllUsers 获取所有员工信息 func GetAllUsers() ([]wecom.ListUserResponseItem, error) { depts, err := GetAllDepts() if err != nil { diff --git a/public/common/init_mysql_data.go b/public/common/init_mysql_data.go index ae06002..da13a4e 100644 --- a/public/common/init_mysql_data.go +++ b/public/common/init_mysql_data.go @@ -340,6 +340,13 @@ func InitData() { Remark: "从企业微信拉取用户信息", Creator: "系统", }, + { + Method: "POST", + Path: "/user/syncFeiShuUsers", + Category: "user", + Remark: "从飞书拉取用户信息", + Creator: "系统", + }, { Method: "GET", Path: "/group/list", @@ -417,6 +424,13 @@ func InitData() { Remark: "从企业微信拉取部门信息", Creator: "系统", }, + { + Method: "POST", + Path: "/group/syncFeiShuDepts", + Category: "group", + Remark: "从飞书拉取部门信息", + Creator: "系统", + }, { Method: "GET", Path: "/role/list", @@ -585,14 +599,10 @@ func InitData() { "/user/info", "/user/list", "/user/changePwd", - "/user/syncDingTalkUsers", - "/user/syncWeComUsers", "/group/list", "/group/tree", "/group/useringroup", "/group/usernoingroup", - "/group/syncDingTalkDepts", - "/group/syncWeComkDepts", "/role/list", "/role/getmenulist", "/role/getapilist", diff --git a/routes/group_routes.go b/routes/group_routes.go index 16fdb49..d5c4fb6 100644 --- a/routes/group_routes.go +++ b/routes/group_routes.go @@ -28,6 +28,7 @@ func InitGroupRoutes(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) g group.POST("/syncDingTalkDepts", controller.Group.SyncDingTalkDepts) // 同步部门 group.POST("/syncWeComDepts", controller.Group.SyncWeComDepts) // 同步部门 + group.POST("/syncFeiShuDepts", controller.Group.SyncFeiShuDepts) // 同步部门 } return r diff --git a/routes/user_routes.go b/routes/user_routes.go index 8da8723..4f6e735 100644 --- a/routes/user_routes.go +++ b/routes/user_routes.go @@ -26,6 +26,7 @@ func InitUserRoutes(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) gi user.POST("/syncDingTalkUsers", controller.User.SyncDingTalkUsers) // 同步用户 user.POST("/syncWeComUsers", controller.User.SyncWeComUsers) // 同步用户 + user.POST("/syncFeiShuUsers", controller.User.SyncFeiShuUsers) // 同步用户 } return r }