添加企业微信的同步能力 (#30)
This commit is contained in:
parent
b29200aec9
commit
56f2cda6dd
|
@ -107,6 +107,11 @@ dingtalk:
|
|||
agent-id: "12121212" # 目前agent-id未使用到,可忽略
|
||||
enable-sync: false # 是否开启定时同步钉钉的任务
|
||||
wecom:
|
||||
flag: "wecom"
|
||||
# 配置获取详细文档参考:http://ldapdoc.eryajf.net/pages/cf1698/
|
||||
flag: "wecom" # 作为微信在平台的标识
|
||||
corp-id: "xxxx" # 企业微信企业ID
|
||||
agent-id: 1000003 # 企业微信中创建的应用ID
|
||||
corp-secret: "xxxxx" # 企业微信中创建的应用secret
|
||||
enable-sync: false # 是否开启定时同步企业微信的任务
|
||||
feishu:
|
||||
flag: "feishu"
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
5
go.mod
5
go.mod
|
@ -11,7 +11,7 @@ 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
|
||||
|
@ -25,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
|
||||
|
@ -33,6 +35,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
|
||||
)
|
||||
|
||||
|
|
14
go.sum
14
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-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=
|
||||
|
@ -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=
|
||||
|
|
|
@ -16,5 +16,6 @@ var (
|
|||
Menu = &MenuLogic{}
|
||||
OperationLog = &OperationLogLogic{}
|
||||
DingTalk = &DingTalkLogic{}
|
||||
WeCom = &WeComLogic{}
|
||||
Base = &BaseLogic{}
|
||||
)
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
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/ildap"
|
||||
"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.WeComGroupAddReq{
|
||||
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.WeComGroupAddReq{
|
||||
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.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 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.将用户拆分成两组,一组状态为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 liveUsers {
|
||||
// 用户名的几种情况
|
||||
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列表
|
||||
// 4.遍历id,开始处理
|
||||
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, userTmp.UserID)}, 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.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
|
||||
}
|
|
@ -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"`
|
||||
|
@ -85,3 +98,7 @@ type UserNoInGroupReq struct {
|
|||
// SyncDingTalkDeptsReq 同步钉钉部门信息
|
||||
type SyncDingTalkDeptsReq struct {
|
||||
}
|
||||
|
||||
// SyncWeComDeptsReq 同步企业微信部门信息
|
||||
type SyncWeComDeptsReq struct {
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
@ -82,10 +104,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"`
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package wechat
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/wenerme/go-wecom/wecom"
|
||||
)
|
||||
|
||||
// GetAllDepts 获取所有部门
|
||||
func GetAllDepts() ([]wecom.ListDepartmentResponseItem, error) {
|
||||
depts, err := InitWeComClient().ListDepartment(
|
||||
&wecom.ListDepartmentRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return depts.Department, nil
|
||||
}
|
||||
|
||||
// GetAllDepts 获取所有部门
|
||||
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
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue