业务需求 需要根据提取企业微信所有员工所拥有客户 也就是提取unionid 特此写个一站式工具类 一次性生成
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
package ins.platfrom.test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import ins.platfrom.dao.WXUserVoMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.*;
/**
* 企微员工所绑定用户提取
* @author zhangle
* @create 2021-04-21 10:41
*/
@Slf4j
@Service
public class QiWeiTest {
/**
* 微信token公共变量
*/
private String access_token;
/**
* HTTP线程池 这个如果需要代理需要配置 看我博客
* https:///HezhezhiyuLe/article/details/92395041
*/
@Autowired
private RestTemplate bean;
/**
* 最终获得用户信息保存接口 自定义 不用删除
*/
@Autowired
private WXUserVoMapper wxUserVoMapper;
/**
* corpid corpsecret 填自己的公司
* 获取token get
* https://work.weixin.qq.com/api/doc/90000/90135/91039 参考文档
*/
private final String tokenurl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=10086&corpsecret=10010";
/**
* 获取部门 post 可省略ID 不填默认拉取当前权限下可见部门
* https://work.weixin.qq.com/api/doc/90000/90135/90208
*/
private String depturl = "https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token=";
/**
* 批量获取客户详情 post
* https://work.weixin.qq.com/api/doc/90000/90135/92994
*/
private String userurl = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/batch/get_by_user?access_token=";
/**
* 企微当前最高权限获取部门所有成员 department_id=1 fetch_child=1 递归 =0当前部门
* https://work.weixin.qq.com/api/doc/90000/90135/90200
*/
private String deptuserurl = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?fetch_child=1&department_id=";
/**
* 1
* 获取企微token
* @return token
*/
public String getToken() {
String tokenstr = bean.getForEntity(tokenurl, String.class).getBody();
log.info("获取token:"+tokenstr);
JSONObject tokenjson = parseStr(tokenstr);
access_token=tokenjson.get("access_token").toString();
return access_token;
}
/**
* 0
*/
public void start(){
//获取token
access_token=getToken();
//获得父节点
Set<String> treeDept = findTreeDept();
if(treeDept==null||treeDept.size()==0){
log.info("查询部门为空");
return;
}
//迭代父节点
for (String deptparent : treeDept) {
//父节点下所有员工 递归
Set<String> deptUserByDId = findDeptUserByDId(deptparent);
int unionidByUserId = findUnionidByUserId(deptUserByDId);
log.info("当前"+deptparent+"部门员工客户人数:"+unionidByUserId);
}
}
/**
* 2
* 获取当前企微最高节点部门ID
* 当自己不是当前公众号最高权限即不可以查看所有部门时使用
* @return 最高父节点
*/
public Set<String> findTreeDept() {
String deptstr = bean.getForEntity(depturl+access_token, String.class).getBody();
deptstr=iflegal(deptstr);
if(deptstr==null||"".equals(deptstr)){
log.info("获取部门失败");
return null;
}
JSONObject jsonObject = parseStr(deptstr);
//获取所有部门
String dept = jsonObject.get("department").toString();
List<JSONObject> departments = JSON.parseArray(dept, JSONObject.class);
return recursiondept(departments);
}
/**
* 判断微信接口返回是否合法
* @param resp
* @return
*/
public String iflegal(String resp){
if(resp.contains("\"errcode\":0")){
return resp;
}
log.info("失敗預警:"+resp);
if(resp.contains("\"errcode\":40014")||resp.contains(" \"errcode\":42001")){
getToken();
log.info("恢复:"+access_token);
}
return null;
}
/**
* 转换企微返回数据为json对象
* @param str
* @return
*/
public JSONObject parseStr(String str){
return JSON.parseObject(str);
}
/**
* 查询部门父ID
* @param departments 当前企微权限下所有部门 包含父子级
* @return 最高父节点
*/
public Set<String> recursiondept(List<JSONObject> departments){
Set<String> deptid = new HashSet<>();
Set<String> treedept = new HashSet<>();
for (JSONObject department : departments) {
deptid.add(department.getString("id"));
}
for (JSONObject department : departments) {
String parentid = department.getString("parentid");
//找不到父ID即为最高节点
if(!deptid.contains(parentid)){
treedept.add(department.getString("id"));
}
}
return treedept;
}
/**
* 3
* 通过部门ID查询当前部门员工 递归获取当前部门所有员工
* @param deptId 部门ID
* @return
*/
public Set<String> findDeptUserByDId(String deptId){
//获取当前部门下的所有员工 包括子节点
String deptuserstr = bean.getForEntity(deptuserurl+deptId+"&access_token="+access_token, String.class).getBody();
deptuserstr=iflegal(deptuserstr);
if(deptuserstr==null||"".equals(deptuserstr)){
log.info("获取部门成员失败");
return null;
}
JSONObject jsonObject = parseStr(deptuserstr);
String dept = jsonObject.get("userlist").toString();
List<JSONObject> departments = JSON.parseArray(dept, JSONObject.class);
if(departments==null||departments.size()==0){
return null;
}
//去重
Set<String> userids = new HashSet<>();
for (JSONObject department : departments) {
userids.add(department.getString("userid"));
}
log.info("用户ID总数:"+userids.size());
return userids;
}
/**
* 4
* 企微员工通过员工ID查找用户unionid
* @param userids 当前部门所有员工
* @return
*/
public int findUnionidByUserId(Set<String> userids){
//用户unionid集合 所有用户 包含重复 如果父级部门平行有多个 用户可能会重复 建议把人员再次汇总 我是因为确定父级只有一个有最大权限才这么写
int count=0;
int size = userids.size();
//去重总用户(一个客户加了N个员工) 这个存储值几十万没问题 SQL写对加主键一把就成功
Set<String> unionids = new HashSet<>();
int formum=0;
for (String userid : userids) {
formum++;
if(formum%1000==0){
log.info("循环到:"+formum+"/"+unionids.size()+"/"+size);
}
//实时更新token 防止失效
Set<String> personnelUsers = handleUserByUserId(userid,userurl+access_token);
unionids.addAll(personnelUsers);
count+=personnelUsers.size();
}
//用户unionid集合有值就保存 可以根据业务需求 自行决定存储 IO SQL
ArrayList<String> unionidarr = new ArrayList<>();
for (String unionid : unionids) {
unionidarr.add(unionid);
//依据自己数据库能力分批次插入 一次插入几十万容易IO溢出
if(unionidarr.size()==1000){
Integer insertnum = wxUserVoMapper.insertQiWei(unionidarr);
log.info(insertnum+"");
unionidarr.clear();
}
}
if(unionidarr.size()>0){
Integer insertnum = wxUserVoMapper.insertQiWei(unionidarr);
log.info(insertnum+"");
}
log.info(" 员工总用户数:"+unionids.size()+"交叉结果集:"+count);
return unionids.size();
}
/**
* 处理单个用户所拥有的全部客户
* @param userid 用户ID
* @param url 实时链接
* @return
*/
public Set<String> handleUserByUserId(String userid,String url) {
Set<String> unionids = new HashSet<>();
Map<String, Object> params = new HashMap<>();
params.put("userid",userid);
//企微最大分页量
params.put("limit",100);
String openid = "";
//封顶值 一个员工10W用户
for (int i = 0; i < 1000; i++) {
params.put("cursor",openid);
String resp = bean.postForEntity(url, params, String.class).getBody();
resp=iflegal(resp);
if (resp == null ||"".equals(resp)) {
log.info("不合法返回:"+userid);
break;
}
if(i==999){
log.info("员工用户超限:"+userid);
}
JSONObject jsonObject = parseStr(resp);
//员工客户列表
List<JSONObject> external_contact_list = JSON.parseArray(jsonObject.get("external_contact_list").toString(), JSONObject.class);
if(external_contact_list==null||external_contact_list.size()==0){
break;
}
for (JSONObject customer : external_contact_list) {
String external_contact = customer.getString("external_contact");
if(external_contact==null||"".equals(external_contact)){
break;
}
//用户粗略详情
JSONObject customercontact = parseStr(external_contact);
if(customercontact==null){
break;
}
// 可以拿到 客户微信名 name 头像 avatar 性别 gender 标识 unionid 类型 type更多详情需要换链接
String unionid = customercontact.getString("unionid");
if(unionid!=null&&!"".equals(unionid)){
unionids.add(unionid);
}
}
String next_cursor = jsonObject.getString("next_cursor");
if ("".equals(next_cursor)) {
break;
}else {
//迭代
openid=next_cursor;
}
}
return unionids;
}
}
因篇幅问题不能全部显示,请点此查看更多更全内容