pruple_boy 9 ヶ月 前
コミット
077b52faeb

+ 22 - 0
mjava-lanyun/src/main/java/com/malk/lanyun/controller/DDController.java

@@ -0,0 +1,22 @@
+package com.malk.lanyun.controller;
+
+import com.malk.controller.DDCallbackController;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 钉钉事件回调 3_1
+ * -
+ * [子项目直接继承即可有调用, 无需实现]
+ * -
+ * 注解 @RequestMapping 路径不能重复 [主子项目属同一个项目];
+ * 获取项目回调请求地址, https://mc.cloudpure.cn/frp/xxx/dd/callback [调试代理: frp + nginx]
+ */
+@Slf4j
+@RestController
+@RequestMapping("/dd")
+public class DDController extends DDCallbackController {
+
+
+}

+ 203 - 0
mjava-lanyun/src/main/java/com/malk/lanyun/controller/TLYController.java

@@ -0,0 +1,203 @@
+package com.malk.lanyun.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.malk.server.aliwork.YDConf;
+import com.malk.server.aliwork.YDParam;
+import com.malk.server.common.McR;
+import com.malk.service.aliwork.YDClient;
+import com.malk.service.dingtalk.DDClient;
+import com.malk.service.dingtalk.DDClient_Workflow;
+import com.malk.utils.UtilDateTime;
+import com.malk.utils.UtilMap;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.map.HashedMap;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.*;
+
+/**
+ * 错误抛出与拦截详见 CatchException
+ */
+@Slf4j
+@RestController
+@RequestMapping("oa")
+public class TLYController {
+
+    @Autowired
+    private DDClient ddClient;
+
+    @Autowired
+    private YDClient ydClient;
+
+    @Autowired
+    private DDClient_Workflow ddClient_workflow;
+
+    /**
+     * 推送审批
+     */
+    @PostMapping("do-approve")
+    public McR doApprove(String processInstanceId, String type) {
+
+        log.info("推送审批, {} {}", type, processInstanceId);
+        // OA组件name, 匹配宜搭组件ID
+        if ("出差".equals(type)) {
+            Map<String, String> compsId_main = UtilMap.map("出差类型, 预算, 出差天数, 出差事由, 出差备注", "textField_lygo4abp, numberField_lygo9owt, numberField_lygo4abs, textareaField_lygo9owv, textareaField_lygo9ox1");
+            Map<String, String> compsId_itinerary = UtilMap.map("交通工具, 单程往返, 出发城市, 目的城市, 开始时间, 结束时间, 时长", "textField_lygo9oww, textField_lygo9owx, textField_lygo9owy, textField_lygo9owz, textField_lygvu31i, textField_lygvu31j, numberField_lygo9ox0");
+            compsId_main.put("出差明细", "tableField_lygo9owu"); // 子表组件
+            syncYD(processInstanceId, "FORM-210DA087671044F8A5CD72F0E9E89060SZ8Q", compsId_main, compsId_itinerary, "itinerary");
+        }
+        if ("加班".equals(type)) {
+            Map<String, String> compsId_main = UtilMap.map("加班人, 加班类型, 开始时间, 结束时间, 时长, 甲方付费, 加班原因", "employeeField_lygnp948, textField_lygnetwa, textField_lygwcloa, textField_lygwclob, numberField_lygnetwe, textField_lygnetwf, textareaField_lygnetwg");
+            Map<String, String> compsId_itinerary = UtilMap.map("加班时间, overtimeDuration", "textField_lygwclo8, numberField_lygnetwd");
+            compsId_main.put("明细", "tableField_lygnetwc"); // 子表组件
+            syncYD(processInstanceId, "FORM-B33388047A42479BB6387F9E0B453588EZ4P", compsId_main, compsId_itinerary, "everyDayDuration");
+        }
+        if ("请假".equals(type)) {
+            Map<String, String> compsId_main = UtilMap.map("DDHolidayField, 请假事由, 代班人", "textField_lyh0xukm / textField_lyh0xukn / numberField_lygo4abs / radioField_lyh0xuko / textField_lygo4abp, textareaField_lygo4abt, employeeField_lygo4abu");
+            syncYD(processInstanceId, "FORM-FB29F35DF0A04DC2A5F2860E9593C9C6XCMT", compsId_main, null, "");
+        }
+        if ("调班".equals(type)) {
+            Map<String, String> compsId_main = UtilMap.map("申请类型, 原班次名称, 原考勤时间, 新班次考勤时间, 新班次有效时长, 处理状态", "selectField_lygnsggb, textField_lygnsgg6, textField_lygnsgg7, textField_lygnsgg8, textField_lygnsgg9, selectField_lygnsgga");
+            syncYD(processInstanceId, "FORM-777EB143B87D45F2ACB228A024290E1A9GSF", compsId_main, null, "");
+
+        }
+        return McR.success();
+    }
+
+    /// dingtalk
+    final String APP_EKY = "dingfwn4kpmb4g3dy4fj";
+    final String APP_SECRET = "OwdkUc9nvBivpwsg0AiaEcyWLtZ678fskqBJwP7B5CQNrycIoyKuWslFTCOOaZG4";
+    /// aliwork
+    final String APP_TYPE = "APP_ERBDTFS82HOVBPL3NFH0";
+    final String SYSTRM_TOKEN = "RRB66F91T97H1WN89QZYC47PKLZO2ZQOUMOQLP";
+
+    /// 同步OA单据到宜搭
+    public void syncYD(String processInstanceId, String formUuid, Map<String, String> compsId_main, Map<String, String> compsId_itinerary, String compId_sub_oa) {
+
+        String token = ddClient.getAccessToken(APP_EKY, APP_SECRET);
+
+        Map processData = ddClient_workflow.getProcessInstanceId(token, processInstanceId);
+        List<Map> formComponentValues = (List<Map>) processData.get("formComponentValues");
+
+        String userId = String.valueOf(processData.get("originatorUserId"));
+        long cDate = UtilDateTime.parse(UtilMap.getString(processData, "createTime"), "yyyy-MM-dd'T'HH:mm").getTime();
+        Map formData = UtilMap.map("employeeField_ltxqs53k, departmentSelectField_lu20ayky, dateField_ltxqs53j, textField_lygnetw9", Arrays.asList(userId), Arrays.asList(processData.get("originatorDeptId")), cDate, UtilMap.getString(processData, "businessId"));
+
+        for (String name : compsId_main.keySet()) {
+            String compId = compsId_main.get(name);
+            // 判定是否子表 [宜搭]
+            if (compId.startsWith("tableField_")) {
+                List<Map> details = new ArrayList<>();
+                // 兼容明细组件, 存在多条情况 [加班跨天才有有明细]
+                Optional optional = formComponentValues.stream().filter(item -> compId_sub_oa.equals(item.get("bizAlias"))).findAny();
+                if (!optional.isPresent()) {
+                    continue;
+                }
+                String schedule = UtilMap.getString((Map) optional.get(), "value");
+                List<Map> itineraryList = ((List<Map>) JSON.parse(schedule));
+                // 循环明细数据
+                for (Map itinerary : itineraryList) {
+                    List<Map> rowValue = (List<Map>) itinerary.get("rowValue");
+                    Map rowData = new HashedMap();
+                    // 循环子表组件
+                    for (String subName : compsId_itinerary.keySet()) {
+                        log.info("子表字段, {}", subName);
+                        //  加班单跨天 [子表label为空]
+                        rowData.put(compsId_itinerary.get(subName), rowValue.stream().filter(item -> subName.equals(item.get("bizAlias")) || subName.equals(item.get("label"))).findAny().get().get("value"));
+                    }
+                    details.add(rowData);
+                }
+                formData.put(compId, details);
+                continue;
+            }
+            log.info("主表字段, {}", name);
+            // 请假套件: 开始时间 / 结束时间 / 时长 / 单位 / 请假类型
+            if ("DDHolidayField".equals(name)) {
+                Optional optional = formComponentValues.stream().filter(item -> "DDHolidayField".equals(item.get("componentType"))).findAny();
+                if (optional.isPresent()) {
+                    String[] ids = compId.split(" / ");
+                    List vas = (List) JSON.parse(UtilMap.getString((Map) optional.get(), "value"));
+                    for (int i = 0; i < ids.length; i++) {
+                        formData.put(ids[i], vas.get(i));
+                    }
+                }
+                continue;
+            }
+            Map formComp = formComponentValues.stream().filter(item -> name.equals(item.get("name"))).findAny().get();
+            Object value = formComp.get("value");
+            // 成员组件, 数据处理
+            if ("InnerContactField".equals(formComp.get("componentType")) && formComp.containsKey("value")) {
+                List<Map> empInfos = (List<Map>) JSON.parse(String.valueOf(formComp.get("extValue")));
+                List<String> emplsId = new ArrayList<>();
+                for (Map empInfo : empInfos) {
+                    emplsId.add(String.valueOf(empInfo.get("emplId")));
+                }
+                value = emplsId; // 成员多选
+            }
+            formData.put(compId, value);
+        }
+
+        // 用于审批回传
+        List<Map> tasks = UtilMap.getList(processData, "tasks");
+        formData.put("textField_lygvvyd9", tasks.get(0).get("taskId"));
+        formData.put("textField_lygvvyda", tasks.get(0).get("userId"));
+        formData.put("textField_lyh4y3th", processInstanceId);
+        formData.put("selectField_lyo1uao4", "否"); // 出差是否报销, 否
+        formData.put("selectField_lyo1zprd", "是"); // 同步存量数据, 否
+
+        log.info("审批数据, {}", JSON.toJSONString(formData));
+        ydClient.operateData(YDParam.builder()
+                .appType(APP_TYPE)
+                .systemToken(SYSTRM_TOKEN)
+                .formUuid(formUuid)
+                .formDataJson(JSON.toJSONString(formData))
+                .userId(userId)
+                .build(), YDConf.FORM_OPERATION.start);
+    }
+
+    /**
+     * 审批回调
+     */
+    @PostMapping("approved")
+    public McR approved(String processInstanceId, String userId, String taskId, String result) {
+        log.info("审批回调, {} {}", processInstanceId, result);
+        String accessToken = ddClient.getAccessToken(APP_EKY, APP_SECRET);
+        ddClient_workflow.executeRunningApprove(accessToken, processInstanceId, userId, taskId, result, "", null);
+        return McR.success();
+    }
+
+    /**
+     * 删除/撤销
+     */
+    @PostMapping("terminate")
+    public McR terminate(String processInstanceId, String userId) {
+        log.info("删除/撤销, {} {}", processInstanceId, userId);
+
+        String accessToken = ddClient.getAccessToken(APP_EKY, APP_SECRET);
+        ddClient_workflow.terminateRunningApprove(accessToken, processInstanceId, true, "发起人撤销", userId);
+        return McR.success();
+    }
+
+
+    /********** ------------------------------------------------------- **********/
+
+    @GetMapping("test")
+    McR test() {
+
+        long beginTime = UtilDateTime.parseDateTime("2024-06-01 00:00:00").getTime();
+        long finishTime = UtilDateTime.parseDateTime("2024-07-17 00:00:00").getTime();
+
+        Map extInfo = UtilMap.map("statuses", Arrays.asList("COMPLETED"));
+        List<String> dataList = ddClient_workflow.getInstanceIds_all(ddClient.getAccessToken(), "PROC-6E40CB5E-F2AE-4CE6-9864-F5C3848D0C7E", beginTime, finishTime, extInfo);
+
+
+        log.info("xxxx, {}", dataList.size());
+        return McR.success();
+    }
+}
+
+

