From f1855d3b61d9564db95efd53d15b0b25e6b98e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B8=AB=E8=AE=B2=E6=A2=B5?= Date: Wed, 15 Jun 2022 23:32:16 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0wecom=E7=9A=84=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E4=BB=A3=E7=A0=81=E5=8A=9F=E8=83=BD,=E7=9B=AE?= =?UTF-8?q?=E5=89=8D=E5=B7=B2=E5=AE=8C=E6=88=90=E9=83=A8=E9=97=A8=E7=9A=84?= =?UTF-8?q?=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.yml | 6 +- config/config.go | 5 +- controller/group_controller.go | 8 ++ controller/user_controller.go | 8 ++ go.mod | 4 +- go.sum | 14 ++- logic/a_logic.go | 1 + logic/wecom_logic.go | 179 +++++++++++++++++++++++++++++++ model/request/group_req.go | 4 + model/request/user_req.go | 6 +- public/client/wechat/client.go | 15 +++ public/client/wechat/wecom.go | 38 +++++++ public/common/init_mysql_data.go | 16 +++ routes/group_routes.go | 4 +- routes/user_routes.go | 16 +-- 15 files changed, 311 insertions(+), 13 deletions(-) create mode 100644 logic/wecom_logic.go create mode 100644 public/client/wechat/client.go create mode 100644 public/client/wechat/wecom.go diff --git a/config.yml b/config.yml index 28d2460..37ed975 100644 --- a/config.yml +++ b/config.yml @@ -107,6 +107,10 @@ dingtalk: agent-id: "12121212" # 目前agent-id未使用到,可忽略 enable-sync: false # 是否开启定时同步钉钉的任务 wecom: - flag: "wecom" + flag: "wecom" # 作为微信在平台的标识 + corp-id: "wwc6656f03c222fd11" # 企业微信企业ID + agent-id: 1000003 # 企业微信中创建的应用ID + corp-secret: "vhgdQfGS7pM46O40oSK8GBFTcDH_N3gkN5ZJJfOMpog" # 企业微信中创建的应用secret + enable-sync: false # 是否开启定时同步企业微信的任务 feishu: flag: "feishu" \ No newline at end of file diff --git a/config/config.go b/config/config.go index 972215c..980c790 100644 --- a/config/config.go +++ b/config/config.go @@ -161,7 +161,10 @@ type DingTalkConfig struct { } type WeComConfig struct { - Flag string `mapstructure:"flag" json:"flag"` + Flag string `mapstructure:"flag" json:"flag"` + CorpID string `mapstructure:"corp-id" json:"corpId"` + AgentID int `mapstructure:"agent-id" json:"agentId"` + CorpSecret string `mapstructure:"corp-secret" json:"corpSecret"` } type FeiShuConfig struct { diff --git a/controller/group_controller.go b/controller/group_controller.go index 3aa8cac..6623ab4 100644 --- a/controller/group_controller.go +++ b/controller/group_controller.go @@ -88,3 +88,11 @@ func (m *GroupController) SyncDingTalkDepts(c *gin.Context) { return logic.DingTalk.SyncDingTalkDepts(c, req) }) } + +//同步企业微信部门信息 +func (m *GroupController) SyncWeComDepts(c *gin.Context) { + req := new(request.SyncWeComDeptsReq) + Run(c, req, func() (interface{}, interface{}) { + return logic.WeCom.SyncWeComDepts(c, req) + }) +} diff --git a/controller/user_controller.go b/controller/user_controller.go index 4a29803..3e56f01 100644 --- a/controller/user_controller.go +++ b/controller/user_controller.go @@ -72,3 +72,11 @@ func (uc UserController) SyncDingTalkUsers(c *gin.Context) { return logic.DingTalk.SyncDingTalkUsers(c, req) }) } + +// 同步企业微信用户信息 +func (uc UserController) SyncWeComUsers(c *gin.Context) { + req := new(request.SyncWeComUserReq) + Run(c, req, func() (interface{}, interface{}) { + return logic.WeCom.SyncWeComUsers(c, req) + }) +} diff --git a/go.mod b/go.mod index f611412..1e5b509 100644 --- a/go.mod +++ b/go.mod @@ -11,12 +11,13 @@ require ( github.com/go-ldap/ldap/v3 v3.4.2 github.com/go-playground/locales v0.14.0 github.com/go-playground/universal-translator v0.18.0 - github.com/go-playground/validator/v10 v10.9.0 + github.com/go-playground/validator/v10 v10.10.0 github.com/juju/ratelimit v1.0.1 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/robfig/cron/v3 v3.0.0 github.com/spf13/viper v1.11.0 github.com/thoas/go-funk v0.7.0 + github.com/wenerme/go-wecom v0.0.0-20220123104505-8a1577192bbb github.com/zhaoyunxing92/dingtalk/v2 v2.0.7-0.20220601083444-173c10c3f835 go.uber.org/zap v1.19.1 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df @@ -33,6 +34,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect 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 ) diff --git a/go.sum b/go.sum index ce93860..e5c9c95 100644 --- a/go.sum +++ b/go.sum @@ -142,6 +142,10 @@ github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= +github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= +github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -160,8 +164,9 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -466,6 +471,7 @@ github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThC github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sbzhu/weworkapi_golang v0.0.0-20210525081115-1799804a7c8d/go.mod h1:gLXVYg36wlOl44Uh8Uw0aDiNMcZNnV+tzZq1FBj+f6A= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= @@ -512,6 +518,10 @@ github.com/ugorji/go v1.2.3/go.mod h1:5l8GZ8hZvmL4uMdy+mhCO1LjswGRYco9Q3HfuisB21 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.3 h1:/mVYEV+Jo3IZKeA5gBngN0AvNnQltEDkR+eQikkWQu0= github.com/ugorji/go/codec v1.2.3/go.mod h1:5FxzDJIgeiWJZslYHPj+LS1dq1ZBQVelZFnjsFGI/Uc= +github.com/wenerme/go-req v0.0.0-20210907160348-d822e81276bb h1:4/6Qqg+E8z98SCi21dFnhL6goSWOYMunJkMc+YanrEw= +github.com/wenerme/go-req v0.0.0-20210907160348-d822e81276bb/go.mod h1:aQUkMiMp1qZkuSsdu2Vy2ZQK33cPNVmyWFzXatfP+Y4= +github.com/wenerme/go-wecom v0.0.0-20220123104505-8a1577192bbb h1:y1pn4W8X44YRAZm6FPvSQYQXuue3s5bsgX2uVvLIk5c= +github.com/wenerme/go-wecom v0.0.0-20220123104505-8a1577192bbb/go.mod h1:Jz7prkOPdCbWbT5bOQOFkZUQp7pqEc8yiWaCgOVWPH0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -564,6 +574,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -747,6 +758,7 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/logic/a_logic.go b/logic/a_logic.go index 4470333..543d9d0 100644 --- a/logic/a_logic.go +++ b/logic/a_logic.go @@ -16,5 +16,6 @@ var ( Menu = &MenuLogic{} OperationLog = &OperationLogLogic{} DingTalk = &DingTalkLogic{} + WeCom = &WeComLogic{} Base = &BaseLogic{} ) diff --git a/logic/wecom_logic.go b/logic/wecom_logic.go new file mode 100644 index 0000000..e828ec4 --- /dev/null +++ b/logic/wecom_logic.go @@ -0,0 +1,179 @@ +package logic + +import ( + "fmt" + "strings" + + "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/wechat" + "github.com/mozillazg/go-pinyin" + "github.com/wenerme/go-wecom/wecom" + + "github.com/eryajf/go-ldap-admin/public/tools" + "github.com/eryajf/go-ldap-admin/service/isql" + "github.com/gin-gonic/gin" +) + +type WeComLogic struct { +} + +//通过钉钉获取部门信息 +func (d *WeComLogic) SyncWeComDepts(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) { + // 1.获取所有部门 + depts, err := wechat.GetAllDepts() + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("获取企业微信部门列表失败:%s", err.Error())) + } + // 2.将部门这个数组进行拆分,一组是父ID为1的,一组是父ID不为1的 + var firstDepts []wecom.ListDepartmentResponseItem // 父ID为1的部门 + var otherDepts []wecom.ListDepartmentResponseItem // 父ID不为1的部门 + for _, dept := range depts { + if dept.ID == 1 { // 跳过ID为1的根部门,由系统配置的根部门进行占位 + continue + } + if dept.ParentID == 1 { + firstDepts = append(firstDepts, dept) + } else { + otherDepts = append(otherDepts, dept) + } + } + // 3.先写父ID为1的,再写父ID不为1的 + for _, dept := range firstDepts { + err := d.AddDepts(&request.DingGroupAddReq{ + GroupType: "cn", + GroupName: strings.Join(pinyin.LazyConvert(dept.Name, nil), ""), + Remark: dept.Name, + SourceDeptId: fmt.Sprintf("%s_%d", config.Conf.WeCom.Flag, dept.ID), + Source: config.Conf.WeCom.Flag, + SourceDeptParentId: fmt.Sprintf("%s_%d", config.Conf.WeCom.Flag, 1), + }) + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("SyncWeComDepts添加根部门失败:%s", err.Error())) + } + } + + for _, dept := range otherDepts { + err := d.AddDepts(&request.DingGroupAddReq{ + GroupType: "cn", + GroupName: strings.Join(pinyin.LazyConvert(dept.Name, nil), ""), + Remark: dept.Name, + SourceDeptId: fmt.Sprintf("%s_%d", config.Conf.WeCom.Flag, dept.ID), + Source: config.Conf.WeCom.Flag, + SourceDeptParentId: fmt.Sprintf("%s_%d", config.Conf.WeCom.Flag, dept.ParentID), + }) + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("SyncWeComDepts添加根部门失败:%s", err.Error())) + } + } + return nil, nil +} + +// AddGroup 添加部门数据 +func (d WeComLogic) AddDepts(r *request.DingGroupAddReq) 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 WeComLogic) SyncWeComUsers(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) { + + return nil, nil +} + +// AddUser 添加用户数据 +func (d WeComLogic) AddUsers(r *request.DingUserAddReq) 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 31154f0..4dc147c 100644 --- a/model/request/group_req.go +++ b/model/request/group_req.go @@ -85,3 +85,7 @@ type UserNoInGroupReq struct { // SyncDingTalkDeptsReq 同步钉钉部门信息 type SyncDingTalkDeptsReq struct { } + +// SyncWeComDeptsReq 同步企业微信部门信息 +type SyncWeComDeptsReq struct { +} diff --git a/model/request/user_req.go b/model/request/user_req.go index 5f94603..04ee86f 100644 --- a/model/request/user_req.go +++ b/model/request/user_req.go @@ -82,10 +82,14 @@ type UserChangeUserStatusReq struct { type UserGetUserInfoReq struct { } -// 同步钉钉用户信息 +// SyncDingUserReq 同步钉钉用户信息 type SyncDingUserReq struct { } +// SyncWeComUserReq 同步企业微信用户信息 +type SyncWeComUserReq struct { +} + // UserListReq 获取用户列表结构体 type UserListReq struct { Username string `json:"username" form:"username"` diff --git a/public/client/wechat/client.go b/public/client/wechat/client.go new file mode 100644 index 0000000..a2a8dfe --- /dev/null +++ b/public/client/wechat/client.go @@ -0,0 +1,15 @@ +package wechat + +import ( + "github.com/eryajf/go-ldap-admin/config" + "github.com/wenerme/go-wecom/wecom" +) + +func InitWeComClient() *wecom.Client { + client := wecom.NewClient(wecom.Conf{ + CorpID: config.Conf.WeCom.CorpID, + AgentID: config.Conf.WeCom.AgentID, + CorpSecret: config.Conf.WeCom.CorpSecret, + }) + return client +} diff --git a/public/client/wechat/wecom.go b/public/client/wechat/wecom.go new file mode 100644 index 0000000..9ae0d9e --- /dev/null +++ b/public/client/wechat/wecom.go @@ -0,0 +1,38 @@ +package wechat + +import ( + "strconv" + + "github.com/wenerme/go-wecom/wecom" +) + +func GetAllDepts() ([]wecom.ListDepartmentResponseItem, error) { + depts, err := InitWeComClient().ListDepartment( + &wecom.ListDepartmentRequest{}, + ) + if err != nil { + return nil, err + } + return depts.Department, nil +} + +func GetAllUsers() ([]wecom.ListUserResponseItem, error) { + depts, err := GetAllDepts() + if err != nil { + return nil, err + } + var us []wecom.ListUserResponseItem + for _, dept := range depts { + users, err := InitWeComClient().ListUser( + &wecom.ListUserRequest{ + DepartmentID: strconv.Itoa(dept.ID), + FetchChild: "1", + }, + ) + if err != nil { + return nil, err + } + us = append(us, users.UserList...) + } + return us, nil +} diff --git a/public/common/init_mysql_data.go b/public/common/init_mysql_data.go index 348defd..ae06002 100644 --- a/public/common/init_mysql_data.go +++ b/public/common/init_mysql_data.go @@ -333,6 +333,13 @@ func InitData() { Remark: "从钉钉拉取用户信息", Creator: "系统", }, + { + Method: "POST", + Path: "/user/syncWeComUsers", + Category: "user", + Remark: "从企业微信拉取用户信息", + Creator: "系统", + }, { Method: "GET", Path: "/group/list", @@ -403,6 +410,13 @@ func InitData() { Remark: "从钉钉拉取部门信息", Creator: "系统", }, + { + Method: "POST", + Path: "/group/syncWeComDepts", + Category: "group", + Remark: "从企业微信拉取部门信息", + Creator: "系统", + }, { Method: "GET", Path: "/role/list", @@ -572,11 +586,13 @@ func InitData() { "/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 93bba90..16fdb49 100644 --- a/routes/group_routes.go +++ b/routes/group_routes.go @@ -25,7 +25,9 @@ func InitGroupRoutes(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) g group.GET("/useringroup", controller.Group.UserInGroup) group.GET("/usernoingroup", controller.Group.UserNoInGroup) - group.POST("/syncDingTalkDepts", controller.Group.SyncDingTalkDepts) + + group.POST("/syncDingTalkDepts", controller.Group.SyncDingTalkDepts) // 同步部门 + group.POST("/syncWeComDepts", controller.Group.SyncWeComDepts) // 同步部门 } return r diff --git a/routes/user_routes.go b/routes/user_routes.go index bd0905f..8da8723 100644 --- a/routes/user_routes.go +++ b/routes/user_routes.go @@ -16,14 +16,16 @@ func InitUserRoutes(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) gi // 开启casbin鉴权中间件 user.Use(middleware.CasbinMiddleware()) { - user.GET("/info", controller.User.GetUserInfo) // 暂时未完成 - user.GET("/list", controller.User.List) // 用户列表 - user.POST("/add", controller.User.Add) // 添加用户 - user.POST("/update", controller.User.Update) // 更新用户 - user.POST("/delete", controller.User.Delete) // 删除用户 - user.POST("/changePwd", controller.User.ChangePwd) // 修改用户密码 - user.POST("/changeUserStatus", controller.User.ChangeUserStatus) // 修改用户状态 + user.GET("/info", controller.User.GetUserInfo) // 暂时未完成 + user.GET("/list", controller.User.List) // 用户列表 + user.POST("/add", controller.User.Add) // 添加用户 + user.POST("/update", controller.User.Update) // 更新用户 + user.POST("/delete", controller.User.Delete) // 删除用户 + user.POST("/changePwd", controller.User.ChangePwd) // 修改用户密码 + user.POST("/changeUserStatus", controller.User.ChangeUserStatus) // 修改用户状态 + user.POST("/syncDingTalkUsers", controller.User.SyncDingTalkUsers) // 同步用户 + user.POST("/syncWeComUsers", controller.User.SyncWeComUsers) // 同步用户 } return r } From a472e5c6acc2e3bd9ec1895df54668d8339a964c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B8=AB=E8=AE=B2=E6=A2=B5?= Date: Fri, 17 Jun 2022 22:55:13 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E8=B5=B0=E9=80=9A=E4=BC=81=E4=B8=9A?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E5=90=8C=E6=AD=A5=E5=90=8C=E6=AD=A5=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 3 +- go.sum | 4 +- logic/dingtalk_logic.go | 8 +-- logic/wecom_logic.go | 101 +++++++++++++++++++++++++++-- model/request/group_req.go | 13 ++++ model/request/user_req.go | 22 +++++++ public/client/dingtalk/dingtalk.go | 8 +-- public/client/wechat/wecom.go | 25 +++++++ service/isql/group_isql.go | 6 +- 9 files changed, 171 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 1e5b509..75da4f4 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,6 @@ require ( github.com/robfig/cron/v3 v3.0.0 github.com/spf13/viper v1.11.0 github.com/thoas/go-funk v0.7.0 - github.com/wenerme/go-wecom v0.0.0-20220123104505-8a1577192bbb github.com/zhaoyunxing92/dingtalk/v2 v2.0.7-0.20220601083444-173c10c3f835 go.uber.org/zap v1.19.1 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df @@ -26,6 +25,8 @@ require ( gorm.io/gorm v1.20.12 ) +require github.com/wenerme/go-wecom v0.0.0-20220617125121-2ee950da3e63 + require ( github.com/BurntSushi/toml v1.1.0 // indirect github.com/golang/mock v1.6.0 // indirect diff --git a/go.sum b/go.sum index e5c9c95..66932aa 100644 --- a/go.sum +++ b/go.sum @@ -520,8 +520,8 @@ github.com/ugorji/go/codec v1.2.3 h1:/mVYEV+Jo3IZKeA5gBngN0AvNnQltEDkR+eQikkWQu0 github.com/ugorji/go/codec v1.2.3/go.mod h1:5FxzDJIgeiWJZslYHPj+LS1dq1ZBQVelZFnjsFGI/Uc= github.com/wenerme/go-req v0.0.0-20210907160348-d822e81276bb h1:4/6Qqg+E8z98SCi21dFnhL6goSWOYMunJkMc+YanrEw= github.com/wenerme/go-req v0.0.0-20210907160348-d822e81276bb/go.mod h1:aQUkMiMp1qZkuSsdu2Vy2ZQK33cPNVmyWFzXatfP+Y4= -github.com/wenerme/go-wecom v0.0.0-20220123104505-8a1577192bbb h1:y1pn4W8X44YRAZm6FPvSQYQXuue3s5bsgX2uVvLIk5c= -github.com/wenerme/go-wecom v0.0.0-20220123104505-8a1577192bbb/go.mod h1:Jz7prkOPdCbWbT5bOQOFkZUQp7pqEc8yiWaCgOVWPH0= +github.com/wenerme/go-wecom v0.0.0-20220617125121-2ee950da3e63 h1:wRIOQxBR5XbUZVMKziAjCnlnDhdAjVjBmLsUSn/j/+M= +github.com/wenerme/go-wecom v0.0.0-20220617125121-2ee950da3e63/go.mod h1:Jz7prkOPdCbWbT5bOQOFkZUQp7pqEc8yiWaCgOVWPH0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/logic/dingtalk_logic.go b/logic/dingtalk_logic.go index a022f6a..25684e2 100644 --- a/logic/dingtalk_logic.go +++ b/logic/dingtalk_logic.go @@ -23,7 +23,7 @@ type DingTalkLogic struct { //通过钉钉获取部门信息 func (d *DingTalkLogic) SyncDingTalkDepts(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) { // 1.获取所有部门 - depts, err := dingtalk.GetDingTalkAllDepts(1) + depts, err := dingtalk.GetAllDepts(1) if err != nil { return nil, tools.NewOperationError(fmt.Errorf("获取钉钉部门列表失败:%s", err.Error())) } @@ -100,7 +100,7 @@ func (d DingTalkLogic) AddDepts(r *request.DingGroupAddReq) error { //根据现有数据库同步到的部门信息,开启用户同步 func (d DingTalkLogic) SyncDingTalkUsers(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) { // 1.获取钉钉用户列表 - users, err := dingtalk.GetDingTalkAllUsers() + users, err := dingtalk.GetAllUsers() if err != nil { return nil, tools.NewOperationError(fmt.Errorf("SyncDingTalkUsers获取钉钉用户列表失败:%s", err.Error())) } @@ -135,7 +135,7 @@ func (d DingTalkLogic) SyncDingTalkUsers(c *gin.Context, req interface{}) (data for _, deptId := range detail.DeptIds { sourceDeptIds = append(sourceDeptIds, fmt.Sprintf("%s_%d", config.Conf.DingTalk.Flag, deptId)) } - groupIds, err := isql.Group.DingTalkDeptIdsToGroupIds(sourceDeptIds) + groupIds, err := isql.Group.DeptIdsToGroupIds(sourceDeptIds) if err != nil { return nil, tools.NewMySqlError(fmt.Errorf("SyncDingTalkUsers获取钉钉部门ids转换为内部部门id失败:%s", err.Error())) } @@ -168,7 +168,7 @@ func (d DingTalkLogic) SyncDingTalkUsers(c *gin.Context, req interface{}) (data } // 3.获取钉钉已离职用户id列表 - userIds, err := dingtalk.GetDingTalkLeaveUserIds() + userIds, err := dingtalk.GetLeaveUserIds() if err != nil { return nil, tools.NewOperationError(fmt.Errorf("SyncDingTalkUsers获取钉钉离职用户列表失败:%s", err.Error())) } diff --git a/logic/wecom_logic.go b/logic/wecom_logic.go index e828ec4..9347fbe 100644 --- a/logic/wecom_logic.go +++ b/logic/wecom_logic.go @@ -12,6 +12,7 @@ import ( "github.com/wenerme/go-wecom/wecom" "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" ) @@ -19,7 +20,7 @@ import ( type WeComLogic struct { } -//通过钉钉获取部门信息 +//通过企业微信获取部门信息 func (d *WeComLogic) SyncWeComDepts(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) { // 1.获取所有部门 depts, err := wechat.GetAllDepts() @@ -41,7 +42,7 @@ func (d *WeComLogic) SyncWeComDepts(c *gin.Context, req interface{}) (data inter } // 3.先写父ID为1的,再写父ID不为1的 for _, dept := range firstDepts { - err := d.AddDepts(&request.DingGroupAddReq{ + err := d.AddDepts(&request.WeComGroupAddReq{ GroupType: "cn", GroupName: strings.Join(pinyin.LazyConvert(dept.Name, nil), ""), Remark: dept.Name, @@ -55,7 +56,7 @@ func (d *WeComLogic) SyncWeComDepts(c *gin.Context, req interface{}) (data inter } for _, dept := range otherDepts { - err := d.AddDepts(&request.DingGroupAddReq{ + err := d.AddDepts(&request.WeComGroupAddReq{ GroupType: "cn", GroupName: strings.Join(pinyin.LazyConvert(dept.Name, nil), ""), Remark: dept.Name, @@ -71,7 +72,7 @@ func (d *WeComLogic) SyncWeComDepts(c *gin.Context, req interface{}) (data inter } // AddGroup 添加部门数据 -func (d WeComLogic) AddDepts(r *request.DingGroupAddReq) error { +func (d WeComLogic) AddDepts(r *request.WeComGroupAddReq) error { // 判断部门名称是否存在 parentGroup := new(model.Group) err := isql.Group.Find(tools.H{"source_dept_id": r.SourceDeptParentId}, parentGroup) @@ -101,12 +102,102 @@ func (d WeComLogic) AddDepts(r *request.DingGroupAddReq) error { //根据现有数据库同步到的部门信息,开启用户同步 func (d WeComLogic) SyncWeComUsers(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) { + // 1.获取企业微信用户列表 + users, err := wechat.GetAllUsers() + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("SyncWeComUsers获取企业微信用户列表失败:%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.BizMail == "" { + detail.BizMail = detail.Email + } + + // 如果企业内没有工号,则工号用名字占位 + // if detail.JobNumber == "" { + // detail.JobNumber = detail.Mobile + // } + //企业微信部门ids,转换为内部部门id + var sourceDeptIds []string + for _, deptId := range detail.Department { + sourceDeptIds = append(sourceDeptIds, fmt.Sprintf("%s_%d", config.Conf.WeCom.Flag, deptId)) + } + groupIds, err := isql.Group.DeptIdsToGroupIds(sourceDeptIds) + if err != nil { + return nil, tools.NewMySqlError(fmt.Errorf("SyncWeComUsers获取企业微信部门ids转换为内部部门id失败:%s", err.Error())) + } + + // 写入用户 + user := request.WeComUserAddReq{ + Username: userName, + Password: config.Conf.Ldap.UserInitPassword, + Nickname: detail.Name, + GivenName: detail.Name, + Mail: detail.BizMail, + JobNumber: detail.Name, // 工号暂用名字替代 + Mobile: detail.Mobile, + Avatar: detail.Avatar, + PostalAddress: detail.Address, + // Departments: dept.GroupName, + Position: detail.Position, + Introduction: detail.Name, + Status: 1, + DepartmentId: groupIds, + Source: config.Conf.WeCom.Flag, + SourceUserId: fmt.Sprintf("%s_%s", config.Conf.WeCom.Flag, detail.UserID), + SourceUnionId: fmt.Sprintf("%s_%s", config.Conf.WeCom.Flag, detail.UserID), + } + // 入库 + err = d.AddUsers(&user) + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("SyncWeComUsers写入用户失败:%s", err.Error())) + } + } + + // 3.获取企业微信已离职用户id列表 + userIds, err := wechat.GetLeaveUserIds() + if err != nil { + return nil, tools.NewOperationError(fmt.Errorf("SyncWeComUsers获取企业微信离职用户列表失败:%s", err.Error())) + } + // 4.遍历id,开始处理 + for _, uid := range userIds { + user := new(model.User) + err = isql.User.Find(tools.H{"source_user_id": fmt.Sprintf("%s_%s", config.Conf.WeCom.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 WeComLogic) AddUsers(r *request.DingUserAddReq) error { +func (d WeComLogic) AddUsers(r *request.WeComUserAddReq) error { // 根据 unionid 查询用户,不存在则创建 if !isql.User.Exist(tools.H{"source_union_id": r.SourceUnionId}) { // 根据角色id获取角色 diff --git a/model/request/group_req.go b/model/request/group_req.go index 4dc147c..bd740ae 100644 --- a/model/request/group_req.go +++ b/model/request/group_req.go @@ -40,6 +40,19 @@ type DingGroupAddReq struct { SourceUserNum int `json:"sourceUserNum"` } +// WeComGroupAddReq 添加企业微信资源结构体 +type WeComGroupAddReq struct { + GroupType string `json:"groupType" validate:"required,min=1,max=20"` + GroupName string `json:"groupName" validate:"required,min=1,max=20"` + //父级Id 大于等于0 必填 + ParentId uint `json:"parentId" validate:"omitempty,min=0"` + Remark string `json:"remark" validate:"min=0,max=100"` // 分组的中文描述 + SourceDeptId string `json:"sourceDeptId"` + Source string `json:"source"` + SourceDeptParentId string `json:"SourceDeptParentId"` + SourceUserNum int `json:"sourceUserNum"` +} + // GroupUpdateReq 更新资源结构体 type GroupUpdateReq struct { ID uint `json:"id" form:"id" validate:"required"` diff --git a/model/request/user_req.go b/model/request/user_req.go index 04ee86f..3e878ce 100644 --- a/model/request/user_req.go +++ b/model/request/user_req.go @@ -42,6 +42,28 @@ type DingUserAddReq struct { SourceUnionId string `json:"sourceUnionId"` // 第三方唯一unionId } +// WeComUserAddReq 企业微信用户创建资源结构体 +type WeComUserAddReq struct { + Username string `json:"username" validate:"required,min=2,max=20"` + Password string `json:"password"` + Nickname string `json:"nickname" validate:"required,min=0,max=20"` + GivenName string `json:"givenName" validate:"min=0,max=20"` + Mail string `json:"mail" validate:"required,min=0,max=100"` + JobNumber string `json:"jobNumber" validate:"required,min=0,max=20"` + PostalAddress string `json:"postalAddress" validate:"min=0,max=255"` + Departments string `json:"departments" validate:"min=0,max=255"` + Position string `json:"position" validate:"min=0,max=255"` + Mobile string `json:"mobile" validate:"required,checkMobile"` + Avatar string `json:"avatar"` + Introduction string `json:"introduction" validate:"min=0,max=255"` + Status uint `json:"status" validate:"oneof=1 2"` + DepartmentId []uint `json:"departmentId" validate:"required"` + Source string `json:"source" validate:"min=0,max=20"` + RoleIds []uint `json:"roleIds" validate:"required"` + SourceUserId string `json:"sourceUserId"` // 第三方用户id + SourceUnionId string `json:"sourceUnionId"` // 第三方唯一unionId +} + // UserUpdateReq 更新资源结构体 type UserUpdateReq struct { ID uint `json:"id" validate:"required"` diff --git a/public/client/dingtalk/dingtalk.go b/public/client/dingtalk/dingtalk.go index 80f7ab5..e968ae0 100644 --- a/public/client/dingtalk/dingtalk.go +++ b/public/client/dingtalk/dingtalk.go @@ -7,7 +7,7 @@ import ( "github.com/zhaoyunxing92/dingtalk/v2/request" ) -func GetDingTalkAllDepts(deptId int) (result []*DingTalkDept, err error) { +func GetAllDepts(deptId int) (result []*DingTalkDept, err error) { depts, err := InitDingTalkClient().FetchDeptList(deptId, true, "zh_CN") if err != nil { return result, err @@ -24,8 +24,8 @@ func GetDingTalkAllDepts(deptId int) (result []*DingTalkDept, err error) { return } -func GetDingTalkAllUsers() (result []*DingTalkUser, err error) { - depts, err := GetDingTalkAllDepts(1) +func GetAllUsers() (result []*DingTalkUser, err error) { + depts, err := GetAllDepts(1) if err != nil { return nil, err } @@ -82,7 +82,7 @@ func GetDingTalkAllUsers() (result []*DingTalkUser, err error) { return } -func GetDingTalkLeaveUserIds() ([]string, error) { +func GetLeaveUserIds() ([]string, error) { var ids []string ReqParm := struct { Cursor int `json:"cursor"` diff --git a/public/client/wechat/wecom.go b/public/client/wechat/wecom.go index 9ae0d9e..b2562ef 100644 --- a/public/client/wechat/wecom.go +++ b/public/client/wechat/wecom.go @@ -6,6 +6,7 @@ import ( "github.com/wenerme/go-wecom/wecom" ) +// GetAllDepts 获取所有部门 func GetAllDepts() ([]wecom.ListDepartmentResponseItem, error) { depts, err := InitWeComClient().ListDepartment( &wecom.ListDepartmentRequest{}, @@ -16,6 +17,7 @@ func GetAllDepts() ([]wecom.ListDepartmentResponseItem, error) { return depts.Department, nil } +// GetAllDepts 获取所有部门 func GetAllUsers() ([]wecom.ListUserResponseItem, error) { depts, err := GetAllDepts() if err != nil { @@ -36,3 +38,26 @@ func GetAllUsers() ([]wecom.ListUserResponseItem, error) { } return us, nil } + +// GetLeaveUserIds 获取离职人员列表 +func GetLeaveUserIds() ([]string, error) { + req := &wecom.GetUnassignedListRequest{ + PageSize: "1000", + Cursor: "", + } + ids := []string{} + for { + rst, err := InitWeComClient().GetUnassignedList(req) + if err != nil { + return nil, err + } + for _, info := range rst.Info { + ids = append(ids, info.HandoverUserID) + } + if !rst.IsLast { + break + } + req.Cursor = rst.NextCursor + } + return ids, nil +} diff --git a/service/isql/group_isql.go b/service/isql/group_isql.go index 9a93101..c2301c3 100644 --- a/service/isql/group_isql.go +++ b/service/isql/group_isql.go @@ -151,10 +151,10 @@ func (s GroupService) RemoveUserFromGroup(group *model.Group, users []model.User return common.DB.Model(&group).Association("Users").Delete(users) } -// DingTalkDeptIdsToGroupIds 将钉钉部门id转换为分组id -func (s GroupService) DingTalkDeptIdsToGroupIds(dingTalkIds []string) (groupIds []uint, err error) { +// DeptIdsToGroupIds 将企业IM部门id转换为MySQL分组id +func (s GroupService) DeptIdsToGroupIds(ids []string) (groupIds []uint, err error) { var tempGroups []model.Group - err = common.DB.Model(&model.Group{}).Where("source_dept_id IN (?)", dingTalkIds).Find(&tempGroups).Error + err = common.DB.Model(&model.Group{}).Where("source_dept_id IN (?)", ids).Find(&tempGroups).Error if err != nil { return nil, err } From 2f335ea805b09d5a3e96adec6920d334d0177f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B8=AB=E8=AE=B2=E6=A2=B5?= Date: Sat, 18 Jun 2022 16:32:40 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E4=BC=81=E4=B8=9A=E5=BE=AE=E4=BF=A1=E7=9A=84=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.yml | 5 +++-- logic/wecom_logic.go | 25 ++++++++++++++++++------- public/client/wechat/wecom.go | 23 ----------------------- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/config.yml b/config.yml index 37ed975..57a0eb1 100644 --- a/config.yml +++ b/config.yml @@ -107,10 +107,11 @@ dingtalk: agent-id: "12121212" # 目前agent-id未使用到,可忽略 enable-sync: false # 是否开启定时同步钉钉的任务 wecom: + # 配置获取详细文档参考:http://ldapdoc.eryajf.net/pages/cf1698/ flag: "wecom" # 作为微信在平台的标识 - corp-id: "wwc6656f03c222fd11" # 企业微信企业ID + corp-id: "xxxx" # 企业微信企业ID agent-id: 1000003 # 企业微信中创建的应用ID - corp-secret: "vhgdQfGS7pM46O40oSK8GBFTcDH_N3gkN5ZJJfOMpog" # 企业微信中创建的应用secret + corp-secret: "xxxxx" # 企业微信中创建的应用secret enable-sync: false # 是否开启定时同步企业微信的任务 feishu: flag: "feishu" \ No newline at end of file diff --git a/logic/wecom_logic.go b/logic/wecom_logic.go index 9347fbe..8126501 100644 --- a/logic/wecom_logic.go +++ b/logic/wecom_logic.go @@ -107,8 +107,23 @@ func (d WeComLogic) SyncWeComUsers(c *gin.Context, req interface{}) (data interf if err != nil { return nil, tools.NewOperationError(fmt.Errorf("SyncWeComUsers获取企业微信用户列表失败:%s", err.Error())) } + // 2.将用户拆分成两组,一组状态为1的在职正常用户,一组状态为2的禁用账户 + // 此问题在企业微信这里没有统一的定论 + // 有的公司是员工离职,直接在企业微信后台删除(企业微信又没有对应接口拿到已经被删除的用户) + // 也有公司是在后台禁用账号,这两者不同,处理方式也不一样 + // 目前先以第二种为准,判断用户状态字段来判断用户是否离职 + var liveUsers []wecom.ListUserResponseItem + var leaveUsers []wecom.ListUserResponseItem + for _, user := range users { + if user.Status == 1 { + liveUsers = append(liveUsers, user) + } + if user.Status == 2 { + leaveUsers = append(leaveUsers, user) + } + } // 2.遍历用户,开始写入 - for _, detail := range users { + for _, detail := range liveUsers { // 用户名的几种情况 var userName string if detail.Email != "" { @@ -171,14 +186,10 @@ func (d WeComLogic) SyncWeComUsers(c *gin.Context, req interface{}) (data interf } // 3.获取企业微信已离职用户id列表 - userIds, err := wechat.GetLeaveUserIds() - if err != nil { - return nil, tools.NewOperationError(fmt.Errorf("SyncWeComUsers获取企业微信离职用户列表失败:%s", err.Error())) - } // 4.遍历id,开始处理 - for _, uid := range userIds { + for _, userTmp := range leaveUsers { user := new(model.User) - err = isql.User.Find(tools.H{"source_user_id": fmt.Sprintf("%s_%s", config.Conf.WeCom.Flag, uid)}, user) + err = isql.User.Find(tools.H{"source_user_id": fmt.Sprintf("%s_%s", config.Conf.WeCom.Flag, userTmp.UserID)}, user) if err != nil { return nil, tools.NewMySqlError(fmt.Errorf("在MySQL查询用户失败: " + err.Error())) } diff --git a/public/client/wechat/wecom.go b/public/client/wechat/wecom.go index b2562ef..e81e1ba 100644 --- a/public/client/wechat/wecom.go +++ b/public/client/wechat/wecom.go @@ -38,26 +38,3 @@ func GetAllUsers() ([]wecom.ListUserResponseItem, error) { } return us, nil } - -// GetLeaveUserIds 获取离职人员列表 -func GetLeaveUserIds() ([]string, error) { - req := &wecom.GetUnassignedListRequest{ - PageSize: "1000", - Cursor: "", - } - ids := []string{} - for { - rst, err := InitWeComClient().GetUnassignedList(req) - if err != nil { - return nil, err - } - for _, info := range rst.Info { - ids = append(ids, info.HandoverUserID) - } - if !rst.IsLast { - break - } - req.Cursor = rst.NextCursor - } - return ids, nil -}