EiamLocalService.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. package com.malk.benteler.service;
  2. import com.malk.benteler.dto.EiamBatchItemResult;
  3. import com.malk.benteler.dto.EiamBatchOperation;
  4. import com.malk.benteler.dto.EiamBatchResult;
  5. import com.malk.benteler.dto.EiamBatchStage;
  6. import com.malk.benteler.dto.EiamCreateUserItem;
  7. import com.malk.benteler.dto.EiamDeleteUserItem;
  8. import com.malk.benteler.dto.EiamUpdateUserItem;
  9. import com.malk.server.common.McException;
  10. import com.malk.server.eiam.EiamApiResponse;
  11. import com.malk.server.eiam.EiamConf;
  12. import com.malk.server.eiam.EiamCustomField;
  13. import com.malk.server.eiam.EiamCustomFieldUpdate;
  14. import com.malk.service.eiam.EiamClient_User;
  15. import com.malk.service.eiam.EiamService;
  16. import org.apache.commons.lang3.StringUtils;
  17. import org.springframework.stereotype.Service;
  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.HashMap;
  21. import java.util.List;
  22. import java.util.Map;
  23. /**
  24. * 本特勒 EIAM 批量业务编排。
  25. */
  26. @Service
  27. public class EiamLocalService {
  28. private final EiamService eiamService;
  29. private final EiamClient_User eiamClientUser;
  30. private final EiamConf eiamConf;
  31. public EiamLocalService(EiamService eiamService, EiamClient_User eiamClientUser,
  32. EiamConf eiamConf) {
  33. this.eiamService = eiamService;
  34. this.eiamClientUser = eiamClientUser;
  35. this.eiamConf = eiamConf;
  36. }
  37. /**
  38. * 批量创建用户,单条失败不中断后续条目。
  39. *
  40. * @param items 创建条目
  41. * @return 批量结果
  42. */
  43. public EiamBatchResult batchCreate(List<EiamCreateUserItem> items) {
  44. validateBatch(items);
  45. String token = eiamService.getAccessToken();
  46. List<EiamBatchItemResult> results = new ArrayList<>();
  47. for (int index = 0; index < items.size(); index++) {
  48. results.add(createOne(token, items.get(index), index));
  49. }
  50. return EiamBatchResult.of(EiamBatchOperation.CREATE, results);
  51. }
  52. /**
  53. * 批量更新用户,支持账户属性和主组织变更。
  54. *
  55. * @param items 更新条目
  56. * @return 批量结果
  57. */
  58. public EiamBatchResult batchUpdate(List<EiamUpdateUserItem> items) {
  59. validateBatch(items);
  60. String token = eiamService.getAccessToken();
  61. List<EiamBatchItemResult> results = new ArrayList<>();
  62. for (int index = 0; index < items.size(); index++) {
  63. results.add(updateOne(token, items.get(index), index));
  64. }
  65. return EiamBatchResult.of(EiamBatchOperation.UPDATE, results);
  66. }
  67. /**
  68. * 批量删除用户,单条失败不中断后续条目。
  69. *
  70. * @param items 删除条目
  71. * @return 批量结果
  72. */
  73. public EiamBatchResult batchDelete(List<EiamDeleteUserItem> items) {
  74. validateBatch(items);
  75. String token = eiamService.getAccessToken();
  76. List<EiamBatchItemResult> results = new ArrayList<>();
  77. for (int index = 0; index < items.size(); index++) {
  78. results.add(deleteOne(token, items.get(index), index));
  79. }
  80. return EiamBatchResult.of(EiamBatchOperation.DELETE, results);
  81. }
  82. private EiamBatchItemResult createOne(String token, EiamCreateUserItem item, int index) {
  83. EiamBatchStage stage = EiamBatchStage.VALIDATE;
  84. try {
  85. validateCreateItem(item);
  86. stage = EiamBatchStage.CREATE_USER;
  87. EiamApiResponse response = eiamClientUser.createUser(token, eiamConf.getInstanceId(),
  88. eiamConf.getApplicationId(), item.getUsername(),
  89. item.getPrimaryOrganizationalUnitId(), createBody(item));
  90. assertEiamSuccess(response);
  91. if (StringUtils.isBlank(response.getUserId())) {
  92. throw new McException("EIAM_EMPTY_USER_ID", "CreateUser 成功响应缺少 userId");
  93. }
  94. return success(index, item.getFormInstanceId(), item.getUsername(), response.getUserId());
  95. } catch (RuntimeException ex) {
  96. return failure(index, item == null ? null : item.getFormInstanceId(),
  97. item == null ? null : item.getUsername(), null, stage, ex, false);
  98. }
  99. }
  100. private EiamBatchItemResult updateOne(String token, EiamUpdateUserItem item, int index) {
  101. EiamBatchStage stage = EiamBatchStage.VALIDATE;
  102. boolean updateCompleted = false;
  103. try {
  104. validateUpdateItem(item);
  105. Map<String, Object> patchBody = updateBody(item);
  106. if (!patchBody.isEmpty()) {
  107. stage = EiamBatchStage.PATCH_USER;
  108. assertEiamSuccess(eiamClientUser.patchUser(token, eiamConf.getInstanceId(),
  109. eiamConf.getApplicationId(), item.getUserId(), patchBody));
  110. updateCompleted = true;
  111. }
  112. if (StringUtils.isNotBlank(item.getPrimaryOrganizationalUnitId())) {
  113. stage = EiamBatchStage.SET_PRIMARY_ORG;
  114. assertEiamSuccess(eiamClientUser.setUserPrimaryOrganizationalUnit(token,
  115. eiamConf.getInstanceId(), eiamConf.getApplicationId(), item.getUserId(),
  116. item.getPrimaryOrganizationalUnitId()));
  117. updateCompleted = true;
  118. }
  119. if (StringUtils.isNotBlank(item.getAdditionalOrganizationalUnitId())) {
  120. stage = EiamBatchStage.ADD_ORG;
  121. assertEiamSuccess(eiamClientUser.addUserToOrganizationalUnits(token,
  122. eiamConf.getInstanceId(), eiamConf.getApplicationId(), item.getUserId(),
  123. Collections.singletonList(item.getAdditionalOrganizationalUnitId())));
  124. }
  125. return success(index, item.getFormInstanceId(), userKey(item), item.getUserId());
  126. } catch (RuntimeException ex) {
  127. return failure(index, item == null ? null : item.getFormInstanceId(),
  128. item == null ? null : userKey(item), item == null ? null : item.getUserId(),
  129. stage, ex, updateCompleted);
  130. }
  131. }
  132. private EiamBatchItemResult deleteOne(String token, EiamDeleteUserItem item, int index) {
  133. EiamBatchStage stage = EiamBatchStage.VALIDATE;
  134. try {
  135. validateFormInstanceId(item == null ? null : item.getFormInstanceId());
  136. if (item == null || StringUtils.isBlank(item.getUserId())) {
  137. throw new McException("EIAM_ITEM_INVALID", "userId 不能为空");
  138. }
  139. stage = EiamBatchStage.DELETE_USER;
  140. assertEiamSuccess(eiamClientUser.deleteUser(token, eiamConf.getInstanceId(),
  141. eiamConf.getApplicationId(), item.getUserId()));
  142. return success(index, item.getFormInstanceId(), deleteUserKey(item), item.getUserId());
  143. } catch (RuntimeException ex) {
  144. return failure(index, item == null ? null : item.getFormInstanceId(),
  145. item == null ? null : deleteUserKey(item), item == null ? null : item.getUserId(),
  146. stage, ex, false);
  147. }
  148. }
  149. private Map<String, Object> createBody(EiamCreateUserItem item) {
  150. Map<String, Object> body = new HashMap<>();
  151. putIfNotBlank(body, "displayName", item.getDisplayName());
  152. String phoneNumber = StringUtils.defaultIfBlank(item.getPhoneNumber(), item.getUsername());
  153. putIfNotBlank(body, "phoneNumber", phoneNumber);
  154. if (StringUtils.isNotBlank(phoneNumber)) {
  155. body.put("phoneRegion", StringUtils.defaultIfBlank(item.getPhoneRegion(), "86"));
  156. body.put("phoneNumberVerified", true);
  157. }
  158. putIfNotBlank(body, "email", item.getEmail());
  159. if (StringUtils.isNotBlank(item.getEmail())) {
  160. body.put("emailVerified", false);
  161. }
  162. putIfNotBlank(body, "description", item.getDescription());
  163. if (item.getCustomFields() != null) {
  164. body.put("customFields", item.getCustomFields());
  165. }
  166. Map<String, Object> passwordConfig = new HashMap<>();
  167. passwordConfig.put("passwordInitializationPolicyPriority", "custom");
  168. passwordConfig.put("passwordForcedUpdateStatus", "enabled");
  169. // prd 客户不接收初始密码通知,用户首次登录时通过重置密码流程完成密码设置。
  170. passwordConfig.put("passwordInitializationType", "random");
  171. body.put("passwordInitializationConfig", passwordConfig);
  172. return body;
  173. }
  174. private Map<String, Object> updateBody(EiamUpdateUserItem item) {
  175. Map<String, Object> body = new HashMap<>();
  176. putIfNotBlank(body, "username", item.getUsername());
  177. putIfNotBlank(body, "displayName", item.getDisplayName());
  178. putIfNotBlank(body, "phoneNumber", item.getPhoneNumber());
  179. if (StringUtils.isNotBlank(item.getPhoneNumber())) {
  180. body.put("phoneRegion", StringUtils.defaultIfBlank(item.getPhoneRegion(), "86"));
  181. body.put("phoneNumberVerified", true);
  182. }
  183. putIfNotBlank(body, "email", item.getEmail());
  184. if (StringUtils.isNotBlank(item.getEmail())) {
  185. body.put("emailVerified", false);
  186. }
  187. if (item.getCustomFields() != null && !item.getCustomFields().isEmpty()) {
  188. body.put("customFields", item.getCustomFields());
  189. }
  190. return body;
  191. }
  192. private void validateCreateItem(EiamCreateUserItem item) {
  193. validateFormInstanceId(item == null ? null : item.getFormInstanceId());
  194. if (item == null || StringUtils.isBlank(item.getUsername())) {
  195. throw new McException("EIAM_ITEM_INVALID", "username 不能为空");
  196. }
  197. if (StringUtils.isBlank(item.getDisplayName())) {
  198. throw new McException("EIAM_ITEM_INVALID", "displayName 不能为空");
  199. }
  200. if (StringUtils.isBlank(item.getPrimaryOrganizationalUnitId())) {
  201. throw new McException("EIAM_ITEM_INVALID", "primaryOrganizationalUnitId 不能为空");
  202. }
  203. // prd 新增账号必须完整携带宜搭主表和人员子表中的 EIAM 自定义字段。
  204. validateRequiredCustomField(item, "department", "iDaaS 部门名称");
  205. validateRequiredCustomField(item, "company_name", "所属公司");
  206. validateRequiredCustomField(item, "company_code", "公司代码");
  207. validateRequiredCustomField(item, "job_title", "职位");
  208. }
  209. private void validateRequiredCustomField(EiamCreateUserItem item, String fieldName,
  210. String displayName) {
  211. if (item.getCustomFields() != null) {
  212. for (EiamCustomField field : item.getCustomFields()) {
  213. if (field != null && StringUtils.equals(fieldName, field.getFieldName())
  214. && StringUtils.isNotBlank(field.getFieldValue())) {
  215. return;
  216. }
  217. }
  218. }
  219. throw new McException("EIAM_ITEM_INVALID", displayName + " 不能为空");
  220. }
  221. private void validateUpdateItem(EiamUpdateUserItem item) {
  222. validateFormInstanceId(item == null ? null : item.getFormInstanceId());
  223. if (item == null || StringUtils.isBlank(item.getUserId())) {
  224. throw new McException("EIAM_ITEM_INVALID", "userId 不能为空");
  225. }
  226. if (containsUpdateContent(item, BentelerYidaFormMapper.UPDATE_PHONE)
  227. && StringUtils.isBlank(item.getPhoneNumber())) {
  228. throw new McException("EIAM_ITEM_INVALID", "手机号不能为空");
  229. }
  230. if (containsUpdateContent(item, BentelerYidaFormMapper.UPDATE_EMPLOYEE_NUMBER)) {
  231. validateCustomFieldUpdate(item, "employee_id_number", "工号");
  232. }
  233. if (containsUpdateContent(item, BentelerYidaFormMapper.UPDATE_JOB_TITLE)) {
  234. validateCustomFieldUpdate(item, "job_title", "职位");
  235. }
  236. if (updateBody(item).isEmpty() && StringUtils.isBlank(item.getPrimaryOrganizationalUnitId())
  237. && StringUtils.isBlank(item.getAdditionalOrganizationalUnitId())) {
  238. throw new McException("EIAM_ITEM_INVALID", "更新内容不能为空");
  239. }
  240. }
  241. private boolean containsUpdateContent(EiamUpdateUserItem item, String updateContent) {
  242. return item.getUpdateContents() != null && item.getUpdateContents().contains(updateContent);
  243. }
  244. private void validateCustomFieldUpdate(EiamUpdateUserItem item, String fieldName,
  245. String displayName) {
  246. if (item.getCustomFields() != null) {
  247. for (EiamCustomFieldUpdate field : item.getCustomFields()) {
  248. if (field != null && StringUtils.equals(fieldName, field.getFieldName())
  249. && StringUtils.isNotBlank(field.getFieldValue())) {
  250. return;
  251. }
  252. }
  253. }
  254. throw new McException("EIAM_ITEM_INVALID", displayName + " 不能为空");
  255. }
  256. private void validateBatch(List<?> items) {
  257. if (items == null || items.isEmpty()) {
  258. throw new McException("EIAM_BATCH_EMPTY", "items 不能为空");
  259. }
  260. }
  261. private void validateFormInstanceId(String formInstanceId) {
  262. if (StringUtils.isBlank(formInstanceId)) {
  263. throw new McException("EIAM_ITEM_INVALID", "formInstanceId 不能为空");
  264. }
  265. }
  266. private void assertEiamSuccess(EiamApiResponse response) {
  267. if (response == null) {
  268. throw new McException("EIAM_EMPTY_RESPONSE", "EIAM 响应为空");
  269. }
  270. String code = StringUtils.defaultIfBlank(response.getCode(), response.getError());
  271. if (StringUtils.isBlank(code)) {
  272. return;
  273. }
  274. String message = StringUtils.defaultIfBlank(response.getMessage(), response.getErrorDescription());
  275. throw new McException(code, StringUtils.defaultIfBlank(message, "EIAM 调用失败"));
  276. }
  277. private EiamBatchItemResult success(int index, String formInstanceId, String userKey,
  278. String userId) {
  279. return EiamBatchItemResult.builder().index(index).formInstanceId(formInstanceId)
  280. .userKey(userKey).userId(userId).success(true).stage(EiamBatchStage.COMPLETED)
  281. .code("SUCCESS").message("执行成功").build();
  282. }
  283. private EiamBatchItemResult failure(int index, String formInstanceId, String userKey,
  284. String userId, EiamBatchStage stage, RuntimeException ex,
  285. boolean partialUpdate) {
  286. String code = ex instanceof McException ? ((McException) ex).getCode() : "EIAM_CALL_ERROR";
  287. String message = StringUtils.defaultIfBlank(ex.getMessage(), "EIAM 调用失败");
  288. if (partialUpdate) {
  289. message = "账户属性可能已更新;" + message;
  290. }
  291. return EiamBatchItemResult.builder().index(index).formInstanceId(formInstanceId)
  292. .userKey(userKey).userId(userId).success(false).stage(stage)
  293. .code(code).message(message).build();
  294. }
  295. private void putIfNotBlank(Map<String, Object> target, String key, String value) {
  296. if (StringUtils.isNotBlank(value)) {
  297. target.put(key, value);
  298. }
  299. }
  300. private String userKey(EiamUpdateUserItem item) {
  301. return StringUtils.defaultIfBlank(item.getUsername(), item.getUserId());
  302. }
  303. private String deleteUserKey(EiamDeleteUserItem item) {
  304. return StringUtils.defaultIfBlank(item.getUsername(), item.getUserId());
  305. }
  306. }