|
|
@@ -1,26 +1,48 @@
|
|
|
package com.malk.lanyun.service.impl;
|
|
|
|
|
|
-
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
import com.malk.lanyun.service.DingTalkService;
|
|
|
import com.malk.server.dingtalk.DDConf;
|
|
|
import com.malk.server.dingtalk.DDR;
|
|
|
-import com.malk.server.dingtalk.DDR_New;
|
|
|
+import com.malk.service.dingtalk.DDClient;
|
|
|
+import com.malk.service.dingtalk.DDClient_Personnel;
|
|
|
+import com.malk.service.dingtalk.DDClient_Workflow;
|
|
|
import com.malk.utils.UtilMap;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
|
|
|
@Service
|
|
|
@Slf4j
|
|
|
public class DingTalkServiceImpl implements DingTalkService {
|
|
|
|
|
|
+ private final DDClient ddClient;
|
|
|
+
|
|
|
+ private final DDClient_Workflow ddClientWorkflow;
|
|
|
+
|
|
|
+ private final DDClient_Personnel ddClientPersonnel;
|
|
|
+
|
|
|
+ private final DDConf ddConf;
|
|
|
+
|
|
|
+ public DingTalkServiceImpl(DDClient ddClient, DDClient_Workflow ddClientWorkflow,
|
|
|
+ DDClient_Personnel ddClientPersonnel, DDConf ddConf) {
|
|
|
+ this.ddClient = ddClient;
|
|
|
+ this.ddClientWorkflow = ddClientWorkflow;
|
|
|
+ this.ddClientPersonnel = ddClientPersonnel;
|
|
|
+ this.ddConf = ddConf;
|
|
|
+ }
|
|
|
|
|
|
@Override
|
|
|
public Map getAllUserIdInfo(String access_token, String status_list, Number offset, Number size) {
|
|
|
Map param = UtilMap.map("access_token", access_token);
|
|
|
- Map body = UtilMap.map("status_list, offset, size", status_list, offset,size);
|
|
|
+ Map body = UtilMap.map("status_list, offset, size", status_list, offset, size);
|
|
|
return (Map) DDR.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/queryonjob", null, param, body).getResult();
|
|
|
}
|
|
|
|
|
|
@@ -31,4 +53,178 @@ public class DingTalkServiceImpl implements DingTalkService {
|
|
|
return (List<Map>) DDR.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/v2/list", null, param, body).getResult();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 审批通过后: 将审批单字段按名称回写到发起人的人员花名册
|
|
|
+ *
|
|
|
+ * @param processInstanceId 审批实例ID
|
|
|
+ * @return 成功写入花名册的字段数量
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public int syncApprovalFormToRoster(String processInstanceId) {
|
|
|
+ if (isBlank(processInstanceId)) {
|
|
|
+ throw new IllegalArgumentException("processInstanceId不能为空");
|
|
|
+ }
|
|
|
+ // 审批应用凭证: 用于查询审批实例
|
|
|
+ String approvalAccessToken = this.ddClient.getAccessToken(this.ddConf.getApprovalAppKey(), this.ddConf.getApprovalAppSecret());
|
|
|
+ Map processData = this.ddClientWorkflow.getProcessInstanceId(approvalAccessToken, processInstanceId);
|
|
|
+ if (processData == null || processData.isEmpty()) {
|
|
|
+ throw new IllegalStateException("未查询到审批实例, processInstanceId=" + processInstanceId);
|
|
|
+ }
|
|
|
+ String userId = stringValue(processData.get("originatorUserId"));
|
|
|
+ if (isBlank(userId)) {
|
|
|
+ throw new IllegalStateException("审批实例缺少发起人, processInstanceId=" + processInstanceId);
|
|
|
+ }
|
|
|
+ // 花名册应用凭证: 用于读写智能人事花名册
|
|
|
+ String personnelAccessToken = this.ddClient.getAccessToken(this.ddConf.getAppKey(), this.ddConf.getAppSecret());
|
|
|
+ List<Map> formComponentValues = asMapList(processData.get("formComponentValues"));
|
|
|
+ List<Map> metadataGroups = this.ddClientPersonnel.getPersonnelMeta(personnelAccessToken, this.ddConf.getAgentId());
|
|
|
+
|
|
|
+ // 花名册字段: 按字段名称建索引, 名称重复或为派生字段时忽略
|
|
|
+ Map<String, FieldMeta> metadataByName = indexMetadata(metadataGroups);
|
|
|
+ Map<String, LinkedHashMap<String, String>> fieldsByGroup = new LinkedHashMap<>();
|
|
|
+ int matchedCount = 0;
|
|
|
+ for (Map component : formComponentValues) {
|
|
|
+ String name = stringValue(component.get("name"));
|
|
|
+ String value = fieldValue(component.get("value"));
|
|
|
+ if (isBlank(name) || value == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ FieldMeta metadata = metadataByName.get(name.trim());
|
|
|
+ if (metadata == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ LinkedHashMap<String, String> groupFields = fieldsByGroup.computeIfAbsent(metadata.groupId, key -> new LinkedHashMap<>());
|
|
|
+ if (!groupFields.containsKey(metadata.fieldCode)) {
|
|
|
+ matchedCount++;
|
|
|
+ }
|
|
|
+ groupFields.put(metadata.fieldCode, value);
|
|
|
+ }
|
|
|
+ if (matchedCount == 0) {
|
|
|
+ log.info("审批单无匹配的花名册字段, processInstanceId={}", processInstanceId);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ List<Map> groups = buildUpdateGroups(fieldsByGroup);
|
|
|
+ boolean updated = this.ddClientPersonnel.updateEmployeeRoster(personnelAccessToken, userId, this.ddConf.getAgentId(), groups);
|
|
|
+ if (!updated) {
|
|
|
+ throw new IllegalStateException("花名册更新失败, processInstanceId=" + processInstanceId);
|
|
|
+ }
|
|
|
+ return matchedCount;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 花名册字段索引: 字段名称 -> 分组ID + 字段编码
|
|
|
+ */
|
|
|
+ private Map<String, FieldMeta> indexMetadata(List<Map> metadataGroups) {
|
|
|
+ Map<String, FieldMeta> metadataByName = new LinkedHashMap<>();
|
|
|
+ Set<String> ambiguousNames = new HashSet<>();
|
|
|
+ for (Map group : safeList(metadataGroups)) {
|
|
|
+ String groupId = stringValue(group.get("group_id"));
|
|
|
+ if (isBlank(groupId)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ for (Map field : metadataFields(group)) {
|
|
|
+ String fieldName = stringValue(field.get("field_name"));
|
|
|
+ String fieldCode = stringValue(field.get("field_code"));
|
|
|
+ if (isBlank(fieldName) || isBlank(fieldCode) || isDerived(field.get("derived"))) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String normalizedName = fieldName.trim();
|
|
|
+ FieldMeta existing = metadataByName.get(normalizedName);
|
|
|
+ if (existing != null && (!existing.groupId.equals(groupId) || !existing.fieldCode.equals(fieldCode))) {
|
|
|
+ ambiguousNames.add(normalizedName);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (existing == null) {
|
|
|
+ metadataByName.put(normalizedName, new FieldMeta(groupId, fieldCode));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (String ambiguousName : ambiguousNames) {
|
|
|
+ metadataByName.remove(ambiguousName);
|
|
|
+ log.warn("花名册字段名称存在歧义, 已忽略: {}", ambiguousName);
|
|
|
+ }
|
|
|
+ return metadataByName;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Map> metadataFields(Map group) {
|
|
|
+ Object detail = group.get("detail");
|
|
|
+ if (detail instanceof Map) {
|
|
|
+ return asMapList(((Map) detail).get("field_meta_info_list"));
|
|
|
+ }
|
|
|
+ if (detail instanceof List) {
|
|
|
+ return asMapList(detail);
|
|
|
+ }
|
|
|
+ return asMapList(group.get("field_meta_info_list"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 组装花名册更新入参: groups[].sections[].section[].{field_code, value}
|
|
|
+ */
|
|
|
+ private List<Map> buildUpdateGroups(Map<String, LinkedHashMap<String, String>> fieldsByGroup) {
|
|
|
+ List<Map> groups = new ArrayList<>();
|
|
|
+ for (Map.Entry<String, LinkedHashMap<String, String>> groupEntry : fieldsByGroup.entrySet()) {
|
|
|
+ List<Map> fields = new ArrayList<>();
|
|
|
+ for (Map.Entry<String, String> fieldEntry : groupEntry.getValue().entrySet()) {
|
|
|
+ fields.add(UtilMap.map("field_code, value", fieldEntry.getKey(), fieldEntry.getValue()));
|
|
|
+ }
|
|
|
+ Map section = UtilMap.map("section", fields);
|
|
|
+ groups.add(UtilMap.map("group_id, sections", groupEntry.getKey(), Collections.singletonList(section)));
|
|
|
+ }
|
|
|
+ return groups;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String fieldValue(Object value) {
|
|
|
+ if (value == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (value instanceof String) {
|
|
|
+ String string = (String) value;
|
|
|
+ return isBlank(string) ? null : string;
|
|
|
+ }
|
|
|
+ return JSON.toJSONString(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isDerived(Object value) {
|
|
|
+ return (Boolean.TRUE.equals(value) || "true".equalsIgnoreCase(String.valueOf(value)));
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Map> asMapList(Object value) {
|
|
|
+ if (!(value instanceof List)) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ List<Map> result = new ArrayList<>();
|
|
|
+ for (Object item : (List) value) {
|
|
|
+ if (item instanceof Map) {
|
|
|
+ result.add((Map) item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Map> safeList(List<Map> value) {
|
|
|
+ return (value == null) ? Collections.<Map>emptyList() : value;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String stringValue(Object value) {
|
|
|
+ return (value == null) ? null : String.valueOf(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isBlank(String value) {
|
|
|
+ return (value == null || value.trim().isEmpty());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 花名册字段元信息
|
|
|
+ */
|
|
|
+ private static final class FieldMeta {
|
|
|
+
|
|
|
+ private final String groupId;
|
|
|
+
|
|
|
+ private final String fieldCode;
|
|
|
+
|
|
|
+ private FieldMeta(String groupId, String fieldCode) {
|
|
|
+ this.groupId = groupId;
|
|
|
+ this.fieldCode = fieldCode;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|