+ 11 - 0
mjava-lanyun/src/main/java/com/malk/lanyun/controller/TimerController.java

@@ -50,4 +50,15 @@ public class TimerController {
         timerService.execlExport(response,request);
     }
 
+
+    /**
+     * 催款函
+     */
+    @GetMapping("sendReminderLetter")
+    public void updateBigOut() {
+        timerService.sendReminderLetter();
+    }
+
+
+
 }

+ 4 - 0
mjava-lanyun/src/main/java/com/malk/lanyun/service/TimerService.java

@@ -14,4 +14,8 @@ public interface TimerService {
 
     void fresh();
     void execlExport(HttpServletResponse response, HttpServletRequest request);
+
+
+    // 催款函
+    void sendReminderLetter();
 }

+ 36 - 1
mjava-lanyun/src/main/java/com/malk/lanyun/service/impl/TimerServiceImpl.java

@@ -231,6 +231,41 @@ public class TimerServiceImpl implements TimerService {
 
         List<Map> dataList = (List<Map>) formData.get("tableField_ltxyt76f");
         log.info("dataList:{}",dataList);
-        UtilExcel.exportMapAndListByTemplate(response, dataMain, dataList, Map.class, fileName, "Purchase_order.xlsx");
+        UtilExcel.exportMapAndListByTemplate(response, dataMain, dataList, Map.class, fileName, "Template_days.xlsx");
+    }
+
+
+    /**
+     * 应收款通知:
+     * 	每月10日,拉取上月底之前开票档案,已开未有任何回款数据。按照客户纬度汇总生成催款单据
+     *  - 只要有已回款,则不在通知
+     *  - 红字发票不需要通知
+     */
+    @Override
+    public void sendReminderLetter() {
+
+        List<Map> dataList = ydService.queryFormData_all(YDParam.builder()
+                .formUuid("FORM-6603375ED27B4D059CBB919C2BEFA44BZVOL")
+                .searchCondition(JSON.toJSONString(UtilMap.map("radioField_m06hhw2p, selectField_lvituew9", "未回款", "正常")))
+                .build());
+
+        // 数据字典
+        UtilMap.map("大业主, 小业主, 工程订单, 日保一次性",
+                UtilMap.map("table, content", "tableField_lvc9x4vt, textField_lvd8pp2t"),
+                UtilMap.map("table, content", "tableField_lvd8pp44, textField_lvd8pp3w"),
+                UtilMap.map("table, content", "tableField_lvdnme13, textField_lvdnme0u")
+        );
+        // 按照客户维度
+        dataList.forEach(e -> {
+
+            String type = UtilMap.getString(e, "selectField_lvc9x4vn");
+
+
+            String invoiceNo = UtilMap.getString(e, "textField_m06ij3z9");
+            String invoiceDate = UtilMap.getString(e, "dateField_m06ij3zc");
+            String amount = UtilMap.getString(e, "numberField_m06ij3zf");
+            String customerName = UtilMap.getString(e, "textField_m06ij3z8");
+        });
+
     }
 }

