AddrBookSdk.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. package com.usoftchina.qywx.sdk;
  2. import com.usoftchina.qywx.sdk.config.Agent;
  3. import com.usoftchina.qywx.sdk.config.QywxProperties;
  4. import com.usoftchina.qywx.sdk.dto.*;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.http.ResponseEntity;
  8. import org.springframework.ui.ModelMap;
  9. import org.springframework.util.CollectionUtils;
  10. import org.springframework.util.StringUtils;
  11. import java.util.ArrayList;
  12. import java.util.List;
  13. import java.util.concurrent.atomic.AtomicReference;
  14. /**
  15. * 通讯录管理
  16. *
  17. * @author yingp
  18. */
  19. public class AddrBookSdk extends BaseSdk {
  20. /**
  21. * 通讯录管理私钥
  22. * <p>使用应用secret只能进行“查询”、“邀请”等非写操作,而且只能操作应用可见范围内的通讯录</p>
  23. */
  24. public final static String ADDRESS_BOOK_AGENT_CODE = "AddressBook";
  25. private final Logger logger = LoggerFactory.getLogger(AddrBookSdk.class);
  26. public AddrBookSdk(QywxProperties properties) {
  27. super(properties);
  28. }
  29. /**
  30. * 是否启用
  31. *
  32. * @return
  33. */
  34. public boolean enabled() {
  35. Agent agent = getAgentMap().get(ADDRESS_BOOK_AGENT_CODE);
  36. return null != agent && null != agent.getSecret();
  37. }
  38. /**
  39. * sdk是否只有读权限
  40. *
  41. * @return
  42. */
  43. public boolean isReadonly() {
  44. return enabled() && getAgentMap().get(ADDRESS_BOOK_AGENT_CODE).isReadonly();
  45. }
  46. /**
  47. * 创建成员
  48. *
  49. * @param req
  50. */
  51. public void createUser(CreateUserReq req) {
  52. tryAndIgnoreSystemError(() ->
  53. restTemplate.postForEntity(baseUrl + "/cgi-bin/user/create?access_token={access_token}",
  54. req.build(), BaseResp.class,
  55. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)))
  56. );
  57. }
  58. /**
  59. * 读取成员
  60. *
  61. * @param userId 成员UserID。对应管理端的帐号,企业内必须唯一。不区分大小写,长度为1~64个字节
  62. */
  63. public GetUserResp getUser(String userId) {
  64. ResponseEntity<GetUserResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/user/get?access_token={access_token}&userid={userid}",
  65. GetUserResp.class,
  66. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)).addAttribute("userid", userId));
  67. assertOK(resp);
  68. return resp.getBody();
  69. }
  70. /**
  71. * 更新成员
  72. *
  73. * @param req
  74. */
  75. public void updateUser(UpdateUserReq req) {
  76. tryAndIgnoreSystemError(() ->
  77. restTemplate.postForEntity(baseUrl + "/cgi-bin/user/update?access_token={access_token}",
  78. req.build(), BaseResp.class,
  79. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)))
  80. );
  81. }
  82. /**
  83. * 删除成员
  84. *
  85. * @param userId 成员UserID。对应管理端的帐号
  86. */
  87. public void deleteUser(String userId) {
  88. tryAndIgnoreSystemError(() ->
  89. restTemplate.getForEntity(baseUrl + "/cgi-bin/user/delete?access_token={access_token}&userid={userid}",
  90. BaseResp.class,
  91. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)).addAttribute("userid", userId))
  92. );
  93. }
  94. /**
  95. * 批量删除成员
  96. *
  97. * @param userIdList 成员UserID列表。对应管理端的帐号。最多支持200个。若存在无效UserID,直接返回错误
  98. */
  99. public void deleteUser(List<String> userIdList) {
  100. tryAndIgnoreSystemError(() ->
  101. restTemplate.postForEntity(baseUrl + "/cgi-bin/user/batchdelete?access_token={access_token}",
  102. new ModelMap("useridlist", userIdList),
  103. BaseResp.class,
  104. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)))
  105. );
  106. }
  107. /**
  108. * 获取部门成员
  109. *
  110. * @param departmentId 部门ID
  111. * @param fetchChild 是否递归获取子部门下面的成员:1-递归获取,0-只获取本部门
  112. * 企业微信升级,方法不可用
  113. */
  114. @Deprecated
  115. public List<GetSimpleUserListResp.User> getSimpleUserList(Integer departmentId, boolean fetchChild) {
  116. ResponseEntity<GetSimpleUserListResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/user/simplelist?access_token={access_token}&department_id={department_id}&fetch_child={fetch_child}",
  117. GetSimpleUserListResp.class,
  118. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE))
  119. .addAttribute("department_id", departmentId)
  120. .addAttribute("fetch_child", fetchChild ? 1 : 0));
  121. assertOK(resp);
  122. return resp.getBody().getUserlist();
  123. }
  124. /**
  125. * 获取部门成员详情
  126. *
  127. * @param departmentId 部门ID
  128. * @param fetchChild 是否递归获取子部门下面的成员:1-递归获取,0-只获取本部门
  129. * 企业微信升级,方法不可用
  130. * @return
  131. */
  132. @Deprecated
  133. public List<GetUserListResp.User> getUserList(Integer departmentId, boolean fetchChild) {
  134. ResponseEntity<GetUserListResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/user/list?access_token={access_token}&department_id={department_id}&fetch_child={fetch_child}",
  135. GetUserListResp.class,
  136. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE))
  137. .addAttribute("department_id", departmentId)
  138. .addAttribute("fetch_child", fetchChild ? 1 : 0));
  139. assertOK(resp);
  140. return resp.getBody().getUserlist();
  141. }
  142. /**
  143. * 获取部门成员详情
  144. * @param cursor 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用不填
  145. * limit 分页,预期请求的数据量,取值范围 1 ~ 10000 默认10000
  146. * @return
  147. */
  148. public List<GetSimpleUserIDListResp.User> getUserIdList(List<GetSimpleUserIDListResp.User> userList,String cursor) {
  149. ResponseEntity<GetSimpleUserIDListResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/user/list_id?access_token={access_token}&cursor={cursor}&limit={limit}",
  150. GetSimpleUserIDListResp.class,
  151. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE))
  152. .addAttribute("cursor", cursor)
  153. .addAttribute("limit", 10000));
  154. assertOK(resp);
  155. if(StringUtils.hasText(resp.getBody().getNext_cursor())){
  156. System.out.println("Next_cursor:"+resp.getBody().getNext_cursor());
  157. return getUserIdList(resp.getBody().getDept_user(),resp.getBody().getNext_cursor());
  158. }else {
  159. userList.addAll(resp.getBody().getDept_user());
  160. return userList;
  161. }
  162. }
  163. /**
  164. * userid转openid
  165. * <p>该接口使用场景为企业支付,在使用企业红包和向员工付款时,需要自行将企业微信的userid转成openid。</p>
  166. *
  167. * @param userId
  168. * @return
  169. */
  170. public String getOpenId(String userId) {
  171. ResponseEntity<GetOpenIdResp> resp = restTemplate.postForEntity(baseUrl + "/cgi-bin/user/convert_to_openid?access_token={access_token}",
  172. new ModelMap("userid", userId),
  173. GetOpenIdResp.class,
  174. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)));
  175. assertOK(resp);
  176. return resp.getBody().getOpenid();
  177. }
  178. /**
  179. * 二次验证
  180. * <p>
  181. * 企业在开启二次验证时,必须在管理端填写企业二次验证页面的url。
  182. * 当成员登录企业微信或关注微工作台(原企业号)加入企业时,会自动跳转到企业的验证页面。在跳转到企业的验证页面时,会带上如下参数:code=CODE。
  183. * 企业收到code后,使用“通讯录同步助手”调用接口“根据code获取成员信息”获取成员的userid。然后在验证成员信息成功后,调用如下接口即可让成员成功加入企业
  184. * </p>
  185. *
  186. * @param userId
  187. */
  188. public void authSuccess(String userId) {
  189. ResponseEntity<BaseResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/user/authsucc?access_token={access_token}&userid={userid}",
  190. BaseResp.class,
  191. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE))
  192. .addAttribute("userid", userId));
  193. assertOK(resp);
  194. }
  195. /**
  196. * 邀请成员
  197. * <p>企业可通过接口批量邀请成员使用企业微信,邀请后将通过短信或邮件下发通知</p>
  198. *
  199. * @param req
  200. */
  201. public InviteResp invite(InviteReq req) {
  202. ResponseEntity<InviteResp> resp = restTemplate.postForEntity(baseUrl + "/cgi-bin/batch/invite?access_token={access_token}",
  203. req.build(), InviteResp.class,
  204. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)));
  205. assertOK(resp);
  206. return resp.getBody();
  207. }
  208. /**
  209. * 获取加入企业二维码
  210. *
  211. * @param type 尺寸
  212. * @return 二维码链接,有效期7天
  213. */
  214. public String getJoinQrCode(QrCodeType type) {
  215. ResponseEntity<GetJoinQrCodeResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/corp/get_join_qrcode?access_token={access_token}&size_type={size_type}",
  216. GetJoinQrCodeResp.class,
  217. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE))
  218. .addAttribute("size_type", type.code));
  219. assertOK(resp);
  220. return resp.getBody().getJoin_qrcode();
  221. }
  222. /**
  223. * qrcode尺寸类型
  224. */
  225. public enum QrCodeType {
  226. T_171_171(1),
  227. T_399_399(2),
  228. T_741_741(3),
  229. T_2052_2052(4);
  230. private final int code;
  231. QrCodeType(int code) {
  232. this.code = code;
  233. }
  234. public int getCode() {
  235. return code;
  236. }
  237. }
  238. /**
  239. * 创建部门
  240. *
  241. * @param req
  242. * @return 部门ID
  243. */
  244. public Integer createDepartment(CreateDepartmentReq req) {
  245. ResponseEntity<CreateDepartmentResp> resp = tryAndIgnoreSystemError(() ->
  246. restTemplate.postForEntity(baseUrl + "/cgi-bin/department/create?access_token={access_token}",
  247. req.build(), CreateDepartmentResp.class,
  248. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)))
  249. );
  250. return null == resp ? null : resp.getBody().getId();
  251. }
  252. /**
  253. * 更新部门
  254. *
  255. * @param req
  256. */
  257. public void updateDepartment(UpdateDepartmentReq req) {
  258. tryAndIgnoreSystemError(() ->
  259. restTemplate.postForEntity(baseUrl + "/cgi-bin/department/update?access_token={access_token}",
  260. req.build(), BaseResp.class,
  261. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)))
  262. );
  263. }
  264. /**
  265. * 删除部门
  266. *
  267. * @param departmentId
  268. */
  269. public void deleteDepartment(int departmentId) {
  270. tryAndIgnoreSystemError(() ->
  271. restTemplate.getForEntity(baseUrl + "/cgi-bin/department/delete?access_token={access_token}&id={id}",
  272. BaseResp.class,
  273. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE))
  274. .addAttribute("id", departmentId))
  275. );
  276. }
  277. /**
  278. * 获取部门
  279. * 企业微信升级,方法不可用
  280. * @param departmentId
  281. * @return
  282. */
  283. @Deprecated
  284. public GetDepartmentListResp.Department getDepartment(int departmentId) {
  285. List<GetDepartmentListResp.Department> departmentList = getDepartmentList(departmentId);
  286. if (!CollectionUtils.isEmpty(departmentList)) {
  287. for (GetDepartmentListResp.Department department : departmentList) {
  288. if (department.getId().equals(departmentId)) {
  289. return department;
  290. }
  291. }
  292. }
  293. return null;
  294. }
  295. /**
  296. * 获取部门,包括下级
  297. * 企业微信升级,方法不可用
  298. * @param departmentId
  299. * @return
  300. */
  301. @Deprecated
  302. public List<GetDepartmentListResp.Department> getDepartmentList(int departmentId) {
  303. ResponseEntity<GetDepartmentListResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/department/list?access_token={access_token}&id={id}",
  304. GetDepartmentListResp.class,
  305. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE))
  306. .addAttribute("id", departmentId));
  307. assertOK(resp);
  308. if (!CollectionUtils.isEmpty(resp.getBody().getDepartment())) {
  309. return resp.getBody().getDepartment();
  310. }
  311. return null;
  312. }
  313. /**
  314. * 获取部门列表
  315. * 企业微信升级,方法不可用
  316. * @return
  317. */
  318. @Deprecated
  319. public List<GetDepartmentListResp.Department> getDepartmentList() {
  320. ResponseEntity<GetDepartmentListResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/department/list?access_token={access_token}",
  321. GetDepartmentListResp.class,
  322. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)));
  323. assertOK(resp);
  324. return resp.getBody().getDepartment();
  325. }
  326. /**
  327. * 获取部门列表
  328. *
  329. * @return
  330. */
  331. public List<GetDepartmentSimpleListResp.Department> getSimpleDepartmentList() {
  332. ResponseEntity<GetDepartmentSimpleListResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/department/simplelist?access_token={access_token}&id={id}",
  333. GetDepartmentSimpleListResp.class,
  334. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE))
  335. .addAttribute("id", 0));
  336. assertOK(resp);
  337. return resp.getBody().getDepartment_id();
  338. }
  339. /**
  340. * 获取部门列表
  341. *
  342. * @return
  343. */
  344. public List<GetDepartmentSimpleListResp.Department> getSimpleDepartmentList(Integer departmentId) {
  345. ResponseEntity<GetDepartmentSimpleListResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/department/simplelist?access_token={access_token}&id={id}",
  346. GetDepartmentSimpleListResp.class,
  347. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE))
  348. .addAttribute("id", departmentId));
  349. assertOK(resp);
  350. return resp.getBody().getDepartment_id();
  351. }
  352. /**
  353. * 获取访问用户身份
  354. *
  355. * @param agentCode 应用编号
  356. * @param code 授权码
  357. */
  358. public GetUserInfoResp getUserInfo(String agentCode, String code) {
  359. ResponseEntity<GetUserInfoResp> resp = restTemplate.getForEntity(baseUrl + "/cgi-bin/user/getuserinfo?access_token={access_token}&code={code}",
  360. GetUserInfoResp.class,
  361. new ModelMap("access_token", getAccessToken(agentCode))
  362. .addAttribute("code", code));
  363. assertOK(resp);
  364. return resp.getBody();
  365. }
  366. /**
  367. * 获取用户UserID
  368. *
  369. * @param mobile 手机号
  370. * */
  371. public GetUserInfoResp getUserInfoByMobile(String mobile) {
  372. ResponseEntity<GetUserInfoResp> resp = restTemplate.postForEntity(baseUrl + "/cgi-bin/user/getuserid?access_token={access_token}&debug=1",
  373. new ModelMap("mobile", mobile),
  374. GetUserInfoResp.class,
  375. new ModelMap("access_token", getAccessToken(ADDRESS_BOOK_AGENT_CODE)));
  376. return resp.getBody();
  377. }
  378. /**
  379. * 获取用户openid
  380. * access_token 代开发企业应用的access_token
  381. * */
  382. public GetUserOpenIDListResp convert_openUserId(List<String> userList) {
  383. logger.info("convert_openUserId:{}",String.join("," , userList));
  384. ResponseEntity<GetUserOpenIDListResp> resp = restTemplate.postForEntity(
  385. baseUrl + "/cgi-bin/batch/userid_to_openuserid?access_token={access_token}",
  386. new ModelMap("userid_list", userList),
  387. GetUserOpenIDListResp.class,
  388. new ModelMap("access_token", getAccessToken(null)));
  389. logger.info("convert_openUserId--:{}",resp.getBody());
  390. assertOK(resp);
  391. return resp.getBody();
  392. }
  393. /**
  394. * 以下接口仅能获取企业内部openuserid
  395. * */
  396. /* public List<GetUserOpenIDListResp.OpenUser> convert_openUserId(List<String> userList) {
  397. List<GetUserOpenIDListResp.OpenUser> openUsers = new ArrayList<>();
  398. for(String userId : userList) {
  399. try {
  400. GetUserOpenIDListResp.OpenUser openUser = new GetUserOpenIDListResp.OpenUser();
  401. openUser.setUserid(userId);
  402. openUser.setOpen_userid(getOpenId(userId));
  403. openUsers.add(openUser);
  404. } catch (Exception e) {
  405. logger.error("转换用户ID失败: {}", userId, e);
  406. // 失败时仍保留用户ID,但openid为null
  407. GetUserOpenIDListResp.OpenUser openUser = new GetUserOpenIDListResp.OpenUser();
  408. openUser.setUserid(userId);
  409. openUsers.add(openUser);
  410. }
  411. }
  412. return openUsers;
  413. }*/
  414. }