31359a9b29eda25ccb515fd70d49ae5653470162.svn-base 39 KB

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