+ 2 - 2
mjava-lanyun/src/main/resources/application-dev.yml

@@ -43,6 +43,6 @@ dingtalk:
   appKey: dingfwn4kpmb4g3dy4fj
   appSecret: OwdkUc9nvBivpwsg0AiaEcyWLtZ678fskqBJwP7B5CQNrycIoyKuWslFTCOOaZG4
   corpId: dingef2eb0ecba261b8935c2f4657eb6378f
-  aesKey:
-  token:
+  aesKey: VjFDthsijenVQdqB7wjfQGVNemIBkNixyqzsPHfX9IG
+  token: 3PYhnTADhUjTD
   operator: ""   # OA管理员账号 [首字符若为0需要转一下字符串]

+ 5 - 0
mjava/src/main/java/com/malk/service/dingtalk/DDClient_Workflow.java

@@ -33,6 +33,11 @@ public interface DDClient_Workflow {
      */
     boolean terminateRunningApprove(String access_token, String processInstanceId, boolean isSystem, String remark, String operatingUserId);
 
+    /**
+     * 同意或拒绝审批任务
+     */
+    boolean executeRunningApprove(String access_token, String processInstanceId, String actionerUserId, String taskId, String result, String remark, Map file);
+
     /**
      * 通知审批撤销
      */

+ 25 - 4
mjava/src/main/java/com/malk/service/dingtalk/impl/DDImplClient_Workflow.java

@@ -69,6 +69,21 @@ public class DDImplClient_Workflow implements DDClient_Workflow {
         return (Boolean) r.getResult();
     }
 
+    /**
+     * 同意或拒绝审批任务
+     *
+     * @apiNote https://open.dingtalk.com/document/orgapp/approve-or-reject-the-approval-task
+     */
+    @Override
+    public boolean executeRunningApprove(String access_token, String processInstanceId, String actionerUserId, String taskId, String result, String remark, Map file) {
+
+        Map header = DDConf.initTokenHeader(access_token);
+        Map body = UtilMap.map("processInstanceId, result, actionerUserId, taskId, file", processInstanceId, result, actionerUserId, taskId, file);
+        UtilMap.putNotEmpty(body, "remark", remark);
+        DDR_New r = DDR_New.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/execute", header, null, body);
+        return (Boolean) r.getResult();
+    }
+
     /**
      * 通知审批撤销
      *
@@ -82,13 +97,15 @@ public class DDImplClient_Workflow implements DDClient_Workflow {
         return (Boolean) r.getResult();
     }
 
+
     /**
      * 添加审批评论
      *
      * @apiNote https://open.dingtalk.com/document/orgapp-service/add-an-approval-comment
      */
     @Override
-    public boolean commentApproveInstance(String access_token, String processInstanceId, File file, String text, String commentUserid) {
+    public boolean commentApproveInstance(String access_token, String processInstanceId, File file, String
+            text, String commentUserid) {
         Map header = DDConf.initTokenParams(access_token);
         Map body = UtilMap.map("process_instance_id, file, text, comment_userid", processInstanceId, file, text, commentUserid);
         Map req = UtilMap.map("request", body);
@@ -122,12 +139,14 @@ public class DDImplClient_Workflow implements DDClient_Workflow {
      * 获取审批实例ID列表_全部
      */
     @Override
-    public List<String> getInstanceIds_all(String access_token, String processCode, long startTime, long endTime, Map extInfo) {
+    public List<String> getInstanceIds_all(String access_token, String processCode, long startTime,
+                                           long endTime, Map extInfo) {
         return _getInstanceIds_all(access_token, processCode, startTime, endTime, extInfo, new ArrayList());
     }
 
     // 递归查询
-    private List<String> _getInstanceIds_all(String access_token, String processCode, long startTime, long endTime, Map extInfo, List dataList) {
+    private List<String> _getInstanceIds_all(String access_token, String processCode, long startTime,
+                                             long endTime, Map extInfo, List dataList) {
         Map rsp = getInstanceIds(access_token, processCode, startTime, endTime, extInfo);
         dataList.addAll(((List) rsp.get("list")));
         if (rsp.containsKey("nextToken")) {
@@ -146,7 +165,9 @@ public class DDImplClient_Workflow implements DDClient_Workflow {
      * @apiNote https://open.dingtalk.com/document/isvapp/add-dingtalk-to-do-task
      */
     @Override
-    public DDR_New createTBTask(String access_token, String createUserId, String subject, String description, long dueTime, List<String> executorIds, List<String> participantIds, Map detailUrl, boolean isOnlyShowExecutor, int priority, Map notifyConfigs) {
+    public DDR_New createTBTask(String access_token, String createUserId, String subject, String description,
+                                long dueTime, List<String> executorIds, List<String> participantIds, Map detailUrl,
+                                boolean isOnlyShowExecutor, int priority, Map notifyConfigs) {
         String unionId = String.valueOf(ddClient_contacts.getUserInfoById(access_token, createUserId).get("unionid"));
         Map param = new HashMap();
         param.put("operatorId", unionId);