90052084bfc0416c18ef7462b3ff703ab18b01ca.svn-base 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. package org.jeecg.modules.system.service.impl;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  4. import com.jeecg.qywx.api.base.JwAccessTokenAPI;
  5. import com.jeecg.qywx.api.core.common.AccessToken;
  6. import com.jeecg.qywx.api.department.JwDepartmentAPI;
  7. import com.jeecg.qywx.api.department.vo.DepartMsgResponse;
  8. import com.jeecg.qywx.api.department.vo.Department;
  9. import com.jeecg.qywx.api.message.JwMessageAPI;
  10. import com.jeecg.qywx.api.message.vo.Text;
  11. import com.jeecg.qywx.api.message.vo.TextCard;
  12. import com.jeecg.qywx.api.message.vo.TextCardEntity;
  13. import com.jeecg.qywx.api.message.vo.TextEntity;
  14. import com.jeecg.qywx.api.user.JwUserAPI;
  15. import com.jeecg.qywx.api.user.vo.User;
  16. import lombok.extern.slf4j.Slf4j;
  17. import org.apache.commons.lang.StringUtils;
  18. import org.jeecg.common.api.dto.message.MessageDTO;
  19. import org.jeecg.common.constant.CommonConstant;
  20. import org.jeecg.common.system.util.JwtUtil;
  21. import org.jeecg.common.util.PasswordUtil;
  22. import org.jeecg.common.util.RestUtil;
  23. import org.jeecg.common.util.SpringContextUtils;
  24. import org.jeecg.common.util.oConvertUtils;
  25. import org.jeecg.config.thirdapp.ThirdAppConfig;
  26. import org.jeecg.modules.system.entity.*;
  27. import org.jeecg.modules.system.mapper.SysAnnouncementSendMapper;
  28. import org.jeecg.modules.system.model.SysDepartTreeModel;
  29. import org.jeecg.modules.system.model.ThirdLoginModel;
  30. import org.jeecg.modules.system.service.*;
  31. import org.jeecg.modules.system.vo.thirdapp.JwDepartmentTreeVo;
  32. import org.jeecg.modules.system.vo.thirdapp.SyncInfoVo;
  33. import org.springframework.beans.BeanUtils;
  34. import org.springframework.beans.factory.annotation.Autowired;
  35. import org.springframework.dao.DuplicateKeyException;
  36. import org.springframework.stereotype.Service;
  37. import java.util.ArrayList;
  38. import java.util.Arrays;
  39. import java.util.List;
  40. import java.util.stream.Collectors;
  41. /**
  42. * 第三方App对接:企业微信实现类
  43. */
  44. @Slf4j
  45. @Service
  46. public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
  47. @Autowired
  48. ThirdAppConfig thirdAppConfig;
  49. @Autowired
  50. private ISysDepartService sysDepartService;
  51. @Autowired
  52. private ISysUserService sysUserService;
  53. @Autowired
  54. private ISysThirdAccountService sysThirdAccountService;
  55. @Autowired
  56. private ISysUserDepartService sysUserDepartService;
  57. @Autowired
  58. private ISysPositionService sysPositionService;
  59. @Autowired
  60. private SysAnnouncementSendMapper sysAnnouncementSendMapper;
  61. // 第三方APP类型,当前固定为 wechat_enterprise
  62. public final String THIRD_TYPE = ThirdAppConfig.WECHAT_ENTERPRISE.toLowerCase();
  63. @Override
  64. public String getAccessToken() {
  65. String CORP_ID = thirdAppConfig.getWechatEnterprise().getClientId();
  66. String SECRET = thirdAppConfig.getWechatEnterprise().getClientSecret();
  67. AccessToken accessToken = JwAccessTokenAPI.getAccessToken(CORP_ID, SECRET);
  68. if (accessToken != null) {
  69. return accessToken.getAccesstoken();
  70. }
  71. log.warn("获取AccessToken失败");
  72. return null;
  73. }
  74. /** 获取APPToken,新版企业微信的秘钥是分开的 */
  75. public String getAppAccessToken() {
  76. String CORP_ID = thirdAppConfig.getWechatEnterprise().getClientId();
  77. String SECRET = thirdAppConfig.getWechatEnterprise().getAgentAppSecret();
  78. // 如果没有配置APP秘钥,就说明是老企业,可以通用秘钥
  79. if (oConvertUtils.isEmpty(SECRET)) {
  80. SECRET = thirdAppConfig.getWechatEnterprise().getClientSecret();
  81. }
  82. AccessToken accessToken = JwAccessTokenAPI.getAccessToken(CORP_ID, SECRET);
  83. if (accessToken != null) {
  84. return accessToken.getAccesstoken();
  85. }
  86. log.warn("获取AccessToken失败");
  87. return null;
  88. }
  89. @Override
  90. public boolean syncLocalDepartmentToThirdApp(String ids) {
  91. String accessToken = this.getAccessToken();
  92. if (accessToken == null) {
  93. return false;
  94. }
  95. // 获取企业微信所有的部门
  96. List<Department> departments = JwDepartmentAPI.getAllDepartment(accessToken);
  97. if (departments == null) {
  98. return false;
  99. }
  100. // 删除企业微信有但本地没有的部门(以本地部门数据为主)(以为企业微信不能创建同名部门,所以只能先删除)
  101. List<JwDepartmentTreeVo> departmentTreeList = JwDepartmentTreeVo.listToTree(departments);
  102. this.deleteDepartRecursion(departmentTreeList, accessToken, true);
  103. // 获取本地所有部门树结构
  104. List<SysDepartTreeModel> sysDepartsTree = sysDepartService.queryTreeList();
  105. // -- 企业微信不能创建新的顶级部门,所以新的顶级部门的parentId就为1
  106. Department parent = new Department();
  107. parent.setId("1");
  108. // 递归同步部门
  109. departments = JwDepartmentAPI.getAllDepartment(accessToken);
  110. this.syncDepartmentRecursion(sysDepartsTree, departments, parent, accessToken);
  111. return true;
  112. }
  113. // 递归删除部门以及子部门,由于企业微信不允许删除带有成员和子部门的部门,所以需要递归删除下子部门,然后把部门成员移动端根部门下
  114. private void deleteDepartRecursion(List<JwDepartmentTreeVo> children, String accessToken, boolean ifLocal) {
  115. for (JwDepartmentTreeVo departmentTree : children) {
  116. String depId = departmentTree.getId();
  117. // 过滤根部门
  118. if (!"1".equals(depId)) {
  119. // 判断本地是否有该部门
  120. if (ifLocal) {
  121. LambdaQueryWrapper<SysDepart> queryWrapper = new LambdaQueryWrapper<>();
  122. queryWrapper.eq(SysDepart::getQywxIdentifier, depId);
  123. SysDepart sysDepart = sysDepartService.getOne(queryWrapper);
  124. // 本地有该部门,不删除
  125. if (sysDepart != null) {
  126. if (departmentTree.hasChildren()) {
  127. this.deleteDepartRecursion(departmentTree.getChildren(), accessToken, true);
  128. }
  129. continue;
  130. }
  131. }
  132. // 判断是否有成员,有就移动到根部门
  133. List<User> departUserList = JwUserAPI.getUsersByDepartid(depId, "1", null, accessToken);
  134. if (departUserList != null && departUserList.size() > 0) {
  135. for (User user : departUserList) {
  136. User updateUser = new User();
  137. updateUser.setUserid(user.getUserid());
  138. updateUser.setDepartment(new Integer[]{1});
  139. JwUserAPI.updateUser(updateUser, accessToken);
  140. }
  141. }
  142. // 有子部门优先删除子部门
  143. if (departmentTree.hasChildren()) {
  144. this.deleteDepartRecursion(departmentTree.getChildren(), accessToken, false);
  145. }
  146. // 执行删除操作
  147. JwDepartmentAPI.deleteDepart(depId, accessToken);
  148. }
  149. }
  150. }
  151. // 递归同步部门到第三方APP
  152. private void syncDepartmentRecursion(List<SysDepartTreeModel> sysDepartsTree, List<Department> departments, Department parent, String accessToken) {
  153. if (sysDepartsTree != null && sysDepartsTree.size() != 0) {
  154. for1:
  155. for (SysDepartTreeModel depart : sysDepartsTree) {
  156. for (Department department : departments) {
  157. // id相同,代表已存在,执行修改操作
  158. if (department.getId().equals(depart.getQywxIdentifier())) {
  159. this.sysDepartToQwDepartment(depart, department, parent.getId());
  160. JwDepartmentAPI.updateDepart(department, accessToken);
  161. // 紧接着同步子级
  162. this.syncDepartmentRecursion(depart.getChildren(), departments, department, accessToken);
  163. // 跳出外部循环
  164. continue for1;
  165. }
  166. }
  167. // 循环到此说明是新部门,直接调接口创建
  168. Department newDepartment = this.sysDepartToQwDepartment(depart, parent.getId());
  169. DepartMsgResponse response = JwDepartmentAPI.createDepartment(newDepartment, accessToken);
  170. // 创建成功,将返回的id绑定到本地
  171. if (response != null && response.getId() != null) {
  172. SysDepart sysDepart = new SysDepart();
  173. sysDepart.setId(depart.getId());
  174. sysDepart.setQywxIdentifier(response.getId().toString());
  175. sysDepartService.updateById(sysDepart);
  176. Department newParent = new Department();
  177. newParent.setId(response.getId().toString());
  178. // 紧接着同步子级
  179. this.syncDepartmentRecursion(depart.getChildren(), departments, newParent, accessToken);
  180. }
  181. // 收集错误信息
  182. // this.syncUserCollectErrInfo(errCode, sysUser, errInfo);
  183. }
  184. }
  185. }
  186. @Override
  187. public SyncInfoVo syncThirdAppDepartmentToLocal(String ids) {
  188. SyncInfoVo syncInfo = new SyncInfoVo();
  189. String accessToken = this.getAccessToken();
  190. if (accessToken == null) {
  191. syncInfo.addFailInfo("accessToken获取失败!");
  192. return syncInfo;
  193. }
  194. // 获取企业微信所有的部门
  195. List<Department> departments = JwDepartmentAPI.getAllDepartment(accessToken);
  196. if (departments == null) {
  197. syncInfo.addFailInfo("企业微信部门信息获取失败!");
  198. return syncInfo;
  199. }
  200. String username = JwtUtil.getUserNameByToken(SpringContextUtils.getHttpServletRequest());
  201. // 将list转为tree
  202. List<JwDepartmentTreeVo> departmentTreeList = JwDepartmentTreeVo.listToTree(departments);
  203. // 递归同步部门
  204. this.syncDepartmentToLocalRecursion(departmentTreeList, null, username, syncInfo);
  205. return syncInfo;
  206. }
  207. /**
  208. * 递归同步部门到本地
  209. */
  210. private void syncDepartmentToLocalRecursion(List<JwDepartmentTreeVo> departmentTreeList, String sysParentId, String username, SyncInfoVo syncInfo) {
  211. if (departmentTreeList != null && departmentTreeList.size() != 0) {
  212. for (JwDepartmentTreeVo departmentTree : departmentTreeList) {
  213. String depId = departmentTree.getId();
  214. LambdaQueryWrapper<SysDepart> queryWrapper = new LambdaQueryWrapper<>();
  215. // 根据 qywxIdentifier 字段查询
  216. queryWrapper.eq(SysDepart::getQywxIdentifier, depId);
  217. SysDepart sysDepart = sysDepartService.getOne(queryWrapper);
  218. if (sysDepart != null) {
  219. // 执行更新操作
  220. SysDepart updateSysDepart = this.qwDepartmentToSysDepart(departmentTree, sysDepart);
  221. if (sysParentId != null) {
  222. updateSysDepart.setParentId(sysParentId);
  223. }
  224. try {
  225. sysDepartService.updateDepartDataById(updateSysDepart, username);
  226. String str = String.format("部门 %s 更新成功!", updateSysDepart.getDepartName());
  227. syncInfo.addSuccessInfo(str);
  228. } catch (Exception e) {
  229. this.syncDepartCollectErrInfo(e, departmentTree, syncInfo);
  230. }
  231. if (departmentTree.hasChildren()) {
  232. // 紧接着同步子级
  233. this.syncDepartmentToLocalRecursion(departmentTree.getChildren(), updateSysDepart.getId(), username, syncInfo);
  234. }
  235. } else {
  236. // 执行新增操作
  237. SysDepart newSysDepart = this.qwDepartmentToSysDepart(departmentTree, null);
  238. if (sysParentId != null) {
  239. newSysDepart.setParentId(sysParentId);
  240. }
  241. try {
  242. sysDepartService.saveDepartData(newSysDepart, username);
  243. String str = String.format("部门 %s 创建成功!", newSysDepart.getDepartName());
  244. syncInfo.addSuccessInfo(str);
  245. } catch (Exception e) {
  246. this.syncDepartCollectErrInfo(e, departmentTree, syncInfo);
  247. }
  248. // 紧接着同步子级
  249. if (departmentTree.hasChildren()) {
  250. this.syncDepartmentToLocalRecursion(departmentTree.getChildren(), newSysDepart.getId(), username, syncInfo);
  251. }
  252. }
  253. }
  254. }
  255. }
  256. @Override
  257. public SyncInfoVo syncLocalUserToThirdApp(String ids) {
  258. SyncInfoVo syncInfo = new SyncInfoVo();
  259. String accessToken = this.getAccessToken();
  260. if (accessToken == null) {
  261. syncInfo.addFailInfo("accessToken获取失败!");
  262. return syncInfo;
  263. }
  264. // 获取企业微信所有的用户
  265. List<User> qwUsers = JwUserAPI.getDetailUsersByDepartid("1", null, null, accessToken);
  266. if (qwUsers == null) {
  267. syncInfo.addFailInfo("企业微信用户列表查询失败!");
  268. return syncInfo;
  269. }
  270. List<SysUser> sysUsers;
  271. if (StringUtils.isNotBlank(ids)) {
  272. String[] idList = ids.split(",");
  273. LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
  274. queryWrapper.in(SysUser::getId, (Object[]) idList);
  275. // 获取本地指定用户
  276. sysUsers = sysUserService.list(queryWrapper);
  277. } else {
  278. // 获取本地所有用户
  279. sysUsers = sysUserService.list();
  280. }
  281. // 循环判断新用户和需要更新的用户
  282. for1:
  283. for (SysUser sysUser : sysUsers) {
  284. // 外部模拟登陆临时账号,不同步
  285. if ("_reserve_user_external".equals(sysUser.getUsername())) {
  286. continue;
  287. }
  288. /*
  289. * 判断是否同步过的逻辑:
  290. * 1. 查询 sys_third_account(第三方账号表)是否有数据,如果有代表已同步
  291. * 2. 本地表里没有,就先用手机号判断,不通过再用username判断。
  292. */
  293. User qwUser;
  294. SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneBySysUserId(sysUser.getId(), THIRD_TYPE);
  295. for (User qwUserTemp : qwUsers) {
  296. if (sysThirdAccount == null || oConvertUtils.isEmpty(sysThirdAccount.getThirdUserId()) || !sysThirdAccount.getThirdUserId().equals(qwUserTemp.getUserid())) {
  297. // sys_third_account 表匹配失败,尝试用手机号匹配
  298. String phone = sysUser.getPhone();
  299. if (!(oConvertUtils.isEmpty(phone) || phone.equals(qwUserTemp.getMobile()))) {
  300. // 手机号匹配失败,再尝试用username匹配
  301. String username = sysUser.getUsername();
  302. if (!(oConvertUtils.isEmpty(username) || username.equals(qwUserTemp.getUserid()))) {
  303. // username 匹配失败,直接跳到下一次循环继续
  304. continue;
  305. }
  306. }
  307. }
  308. // 循环到此说明用户匹配成功,进行更新操作
  309. qwUser = this.sysUserToQwUser(sysUser, qwUserTemp);
  310. int errCode = JwUserAPI.updateUser(qwUser, accessToken);
  311. // 收集错误信息
  312. this.syncUserCollectErrInfo(errCode, sysUser, syncInfo);
  313. this.thirdAccountSaveOrUpdate(sysThirdAccount, sysUser.getId(), qwUser.getUserid());
  314. // 更新完成,直接跳到下一次外部循环继续
  315. continue for1;
  316. }
  317. // 循环到此说明是新用户,直接调接口创建
  318. qwUser = this.sysUserToQwUser(sysUser);
  319. int errCode = JwUserAPI.createUser(qwUser, accessToken);
  320. // 收集错误信息
  321. boolean apiSuccess = this.syncUserCollectErrInfo(errCode, sysUser, syncInfo);
  322. if (apiSuccess) {
  323. this.thirdAccountSaveOrUpdate(sysThirdAccount, sysUser.getId(), qwUser.getUserid());
  324. }
  325. }
  326. return syncInfo;
  327. }
  328. @Override
  329. public SyncInfoVo syncThirdAppUserToLocal() {
  330. SyncInfoVo syncInfo = new SyncInfoVo();
  331. String accessToken = this.getAccessToken();
  332. if (accessToken == null) {
  333. syncInfo.addFailInfo("accessToken获取失败!");
  334. return syncInfo;
  335. }
  336. // 获取企业微信所有的用户
  337. List<User> qwUsersList = JwUserAPI.getDetailUsersByDepartid("1", null, null, accessToken);
  338. if (qwUsersList == null) {
  339. syncInfo.addFailInfo("企业微信用户列表查询失败!");
  340. return syncInfo;
  341. }
  342. //查询本地用户
  343. List<SysUser> sysUsersList = sysUserService.list();
  344. // 循环判断新用户和需要更新的用户
  345. for (User qwUser : qwUsersList) {
  346. /*
  347. * 判断是否同步过的逻辑:
  348. * 1. 查询 sys_third_account(第三方账号表)是否有数据,如果有代表已同步
  349. * 2. 本地表里没有,就先用手机号判断,不通过再用username判断。
  350. */
  351. SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneByThirdUserId(qwUser.getUserid(), THIRD_TYPE);
  352. List<SysUser> collect = sysUsersList.stream().filter(user -> (qwUser.getMobile().equals(user.getPhone()) || qwUser.getUserid().equals(user.getUsername()))
  353. ).collect(Collectors.toList());
  354. if (collect != null && collect.size() > 0) {
  355. SysUser sysUserTemp = collect.get(0);
  356. // 循环到此说明用户匹配成功,进行更新操作
  357. SysUser updateSysUser = this.qwUserToSysUser(qwUser, sysUserTemp);
  358. try {
  359. sysUserService.updateById(updateSysUser);
  360. String str = String.format("用户 %s(%s) 更新成功!", updateSysUser.getRealname(), updateSysUser.getUsername());
  361. syncInfo.addSuccessInfo(str);
  362. } catch (Exception e) {
  363. this.syncUserCollectErrInfo(e, qwUser, syncInfo);
  364. }
  365. this.thirdAccountSaveOrUpdate(sysThirdAccount, updateSysUser.getId(), qwUser.getUserid());
  366. // 更新完成,直接跳到下一次外部循环继续
  367. }else{
  368. // 没匹配到用户则走新增逻辑
  369. SysUser newSysUser = this.qwUserToSysUser(qwUser);
  370. try {
  371. sysUserService.save(newSysUser);
  372. String str = String.format("用户 %s(%s) 创建成功!", newSysUser.getRealname(), newSysUser.getUsername());
  373. syncInfo.addSuccessInfo(str);
  374. } catch (Exception e) {
  375. this.syncUserCollectErrInfo(e, qwUser, syncInfo);
  376. }
  377. this.thirdAccountSaveOrUpdate(sysThirdAccount, newSysUser.getId(), qwUser.getUserid());
  378. }
  379. }
  380. return syncInfo;
  381. }
  382. /**
  383. * 保存或修改第三方登录表
  384. *
  385. * @param sysThirdAccount 第三方账户表对象,为null就新增数据,否则就修改
  386. * @param sysUserId 本地系统用户ID
  387. * @param qwUserId 企业微信用户ID
  388. */
  389. private void thirdAccountSaveOrUpdate(SysThirdAccount sysThirdAccount, String sysUserId, String qwUserId) {
  390. if (sysThirdAccount == null) {
  391. sysThirdAccount = new SysThirdAccount();
  392. sysThirdAccount.setSysUserId(sysUserId);
  393. sysThirdAccount.setStatus(1);
  394. sysThirdAccount.setDelFlag(0);
  395. sysThirdAccount.setThirdType(THIRD_TYPE);
  396. }
  397. sysThirdAccount.setThirdUserId(qwUserId);
  398. sysThirdAccountService.saveOrUpdate(sysThirdAccount);
  399. }
  400. /**
  401. * 【同步用户】收集同步过程中的错误信息
  402. */
  403. private boolean syncUserCollectErrInfo(int errCode, SysUser sysUser, SyncInfoVo syncInfo) {
  404. if (errCode != 0) {
  405. String msg = "";
  406. // https://open.work.weixin.qq.com/api/doc/90000/90139/90313
  407. switch (errCode) {
  408. case 40003:
  409. msg = "无效的UserID";
  410. break;
  411. case 60129:
  412. msg = "手机和邮箱不能都为空";
  413. break;
  414. case 60102:
  415. msg = "UserID已存在";
  416. break;
  417. case 60103:
  418. msg = "手机号码不合法";
  419. break;
  420. case 60104:
  421. msg = "手机号码已存在";
  422. break;
  423. }
  424. String str = String.format("用户 %s(%s) 同步失败!错误码:%s——%s", sysUser.getUsername(), sysUser.getRealname(), errCode, msg);
  425. syncInfo.addFailInfo(str);
  426. return false;
  427. } else {
  428. String str = String.format("用户 %s(%s) 同步成功!", sysUser.getUsername(), sysUser.getRealname());
  429. syncInfo.addSuccessInfo(str);
  430. return true;
  431. }
  432. }
  433. private boolean syncUserCollectErrInfo(Exception e, User qwUser, SyncInfoVo syncInfo) {
  434. String msg;
  435. if (e instanceof DuplicateKeyException) {
  436. msg = e.getCause().getMessage();
  437. } else {
  438. msg = e.getMessage();
  439. }
  440. String str = String.format("用户 %s(%s) 同步失败!错误信息:%s", qwUser.getUserid(), qwUser.getName(), msg);
  441. syncInfo.addFailInfo(str);
  442. return false;
  443. }
  444. private boolean syncDepartCollectErrInfo(Exception e, Department department, SyncInfoVo syncInfo) {
  445. String msg;
  446. if (e instanceof DuplicateKeyException) {
  447. msg = e.getCause().getMessage();
  448. } else {
  449. msg = e.getMessage();
  450. }
  451. String str = String.format("部门 %s(%s) 同步失败!错误信息:%s", department.getName(), department.getId(), msg);
  452. syncInfo.addFailInfo(str);
  453. return false;
  454. }
  455. /**
  456. * 【同步用户】将SysUser转为企业微信的User对象(创建新用户)
  457. */
  458. private User sysUserToQwUser(SysUser sysUser) {
  459. User user = new User();
  460. // 通过 username 来关联
  461. user.setUserid(sysUser.getUsername());
  462. return this.sysUserToQwUser(sysUser, user);
  463. }
  464. /**
  465. * 【同步用户】将SysUser转为企业微信的User对象(更新旧用户)
  466. */
  467. private User sysUserToQwUser(SysUser sysUser, User user) {
  468. user.setName(sysUser.getRealname());
  469. user.setMobile(sysUser.getPhone());
  470. // 查询并同步用户部门关系
  471. List<SysDepart> departList = this.getUserDepart(sysUser);
  472. if (departList != null) {
  473. List<Integer> departmentIdList = new ArrayList<>();
  474. // 企业微信 1表示为上级,0表示非上级
  475. List<Integer> isLeaderInDept = new ArrayList<>();
  476. // 当前用户管理的部门
  477. List<String> manageDepartIdList = new ArrayList<>();
  478. if (oConvertUtils.isNotEmpty(sysUser.getDepartIds())) {
  479. manageDepartIdList = Arrays.asList(sysUser.getDepartIds().split(","));
  480. }
  481. for (SysDepart sysDepart : departList) {
  482. // 企业微信的部门id
  483. if (oConvertUtils.isNotEmpty(sysDepart.getQywxIdentifier())) {
  484. try {
  485. departmentIdList.add(Integer.parseInt(sysDepart.getQywxIdentifier()));
  486. } catch (NumberFormatException ignored) {
  487. continue;
  488. }
  489. // 判断用户身份,是否为上级
  490. if (CommonConstant.USER_IDENTITY_2.equals(sysUser.getUserIdentity())) {
  491. // 判断当前部门是否为该用户管理的部门
  492. isLeaderInDept.add(manageDepartIdList.contains(sysDepart.getId()) ? 1 : 0);
  493. } else {
  494. isLeaderInDept.add(0);
  495. }
  496. }
  497. }
  498. user.setDepartment(departmentIdList.toArray(new Integer[]{}));
  499. // 个数必须和参数department的个数一致,表示在所在的部门内是否为上级。1表示为上级,0表示非上级。在审批等应用里可以用来标识上级审批人
  500. user.setIs_leader_in_dept(isLeaderInDept.toArray(new Integer[]{}));
  501. }
  502. if (user.getDepartment() == null || user.getDepartment().length == 0) {
  503. // 没有找到匹配部门,同步到根部门下
  504. user.setDepartment(new Integer[]{1});
  505. user.setIs_leader_in_dept(new Integer[]{0});
  506. }
  507. // 职务翻译
  508. if (oConvertUtils.isNotEmpty(sysUser.getPost())) {
  509. SysPosition position = sysPositionService.getByCode(sysUser.getPost());
  510. if (position != null) {
  511. user.setPosition(position.getName());
  512. }
  513. }
  514. if (sysUser.getSex() != null) {
  515. user.setGender(sysUser.getSex().toString());
  516. }
  517. user.setEmail(sysUser.getEmail());
  518. // 启用/禁用成员(状态),规则不同,需要转换
  519. // 企业微信规则:1表示启用成员,0表示禁用成员
  520. // JEECG规则:1正常,2冻结
  521. if (sysUser.getStatus() != null) {
  522. if (sysUser.getStatus() == 1 || sysUser.getStatus() == 2) {
  523. user.setEnable(sysUser.getStatus() == 1 ? 1 : 0);
  524. } else {
  525. user.setEnable(1);
  526. }
  527. }
  528. user.setTelephone(sysUser.getTelephone());// 座机号
  529. // --- 企业微信没有逻辑删除的功能
  530. // update-begin--Author:sunjianlei Date:20210520 for:本地逻辑删除的用户,在企业微信里禁用 -----
  531. if (CommonConstant.DEL_FLAG_1.equals(sysUser.getDelFlag())) {
  532. user.setEnable(0);
  533. }
  534. // update-end--Author:sunjianlei Date:20210520 for:本地逻辑删除的用户,在企业微信里冻结 -----
  535. return user;
  536. }
  537. /**
  538. * 查询用户和部门的关系
  539. */
  540. private List<SysDepart> getUserDepart(SysUser sysUser) {
  541. // 根据用户部门关系表查询出用户的部门
  542. LambdaQueryWrapper<SysUserDepart> queryWrapper = new LambdaQueryWrapper<>();
  543. queryWrapper.eq(SysUserDepart::getUserId, sysUser.getId());
  544. List<SysUserDepart> sysUserDepartList = sysUserDepartService.list(queryWrapper);
  545. if (sysUserDepartList.size() == 0) {
  546. return null;
  547. }
  548. // 根据用户部门
  549. LambdaQueryWrapper<SysDepart> departQueryWrapper = new LambdaQueryWrapper<>();
  550. List<String> departIdList = sysUserDepartList.stream().map(SysUserDepart::getDepId).collect(Collectors.toList());
  551. departQueryWrapper.in(SysDepart::getId, departIdList);
  552. List<SysDepart> departList = sysDepartService.list(departQueryWrapper);
  553. return departList.size() == 0 ? null : departList;
  554. }
  555. /**
  556. * 【同步用户】将企业微信的User对象转为SysUser(创建新用户)
  557. */
  558. private SysUser qwUserToSysUser(User user) {
  559. SysUser sysUser = new SysUser();
  560. sysUser.setDelFlag(0);
  561. sysUser.setStatus(1);
  562. // 通过 username 来关联
  563. sysUser.setUsername(user.getUserid());
  564. // 密码默认为 “123456”,随机加盐
  565. String password = "123456", salt = oConvertUtils.randomGen(8);
  566. String passwordEncode = PasswordUtil.encrypt(sysUser.getUsername(), password, salt);
  567. sysUser.setSalt(salt);
  568. sysUser.setPassword(passwordEncode);
  569. return this.qwUserToSysUser(user, sysUser);
  570. }
  571. /**
  572. * 【同步用户】将企业微信的User对象转为SysUser(更新旧用户)
  573. */
  574. private SysUser qwUserToSysUser(User qwUser, SysUser oldSysUser) {
  575. SysUser sysUser = new SysUser();
  576. BeanUtils.copyProperties(oldSysUser, sysUser);
  577. sysUser.setRealname(qwUser.getName());
  578. sysUser.setPost(qwUser.getPosition());
  579. try {
  580. sysUser.setSex(Integer.parseInt(qwUser.getGender()));
  581. } catch (NumberFormatException ignored) {
  582. }
  583. // 因为唯一键约束的原因,如果原数据和旧数据相同,就不更新
  584. if (oConvertUtils.isNotEmpty(qwUser.getEmail()) && !qwUser.getEmail().equals(sysUser.getEmail())) {
  585. sysUser.setEmail(qwUser.getEmail());
  586. } else {
  587. sysUser.setEmail(null);
  588. }
  589. // 因为唯一键约束的原因,如果原数据和旧数据相同,就不更新
  590. if (oConvertUtils.isNotEmpty(qwUser.getMobile()) && !qwUser.getMobile().equals(sysUser.getPhone())) {
  591. sysUser.setPhone(qwUser.getMobile());
  592. } else {
  593. sysUser.setPhone(null);
  594. }
  595. // 启用/禁用成员(状态),规则不同,需要转换
  596. // 企业微信规则:1表示启用成员,0表示禁用成员
  597. // JEECG规则:1正常,2冻结
  598. if (qwUser.getEnable() != null) {
  599. sysUser.setStatus(qwUser.getEnable() == 1 ? 1 : 2);
  600. }
  601. sysUser.setTelephone(qwUser.getTelephone());// 座机号
  602. // --- 企业微信没有逻辑删除的功能
  603. // sysUser.setDelFlag()
  604. return sysUser;
  605. }
  606. /**
  607. * 【同步部门】将SysDepartTreeModel转为企业微信的Department对象(创建新部门)
  608. */
  609. private Department sysDepartToQwDepartment(SysDepartTreeModel departTree, String parentId) {
  610. Department department = new Department();
  611. return this.sysDepartToQwDepartment(departTree, department, parentId);
  612. }
  613. /**
  614. * 【同步部门】将SysDepartTreeModel转为企业微信的Department对象
  615. */
  616. private Department sysDepartToQwDepartment(SysDepartTreeModel departTree, Department department, String parentId) {
  617. department.setName(departTree.getDepartName());
  618. department.setParentid(parentId);
  619. if (departTree.getDepartOrder() != null) {
  620. department.setOrder(departTree.getDepartOrder().toString());
  621. }
  622. return department;
  623. }
  624. /**
  625. * 【同步部门】将企业微信的Department对象转为SysDepart
  626. */
  627. private SysDepart qwDepartmentToSysDepart(Department department, SysDepart oldSysDepart) {
  628. SysDepart sysDepart = new SysDepart();
  629. if (oldSysDepart != null) {
  630. BeanUtils.copyProperties(oldSysDepart, sysDepart);
  631. }
  632. sysDepart.setQywxIdentifier(department.getId());
  633. sysDepart.setDepartName(department.getName());
  634. try {
  635. sysDepart.setDepartOrder(Integer.parseInt(department.getOrder()));
  636. } catch (NumberFormatException ignored) {
  637. }
  638. return sysDepart;
  639. }
  640. @Override
  641. public int removeThirdAppUser(List<String> userIdList) {
  642. // 判断启用状态
  643. if (!thirdAppConfig.isWechatEnterpriseEnabled()) {
  644. return -1;
  645. }
  646. int count = 0;
  647. if (userIdList != null && userIdList.size() > 0) {
  648. String accessToken = this.getAccessToken();
  649. if (accessToken == null) {
  650. return count;
  651. }
  652. LambdaQueryWrapper<SysThirdAccount> queryWrapper = new LambdaQueryWrapper<>();
  653. queryWrapper.eq(SysThirdAccount::getThirdType, THIRD_TYPE);
  654. queryWrapper.in(SysThirdAccount::getSysUserId, userIdList);
  655. // 根据userId,获取第三方用户的id
  656. List<SysThirdAccount> thirdAccountList = sysThirdAccountService.list(queryWrapper);
  657. List<String> thirdUserIdList = thirdAccountList.stream().map(SysThirdAccount::getThirdUserId).collect(Collectors.toList());
  658. for (String thirdUserId : thirdUserIdList) {
  659. if (oConvertUtils.isNotEmpty(thirdUserId)) {
  660. // 没有批量删除的接口
  661. int err = JwUserAPI.deleteUser(thirdUserId, accessToken);
  662. if (err == 0) {
  663. count++;
  664. }
  665. }
  666. }
  667. }
  668. return count;
  669. }
  670. @Override
  671. public boolean sendMessage(MessageDTO message) {
  672. return this.sendMessage(message, false);
  673. }
  674. @Override
  675. public boolean sendMessage(MessageDTO message, boolean verifyConfig) {
  676. JSONObject response = this.sendMessageResponse(message, verifyConfig);
  677. if (response != null) {
  678. return response.getIntValue("errcode") == 0;
  679. }
  680. return false;
  681. }
  682. public JSONObject sendMessageResponse(MessageDTO message, boolean verifyConfig) {
  683. if (verifyConfig && !thirdAppConfig.isWechatEnterpriseEnabled()) {
  684. return null;
  685. }
  686. String accessToken = this.getAppAccessToken();
  687. if (accessToken == null) {
  688. return null;
  689. }
  690. Text text = new Text();
  691. text.setMsgtype("text");
  692. text.setTouser(this.getTouser(message.getToUser(), message.isToAll()));
  693. TextEntity entity = new TextEntity();
  694. entity.setContent(message.getContent());
  695. text.setText(entity);
  696. text.setAgentid(thirdAppConfig.getWechatEnterprise().getAgentIdInt());
  697. return JwMessageAPI.sendTextMessage(text, accessToken);
  698. }
  699. /**
  700. * 发送文本卡片消息(SysAnnouncement定制)
  701. *
  702. * @param announcement
  703. * @param verifyConfig 是否验证配置(未启用的APP会拒绝发送)
  704. * @return
  705. */
  706. public JSONObject sendTextCardMessage(SysAnnouncement announcement, boolean verifyConfig) {
  707. if (verifyConfig && !thirdAppConfig.isWechatEnterpriseEnabled()) {
  708. return null;
  709. }
  710. String accessToken = this.getAppAccessToken();
  711. if (accessToken == null) {
  712. return null;
  713. }
  714. TextCard textCard = new TextCard();
  715. textCard.setAgentid(thirdAppConfig.getWechatEnterprise().getAgentIdInt());
  716. boolean isToAll = CommonConstant.MSG_TYPE_ALL.equals(announcement.getMsgType());
  717. String usernameString = "";
  718. if (!isToAll) {
  719. // 将userId转为username
  720. String userId = announcement.getUserIds();
  721. String[] userIds = null;
  722. if(oConvertUtils.isNotEmpty(userId)){
  723. userIds = userId.substring(0, (userId.length() - 1)).split(",");
  724. }else{
  725. LambdaQueryWrapper<SysAnnouncementSend> queryWrapper = new LambdaQueryWrapper<>();
  726. queryWrapper.eq(SysAnnouncementSend::getAnntId, announcement.getId());
  727. SysAnnouncementSend sysAnnouncementSend = sysAnnouncementSendMapper.selectOne(queryWrapper);
  728. userIds = new String[] {sysAnnouncementSend.getUserId()};
  729. }
  730. List<String> usernameList = sysUserService.userIdToUsername(Arrays.asList(userIds));
  731. usernameString = String.join(",", usernameList);
  732. }
  733. textCard.setTouser(this.getTouser(usernameString, isToAll));
  734. TextCardEntity entity = new TextCardEntity();
  735. entity.setTitle(announcement.getTitile());
  736. entity.setDescription(oConvertUtils.getString(announcement.getMsgAbstract(),"空"));
  737. entity.setUrl(RestUtil.getBaseUrl() + "/sys/annountCement/show/" + announcement.getId());
  738. textCard.setTextcard(entity);
  739. return JwMessageAPI.sendTextCardMessage(textCard, accessToken);
  740. }
  741. private String getTouser(String origin, boolean toAll) {
  742. if (toAll) {
  743. return "@all";
  744. } else {
  745. String[] toUsers = origin.split(",");
  746. // 通过第三方账号表查询出第三方userId
  747. List<SysThirdAccount> thirdAccountList = sysThirdAccountService.listThirdUserIdByUsername(toUsers, THIRD_TYPE);
  748. List<String> toUserList = thirdAccountList.stream().map(SysThirdAccount::getThirdUserId).collect(Collectors.toList());
  749. // 多个接收者用‘|’分隔
  750. return String.join("|", toUserList);
  751. }
  752. }
  753. /**
  754. * 根据第三方登录获取到的code来获取第三方app的用户ID
  755. *
  756. * @param code
  757. * @return
  758. */
  759. public String getUserIdByThirdCode(String code, String accessToken) {
  760. JSONObject response = JwUserAPI.getUserInfoByCode(code, accessToken);
  761. if (response != null) {
  762. log.info("response: " + response.toJSONString());
  763. if (response.getIntValue("errcode") == 0) {
  764. return response.getString("UserId");
  765. }
  766. }
  767. return null;
  768. }
  769. /**
  770. * OAuth2登录,成功返回登录的SysUser,失败返回null
  771. */
  772. public SysUser oauth2Login(String code) {
  773. String accessToken = this.getAppAccessToken();
  774. if (accessToken == null) {
  775. return null;
  776. }
  777. String appUserId = this.getUserIdByThirdCode(code, accessToken);
  778. if (appUserId != null) {
  779. // 判断第三方用户表有没有这个人
  780. LambdaQueryWrapper<SysThirdAccount> queryWrapper = new LambdaQueryWrapper<>();
  781. queryWrapper.eq(SysThirdAccount::getThirdUserUuid, appUserId);
  782. queryWrapper.or().eq(SysThirdAccount::getThirdUserId, appUserId);
  783. queryWrapper.eq(SysThirdAccount::getThirdType, THIRD_TYPE);
  784. SysThirdAccount thirdAccount = sysThirdAccountService.getOne(queryWrapper);
  785. if (thirdAccount != null) {
  786. return this.getSysUserByThird(thirdAccount, null, appUserId, accessToken);
  787. } else {
  788. // 直接创建新账号
  789. User appUser = JwUserAPI.getUserByUserid(appUserId, accessToken);
  790. ThirdLoginModel tlm = new ThirdLoginModel(THIRD_TYPE, appUser.getUserid(), appUser.getName(), appUser.getAvatar());
  791. thirdAccount = sysThirdAccountService.saveThirdUser(tlm);
  792. return this.getSysUserByThird(thirdAccount, appUser, null, null);
  793. }
  794. }
  795. return null;
  796. }
  797. /**
  798. * 根据第三方账号获取本地账号,如果不存在就创建
  799. *
  800. * @param thirdAccount
  801. * @param appUser
  802. * @param appUserId
  803. * @param accessToken
  804. * @return
  805. */
  806. private SysUser getSysUserByThird(SysThirdAccount thirdAccount, User appUser, String appUserId, String accessToken) {
  807. String sysUserId = thirdAccount.getSysUserId();
  808. if (oConvertUtils.isNotEmpty(sysUserId)) {
  809. return sysUserService.getById(sysUserId);
  810. } else {
  811. // 如果没有 sysUserId ,说明没有绑定账号,获取到手机号之后进行绑定
  812. if (appUser == null) {
  813. appUser = JwUserAPI.getUserByUserid(appUserId, accessToken);
  814. }
  815. // 判断系统里是否有这个手机号的用户
  816. SysUser sysUser = sysUserService.getUserByPhone(appUser.getMobile());
  817. if (sysUser != null) {
  818. thirdAccount.setAvatar(appUser.getAvatar());
  819. thirdAccount.setRealname(appUser.getName());
  820. thirdAccount.setThirdUserId(appUser.getUserid());
  821. thirdAccount.setThirdUserUuid(appUser.getUserid());
  822. thirdAccount.setSysUserId(sysUser.getId());
  823. sysThirdAccountService.updateById(thirdAccount);
  824. return sysUser;
  825. } else {
  826. // 没有就走创建逻辑
  827. return sysThirdAccountService.createUser(appUser.getMobile(), appUser.getUserid());
  828. }
  829. }
  830. }
  831. }