|
@@ -0,0 +1,196 @@
|
|
|
|
|
+package com.malk.service.workhours;
|
|
|
|
|
+
|
|
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
|
|
+import com.malk.server.aliwork.YDConf;
|
|
|
|
|
+import com.malk.server.aliwork.YDParam;
|
|
|
|
|
+import com.malk.server.common.McException;
|
|
|
|
|
+import com.malk.server.dingtalk.DDR_New;
|
|
|
|
|
+import com.malk.server.workhours.WHConf;
|
|
|
|
|
+import com.malk.service.aliwork.YDClient;
|
|
|
|
|
+import com.malk.service.aliwork.YDService;
|
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.Collections;
|
|
|
|
|
+import java.util.LinkedHashSet;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.Set;
|
|
|
|
|
+
|
|
|
|
|
+/** 按工时变更单中的实际变更项目,实时读取项目成员行的审批经理。 */
|
|
|
|
|
+@Service
|
|
|
|
|
+public class ChangeProjectManagerLookupService {
|
|
|
|
|
+
|
|
|
|
|
+ private static final String CHANGE_FORM = "FORM-1C9C7E3981CC4D32A4404D457E766DFCNBYD";
|
|
|
|
|
+ private static final String SUBMITTER = "employeeField_mmacxew4";
|
|
|
|
|
+ private static final String SUBMITTER_TEXT_ID = "textField_mmbffvd9";
|
|
|
|
|
+ private static final String BILLABLE_TABLE = "tableField_mmczo634";
|
|
|
|
|
+ private static final String BILLABLE_CODE = "textField_mmd25ngw";
|
|
|
|
|
+ private static final String BILLABLE_DELTA = "numberField_mmd25ngz";
|
|
|
|
|
+ private static final String NON_BILLABLE_TABLE = "tableField_mmecojwk";
|
|
|
|
|
+ private static final String NON_BILLABLE_CODE = "textField_mmecojwe";
|
|
|
|
|
+ private static final String NON_BILLABLE_DELTA = "numberField_mmecojwh";
|
|
|
|
|
+ private static final String PROJECT_CODE = "textField_mkov5ozb";
|
|
|
|
|
+ private static final String PROJECT_MEMBERS = "tableField_mkowyn6d";
|
|
|
|
|
+ private static final String PROJECT_MEMBER = "employeeField_mmbfe0ij";
|
|
|
|
|
+ private static final String PROJECT_MANAGER = "employeeField_mkoxpswf";
|
|
|
|
|
+
|
|
|
|
|
+ private final YDClient ydClient;
|
|
|
|
|
+ private final YDService ydService;
|
|
|
|
|
+ private final WHConf conf;
|
|
|
|
|
+
|
|
|
|
|
+ public ChangeProjectManagerLookupService(YDClient ydClient, YDService ydService, WHConf conf) {
|
|
|
|
|
+ this.ydClient = ydClient;
|
|
|
|
|
+ this.ydService = ydService;
|
|
|
|
|
+ this.conf = conf;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public List<String> lookup(String processInstanceId) {
|
|
|
|
|
+ McException.assertParamException(StringUtils.isBlank(processInstanceId), "工时变更单流程实例 ID 不能为空");
|
|
|
|
|
+ DDR_New detail = ydClient.queryData(YDParam.builder()
|
|
|
|
|
+ .appType(conf.getYidaAppType()).systemToken(conf.getYidaSystemToken())
|
|
|
|
|
+ .processInstanceId(processInstanceId).build(), YDConf.FORM_QUERY.retrieve_search_process_id);
|
|
|
|
|
+ Map formData = ApprovalWriteBackService.extractProcessFormData(detail);
|
|
|
|
|
+ // fixme 流程详情的 data 也可能直接是表单字段 Map,审批回写工具只识别审批单子表。
|
|
|
|
|
+ if (formData == null && detail != null && detail.getData() instanceof Map) {
|
|
|
|
|
+ formData = (Map) detail.getData();
|
|
|
|
|
+ }
|
|
|
|
|
+ McException.assertAccessException(formData == null || !formData.containsKey(SUBMITTER),
|
|
|
|
|
+ "未查询到工时变更单详情,或实例 ID 不属于工时变更单");
|
|
|
|
|
+ String submitterId = firstId(formData, SUBMITTER);
|
|
|
|
|
+ if (StringUtils.isBlank(submitterId)) {
|
|
|
|
|
+ submitterId = StringUtils.trimToEmpty(String.valueOf(formData.get(SUBMITTER_TEXT_ID)));
|
|
|
|
|
+ }
|
|
|
|
|
+ McException.assertAccessException(StringUtils.isBlank(submitterId) || "null".equals(submitterId),
|
|
|
|
|
+ "工时变更单填报人 ID 为空");
|
|
|
|
|
+
|
|
|
|
|
+ Set<String> projectCodes = new LinkedHashSet<>();
|
|
|
|
|
+ collectProjectCodes(projectCodes, formData, processInstanceId,
|
|
|
|
|
+ BILLABLE_TABLE, BILLABLE_CODE, BILLABLE_DELTA);
|
|
|
|
|
+ collectProjectCodes(projectCodes, formData, processInstanceId,
|
|
|
|
|
+ NON_BILLABLE_TABLE, NON_BILLABLE_CODE, NON_BILLABLE_DELTA);
|
|
|
|
|
+ Set<String> managerIds = new LinkedHashSet<>();
|
|
|
|
|
+ for (String code : projectCodes) {
|
|
|
|
|
+ managerIds.addAll(findProjectManagers(code, submitterId));
|
|
|
|
|
+ }
|
|
|
|
|
+ return new ArrayList<>(managerIds);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void collectProjectCodes(Set<String> codes, Map formData, String instanceId,
|
|
|
|
|
+ String tableId, String codeField, String hoursField) {
|
|
|
|
|
+ List<Map> rows = rows(formData.get(tableId));
|
|
|
|
|
+ if (rows.size() >= 50) {
|
|
|
|
|
+ List<Map> full = ydService.queryDetails(YDParam.builder()
|
|
|
|
|
+ .appType(conf.getYidaAppType()).systemToken(conf.getYidaSystemToken())
|
|
|
|
|
+ .formUuid(CHANGE_FORM).formInstanceId(instanceId)
|
|
|
|
|
+ .tableFieldId(tableId).pageNumber(1).build());
|
|
|
|
|
+ McException.assertAccessException(full == null || full.size() < rows.size(),
|
|
|
|
|
+ "工时变更单子表完整数据获取失败: " + tableId);
|
|
|
|
|
+ rows = full;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (Map row : rows) {
|
|
|
|
|
+ if (changedHours(row, hoursField).compareTo(BigDecimal.ZERO) == 0) continue;
|
|
|
|
|
+ String code = StringUtils.trimToEmpty(String.valueOf(row.get(codeField)));
|
|
|
|
|
+ McException.assertAccessException(code.isEmpty() || "null".equals(code),
|
|
|
|
|
+ "变更工时不为 0,但项目编号为空: " + tableId);
|
|
|
|
|
+ codes.add(code);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<String> findProjectManagers(String code, String submitterId) {
|
|
|
|
|
+ List<Map> exactProjects = new ArrayList<>();
|
|
|
|
|
+ int page = 1;
|
|
|
|
|
+ long total;
|
|
|
|
|
+ do {
|
|
|
|
|
+ DDR_New result = ydClient.queryData(YDParam.builder()
|
|
|
|
|
+ .appType(conf.getYidaAppType()).systemToken(conf.getYidaSystemToken())
|
|
|
|
|
+ .formUuid(conf.getFormUuidProject())
|
|
|
|
|
+ .searchFieldJson(JSON.toJSONString(Collections.singletonMap(PROJECT_CODE, code)))
|
|
|
|
|
+ .currentPage(page).pageSize(100).build(), YDConf.FORM_QUERY.retrieve_search_form);
|
|
|
|
|
+ List<Map> data = result == null ? null : (List<Map>) result.getData();
|
|
|
|
|
+ McException.assertAccessException(data == null, "项目档案查询失败: " + code);
|
|
|
|
|
+ total = result.getTotalCount();
|
|
|
|
|
+ for (Map item : data) {
|
|
|
|
|
+ Map formData = (Map) item.get("formData");
|
|
|
|
|
+ if (formData != null && code.equals(formData.get(PROJECT_CODE))) exactProjects.add(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (data.isEmpty()) break;
|
|
|
|
|
+ page++;
|
|
|
|
|
+ } while ((long) (page - 1) * 100 < total);
|
|
|
|
|
+ McException.assertAccessException(exactProjects.size() != 1,
|
|
|
|
|
+ "项目编号未唯一命中项目档案: " + code);
|
|
|
|
|
+ return managersInProject(exactProjects.get(0), submitterId, code);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<String> managersInProject(Map project, String submitterId, String code) {
|
|
|
|
|
+ Map formData = (Map) project.get("formData");
|
|
|
|
|
+ List<Map> members = rows(formData.get(PROJECT_MEMBERS));
|
|
|
|
|
+ if (members.size() >= 50) {
|
|
|
|
|
+ Object instanceId = project.get("formInstanceId");
|
|
|
|
|
+ if (instanceId == null) instanceId = project.get("formInstId");
|
|
|
|
|
+ McException.assertAccessException(instanceId == null, "项目档案实例 ID 为空: " + code);
|
|
|
|
|
+ List<Map> full = ydService.queryDetails(YDParam.builder()
|
|
|
|
|
+ .appType(conf.getYidaAppType()).systemToken(conf.getYidaSystemToken())
|
|
|
|
|
+ .formUuid(conf.getFormUuidProject()).formInstanceId(String.valueOf(instanceId))
|
|
|
|
|
+ .tableFieldId(PROJECT_MEMBERS).pageNumber(1).build());
|
|
|
|
|
+ McException.assertAccessException(full == null || full.size() < members.size(),
|
|
|
|
|
+ "项目人员明细完整数据获取失败: " + code);
|
|
|
|
|
+ members = full;
|
|
|
|
|
+ }
|
|
|
|
|
+ Set<String> managerIds = new LinkedHashSet<>();
|
|
|
|
|
+ boolean foundMember = false;
|
|
|
|
|
+ for (Map member : members) {
|
|
|
|
|
+ if (!ids(member, PROJECT_MEMBER).contains(submitterId)) continue;
|
|
|
|
|
+ foundMember = true;
|
|
|
|
|
+ managerIds.addAll(ids(member, PROJECT_MANAGER));
|
|
|
|
|
+ }
|
|
|
|
|
+ McException.assertAccessException(!foundMember || managerIds.isEmpty(),
|
|
|
|
|
+ "项目人员明细未找到填报人或项目经理: " + code);
|
|
|
|
|
+ return new ArrayList<>(managerIds);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static BigDecimal changedHours(Map row, String fieldId) {
|
|
|
|
|
+ Object value = row.get(fieldId);
|
|
|
|
|
+ if (value == null || StringUtils.isBlank(String.valueOf(value))) value = row.get(fieldId + "_value");
|
|
|
|
|
+ if (value == null || StringUtils.isBlank(String.valueOf(value))) return BigDecimal.ZERO;
|
|
|
|
|
+ try {
|
|
|
|
|
+ return new BigDecimal(String.valueOf(value));
|
|
|
|
|
+ } catch (NumberFormatException ex) {
|
|
|
|
|
+ throw new McException("INVALID_HOURS", "变更工时不是有效数字: " + fieldId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static String firstId(Map row, String fieldId) {
|
|
|
|
|
+ List<String> values = ids(row, fieldId);
|
|
|
|
|
+ return values.isEmpty() ? null : values.get(0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static List<String> ids(Map row, String fieldId) {
|
|
|
|
|
+ Object value = row.get(fieldId + "_id");
|
|
|
|
|
+ if (value == null) value = row.get(fieldId);
|
|
|
|
|
+ List values = value instanceof List ? (List) value : Collections.singletonList(value);
|
|
|
|
|
+ List<String> result = new ArrayList<>();
|
|
|
|
|
+ for (Object item : values) {
|
|
|
|
|
+ if (item instanceof Map) {
|
|
|
|
|
+ Map person = (Map) item;
|
|
|
|
|
+ for (String key : new String[]{"value", "userId", "key", "workNo", "employeeId"}) {
|
|
|
|
|
+ Object id = person.get(key);
|
|
|
|
|
+ if (id != null && StringUtils.isNotBlank(String.valueOf(id))) {
|
|
|
|
|
+ result.add(String.valueOf(id));
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (row.containsKey(fieldId + "_id") && item != null
|
|
|
|
|
+ && StringUtils.isNotBlank(String.valueOf(item))) {
|
|
|
|
|
+ result.add(String.valueOf(item));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static List<Map> rows(Object value) {
|
|
|
|
|
+ return value instanceof List ? (List<Map>) value : Collections.emptyList();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|