| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package wecom |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "strings" |
| |
| "github.com/go-resty/resty/v2" |
| "github.com/segmentfault/pacman/log" |
| "github.com/silenceper/wechat/v2/cache" |
| "github.com/silenceper/wechat/v2/work" |
| workConfig "github.com/silenceper/wechat/v2/work/config" |
| "github.com/tidwall/gjson" |
| ) |
| |
| type Company struct { |
| CorpID string |
| CorpSecret string |
| AgentID string |
| CallbackURL string |
| |
| Work *work.Work |
| DepartmentMapping map[int]*Department |
| EmployeeMapping map[string]*Employee |
| UserDetailInfoMapping map[string]*UserDetailInfo |
| } |
| |
| func NewCompany(corpID, corpSecret, agentID string) *Company { |
| memory := cache.NewMemory() |
| cfg := &workConfig.Config{ |
| CorpID: corpID, |
| CorpSecret: corpSecret, |
| AgentID: agentID, |
| Cache: memory, |
| } |
| newWork := work.NewWork(cfg) |
| return &Company{ |
| CorpID: corpID, |
| CorpSecret: corpSecret, |
| AgentID: agentID, |
| Work: newWork, |
| DepartmentMapping: make(map[int]*Department), |
| EmployeeMapping: make(map[string]*Employee), |
| UserDetailInfoMapping: make(map[string]*UserDetailInfo), |
| } |
| } |
| |
| func (c *Company) ListDepartmentAll() (err error) { |
| log.Debugf("try to list department all") |
| token, err := c.Work.GetOauth().GetAccessToken() |
| if err != nil { |
| return fmt.Errorf("get access token failed: %w", err) |
| } |
| log.Debugf("get access token success") |
| |
| resp, err := resty.New().R().Get("https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token=" + token) |
| if err != nil { |
| return fmt.Errorf("get department list failed: %w", err) |
| } |
| if resp.StatusCode() != 200 { |
| return fmt.Errorf("get department list failed: %s", resp.String()) |
| } |
| |
| log.Debugf("get department success: %s", resp.String()) |
| |
| department := gjson.Get(resp.String(), "department").String() |
| var departments []*Department |
| err = json.Unmarshal([]byte(department), &departments) |
| if err != nil { |
| return fmt.Errorf("unmarshal department failed: %w", err) |
| } |
| |
| departmentMapping := make(map[int]*Department) |
| for _, d := range departments { |
| departmentMapping[d.Id] = d |
| } |
| c.DepartmentMapping = departmentMapping |
| log.Debugf("get department list: %d", len(departments)) |
| return nil |
| } |
| |
| func (c *Company) ListUser() (err error) { |
| token, err := c.Work.GetOauth().GetAccessToken() |
| if err != nil { |
| return fmt.Errorf("get access token failed: %w", err) |
| } |
| log.Debugf("get access token success") |
| |
| for _, department := range c.DepartmentMapping { |
| log.Debugf("try to get department user list: %d %s", department.Id, department.Name) |
| resp, err := resty.New().R().Get(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=%d&access_token=%s", |
| department.Id, token)) |
| if err != nil { |
| log.Errorf("get department user list failed: %v", err) |
| continue |
| } |
| if gjson.Get(resp.String(), "errcode").Int() != 0 { |
| log.Errorf("get department user list failed: %v", resp.String()) |
| continue |
| } |
| |
| userList := gjson.Get(resp.String(), "userlist").String() |
| var employees []*Employee |
| err = json.Unmarshal([]byte(userList), &employees) |
| if err != nil { |
| log.Errorf("unmarshal userList failed: %w", err) |
| continue |
| } |
| log.Debugf("get department user list: %d", len(employees)) |
| for _, employee := range employees { |
| c.EmployeeMapping[employee.Userid] = employee |
| log.Debugf(employee.Userid) |
| } |
| } |
| |
| log.Debugf("all user amount: %d", len(c.EmployeeMapping)) |
| return nil |
| } |
| |
| func (c *Company) AuthUser(code string) (info *UserInfo, err error) { |
| token, err := c.Work.GetOauth().GetAccessToken() |
| if err != nil { |
| return nil, fmt.Errorf("get access token failed: %w", err) |
| } |
| |
| getUserInfoResp, err := resty.New().R().Get(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=" + token + "&code=" + code)) |
| if err != nil { |
| log.Errorf("get user info failed: %v", err) |
| return nil, err |
| } |
| log.Debugf("get user info: %s", getUserInfoResp.String()) |
| |
| userTicket := gjson.Get(getUserInfoResp.String(), "user_ticket").String() |
| |
| getUserDetailResp, err := resty.New().R(). |
| SetHeader("Content-Type", "application/json"). |
| SetBody(map[string]string{"user_ticket": userTicket}). |
| Post(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail?access_token=" + token)) |
| if err != nil { |
| log.Errorf("get user info failed: %v", err) |
| return nil, err |
| } |
| |
| var userInfoResp *AuthUserInfoResp |
| err = json.Unmarshal([]byte(getUserDetailResp.String()), &userInfoResp) |
| if err != nil { |
| log.Errorf("unmarshal user info failed: %s", err) |
| return nil, err |
| } |
| if userInfoResp.Errcode != 0 { |
| log.Errorf("get user info failed: %v", getUserDetailResp.String()) |
| return nil, fmt.Errorf("get user info failed") |
| } |
| log.Debugf("get user info: %s", getUserDetailResp.String()) |
| |
| employee := c.EmployeeMapping[userInfoResp.Userid] |
| if employee == nil { |
| return nil, fmt.Errorf("user %s not found in employee list", userInfoResp.Userid) |
| } |
| |
| userDetailInfo, err := c.GetUserDetailInfo(userInfoResp.Userid) |
| if err != nil { |
| return nil, err |
| } |
| |
| userInfo := &UserInfo{ |
| Userid: userInfoResp.Userid, |
| Mobile: userInfoResp.Mobile, |
| Gender: userInfoResp.Gender, |
| Email: userInfoResp.Email, |
| Avatar: userInfoResp.Avatar, |
| QrCode: userInfoResp.QrCode, |
| Address: userInfoResp.Address, |
| Name: employee.Name, |
| Position: userDetailInfo.Position, |
| IsAvailable: userDetailInfo.Status == 1, |
| DepartmentIDs: employee.Department, |
| } |
| return userInfo, nil |
| } |
| |
| func (c *Company) GetRedirectURL(callbackURl string) (redirectURL string) { |
| return c.Work.GetOauth().GetTargetPrivateURL(callbackURl, c.AgentID) |
| } |
| |
| func (c *Company) GetUserDetailInfo(userid string) (info *UserDetailInfo, err error) { |
| token, err := c.Work.GetOauth().GetAccessToken() |
| if err != nil { |
| return nil, fmt.Errorf("get access token failed: %w", err) |
| } |
| |
| userDetailInfoResp, err := resty.New().R().Get(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=" + token + "&userid=" + userid)) |
| if err != nil { |
| log.Errorf("get user info failed: %v", err) |
| return nil, err |
| } |
| var userDetailInfo *UserDetailInfo |
| _ = json.Unmarshal([]byte(userDetailInfoResp.String()), &userDetailInfo) |
| if userDetailInfo.Errcode != 0 { |
| log.Errorf("get user info failed: %v", userDetailInfoResp.String()) |
| return nil, fmt.Errorf("get user info failed") |
| } |
| log.Debugf("get user detail info: %s", userDetailInfoResp.String()) |
| c.UserDetailInfoMapping[userid] = userDetailInfo |
| return userDetailInfo, nil |
| } |
| |
| func (c *Company) formatDepartmentAndPosition(departmentIDs []int, position string) string { |
| var departmentName []string |
| for _, t := range departmentIDs { |
| name := c.fullDepartmentName(t) |
| if len(name) == 0 { |
| continue |
| } |
| departmentName = append(departmentName, name) |
| } |
| var desc []string |
| if dep := strings.Join(departmentName, ","); len(dep) > 0 { |
| desc = append(desc, fmt.Sprintf("部门:%s", dep)) |
| } |
| if len(position) > 0 { |
| desc = append(desc, fmt.Sprintf("职位:%s", position)) |
| } |
| return strings.Join(desc, "\n") |
| } |
| |
| func (c *Company) fullDepartmentName(departmentID int) string { |
| departmentNames := make([]string, 0) |
| for { |
| department := c.DepartmentMapping[departmentID] |
| if department == nil { |
| break |
| } |
| departmentNames = append([]string{department.Name}, departmentNames...) |
| if department.ParentID == 0 { |
| break |
| } |
| departmentID = department.ParentID |
| } |
| return strings.Join(departmentNames, "/") |
| } |