package com.malk.benteler.service; import com.alibaba.fastjson.JSON; import com.malk.benteler.config.BentelerYidaConf; import com.malk.benteler.dto.EiamBatchOperation; import com.malk.benteler.dto.EiamBatchResult; import com.malk.benteler.dto.EiamFormSyncResult; import com.malk.server.aliwork.YDAuth; import com.malk.server.aliwork.YDConf; import com.malk.server.aliwork.YDParam; import com.malk.server.common.McException; import com.malk.service.aliwork.YDClient_Form; import com.malk.service.aliwork.YDService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 基于一个宜搭实例 ID 完成查询、批量 EIAM 执行和全量回写。 */ @Slf4j @Service public class BentelerYidaSyncService { private final YDClient_Form ydClientForm; private final YDService ydService; private final YDConf ydConf; private final BentelerYidaConf conf; private final BentelerYidaFormMapper mapper; private final EiamLocalService eiamLocalService; public BentelerYidaSyncService(YDClient_Form ydClientForm, YDService ydService, YDConf ydConf, BentelerYidaConf conf, BentelerYidaFormMapper mapper, EiamLocalService eiamLocalService) { this.ydClientForm = ydClientForm; this.ydService = ydService; this.ydConf = ydConf; this.conf = conf; this.mapper = mapper; this.eiamLocalService = eiamLocalService; } /** * 查询入职实例并批量创建 EIAM 用户。 * * @param formInstanceId 宜搭实例 ID * @return 执行及回写结果 */ public EiamFormSyncResult syncCreate(String formInstanceId) { return sync(formInstanceId, EiamBatchOperation.CREATE, conf.getOnboardingFormUuid(), conf.getOnboardingTableFieldId()); } /** * 查询人员部门更新实例并批量更新 EIAM 用户。 * * @param formInstanceId 宜搭实例 ID * @return 执行及回写结果 */ public EiamFormSyncResult syncUpdate(String formInstanceId) { return sync(formInstanceId, EiamBatchOperation.UPDATE, conf.getUpdateFormUuid(), conf.getUpdateTableFieldId()); } /** * 查询离职实例并批量删除 EIAM 用户。 * * @param formInstanceId 宜搭实例 ID * @return 执行及回写结果 */ public EiamFormSyncResult syncDelete(String formInstanceId) { return sync(formInstanceId, EiamBatchOperation.DELETE, conf.getOffboardingFormUuid(), conf.getOffboardingTableFieldId()); } private EiamFormSyncResult sync(String formInstanceId, EiamBatchOperation operation, String expectedFormUuid, String tableFieldId) { validateRequest(formInstanceId, tableFieldId); Map instance = ydClientForm.getForm(auth(), formInstanceId, null); validateForm(instance, expectedFormUuid); Map formData = formData(instance); List> rows = resolveDetailRows(formInstanceId, tableFieldId, formData.get(tableFieldId)); if (rows.isEmpty()) { throw new McException("YIDA_FORM_EMPTY", "宜搭人员子表不能为空"); } EiamBatchResult batchResult = execute(operation, formInstanceId, formData, rows); return writeback(formInstanceId, tableFieldId, rows, batchResult); } private EiamBatchResult execute(EiamBatchOperation operation, String formInstanceId, Map formData, List> rows) { if (operation == EiamBatchOperation.CREATE) { String organizationalUnitId = text(formData.get( conf.getOnboardingOrganizationalUnitIdFieldId())); if (!StringUtils.startsWith(organizationalUnitId, "ou_")) { throw new McException("EIAM_ORG_INVALID", "iDaaS 部门 ID 无效"); } return eiamLocalService.batchCreate(mapper.mapCreateItems( formInstanceId, formData, rows)); } if (operation == EiamBatchOperation.UPDATE) { List updateContents = texts(formData.get(conf.getUpdateContentFieldId())); if (updateContents.isEmpty()) { throw new McException("EIAM_UPDATE_CONTENT_EMPTY", "更新内容不能为空"); } String organizationalUnitId = null; if (requiresOrganizationalUnit(updateContents)) { organizationalUnitId = firstText(formData.get( conf.getUpdateOrganizationalUnitIdFieldId())); if (!StringUtils.startsWith(organizationalUnitId, "ou_")) { throw new McException("EIAM_ORG_INVALID", "iDaaS 部门 ID 无效"); } } return eiamLocalService.batchUpdate(mapper.mapUpdateItems( formInstanceId, rows, organizationalUnitId, updateContents)); } return eiamLocalService.batchDelete(mapper.mapDeleteItems(formInstanceId, rows)); } private EiamFormSyncResult writeback(String formInstanceId, String tableFieldId, List> rows, EiamBatchResult batchResult) { Map update = new HashMap<>(); update.put(tableFieldId, mapper.rebuildRows(tableFieldId, rows, batchResult.getItems())); update.put(conf.getFailureMessageFieldId(), mapper.buildFailureMessage( tableFieldId, rows, batchResult.getItems())); update.put(conf.getTotalFieldId(), batchResult.getTotal()); update.put(conf.getSuccessFieldId(), batchResult.getSuccessCount()); update.put(conf.getFailedFieldId(), batchResult.getFailedCount()); Map bodyExt = new HashMap<>(); bodyExt.put("useLatestVersion", true); bodyExt.put("ignoreEmpty", false); try { ydClientForm.updateForm(auth(), formInstanceId, JSON.toJSONString(update), bodyExt); return EiamFormSyncResult.of(formInstanceId, batchResult, true, "回写成功"); } catch (RuntimeException ex) { log.error("宜搭实例执行结果回写失败, formInstanceId={}", formInstanceId, ex); return EiamFormSyncResult.of(formInstanceId, batchResult, false, StringUtils.defaultIfBlank(ex.getMessage(), "宜搭回写失败")); } } @SuppressWarnings("unchecked") private Map formData(Map instance) { if (instance == null || instance.get("formData") == null) { throw new McException("YIDA_FORM_DATA_EMPTY", "宜搭实例数据为空"); } Object value = instance.get("formData"); if (value instanceof Map) { return (Map) value; } Map parsed = JSON.parseObject(String.valueOf(value), Map.class); if (parsed == null) { throw new McException("YIDA_FORM_DATA_INVALID", "宜搭实例 formData 格式错误"); } return parsed; } @SuppressWarnings("unchecked") private List> resolveDetailRows(String formInstanceId, String tableFieldId, Object inlineValue) { List> inlineRows = inlineValue instanceof List ? (List>) inlineValue : new ArrayList<>(); // fixme 详情接口最多内联 50 行;只有恰好 50 行时才可能被截断,必须单独分页查询子表。 if (inlineRows.size() == YDConf.PAGE_SIZE_DETAILS) { try { List details = ydService.queryDetails(YDParam.builder() .appType(ydConf.getAppType()) .systemToken(ydConf.getSystemToken()) .formInstanceId(formInstanceId) .tableFieldId(tableFieldId) .pageNumber(1) .build()); return details == null ? inlineRows : (List>) (List) details; } catch (NullPointerException ex) { log.warn("宜搭子表分页查询空数据,退回内联 50 行, formInstanceId={}, tableFieldId={}", formInstanceId, tableFieldId); } } return inlineRows; } private void validateRequest(String formInstanceId, String tableFieldId) { if (StringUtils.isBlank(formInstanceId)) { throw new McException("YIDA_FORM_INSTANCE_ID_EMPTY", "formInstanceId 不能为空"); } if (StringUtils.isBlank(tableFieldId)) { throw new McException("YIDA_FIELD_CONFIG_EMPTY", "人员子表 fieldId 未配置"); } } private void validateForm(Map instance, String expectedFormUuid) { if (instance == null) { throw new McException("YIDA_FORM_EMPTY", "宜搭实例查询结果为空"); } String actualFormUuid = text(instance.get("modelUuid")); if (StringUtils.isNotBlank(expectedFormUuid) && StringUtils.isNotBlank(actualFormUuid) && !StringUtils.equals(expectedFormUuid, actualFormUuid)) { throw new McException("YIDA_FORM_MISMATCH", "宜搭实例与当前同步场景不匹配"); } } private YDAuth auth() { return YDAuth.ofGlobal(ydConf); } private String firstText(Object value) { if (value instanceof Iterable) { for (Object item : (Iterable) value) { return text(item); } return null; } return text(value); } private boolean requiresOrganizationalUnit(List updateContents) { return updateContents.contains(BentelerYidaFormMapper.UPDATE_PRIMARY_ORG) || updateContents.contains(BentelerYidaFormMapper.ADD_ORG); } private List texts(Object value) { List values = new ArrayList<>(); if (value instanceof Iterable) { for (Object item : (Iterable) value) { String itemText = text(item); if (StringUtils.isNotBlank(itemText)) { values.add(itemText); } } return values; } String itemText = text(value); if (StringUtils.isNotBlank(itemText)) { values.add(itemText); } return values; } private String text(Object value) { return value == null ? null : StringUtils.trimToNull(String.valueOf(value)); } }