|
|
@@ -18,6 +18,7 @@ import com.malk.sdzl.service.PayService;
|
|
|
import com.malk.server.aliwork.YDConf;
|
|
|
import com.malk.server.aliwork.YDParam;
|
|
|
import com.malk.server.common.McR;
|
|
|
+import com.malk.server.dingtalk.DDR_New;
|
|
|
import com.malk.service.aliwork.YDClient;
|
|
|
import com.malk.service.dingtalk.DDClient;
|
|
|
import com.malk.service.dingtalk.DDClient_Personnel;
|
|
|
@@ -46,6 +47,7 @@ import java.time.LocalDateTime;
|
|
|
import java.time.OffsetDateTime;
|
|
|
import java.time.ZoneId;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
+import java.time.format.DateTimeParseException;
|
|
|
import java.util.*;
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
|
@@ -75,6 +77,9 @@ public class PayServiceImpl implements PayService {
|
|
|
private YDClient ydClient;
|
|
|
|
|
|
private ConcurrentHashMap<String, LocalDateTime> tokenStore = new ConcurrentHashMap<>();
|
|
|
+ private static final String CONTRACT_FORM_UUID = "FORM-AAC94DE308FA4B40BC149E29C036166CXB6Y";
|
|
|
+ private static final ZoneId SHANGHAI_ZONE = ZoneId.of("Asia/Shanghai");
|
|
|
+ private static final DateTimeFormatter CONTRACT_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
//出差类型枚举
|
|
|
private static final Map<String, String> BUSINESS_TYPE = new HashMap<>();
|
|
|
|
|
|
@@ -1190,6 +1195,7 @@ public class PayServiceImpl implements PayService {
|
|
|
|
|
|
ContractPageListResponse response = getContractList(bodyMap);
|
|
|
if (ObjectUtil.isNotNull(response) && response.getStatus() == 200){
|
|
|
+
|
|
|
int totalRows = response.getData().getRows();
|
|
|
int maxPage = (totalRows + pageSize - 1)/pageSize;
|
|
|
for (int i = 1; i <= maxPage; i++) {
|
|
|
@@ -1217,10 +1223,17 @@ public class PayServiceImpl implements PayService {
|
|
|
s.getAchievementAmount()));
|
|
|
});
|
|
|
}
|
|
|
+
|
|
|
+ List<Map> searchCondition = new ArrayList<>();
|
|
|
+ searchCondition.addAll(Arrays.asList(
|
|
|
+ YDConf.searchCondition_TextFiled("textField_mt804b4o", e.getOmId(), "eq")
|
|
|
+ ));
|
|
|
+
|
|
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
ydClient.operateData(YDParam.builder()
|
|
|
.appType(appType)
|
|
|
.systemToken(systemToken)
|
|
|
+ .searchCondition(JSON.toJSONString(searchCondition))
|
|
|
.formUuid("FORM-AAC94DE308FA4B40BC149E29C036166CXB6Y")
|
|
|
.formDataJson(JSON.toJSONString(UtilMap.map("textField_mt804b4o, " +
|
|
|
"textField_mt804b4p, " +
|
|
|
@@ -1257,7 +1270,7 @@ public class PayServiceImpl implements PayService {
|
|
|
.toEpochMilli(),
|
|
|
e.getOurCompany()
|
|
|
)))
|
|
|
- .build(), YDConf.FORM_OPERATION.create);
|
|
|
+ .build(), YDConf.FORM_OPERATION.upsert_v2);
|
|
|
});
|
|
|
} catch (Exception e) {
|
|
|
log.info("处理合同失败:{}",bodyMap);
|
|
|
@@ -1277,6 +1290,132 @@ public class PayServiceImpl implements PayService {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 分页获取合同,只同步 updateDate 为当天的合同。
|
|
|
+ * 宜搭通过 omId 精确匹配,使用 upsert_v2 更新已有记录并补建缺失记录。
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void syncTodayContracts() {
|
|
|
+ LocalDate today = LocalDate.now(SHANGHAI_ZONE);
|
|
|
+ int pageSize = 10;
|
|
|
+ Map<String, Object> bodyMap = new HashMap<>();
|
|
|
+ bodyMap.put("pageIndex", 1);
|
|
|
+ bodyMap.put("pageSize", pageSize);
|
|
|
+ bodyMap.put("beginDate", today.format(CONTRACT_DATE_FORMATTER));
|
|
|
+
|
|
|
+ ContractPageListResponse firstPage = getContractList(bodyMap);
|
|
|
+ if (firstPage == null || firstPage.getData() == null || firstPage.getData().getRows() == null) {
|
|
|
+ log.warn("同步当天合同失败:合同分页接口没有返回有效数据");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ int totalRows = firstPage.getData().getRows();
|
|
|
+ int maxPage = (totalRows + pageSize - 1) / pageSize;
|
|
|
+ int syncedCount = 0;
|
|
|
+ for (int pageIndex = 1; pageIndex <= maxPage; pageIndex++) {
|
|
|
+ try {
|
|
|
+ if (pageIndex > 1 && pageIndex % 10 == 0) {
|
|
|
+ Thread.sleep(2000L);
|
|
|
+ }
|
|
|
+ ContractPageListResponse pageResponse;
|
|
|
+ if (pageIndex == 1) {
|
|
|
+ pageResponse = firstPage;
|
|
|
+ } else {
|
|
|
+ bodyMap.put("pageIndex", pageIndex);
|
|
|
+ pageResponse = getContractList(bodyMap);
|
|
|
+ }
|
|
|
+ if (pageResponse == null || pageResponse.getData() == null
|
|
|
+ || pageResponse.getData().getPageList() == null) {
|
|
|
+ log.warn("同步当天合同跳过无效分页:pageIndex={}", pageIndex);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ for (ContractPageListResponse.ContractInfo contract : pageResponse.getData().getPageList()) {
|
|
|
+ if (contract != null && today.toString().equals(contract.getUpdateDate())) {
|
|
|
+ upsertContractToYida(contract);
|
|
|
+ syncedCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ log.error("同步当天合同被中断:pageIndex={}", pageIndex, e);
|
|
|
+ return;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("同步当天合同失败:pageIndex={}", pageIndex, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("当天合同同步完成:date={}, totalRows={}, syncedCount={}", today, totalRows, syncedCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void upsertContractToYida(ContractPageListResponse.ContractInfo contract) {
|
|
|
+ List<Map> details = new ArrayList<>();
|
|
|
+ if (contract.getEstimateExtendList() != null) {
|
|
|
+ for (EstimateExtendItem item : contract.getEstimateExtendList()) {
|
|
|
+ details.add(UtilMap.map("textField_mt804b50, " +
|
|
|
+ "textField_mt804b52, " +
|
|
|
+ "numberField_mt804b54, " +
|
|
|
+ "numberField_mt804b55, " +
|
|
|
+ "numberField_mt804b56",
|
|
|
+ item.getProductName(),
|
|
|
+ item.getSubProjectName(),
|
|
|
+ item.getExecuteAmount(),
|
|
|
+ item.getInvoicedAmount(),
|
|
|
+ item.getAchievementAmount()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Map> searchCondition = Collections.singletonList(
|
|
|
+ YDConf.searchCondition_TextFiled("textField_mt804b4o", contract.getOmId(), "eq"));
|
|
|
+ ydClient.operateData(YDParam.builder()
|
|
|
+ .appType(appType)
|
|
|
+ .systemToken(systemToken)
|
|
|
+ .searchCondition(JSON.toJSONString(searchCondition))
|
|
|
+ .formUuid(CONTRACT_FORM_UUID)
|
|
|
+ .formDataJson(JSON.toJSONString(UtilMap.map("textField_mt804b4o, " +
|
|
|
+ "textField_mt804b4p, " +
|
|
|
+ "textField_mt804b4q, " +
|
|
|
+ "textField_mt804b4r, " +
|
|
|
+ "textField_mt804b4s, " +
|
|
|
+ "textField_mt804b4t, " +
|
|
|
+ "textField_mt804b4u, " +
|
|
|
+ "textField_mt804b4v, " +
|
|
|
+ "textField_mt804b4w, " +
|
|
|
+ "numberField_mt804b4x, " +
|
|
|
+ "tableField_mt804b4y, " +
|
|
|
+ "dateField_mt804b5c, " +
|
|
|
+ "dateField_mt804b5d, " +
|
|
|
+ "textField_mti0qoe1",
|
|
|
+ contract.getOmId(),
|
|
|
+ contract.getCreateDate(),
|
|
|
+ contract.getClientName(),
|
|
|
+ contract.getContractType(),
|
|
|
+ contract.getContractNo(),
|
|
|
+ contract.getContractName(),
|
|
|
+ contract.getOppositeName(),
|
|
|
+ contract.getClientGroupName(),
|
|
|
+ contract.getUpdateDate(),
|
|
|
+ contract.getContractAmount(),
|
|
|
+ details,
|
|
|
+ toYidaDate(contract.getCreateDate()),
|
|
|
+ toYidaDate(contract.getUpdateDate()),
|
|
|
+ contract.getOurCompany())))
|
|
|
+ .build(), YDConf.FORM_OPERATION.upsert_v2);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Long toYidaDate(String value) {
|
|
|
+ if (value == null || value.trim().isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return LocalDate.parse(value, CONTRACT_DATE_FORMATTER)
|
|
|
+ .atStartOfDay(SHANGHAI_ZONE)
|
|
|
+ .toInstant()
|
|
|
+ .toEpochMilli();
|
|
|
+ } catch (DateTimeParseException e) {
|
|
|
+ log.warn("合同日期格式异常:{}", value);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public void getContract(Map<String, Object> bodyMap) {
|
|
|
try {
|
|
|
@@ -1559,6 +1698,8 @@ public class PayServiceImpl implements PayService {
|
|
|
|
|
|
@Override
|
|
|
public AmsOmApprovalCallbackResponse callback(AmsOmApprovalCallbackRequest request) {
|
|
|
+ request = enrichOmApprovalCallbackRequest(request);
|
|
|
+ validateOmApprovalCallbackRequest(request);
|
|
|
|
|
|
String requestUrl = sdzlUrl + CALLBACK_PATH;
|
|
|
|
|
|
@@ -1569,6 +1710,9 @@ public class PayServiceImpl implements PayService {
|
|
|
headers.put("Authorization", authorization);
|
|
|
|
|
|
String responseBody = UtilHttp.doPost(requestUrl, headers, null, request.toRequestBody());
|
|
|
+ if (responseBody == null || responseBody.trim().isEmpty()) {
|
|
|
+ throw new IllegalStateException("AMS OM 审核回写接口返回为空");
|
|
|
+ }
|
|
|
AmsOmApprovalCallbackResponse response = JSON.parseObject(responseBody,
|
|
|
AmsOmApprovalCallbackResponse.class);
|
|
|
if (response == null) {
|
|
|
@@ -1581,6 +1725,142 @@ public class PayServiceImpl implements PayService {
|
|
|
return response;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 仅传流程主键时,从宜搭表单和审批记录补齐 AMS 回写参数。
|
|
|
+ */
|
|
|
+ private AmsOmApprovalCallbackRequest enrichOmApprovalCallbackRequest(
|
|
|
+ AmsOmApprovalCallbackRequest request) {
|
|
|
+ if (request == null) {
|
|
|
+ throw new IllegalArgumentException("OM 审核回写请求不能为空");
|
|
|
+ }
|
|
|
+ if (request.getProcessInstanceId() == null
|
|
|
+ || request.getProcessInstanceId().trim().isEmpty()) {
|
|
|
+ return request;
|
|
|
+ }
|
|
|
+
|
|
|
+ DDR_New formResponse = ydClient.queryData(YDParam.builder()
|
|
|
+ .appType(appType)
|
|
|
+ .systemToken(systemToken)
|
|
|
+ .formInstanceId(request.getProcessInstanceId())
|
|
|
+ .build(), YDConf.FORM_QUERY.retrieve_id);
|
|
|
+ if (formResponse == null || formResponse.getFormData() == null
|
|
|
+ || formResponse.getFormData().isEmpty()) {
|
|
|
+ throw new IllegalStateException("宜搭未查询到流程表单数据,processInstanceId="
|
|
|
+ + request.getProcessInstanceId());
|
|
|
+ }
|
|
|
+
|
|
|
+ Map formData = formResponse.getFormData();
|
|
|
+ if (isBlank(request.getFormContentJson())) {
|
|
|
+ request.setFormContentJson(JSON.toJSONString(formData));
|
|
|
+ }
|
|
|
+ if (isBlank(request.getOaCode())) {
|
|
|
+ request.setOaCode(firstString(formData,
|
|
|
+ "oaCode", "oaCoding", "code",
|
|
|
+ "textField_mpxojd0l", "textField_mpxnklyj"));
|
|
|
+ }
|
|
|
+ if (isBlank(request.getFormName())) {
|
|
|
+ request.setFormName(firstString(formData,
|
|
|
+ "formName", "form_name", "formTitle", "title"));
|
|
|
+ }
|
|
|
+ if (isBlank(request.getOmId())) {
|
|
|
+ request.setOmId(firstString(formData,
|
|
|
+ "omId", "omID", "om_id", "textField_mt804b4o"));
|
|
|
+ }
|
|
|
+
|
|
|
+ JSONObject latestRecord = findLatestApprovalRecord(request.getProcessInstanceId());
|
|
|
+ if (latestRecord != null) {
|
|
|
+ if (isBlank(request.getColId())) {
|
|
|
+ request.setColId(latestRecord.getString("taskId"));
|
|
|
+ }
|
|
|
+ if (isBlank(request.getFlowState())) {
|
|
|
+ request.setFlowState(latestRecord.getString("action"));
|
|
|
+ }
|
|
|
+ if (isBlank(request.getFlowDate())) {
|
|
|
+ request.setFlowDate(toShanghaiDate(latestRecord.getString("operateTimeGMT")));
|
|
|
+ }
|
|
|
+ if (isBlank(request.getMessage())) {
|
|
|
+ request.setMessage(latestRecord.getString("remark"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return request;
|
|
|
+ }
|
|
|
+
|
|
|
+ private JSONObject findLatestApprovalRecord(String processInstanceId) {
|
|
|
+ DDR_New recordResponse = ydClient.queryData(YDParam.builder()
|
|
|
+ .appType(appType)
|
|
|
+ .systemToken(systemToken)
|
|
|
+ .processInstanceId(processInstanceId)
|
|
|
+ .build(), YDConf.FORM_QUERY.retrieve_approval_record);
|
|
|
+ if (recordResponse == null || recordResponse.getResult() == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ JSONArray records;
|
|
|
+ Object result = recordResponse.getResult();
|
|
|
+ if (result instanceof JSONArray) {
|
|
|
+ records = (JSONArray) result;
|
|
|
+ } else {
|
|
|
+ records = JSON.parseArray(JSON.toJSONString(result));
|
|
|
+ }
|
|
|
+ if (records == null || records.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return records.stream()
|
|
|
+ .map(item -> item instanceof JSONObject
|
|
|
+ ? (JSONObject) item : JSON.parseObject(JSON.toJSONString(item)))
|
|
|
+ .filter(item -> item.getString("operateTimeGMT") != null)
|
|
|
+ .max(Comparator.comparing(item -> {
|
|
|
+ OffsetDateTime time = safeParseIsoTime(item.getString("operateTimeGMT"));
|
|
|
+ return time == null ? OffsetDateTime.MIN : time;
|
|
|
+ }))
|
|
|
+ .orElse(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String firstString(Map formData, String... keys) {
|
|
|
+ for (String key : keys) {
|
|
|
+ Object value = formData.get(key);
|
|
|
+ if (value != null && !String.valueOf(value).trim().isEmpty()) {
|
|
|
+ return String.valueOf(value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String toShanghaiDate(String value) {
|
|
|
+ OffsetDateTime time = safeParseIsoTime(value);
|
|
|
+ return time == null ? null : time.atZoneSameInstant(SHANGHAI_ZONE)
|
|
|
+ .toLocalDate().format(CONTRACT_DATE_FORMATTER);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isBlank(String value) {
|
|
|
+ return value == null || value.trim().isEmpty();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验 OM 审核回写接口文档中声明的必填字段。
|
|
|
+ */
|
|
|
+ private void validateOmApprovalCallbackRequest(AmsOmApprovalCallbackRequest request) {
|
|
|
+ if (request == null) {
|
|
|
+ throw new IllegalArgumentException("OM 审核回写请求不能为空");
|
|
|
+ }
|
|
|
+ requireCallbackField(request.getColId(), "colId");
|
|
|
+ requireCallbackField(request.getOaCode(), "oaCode");
|
|
|
+ requireCallbackField(request.getProcessInstanceId(), "processInstanceId");
|
|
|
+ requireCallbackField(request.getFlowState(), "flowState");
|
|
|
+ requireCallbackField(request.getFlowDate(), "flowDate");
|
|
|
+ requireCallbackField(request.getFormName(), "formName");
|
|
|
+ requireCallbackField(request.getFormContentJson(), "formContentJson");
|
|
|
+ if ("OM收入会签单".equals(request.getFormName())) {
|
|
|
+ requireCallbackField(request.getOmId(), "omId");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void requireCallbackField(String value, String fieldName) {
|
|
|
+ if (value == null || value.trim().isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("OM 审核回写字段 " + fieldName + " 不能为空");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 密钥算法
|
|
|
*/
|