package com.malk.taisen.service.impl; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.malk.server.aliwork.YDConf; import com.malk.server.aliwork.YDParam; 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_Notice; import com.malk.service.dingtalk.DDClient_NoticeResult; import com.malk.taisen.dto.ArNotifyRecord; import com.malk.taisen.service.ArNotifyService; import com.malk.utils.UtilList; import com.malk.utils.UtilMap; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @Slf4j @Service public class ArNotifyServiceImpl implements ArNotifyService { private static final String APP_TYPE = "APP_N9NPHVTQLPBPO8MR6WFG"; private static final String FORM_UUID = "FORM-A7AA1BE41C354D5C879925006D3A3F06K6ZY"; private static final int MAX_ATTEMPTS = 6; @Autowired private YDClient ydClient; @Autowired private DDClient ddClient; @Autowired private DDClient_Notice ddClientNotice; @Autowired private DDClient_NoticeResult ddClientNoticeResult; @Value("${ar.notify.user-ids:}") private String notifyUserIds; /** * Persist an AR batch and send one DingTalk work notification. * * @param records AR automatic posting records * @return true only after DingTalk confirms delivery and Yida is updated */ @Override public boolean notify(List records) { try { for (ArNotifyRecord record : records) { validate(record); } final String instanceId = createRecord(records); if (instanceId == null) { return false; } List recipients = recipients(); if (UtilList.isEmpty(recipients)) { markFailure(instanceId, 0, "未配置AR_NOTIFY_USER_IDS"); return false; } return sendAndConfirm(records, instanceId, recipients); } catch (Exception e) { log.error("AR自动入账通知执行失败", e); return false; } } private String createRecord(List records) { Map formData = UtilMap.map("tableField_3ycv10yjn", detailRows(records)); formData.put("selectField_89a55tbg2", "处理中"); formData.put("numberField_89a56h485", 0); Object result = retry("创建宜搭实例", new Action() { @Override public Object execute() { return ydClient.createData(YDParam.builder().appType(APP_TYPE).formUuid(FORM_UUID) .formDataJson(JSON.toJSONString(formData)).build()); } }); return instanceId(result); } private boolean sendAndConfirm(List records, String instanceId, List recipients) { String lastError = ""; String taskId = null; for (int attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) { try { if (StringUtils.isBlank(taskId)) { taskId = ddClientNotice.sendNotification(ddClient.getAccessToken(), recipients, null, false, notificationMessage(records, instanceId)); if (StringUtils.isBlank(taskId)) { throw new IllegalStateException("钉钉未返回工作通知任务编号"); } update(instanceId, UtilMap.map("textField_89a545m8z, numberField_89a56h485", taskId, attempt - 1)); } Map result = ddClientNoticeResult.getSendResult(ddClient.getAccessToken(), taskId); if (!isSent(result)) { throw new IllegalStateException("钉钉工作通知尚未发送成功: " + JSON.toJSONString(result)); } Date now = new Date(); update(instanceId, UtilMap.map( "textField_89a41cak4, dateField_89a42ww2f, textField_89a53tgha, selectField_89a55tbg2, numberField_89a56h485, textareaField_89a575hku", "X", now.getTime(), new SimpleDateFormat("HHmmss").format(now), "成功", attempt - 1, "钉钉工作通知发送成功")); return true; } catch (Exception e) { lastError = e.getMessage(); log.warn("AR自动入账通知第{}次尝试失败, instanceId={}, taskId={}, error={}", attempt, instanceId, taskId, lastError); if (attempt < MAX_ATTEMPTS) { sleep(attempt); } } } log.error("AR自动入账通知重试耗尽, instanceId={}, error={}", instanceId, lastError); markFailure(instanceId, MAX_ATTEMPTS - 1, lastError); return false; } private void markFailure(final String instanceId, final int retryCount, final String message) { retry("回写宜搭失败状态", new Action() { @Override public Object execute() { update(instanceId, UtilMap.map("selectField_89a55tbg2, numberField_89a56h485, textareaField_89a575hku", "失败", retryCount, StringUtils.abbreviate(message, 1900))); return Boolean.TRUE; } }); } private void update(String instanceId, Map formData) { ydClient.operateData(YDParam.builder().appType(APP_TYPE).formInstanceId(instanceId) .updateFormDataJson(JSON.toJSONString(formData)).build(), YDConf.FORM_OPERATION.update); } private Object retry(String actionName, Action action) { for (int attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) { try { return action.execute(); } catch (Exception e) { log.warn("{}第{}次尝试失败: {}", actionName, attempt, e.getMessage()); if (attempt < MAX_ATTEMPTS) { sleep(attempt); } } } log.error("{}重试耗尽", actionName); return null; } private void sleep(int retryNumber) { try { Thread.sleep((1L << (retryNumber - 1)) * 1000L); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new IllegalStateException("重试等待被中断", e); } } private Map notificationMessage(List records, String instanceId) { String detailUrl = "https://www.aliwork.com/" + APP_TYPE + "/formDetail/" + FORM_UUID + "?formInstId=" + instanceId + "&corpid=dinge61fe69900ea236b35c2f4657eb6378f"; ArNotifyRecord first = records.get(0); String text = "本次自动入账记录数:" + records.size() + "\n公司代码:" + first.getBUKRS() + "\n首条客户:" + first.getKUNNR() + " " + StringUtils.defaultString(first.getZNAME()) + "\n首条凭证:" + first.getBELNR() + "\n请点击查看明细。"; Map link = new HashMap<>(); link.put("picUrl", "@lALOACZwe2Rk"); link.put("title", "AR自动入账通知"); link.put("text", text); link.put("messageUrl", detailUrl); Map message = new HashMap<>(); message.put("msgtype", "link"); message.put("link", link); return message; } private boolean isSent(Map result) { if (result == null) { return false; } return !hasFailure(result, "invalid_user_id_list") && !hasFailure(result, "forbidden_list") && !hasFailure(result, "failed_user_id_list") && !hasFailure(result, "invalid_dept_id_list") && !hasFailure(result, "forbidden_dept_id_list") && !hasFailure(result, "failed_dept_id_list"); } private boolean hasFailure(Map result, String key) { Object value = result.get(key); if (value == null) { return false; } if (value instanceof Collection) { return !((Collection) value).isEmpty(); } if (value.getClass().isArray()) { return java.lang.reflect.Array.getLength(value) > 0; } return StringUtils.isNotBlank(String.valueOf(value)); } private String instanceId(Object result) { if (result instanceof DDR_New) { DDR_New response = (DDR_New) result; if (StringUtils.isNotBlank(response.getFormInstId())) { return response.getFormInstId(); } if (StringUtils.isNotBlank(response.getInstanceId())) { return response.getInstanceId(); } result = response.getResult(); } if (result instanceof String && StringUtils.isNotBlank((String) result)) { return (String) result; } if (!(result instanceof Map)) { log.error("宜搭创建响应未包含实例ID, response={}", JSON.toJSONString(result)); return null; } Map map = (Map) result; Object id = map.get("formInstId"); if (id == null) { id = map.get("formInstanceId"); } if (id == null) { id = map.get("instanceId"); } return id == null ? null : String.valueOf(id); } private List recipients() { List result = new ArrayList<>(); String[] userIds = StringUtils.split(StringUtils.defaultString(notifyUserIds), ','); if (userIds == null) { return result; } for (String userId : userIds) { if (StringUtils.isNotBlank(userId)) { result.add(userId.trim()); } } return result; } private List detailRows(List records) { List rows = new ArrayList<>(); for (ArNotifyRecord record : records) { rows.add(UtilMap.map( "textField_3ycw2w508, textField_3ycw3wu79, textField_3ycw4j3ot, textField_3ycw539ws, textField_3ycw6yohj, dateField_3ycw7ql0n, dateField_3ycw8qqry, dateField_3ycw9wnsf, numberField_3ycwa25z7, textField_3ycwbocwu, textField_3ycwcntzu, dateField_3ycwdnk73, textField_3ycwes8k4, textField_3ycwfk3zf, textField_3ycwgmpib, textField_3ycwhz5dp", record.getBUKRS(), record.getKUNNR(), record.getZNAME(), record.getBELNR(), record.getGJAHR(), dateMillis(record.getBUDAT()), dateMillis(record.getBLDAT()), dateMillis(record.getCPUDT()), record.getWRBTR(), record.getWAERS(), "", null, "", "", "", "")); } return rows; } private static long dateMillis(String date) { try { SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); formatter.setLenient(false); return formatter.parse(date).getTime(); } catch (ParseException e) { throw new IllegalArgumentException("日期必须为yyyyMMdd格式: " + date, e); } } private void validate(ArNotifyRecord record) { if (record == null || StringUtils.isAnyBlank(record.getBUKRS(), record.getKUNNR(), record.getBELNR(), record.getGJAHR(), record.getBUDAT(), record.getBLDAT(), record.getCPUDT()) || record.getWRBTR() == null) { throw new IllegalArgumentException("BUKRS、KUNNR、BELNR、GJAHR、BUDAT、BLDAT、CPUDT、WRBTR不能为空"); } } private interface Action { Object execute(); } public static void main(String[] args) { String url = "https://etl-nonprod-tasks.tysondt.com:443/api/1/rest/feed/run/task/TysonNonProd/projects/10-QA-team-cash-to-order/tk_dingtalk_to_sap_otc_inquiry?bearer_token=Rs8eIy614To-g4UAQTsYTt@G9bRMhQrl"; // String url2 = "https://etl-nonprod-tasks.tysondt.com:443/api/1/rest/feed/run/task/TysonNonProd/projects/00-STG-team-cash-to-order/tk_dingtalk_to_sap_otc_unlocked_orders_inquiry?bearer_token=i2AZ93FBOD42aPNLyWACGxXRyK0LdY8U"; String result= HttpUtil.createPost(url).body(JSONObject.toJSONString(UtilMap.map("CUSTOMER_NUMBER","3000045"))).form(JSONObject.toJSONString(UtilMap.map("requestID, interface_no","12345, OTC002"))).execute().body(); System.out.println(result); // String result2= HttpUtil.createPost(url2).body(JSONObject.toJSONString(UtilMap.map("CUSTOMER_NUMBER","1000253"))).form(JSONObject.toJSONString(UtilMap.map("requestID","1234"))).execute().body(); // System.out.println(result2); } }