|
|
@@ -6,6 +6,7 @@ import com.malk.minglei.dto.EmployeeRosterInfo;
|
|
|
import com.malk.minglei.dto.ErpYidaPushMainData;
|
|
|
import com.malk.minglei.dto.ErpYidaPushRequest;
|
|
|
import com.malk.minglei.service.employee.EmployeeRosterService;
|
|
|
+import com.malk.minglei.service.yida.MingleiDocumentService;
|
|
|
import com.malk.minglei.service.yida.YidaFormPushHandler;
|
|
|
import com.malk.minglei.service.yida.YidaProcessOpenApiClient;
|
|
|
import com.malk.server.common.McException;
|
|
|
@@ -13,14 +14,19 @@ import com.malk.server.dingtalk.DDR_New;
|
|
|
import com.malk.server.dingtalk.DDConf;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.ObjectProvider;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.LocalTime;
|
|
|
import java.time.ZoneId;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Collections;
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
@@ -38,6 +44,7 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
private static final String FORM_UUID = "FORM-4B08ADABCDAA45788C043729A855E03ED76G";
|
|
|
private static final String PROCESS_CODE = "TPROC--MQD66371C1N84FPSJZO4W79IN4BW2AE7I08TM0";
|
|
|
private static final String DEFAULT_ORIGINATOR_USER_ID = "41603502201288023";
|
|
|
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
|
private static final String TITLE_FIELD_ID = "textField_msh27unu";
|
|
|
private static final String APPLICANT_FIELD_ID = "employeeField_msh186rr";
|
|
|
@@ -48,6 +55,8 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
private static final String DATA_SOURCE_FIELD_ID = "textField_mt7zi4fy";
|
|
|
private static final String APPLICANT_NO_FIELD_ID = "textField_mt7zi4g0";
|
|
|
private static final String DEPARTMENT_NO_FIELD_ID = "textField_mt80nq6s";
|
|
|
+ private static final String APPLICANT_NAME_FIELD_ID = "employeeField_mw0jwshi";
|
|
|
+ private static final String APPLICANT_DEPARTMENT_FIELD_ID = "departmentSelectField_mw0ko5yp";
|
|
|
private static final String REASON_CODE_FIELD_ID = "textField_mt80nq6t";
|
|
|
private static final String REASON_DESC_FIELD_ID = "textField_mt80nq6v";
|
|
|
private static final String REMARK_FIELD_ID = "textField_mt7zi4fz";
|
|
|
@@ -73,13 +82,16 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
private static final String DETAIL_PROJECT_NO_FIELD_ID = "textField_mt6ur4w8";
|
|
|
private static final String DETAIL_PROJECT_DESC_FIELD_ID = "textField_mt80nq7a";
|
|
|
private static final String DETAIL_SOURCE_DOC_NO_FIELD_ID = "textField_mt80nq7b";
|
|
|
- private static final String DETAIL_KEEPER_FIELD_ID = "employeeField_mt80nq7d";
|
|
|
+ private static final String DETAIL_KEEPER_NO_FIELD_ID = "textField_mw1ekhte";
|
|
|
+ private static final String DETAIL_KEEPER_OA_FIELD_ID = "employeeField_mt80nq7e";
|
|
|
|
|
|
private final YidaProcessOpenApiClient yidaProcessOpenApiClient;
|
|
|
private final DDConf dingTalkConfiguration;
|
|
|
private final YidaApplicationsProperties applications;
|
|
|
private final LocalFormDepartmentEnricher departmentEnricher;
|
|
|
private final EmployeeRosterService employeeRosterService;
|
|
|
+ private final MingleiDocumentService mingleiDocumentService;
|
|
|
+ private final MiscReceiptKeeperLookup keeperLookup;
|
|
|
|
|
|
/**
|
|
|
* 创建杂收发料单推送处理器。
|
|
|
@@ -88,19 +100,77 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
* @param dingTalkConfiguration 钉钉应用配置
|
|
|
* @param applications 宜搭应用配置
|
|
|
* @param departmentEnricher 部门公司和负责人填充组件
|
|
|
- * @param employeeRosterService 员工花名册,用于把明细仓管员工号换成钉钉用户 ID
|
|
|
+ * @param employeeRosterService 员工花名册,用于把工号换成钉钉用户 ID
|
|
|
+ * @param mingleiDocumentService 通讯录部门查询,用于按申请人钉钉用户取所在部门
|
|
|
+ * @param keeperLookupProvider 按料号查询仓管员工号,字典库不可用时跳过仓管员
|
|
|
*/
|
|
|
@Autowired
|
|
|
public MiscReceiptIssueFormPushHandler(YidaProcessOpenApiClient yidaProcessOpenApiClient,
|
|
|
DDConf dingTalkConfiguration,
|
|
|
YidaApplicationsProperties applications,
|
|
|
LocalFormDepartmentEnricher departmentEnricher,
|
|
|
- EmployeeRosterService employeeRosterService) {
|
|
|
+ EmployeeRosterService employeeRosterService,
|
|
|
+ MingleiDocumentService mingleiDocumentService,
|
|
|
+ ObjectProvider<MiscReceiptKeeperLookup> keeperLookupProvider) {
|
|
|
+ this(yidaProcessOpenApiClient, dingTalkConfiguration, applications, departmentEnricher,
|
|
|
+ employeeRosterService, mingleiDocumentService, keeperLookupProvider.getIfAvailable());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建可指定仓管员查询的杂收发料单推送处理器。
|
|
|
+ */
|
|
|
+ public MiscReceiptIssueFormPushHandler(YidaProcessOpenApiClient yidaProcessOpenApiClient,
|
|
|
+ DDConf dingTalkConfiguration,
|
|
|
+ YidaApplicationsProperties applications,
|
|
|
+ LocalFormDepartmentEnricher departmentEnricher,
|
|
|
+ EmployeeRosterService employeeRosterService,
|
|
|
+ MingleiDocumentService mingleiDocumentService,
|
|
|
+ MiscReceiptKeeperLookup keeperLookup) {
|
|
|
this.yidaProcessOpenApiClient = yidaProcessOpenApiClient;
|
|
|
this.dingTalkConfiguration = dingTalkConfiguration;
|
|
|
this.applications = applications;
|
|
|
this.departmentEnricher = departmentEnricher;
|
|
|
this.employeeRosterService = employeeRosterService;
|
|
|
+ this.mingleiDocumentService = mingleiDocumentService;
|
|
|
+ this.keeperLookup = keeperLookup;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建带花名册和通讯录、不查仓管员的杂收发料单推送处理器。
|
|
|
+ */
|
|
|
+ public MiscReceiptIssueFormPushHandler(YidaProcessOpenApiClient yidaProcessOpenApiClient,
|
|
|
+ DDConf dingTalkConfiguration,
|
|
|
+ YidaApplicationsProperties applications,
|
|
|
+ LocalFormDepartmentEnricher departmentEnricher,
|
|
|
+ EmployeeRosterService employeeRosterService,
|
|
|
+ MingleiDocumentService mingleiDocumentService) {
|
|
|
+ this(yidaProcessOpenApiClient, dingTalkConfiguration, applications, departmentEnricher,
|
|
|
+ employeeRosterService, mingleiDocumentService, (MiscReceiptKeeperLookup) null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建带花名册、不查通讯录部门的杂收发料单推送处理器。
|
|
|
+ */
|
|
|
+ public MiscReceiptIssueFormPushHandler(YidaProcessOpenApiClient yidaProcessOpenApiClient,
|
|
|
+ DDConf dingTalkConfiguration,
|
|
|
+ YidaApplicationsProperties applications,
|
|
|
+ LocalFormDepartmentEnricher departmentEnricher,
|
|
|
+ EmployeeRosterService employeeRosterService) {
|
|
|
+ this(yidaProcessOpenApiClient, dingTalkConfiguration, applications, departmentEnricher,
|
|
|
+ employeeRosterService, (MingleiDocumentService) null, (MiscReceiptKeeperLookup) null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建带花名册和仓管员查询、不查通讯录部门的杂收发料单推送处理器。
|
|
|
+ */
|
|
|
+ public MiscReceiptIssueFormPushHandler(YidaProcessOpenApiClient yidaProcessOpenApiClient,
|
|
|
+ DDConf dingTalkConfiguration,
|
|
|
+ YidaApplicationsProperties applications,
|
|
|
+ LocalFormDepartmentEnricher departmentEnricher,
|
|
|
+ EmployeeRosterService employeeRosterService,
|
|
|
+ MiscReceiptKeeperLookup keeperLookup) {
|
|
|
+ this(yidaProcessOpenApiClient, dingTalkConfiguration, applications, departmentEnricher,
|
|
|
+ employeeRosterService, (MingleiDocumentService) null, keeperLookup);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -115,7 +185,8 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
DDConf dingTalkConfiguration,
|
|
|
YidaApplicationsProperties applications,
|
|
|
LocalFormDepartmentEnricher departmentEnricher) {
|
|
|
- this(yidaProcessOpenApiClient, dingTalkConfiguration, applications, departmentEnricher, null);
|
|
|
+ this(yidaProcessOpenApiClient, dingTalkConfiguration, applications, departmentEnricher,
|
|
|
+ (EmployeeRosterService) null, (MingleiDocumentService) null, (MiscReceiptKeeperLookup) null);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -152,8 +223,8 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
body.put("processCode", PROCESS_CODE);
|
|
|
body.put("formUuid", FORM_UUID);
|
|
|
body.put("formDataJson", JSON.toJSONString(formData));
|
|
|
- body.put("executeExpression", true);
|
|
|
- body.put("noExecuteExpression", false);
|
|
|
+ body.put("executeExpression", Boolean.FALSE);
|
|
|
+ body.put("noExecuteExpression", Boolean.TRUE);
|
|
|
body.put("title", title);
|
|
|
body.put("dataTitle", title);
|
|
|
LOGGER.info("Yida process start request endpoint={} formName={} body={}",
|
|
|
@@ -180,15 +251,16 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
Map<String, Object> detailWrapper = objectToMap(request.getDetailData());
|
|
|
List<Map<String, Object>> detailRows = toMapRows(detailWrapper.get("detailData1"));
|
|
|
String docNo = toText(value(main, "inbadocno"));
|
|
|
- String title = toText(firstNonBlank(value(main, "title", "dataTitle"), docNo == null ? null : "miscReceiptIssue-" + docNo));
|
|
|
String reasonDesc = toText(value(main, "oocql004"));
|
|
|
+ String title = joinText(reasonDesc, docNo);
|
|
|
|
|
|
Map<String, Object> formData = new LinkedHashMap<String, Object>();
|
|
|
- putIfNotBlank(formData, TITLE_FIELD_ID, joinText(reasonDesc, docNo));
|
|
|
+ putIfNotBlank(formData, TITLE_FIELD_ID, title);
|
|
|
putIfNotBlank(formData, APPLICANT_FIELD_ID, toStringList(operatorUserId, null));
|
|
|
putIfNotBlank(formData, COMPANY_FIELD_ID, firstNonBlank(value(main, "gs", "applyCompany", "company"), value(main, "inbaent")));
|
|
|
putIfNotBlank(formData, DEPARTMENT_FIELD_ID, toStringList(value(main, "bm", "applyDepartment", "department", "deptId"), null));
|
|
|
- putIfNotBlank(formData, APPLY_DATE_FIELD_ID, toDateMillis(firstNonBlank(value(main, "rq", "applyDate"), value(main, "inbadocdt"))));
|
|
|
+ putIfNotBlank(formData, APPLY_DATE_FIELD_ID, toDateTimeMillis(mostPreciseDate(
|
|
|
+ value(main, "rq"), value(main, "applyDate"), value(main, "inbadocdt"))));
|
|
|
putIfNotBlank(formData, FORM_TITLE_FIELD_ID, title);
|
|
|
putIfNotBlank(formData, DATA_SOURCE_FIELD_ID, value(main, "inba006"));
|
|
|
putIfNotBlank(formData, APPLICANT_NO_FIELD_ID, value(main, "inba003"));
|
|
|
@@ -204,9 +276,51 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
formData.put(DETAIL_TABLE_FIELD_ID, rows);
|
|
|
}
|
|
|
copyYidaFieldValues(main, formData);
|
|
|
+ putApplicantIdentity(formData, value(main, "inba003"));
|
|
|
return formData;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 申请人员 ID 仍保留工号。申请人名称写入花名册对应的钉钉用户,申请人所在部门写入通讯录主部门。
|
|
|
+ */
|
|
|
+ private void putApplicantIdentity(Map<String, Object> formData, Object employeeNoValue) {
|
|
|
+ String employeeNo = toText(employeeNoValue);
|
|
|
+ if (employeeNo == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String dingtalkId = resolveApplicantUserId(employeeNo);
|
|
|
+ formData.put(APPLICANT_NAME_FIELD_ID, Collections.singletonList(dingtalkId));
|
|
|
+ formData.put(APPLICANT_DEPARTMENT_FIELD_ID,
|
|
|
+ Collections.singletonList(resolveApplicantDepartmentId(dingtalkId, employeeNo)));
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveApplicantUserId(String employeeNo) {
|
|
|
+ if (employeeRosterService == null) {
|
|
|
+ throw new IllegalStateException("Employee roster service is not configured");
|
|
|
+ }
|
|
|
+ EmployeeRosterInfo employee = employeeRosterService.findByEmployeeNo(employeeNo);
|
|
|
+ if (employee == null) {
|
|
|
+ throw new IllegalArgumentException("misc receipt issue applicant not found: " + employeeNo);
|
|
|
+ }
|
|
|
+ String dingtalkId = toText(employee.getDingtalkId());
|
|
|
+ if (dingtalkId == null) {
|
|
|
+ throw new IllegalArgumentException("misc receipt issue applicant has no DingTalk userId: " + employeeNo);
|
|
|
+ }
|
|
|
+ return dingtalkId;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveApplicantDepartmentId(String dingtalkId, String employeeNo) {
|
|
|
+ if (mingleiDocumentService == null) {
|
|
|
+ throw new IllegalStateException("Department service is not configured");
|
|
|
+ }
|
|
|
+ Map<String, Object> configuration = mingleiDocumentService.queryApplicantDepartmentConfiguration(dingtalkId);
|
|
|
+ String departmentId = configuration == null ? null : toText(configuration.get("departmentId"));
|
|
|
+ if (departmentId == null) {
|
|
|
+ throw new IllegalArgumentException("misc receipt issue applicant department not found: " + employeeNo);
|
|
|
+ }
|
|
|
+ return departmentId;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 单据别取明细 inbbdocno 中 -ML 后的 3 个字符。单号本身以 ML 开头时同样取其后 3 位。
|
|
|
*/
|
|
|
@@ -238,6 +352,7 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
|
|
|
private List<Map<String, Object>> toDetailRows(List<Map<String, Object>> sourceRows) {
|
|
|
List<Map<String, Object>> rows = new ArrayList<Map<String, Object>>();
|
|
|
+ Map<String, String> keeperEmployeeNos = new HashMap<String, String>();
|
|
|
for (Map<String, Object> source : sourceRows) {
|
|
|
Map<String, Object> row = new LinkedHashMap<String, Object>();
|
|
|
putIfNotBlank(row, DETAIL_SEQ_FIELD_ID, value(source, "inbbseq"));
|
|
|
@@ -259,7 +374,7 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
putIfNotBlank(row, DETAIL_PROJECT_DESC_FIELD_ID, value(source, "pjbal003"));
|
|
|
putIfNotBlank(row, DETAIL_SOURCE_DOC_NO_FIELD_ID, value(source, "inbb017"));
|
|
|
copyYidaFieldValues(source, row);
|
|
|
- putWarehouseKeeper(row, source);
|
|
|
+ putWarehouseKeeper(row, source, keeperEmployeeNos);
|
|
|
if (!row.isEmpty()) {
|
|
|
rows.add(row);
|
|
|
}
|
|
|
@@ -268,14 +383,31 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 明细仓管员传入的是工号。按花名册换成钉钉用户 ID 后再写入员工组件。
|
|
|
+ * 仓管员工号按明细料号查询 imaf052。仓管员OA再用该工号换成钉钉用户,员工组件显示姓名。
|
|
|
*/
|
|
|
- private void putWarehouseKeeper(Map<String, Object> row, Map<String, Object> source) {
|
|
|
- String employeeNo = toText(firstItem(value(source, DETAIL_KEEPER_FIELD_ID, "仓管员", "仓管员工号")));
|
|
|
+ private void putWarehouseKeeper(Map<String, Object> row, Map<String, Object> source, Map<String, String> keeperEmployeeNos) {
|
|
|
+ String itemCode = toText(value(source, "inbb001"));
|
|
|
+ if (itemCode == null || keeperLookup == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String employeeNo = keeperEmployeeNo(itemCode, keeperEmployeeNos);
|
|
|
if (employeeNo == null) {
|
|
|
- row.remove(DETAIL_KEEPER_FIELD_ID);
|
|
|
return;
|
|
|
}
|
|
|
+ row.put(DETAIL_KEEPER_NO_FIELD_ID, employeeNo);
|
|
|
+ row.put(DETAIL_KEEPER_OA_FIELD_ID, Collections.singletonList(resolveKeeperUserId(employeeNo)));
|
|
|
+ }
|
|
|
+
|
|
|
+ private String keeperEmployeeNo(String itemCode, Map<String, String> keeperEmployeeNos) {
|
|
|
+ if (keeperEmployeeNos.containsKey(itemCode)) {
|
|
|
+ return keeperEmployeeNos.get(itemCode);
|
|
|
+ }
|
|
|
+ String employeeNo = toText(keeperLookup.findKeeperEmployeeNo(itemCode));
|
|
|
+ keeperEmployeeNos.put(itemCode, employeeNo);
|
|
|
+ return employeeNo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveKeeperUserId(String employeeNo) {
|
|
|
if (employeeRosterService == null) {
|
|
|
throw new IllegalStateException("Employee roster service is not configured");
|
|
|
}
|
|
|
@@ -287,7 +419,7 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
if (dingtalkId == null) {
|
|
|
throw new IllegalArgumentException("misc receipt issue warehouse keeper employee has no DingTalk userId: " + employeeNo);
|
|
|
}
|
|
|
- row.put(DETAIL_KEEPER_FIELD_ID, Collections.singletonList(dingtalkId));
|
|
|
+ return dingtalkId;
|
|
|
}
|
|
|
|
|
|
private List<Map<String, Object>> toMapRows(Object value) {
|
|
|
@@ -345,15 +477,57 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
|
|
|
return new BigDecimal(String.valueOf(value).trim());
|
|
|
}
|
|
|
|
|
|
- private Long toDateMillis(Object value) {
|
|
|
+ private Object mostPreciseDate(Object first, Object second, Object third) {
|
|
|
+ Object selected = null;
|
|
|
+ for (Object candidate : new Object[]{first, second, third}) {
|
|
|
+ if (isBlank(candidate)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (selected == null) {
|
|
|
+ selected = candidate;
|
|
|
+ }
|
|
|
+ String text = String.valueOf(candidate);
|
|
|
+ if (text.indexOf(':') >= 0 || text.indexOf('T') >= 0) {
|
|
|
+ return candidate;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return selected;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 带时分的文本保留到秒。只有日期时,补上当前的时分秒。
|
|
|
+ */
|
|
|
+ private Long toDateTimeMillis(Object value) {
|
|
|
if (isBlank(value)) {
|
|
|
return null;
|
|
|
}
|
|
|
- String text = String.valueOf(value).trim();
|
|
|
+ String text = String.valueOf(value).trim().replace('/', '-');
|
|
|
if (text.matches("\\d+")) {
|
|
|
return Long.valueOf(text);
|
|
|
}
|
|
|
- return LocalDate.parse(text).atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
|
|
+ if (text.indexOf(':') >= 0 || text.indexOf('T') >= 0) {
|
|
|
+ String normalized = text.replace('T', ' ');
|
|
|
+ int zoneIndex = normalized.indexOf('+');
|
|
|
+ if (zoneIndex < 0 && normalized.lastIndexOf('-') > 10) {
|
|
|
+ zoneIndex = normalized.lastIndexOf('-');
|
|
|
+ }
|
|
|
+ if (zoneIndex > 10) {
|
|
|
+ normalized = normalized.substring(0, zoneIndex);
|
|
|
+ }
|
|
|
+ if (normalized.endsWith("Z")) {
|
|
|
+ normalized = normalized.substring(0, normalized.length() - 1);
|
|
|
+ }
|
|
|
+ if (normalized.length() > 19) {
|
|
|
+ normalized = normalized.substring(0, 19);
|
|
|
+ }
|
|
|
+ if (normalized.length() == 16) {
|
|
|
+ normalized = normalized + ":00";
|
|
|
+ }
|
|
|
+ return LocalDateTime.parse(normalized, DATE_TIME_FORMATTER)
|
|
|
+ .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
|
|
+ }
|
|
|
+ LocalTime now = LocalTime.now(ZoneId.systemDefault());
|
|
|
+ return LocalDate.parse(text).atTime(now).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
|
|
}
|
|
|
|
|
|
private Object value(Map<String, Object> source, String firstKey, String... otherKeys) {
|