Browse Source

初始化

fyz 1 month ago
commit
1c9768c5c8

+ 61 - 0
mjava-aosikang/pom.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>java-mcli</artifactId>
+        <groupId>com.malk</groupId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>mjava-aosikang</artifactId>
+    <description>达利集团云简对接</description>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <!-- 核心模块-->
+        <dependency>
+            <groupId>com.malk</groupId>
+            <artifactId>mjava</artifactId>
+            <version>${mjava.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.xerial</groupId>
+            <artifactId>sqlite-jdbc</artifactId>
+            <version>3.41.2.1</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.1.1.RELEASE</version>
+                <configuration>
+                    <includeSystemScope>true</includeSystemScope>
+                    <!-- 如果没有该配置,devtools不会生效: 打包时关闭 -->
+                    <fork>false</fork>
+                    <!-- 避免中文乱码 -->
+                    <jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
+                </configuration>
+                <!-- 允许生成可运行jar -->
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+</project>

+ 33 - 0
mjava-aosikang/src/main/java/com/malk/aosikang/Boot.java

@@ -0,0 +1,33 @@
+package com.malk.aosikang;
+
+import com.querydsl.jpa.impl.JPAQueryFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+
+import javax.persistence.EntityManager;
+
+/**
+ * corp项目: 扫描公共模块
+ * -
+ * 若是无需数据库模块, 配置无效地址也可启动, 引入mjava不支持直接 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) 配置
+ * 需要配置 jpa.hibernate.ddl-auto 为 none. 标识对表没有任何操作. 若不设置为 none, flyway.enabled 配置会无效, 在没有数库连接情况下程序无法启动
+ */
+@EnableJpaAuditing
+@SpringBootApplication(scanBasePackages = {"com.malk"},exclude={DataSourceAutoConfiguration.class})
+public class Boot {
+
+    public static void main(String... args) {
+        SpringApplication.run(Boot.class, args);
+    }
+
+    /**
+     * 让Spring管理JPAQueryFactory [不使用Qualifier详见mjava-Boot]
+     */
+    @Bean
+    public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) {
+        return new JPAQueryFactory(entityManager);
+    }
+}

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

@@ -0,0 +1,22 @@
+package com.malk.aosikang.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 {
+
+
+}

+ 93 - 0
mjava-aosikang/src/main/java/com/malk/aosikang/controller/DLController.java

@@ -0,0 +1,93 @@
+package com.malk.aosikang.controller;
+
+/**
+ * 错误抛出与拦截详见 CatchException
+ */
+
+import com.malk.aosikang.service.PayService;
+import com.malk.server.common.McException;
+import com.malk.server.common.McR;
+import com.malk.server.common.McREnum;
+import com.malk.service.aliwork.YDClient;
+import com.malk.service.aliwork.YDService;
+import com.malk.utils.UtilServlet;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+@Slf4j
+@RestController
+@RequestMapping
+public class DLController {
+
+    @Autowired
+    private YDService ydService;
+    @Autowired
+    private YDClient ydClient;
+
+    @Autowired
+    private PayService payService;
+
+    @PostMapping(value = "testToken")
+    McR testToken() {
+        payService.testToken();
+        return McR.success();
+    }
+
+    @PostMapping(value = "testGet")
+    McR testGet(@RequestBody Map data) {
+        payService.testGet(data.get("id").toString());
+        return McR.success();
+    }
+    /**
+     * 通用流程发起
+     */
+    @SneakyThrows
+    @PostMapping("process/start")
+    McR startProcess(@RequestBody Map data, HttpServletRequest request, @RequestParam String code) {
+        log.info("开始执行流程发起操作");
+        Map header = UtilServlet.getHeaders(request);
+        log.info("流程发起, {}, {}, {}", code, data, header);
+        McException.assertException(!"dingspvmfolrjzhak6ge".equals(header.get("authorization")), McREnum.NOT_AUTHORIZED);
+        Map result = payService.startProcess(code, data);
+        if ("error".equals(result.get("dtUrl"))){
+            return McR.error("4001","创建审批实例失败");
+        }
+        return McR.success(result);
+    }
+    /**
+     * 撤销流程发起
+     */
+    @SneakyThrows
+    @PostMapping("process/terminate")
+    McR terminateProcess(@RequestBody Map data, HttpServletRequest request) {
+        log.info("开始执行流程撤销操作");
+        Map header = UtilServlet.getHeaders(request);
+        log.info("流程发起, {}, {}", data, header);
+        McException.assertException(!"dingspvmfolrjzhak6ge".equals(header.get("authorization")), McREnum.NOT_AUTHORIZED);
+        Map result = payService.terminateProcess(data);
+        return McR.success(result);
+    }
+
+    /**
+     * 更新审批实例
+     */
+    @SneakyThrows
+    @PostMapping("process/update")
+    McR updateProcess(@RequestBody Map data, HttpServletRequest request, @RequestParam String code) {
+        log.info("开始执行流程更新操作");
+        Map header = UtilServlet.getHeaders(request);
+        log.info("流程发起, {}, {}", data, header);
+        McException.assertException(!"dingspvmfolrjzhak6ge".equals(header.get("authorization")), McREnum.NOT_AUTHORIZED);
+        boolean isOk = payService.updateProcess(code, data);
+        if (isOk){
+            return McR.success();
+        }else {
+            return McR.errorParam("失败");
+        }
+    }
+}

+ 88 - 0
mjava-aosikang/src/main/java/com/malk/aosikang/delegate/DDDelegate.java

@@ -0,0 +1,88 @@
+package com.malk.aosikang.delegate;
+
+import com.malk.aosikang.service.PayService;
+import com.malk.delegate.DDEvent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Primary;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+/**
+ * OA审批事件 [主项目也有实现, 添加 @Primary 优先注入主项目实现]
+ * -
+ * 取消方案: 撤销和拒绝流程不继续执行连接器, 因此不使用连接器与轮询审批记录方案 [低效而且占用较高钉钉api调次数];
+ * 优化方案: 通过事件订阅实现实时同步所有审批状态, 定时查询钉钉回调失败记录 [配置钉钉事件Delegate, 添加定时任务]
+ */
+@Slf4j
+@Service
+@Primary
+public class DDDelegate implements DDEvent {
+
+    @Autowired
+    PayService payService;
+    // 审批任务回调执行业务逻辑
+
+    private static final String DXCPXS = "PROC-BA558102-6DA9-4F02-9E0A-BE4272362C06";
+    private static final String NDKJXS = "PROC-452744BC-BDA4-4641-AA5B-B5D68A47242B";
+    private static final String YPSQ = "PROC-82FE4028-CA69-47A6-8D9C-16239BC4006F";
+    private static final String KS = "PROC-89069560-F910-4048-9DC2-8F82DE724F4C";
+
+    @Async
+    @Override
+    public void executeEvent_Task_Finish(String processInstanceId, String processCode, boolean isAgree, String remark) {
+        log.info("executeEvent_Task_Finish");
+    }
+
+    @Async
+    @Override
+    public void executeEvent_Task_Start(String processInstanceId, String processCode) {
+        log.info("executeEvent_Task_Start");
+    }
+
+    @Async
+    @Override
+    public void executeEvent_Task_Redirect(String processInstanceId, String processCode) {
+        log.info("executeEvent_Task_Redirect");
+    }
+
+    @Async
+    @Override
+    public void executeEvent_Instance_Start(String processInstanceId, String processCode) {
+        log.info("executeEvent_Instance_Start");
+    }
+
+
+    // 审批实例回调执行业务逻辑
+    @Async
+    @Override
+    public void executeEvent_Instance_Finish(String processInstanceId, String processCode, boolean isAgree, boolean isTerminate, String staffId) {
+        log.info("executeEvent_Instance_Finish");
+        String approveResult = isAgree ? "agree" : "refuse";
+//        if (isTerminate) approveResult = "terminated";
+        if  (isAgree){
+            if (processCode.equals(DXCPXS)){
+                payService.approveUpdateCRM(processInstanceId,"DXCPXS","审批通过", isAgree);
+            }else if (processCode.equals(NDKJXS)){
+                payService.approveUpdateCRM(processInstanceId,"NDKJXS","审批通过", isAgree);
+            }else if (processCode.equals(YPSQ)){
+                payService.approveUpdateCRM(processInstanceId,"YPSQ","审批通过", isAgree);
+            }else if (processCode.equals(KS)){
+                payService.approveUpdateCRM(processInstanceId,"KS","审批通过", isAgree);
+            }
+        }else if (isTerminate) {
+            return;
+        }else {
+            if (processCode.equals(DXCPXS)){
+                payService.approveUpdateCRM(processInstanceId,"DXCPXS","审批拒绝", isAgree);
+            }else if (processCode.equals(NDKJXS)){
+                payService.approveUpdateCRM(processInstanceId,"NDKJXS","审批拒绝", isAgree);
+            }else if (processCode.equals(YPSQ)){
+                payService.approveUpdateCRM(processInstanceId,"YPSQ","审批拒绝", isAgree);
+            }else if (processCode.equals(KS)){
+                payService.approveUpdateCRM(processInstanceId,"KS","审批拒绝", isAgree);
+            }
+        }
+//        log.info("审批实例回调执行业务逻辑, {}", approveResult);
+    }
+}

+ 37 - 0
mjava-aosikang/src/main/java/com/malk/aosikang/entity/DocumentHeader.java

@@ -0,0 +1,37 @@
+package com.malk.aosikang.entity;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+public class DocumentHeader implements Serializable {
+    private static final long serialVersionUID = 4820142670157009063L;
+    private String external_id;
+    private String header_type_code;
+    private String status;
+    private String created_by_code;
+    private String submit_user_code;
+    private Long submit_date;
+    private String branch_code;
+    private String submit_department_code;
+    private String column1;
+    private Long start_datetime;
+    private Long end_datetime;
+    private String column2;
+    private String destination_city_name;
+    private String destination_cities;
+    private String destination_city_to_name;
+    private String column15;
+    private String column16;
+    private String description;
+    private BigDecimal total_amount;
+    private BigDecimal total_claim_amount;
+    private BigDecimal total_pay_amount;
+    private BigDecimal total_net_amount;
+    private BigDecimal total_tax_amount;
+    private BigDecimal total_pay_currency_amount;
+
+
+}

+ 27 - 0
mjava-aosikang/src/main/java/com/malk/aosikang/service/DingTalkService.java

@@ -0,0 +1,27 @@
+package com.malk.aosikang.service;
+
+import java.util.List;
+import java.util.Map;
+
+public interface DingTalkService {
+
+
+    /**
+     * 获取在职人员
+     * @param access_token
+     * @param status_list
+     * @param offset
+     * @param size
+     * @return
+     */
+    Map getAllUserIdInfo(String access_token, String status_list, Number offset,Number size);
+
+    /**
+     * 获取员工花名册字段信息
+     * @param access_token
+     * @param userId
+     * @return
+     */
+    List<Map> getDDUserInfoById(String access_token, String userId, Number appAgentId);
+
+}

+ 32 - 0
mjava-aosikang/src/main/java/com/malk/aosikang/service/Impl/DingTalkServiceImpl.java

@@ -0,0 +1,32 @@
+package com.malk.aosikang.service.Impl;
+
+
+import com.malk.aosikang.service.DingTalkService;
+import com.malk.server.dingtalk.DDR;
+import com.malk.utils.UtilMap;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+@Service
+@Slf4j
+public class DingTalkServiceImpl implements DingTalkService {
+
+
+    @Override
+    public Map getAllUserIdInfo(String access_token, String status_list, Number offset, Number size) {
+        Map param = UtilMap.map("access_token", access_token);
+        Map body = UtilMap.map("status_list, offset, size", status_list, offset,size);
+        return (Map) DDR.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/queryonjob", null, param, body).getResult();
+    }
+
+    @Override
+    public List<Map> getDDUserInfoById(String access_token, String userId, Number appAgentId) {
+        Map param = UtilMap.map("access_token", access_token);
+        Map body = UtilMap.map("userid_list, agentid", userId, appAgentId);
+        return (List<Map>) DDR.doPost("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/v2/list", null, param, body).getResult();
+    }
+
+}

+ 778 - 0
mjava-aosikang/src/main/java/com/malk/aosikang/service/Impl/PayServiceImpl.java

@@ -0,0 +1,778 @@
+package com.malk.aosikang.service.Impl;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.malk.aosikang.service.DingTalkService;
+import com.malk.aosikang.service.PayService;
+import com.malk.server.crm.DDR;
+import com.malk.service.dingtalk.DDClient;
+import com.malk.service.dingtalk.DDClient_Personnel;
+import com.malk.service.dingtalk.DDClient_Workflow;
+import com.malk.service.dingtalk.DDService;
+import com.malk.utils.UtilDateTime;
+import com.malk.utils.UtilFile;
+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.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+public class PayServiceImpl implements PayService {
+
+    @Autowired
+    private DDClient ddClient;
+    @Autowired
+    private DDClient_Workflow ddClient_workflow;
+    @Autowired
+    private DDClient_Personnel ddClientPersonnel;
+    @Value("${dingtalk.appKey}")
+    private String APP_EKY;
+    @Value("${dingtalk.appSecret}")
+    private String APP_SECRET;
+    @Value("${dingtalk.agentId}")
+    private Long agentId;
+    @Value("${crm.appId}")
+    private String appId;
+    @Value("${crm.appSecret}")
+    private String appSecret;
+    @Value("${crm.permanentCode}")
+    private String permanentCode;
+    @Value("${crm.url}")
+    private String crmHost;
+    @Autowired
+    DingTalkService dingTalkService;
+    @Autowired
+    DDService ddService;
+
+    private static final String API_TOKEN = "/corpAccessToken/get/V2";
+    private static final String POST_GET_USER = "/user/getByMobile";
+    //    private static final String FIND_BY_CODES = "/common/users/v2/findByCodes";
+    private static final String POST_DATA_UPDATE = "/crm/custom/v2/data/update";
+    private static final String TABLE_PRODUCT_FIELID = "TableField_BEUZFS0B8R40";
+    private static final String TABLE_CELLLINES_FIELID = "TableField_M04O3XGALA80";
+    private ConcurrentHashMap<String, LocalDateTime> tokenStore = new ConcurrentHashMap<>();
+    private static  String CROPID = "";
+
+    //所有的枚举
+    private static final Map<String, String> ALLENUM = new HashMap<>();
+
+    static {
+        //签署法人实体
+        ALLENUM.put("option_aoscience_bio_nantong_co_l__c", "澳斯康生物(南通)股份有限公司");
+        ALLENUM.put("option_gansu_jianshun_biotech_co__c", "甘肃健顺生物科技有限公司");
+        ALLENUM.put("option_jianshun_bio_tech_nantong__c", "健顺生物科技(南通)有限公司");
+        ALLENUM.put("option_shanghai_aoskang_bio_pharm__c", "上海澳斯康生物制药有限公司");
+        ALLENUM.put("option_shanghai_jianshiba_biotech__c", "上海健士拜生物科技有限公司");
+        ALLENUM.put("option_group_research_center__c", "集团研发中心");
+        ALLENUM.put("i5r85d5r8", "集团部门");
+        //区域
+        ALLENUM.put("option_international__c", "国际");
+        ALLENUM.put("cD2b3f", "国内");
+        //合同事宜
+        ALLENUM.put("x8luwTHo7", "合同签订");
+        ALLENUM.put("kptFZ7XFr", "合同变更");
+        ALLENUM.put("8D36ieItP", "合同解除");
+        //是否设计法人章、是否涉密
+        ALLENUM.put("option_yes__c", "是");
+        ALLENUM.put("option_no__c", "否");
+        //合同总价是否为固定金额、客户是否需求赔偿
+        ALLENUM.put("true", "是");
+        ALLENUM.put("false", "否");
+        //原币类型
+        ALLENUM.put("option_example_options__c", "欧元EUR");
+        ALLENUM.put("option_usd_currency__c", "美元USD");
+        ALLENUM.put("option_cny__c", "人民币CNY");
+        //是否加盖公章
+        ALLENUM.put("wSZ3tbp4m", "否");
+        ALLENUM.put("option1", "是");
+        //投诉等级
+        ALLENUM.put("option_B__c", "一般质量投诉");
+        ALLENUM.put("option_A__c", "重大质量投诉");
+        //申请类型
+        ALLENUM.put("1kRYAt2So", "细胞株");
+        ALLENUM.put("9V9fvk5j1", "原材料");
+        ALLENUM.put("5U6yt12hf", "耗材");
+        ALLENUM.put("evF26O4He", "产品");
+        ALLENUM.put("other", "其他");
+
+    }
+
+    //所有的枚举
+    private static final Map<String, String> AFTER_SALE = new HashMap<>();
+
+    static {
+        //售后管理
+        AFTER_SALE.put("option_example_options__c", "投诉处理(非质量投诉)");
+        AFTER_SALE.put("option_quality_complaint_handling__c", "投诉处理(质量投诉)");
+    }
+
+    //各表单审批结果映射
+    private static final Map<String, String> APPROVAL_RESULT = new HashMap<>();
+    static {
+        APPROVAL_RESULT.put("DXCPXS", "approval_result__c");
+        APPROVAL_RESULT.put("NDKJXS", "field_V4t4n__c");
+        APPROVAL_RESULT.put("YPSQ", "field_0Xan7__c");
+        APPROVAL_RESULT.put("KS", "field_AkJi0__c");
+    }
+    //各表单审批结果映射
+    private static final Map<String, String> APPROVAL_TYPE = new HashMap<>();
+    static {
+        APPROVAL_TYPE.put("DXCPXS", "framework_agreement__c");
+        APPROVAL_TYPE.put("NDKJXS", "frameork_agreement__c");
+        APPROVAL_TYPE.put("YPSQ", "sample_application__c");
+        APPROVAL_TYPE.put("KS", "customer_complaint__c");
+    }
+
+    //产品信息子表id
+    private static final Map<String, String> PRODUCTS_IDS = new HashMap<>();
+    static {
+        PRODUCTS_IDS.put("产品名称", "TextField_EKDJALVY7400");
+        PRODUCTS_IDS.put("产品规格", "TextField_EZ369AR1K540");
+        PRODUCTS_IDS.put("有效期", "TextField_15ALDAZR96KG0");
+        PRODUCTS_IDS.put("产品批号", "TextField_OLDI5UFGBO00");
+        PRODUCTS_IDS.put("包装数量", "NumberField_PC0XRBAYFW00");
+    }
+
+    //细胞株信息子表id
+    private static final Map<String, String> CELLLINES_IDS = new HashMap<>();
+    static {
+        CELLLINES_IDS.put("细胞株名称", "TextField_NSL9P2RPBE80");
+        CELLLINES_IDS.put("细胞株类型", "TextField_1HTGO286W6DC0");
+        CELLLINES_IDS.put("数量", "NumberField_1CV9FYA729UO0");
+        CELLLINES_IDS.put("温度", "TextField_23O8J0UZ7PVK0");
+        CELLLINES_IDS.put("储存和运输条件", "TextField_1Y6PNKV1MRC00");
+    }
+
+    //审批拒绝置空CRM
+    private static final Map<String, String> CRM_NULL = new HashMap<>();
+    static {
+        CRM_NULL.put("DXCPXS", "dingtalk_bill_number__c");
+        CRM_NULL.put("NDKJXS", "dd_approval_id__c");
+        CRM_NULL.put("YPSQ", "dd_approval_id__c");
+        CRM_NULL.put("KS", "dd_approval_id__c");
+    }
+
+    private Map MATE;
+
+    /**
+     * 加载json
+     */
+    private Map _getMeta() {
+        if (ObjectUtil.isNull(MATE)) {
+            MATE = (Map) UtilFile.readJsonObjectFromResource("static/json/form.json"); // 本地匹配宜搭组件ID
+        }
+        return MATE;
+    }
+    @Override
+    public void synchronousAndPush(String processInstanceId) {
+        Map formData = queryOAById(processInstanceId);
+        final String[] token = {""};
+        final boolean[] isHave = {false};
+        tokenStore.forEach((k, v) -> {
+            if (validateToken(k)) {
+                token[0] = k;
+                isHave[0] = true;
+            }
+        });
+//        if (!isHave[0]) {
+//            token[0] = getToken();
+//        }
+        Map<String, Object> param = new HashMap<>();
+        param.put("access_token", token[0]);
+        //获取申请人数据
+        List<Map> userName = new ArrayList<>();
+        try {
+            List<String> userIds = Arrays.asList(formData.get("userId").toString());
+            List<String> workNumbers = getNumberByUserId(userIds);
+            log.info("获取申请人工号:{}",workNumbers.toString());
+//            userName = getUserFormByNumber(workNumbers, token[0]);
+            log.info("获取申请人云简平台信息:{}",workNumbers);
+        } catch (NullPointerException e) {
+            log.info("云简平台查无此人,请检查钉钉和云简员工号是否对应");
+        }
+        //获取同行人数据
+        List<Object> names = new ArrayList<>();
+        if (ObjectUtil.isNotNull(formData.get("出行人(同行人)"))){
+            List<String> companionsUserIds = (List<String>)formData.get("出行人(同行人)");
+            List<String> companionsWorkNumbers = getNumberByUserId(companionsUserIds);
+//            List<Map> companionsUserName = getUserFormByNumber(companionsWorkNumbers, token[0]);
+//            userName.addAll(companionsUserName);
+//            names = companionsUserName.stream().map(item -> item.get("name")).collect(Collectors.toList());
+        }
+        // 去重
+        List<Map> uniqueUsers= new ArrayList<>(userName.stream()
+                .filter(m -> m != null) // 过滤掉空值
+                .collect(Collectors.toMap(
+                        m -> m.toString(), // 使用 map 的 toString() 作为唯一标识符
+                        m -> m,           // 保留原 map
+                        (existing, replacement) -> existing // 遇到重复时保留已有元素
+                )).values());
+//        extracted(formData, param, uniqueUsers, names);
+    }
+
+    public String getUserId(String phoneNum){
+
+        Number nextCursor = 0; // 初始的offset值设置为"0"
+        List<String> allUserIds = new ArrayList<>();
+        while (nextCursor != null ) {
+            Map userIds  =  dingTalkService.getAllUserIdInfo(ddClient.getAccessToken(),"2,3,5",nextCursor,50);
+            //获取在职人员信息
+            List<String> userIdList = (List<String>) userIds.get("data_list");
+            Number newNextCursor = (Number) userIds.getOrDefault("next_cursor", null);
+            if (!userIdList.isEmpty()) {
+                allUserIds.addAll(userIdList);
+            }
+            // 更新nextCursor为下一次请求的offset
+            nextCursor = newNextCursor;
+        }
+
+        return phoneNum;
+    }
+
+    /**
+     * 整合传参及OA数据格式
+     * @param code 单据类型
+     * @param data 传参
+     * @return 流程实例id
+     */
+    private String dataProcessing(String code, Map data){
+        //加载json映射
+        Map meta = _getMeta();
+        //表单字段映射
+        List<Map> formValue = new ArrayList<>();
+        log.info("code:{} ",code);
+        IntegrationData(code, data, meta, formValue);
+        List<String> workingEmployeeIds = new ArrayList<>();
+        workingEmployeeIds.add(data.get(((Map) meta.get(code)).get("creator")).toString());
+        List<String> deptIdByUserId = getDeptIdByUserId(workingEmployeeIds);
+        return ddClient_workflow.doProcessInstancesNew(ddClient.getAccessToken(), data.get(((Map) meta.get(code)).get("creator")).toString(), ((Map) meta.get(code)).get("processCode").toString(), formValue, deptIdByUserId.get(0));
+//        return code;
+    }
+
+    /**
+     * 整合传参数据为OA审批 name - value 数据格式
+     * @param code 单据类型
+     * @param data 传参
+     * @param meta json
+     * @param formValue OA审批数据格式
+     */
+    private void IntegrationData(String code, Map data, Map meta, List<Map> formValue) {
+        log.info("开始处理接口参数数据转为OA审批接口数据格式");
+        //json数据列
+        Map<String, ?> component = (Map) ((Map) meta.get(code)).get("compIds");
+        for (String key : component.keySet()) {
+            Map formData = UtilMap.empty();
+            if (key.startsWith("tableField_")) {
+                String[] split = key.split("_");
+                List<Map> rows = UtilMap.getList(data,split[1]);
+                List<List<Map>> lastList = new ArrayList<>();
+                Map<String, String> compIds = (Map) component.get(key);
+                if (rows.size() > 0 ){
+                    for (Map row : rows) {
+                        List<Map> details = new ArrayList<>();
+                        for (String prop : compIds.keySet()) {
+                            Map detail = UtilMap.empty();
+                            detail.put("value", row.get(compIds.get(prop)));
+                            detail.put("name", prop);
+                            details.add(detail);
+                        }
+                        lastList.add(details);
+                    }
+                    formData.put( "value", JSON.toJSONString(lastList));
+                    formData.put( "name",((Map) meta.get(code)).get(split[1]).toString());
+                }else {
+                    List<Map> details = new ArrayList<>();
+                    for (String prop : compIds.keySet()) {
+                        Map detail = UtilMap.empty();
+                        if ("quantity".equals(compIds.get(prop))){
+                            detail.put("value", "0");
+                            detail.put("name", prop);
+                        }else {
+                            detail.put("value", "无");
+                            detail.put("name", prop);
+                        }
+                        details.add(detail);
+                    }
+                    lastList.add(details);
+                    formData.put( "value", JSON.toJSONString(lastList));
+                    formData.put( "name",((Map) meta.get(code)).get(split[1]).toString());
+                }
+            } else {
+                if (data.containsKey(component.get(key))){
+                    if (ObjectUtil.isNotNull(data.get(component.get(key)))){
+                        Object value = data.get(component.get(key));
+                        formData.put("name", key);
+                        formData.put("value",value.toString());
+                        if (ALLENUM.containsKey(value.toString())){
+                            formData.put("value",ALLENUM.get(value.toString()));
+                        }
+                        if (AFTER_SALE.containsKey(value.toString()) && "KS".equals(code) && "售后管理".equals(key)) {
+                            formData.put("value",AFTER_SALE.get(value.toString()));
+                        }
+                    }
+                }
+            }
+            if (formData.size()>0){
+                formValue.add(formData);
+            }
+        }
+        if (data.containsKey("attachmentUrl") && ObjectUtil.isNotNull(data.get("attachmentUrl"))){
+            log.info("开始处理附件上传");
+            List<Map> attachment = (List<Map>) data.get("attachmentUrl");
+            List<Map<String, Object>> resultList = new ArrayList<>();
+            attachment.forEach(e->{
+                String[] split = UtilMap.getString(e, "name").split("\\.");
+                String fileName = split[0];
+                Map map = ddService.uploadFileFormUrlForOverflow(ddClient.getAccessToken(), data.get(((Map) meta.get(code)).get("creator")).toString(), e.get("downloadUrl").toString(), fileName, UtilMap.getString(e,"type"));
+                Map<String, Object> result = UtilMap.map("spaceId, fileName, fileSize, fileType, fileId", UtilMap.getString(map, "spaceId"), fileName,
+                        UtilMap.getString(map, "size"), UtilMap.getString(map, "extension"), UtilMap.getString(map, "id"));
+                resultList.add(result);
+            });
+            formValue.add(UtilMap.map("name, value","附件",JSON.toJSONString(resultList)));
+        }
+    }
+
+    /**
+     * 发起流程审批
+     * @param code
+     * @param data
+     * @return 流程实例id和地址
+     */
+    @Override
+    public Map<String, Object> startProcess(String code, Map data) {
+        String instanceId = dataProcessing(code, data);
+        JSONObject jsonObject = JSONObject.parseObject(instanceId);
+        if (!ObjectUtil.isNotNull(jsonObject.get("instanceId"))){
+            return UtilMap.map("ddProcessId, dtUrl, reqMsg",  jsonObject.get("instanceId"), "error", instanceId);
+        }
+        String url = "https://applink.dingtalk.com/approval/detail?corpId=ding226da4276814d290a1320dcb25e91351&instanceId=" + jsonObject.get("instanceId");
+        return UtilMap.map("ddProcessId, dtUrl, reqMsg",  jsonObject.get("instanceId"),  url, instanceId);
+    }
+
+    /**
+     * 终止流程
+     * @param data 传参
+     * @return 是否成功
+     */
+    @Override
+    public Map terminateProcess(Map data) {
+        boolean isSystem = false;
+        if (data.containsKey("isSystem") && ObjectUtil.isNotNull(data.get("isSystem"))){
+            isSystem = UtilMap.getBoolean(data,"isSystem");
+        }
+        String operatingUserId = "";
+        if (data.containsKey("operatingUserId")){
+            operatingUserId = UtilMap.getString(data,"operatingUserId");
+        }
+        boolean isOk = ddClient_workflow.terminateRunningApproveNew(ddClient.getAccessToken(), UtilMap.getString(data, "processInstanceId"), isSystem, UtilMap.getString(data, "remark"), operatingUserId);
+        return UtilMap.map("result",isOk);
+    }
+
+    /**
+     * 更新表单实例
+     * @param code 单据类型
+     * @param data 传参
+     * @return 是否成功
+     */
+    @Override
+    public boolean updateProcess(String code, Map data) {
+        Map formData = ddClient_workflow.getProcessInstanceId(ddClient.getAccessToken(), UtilMap.getString(data, "instanceId"));
+        boolean isOk = false;
+        if (ObjectUtil.isNotNull(formData)){
+            List<Map> mapList = (List<Map>) formData.get("formComponentValues");
+            //加载json映射
+            Map meta = _getMeta();
+            //表单字段映射
+            List<Map> formValue = new ArrayList<>();
+            log.info("code:{} ",code);
+            IntegrationData(code, data, meta, formValue);
+            //更新值
+            List<Map> variables = new ArrayList<>();
+            formValue.forEach(f->{
+                List<Map> lastValues = new ArrayList<>();
+                String fielId ="";
+                if ("样品信息".equals(f.get("name")) || "细胞株信息".equals(f.get("name"))){
+                    switch (f.get("name").toString()){
+                        case "样品信息": fielId = TABLE_PRODUCT_FIELID;break;
+                        case "细胞株信息": fielId = TABLE_CELLLINES_FIELID;break;
+
+                    }
+                    JSONArray jsonArray2 = JSONArray.parseArray(f.get("value").toString());
+                    String finalFielId = fielId;
+                    jsonArray2.forEach(a2->{
+                        JSONArray jsonArray = JSONArray.parseArray(a2.toString());
+                        List<Map> values = new ArrayList<>();
+                        jsonArray.forEach(a3->{
+                            if ("样品信息".equals(f.get("name"))){
+                                JSONObject jsonObject = JSONObject.parseObject(a3.toString());
+                                Map<String, Object> value = UtilMap.map("key, value",PRODUCTS_IDS.get(jsonObject.get("name").toString()), jsonObject.get("value"));
+                                values.add(value);
+                            }else if ("细胞株信息".equals(f.get("name"))){
+                                JSONObject jsonObject = JSONObject.parseObject(a3.toString());
+                                Map<String, Object> value = UtilMap.map("key, value",CELLLINES_IDS.get(jsonObject.get("name").toString()), jsonObject.get("value"));
+                                values.add(value);
+                            }
+                        });
+                        lastValues.add( UtilMap.map("rowValue, rowNumber", values, finalFielId+ "_" +UUID.randomUUID().toString().substring(0,7)));
+                    });
+                    Map<String, Object> variable = UtilMap.map("id, value", fielId, JSON.toJSONString(lastValues));
+                    variables.add(variable);
+                }
+                mapList.forEach(map -> {
+                    if (f.get("name").equals(map.get("name"))){
+                        if ("附件".equals(f.get("name")) || "TableField".equals(map.get("componentType"))){
+
+                        }else {
+                            Map<String, Object> variable = UtilMap.map("id, value",map.get("id"), UtilMap.getString(f,"value"));
+                            variables.add(variable);
+                        }
+                    }
+                });
+            });
+            String remark = "";
+            if (data.containsKey("remark")){
+                remark = UtilMap.getString(data,"remark");
+            }
+            isOk = ddClient_workflow.updateApprove(ddClient.getAccessToken(), UtilMap.getString(data, "opUserId"), UtilMap.getString(data, "instanceId"), "", variables, remark);
+        }
+        return isOk;
+    }
+
+    @Override
+    public void approveUpdateCRM(String processInstanceId, String code, String result, boolean isAgree) {
+        log.info("开始执行审批回调:{}  审批结果:{}",processInstanceId,result);
+        Map processData = ddClient_workflow.getProcessInstanceId(ddClient.getAccessToken(APP_EKY, APP_SECRET), processInstanceId);
+        String userId = String.valueOf(processData.get("originatorUserId"));
+        List<String> mapList = new ArrayList<>();
+        mapList.add(userId);
+        List<String> numberByUserId = getNumberByUserId(mapList);
+        String trim = numberByUserId.get(0);
+        if (trim.contains("-")){
+            String[] split = trim.split("-");
+            trim = split[1].trim();
+        }
+        List<Map> formComponentValues = (List<Map>) processData.get("formComponentValues");
+        List<Map> operationRecords = (List<Map>) processData.get("operationRecords");
+        final String[] remark = {""};
+        operationRecords.forEach(o->{
+            if(!"NONE".equals(o.get("result"))){
+                remark[0] = UtilMap.getString(o,"remark");
+            }
+        });
+        Map formComp = formComponentValues.stream().filter(item -> "crmId".equals(item.get("name"))).findAny().get();
+        String value = formComp.get("value").toString();
+
+        Map<String, String> objectData = new HashMap<>();
+        objectData.put(APPROVAL_RESULT.get(code),"审批结果:"+result+",审批意见:"+ remark[0]);
+        objectData.put("dataObjectApiName",APPROVAL_TYPE.get(code));
+        objectData.put("_id",value);
+        if (!isAgree){
+            objectData.put(CRM_NULL.get(code),"");
+        }
+        final String[] crmValue = {"",""};
+        final boolean[] isHave = {false};
+        tokenStore.forEach((k, v) -> {
+            if (validateToken(k)) {
+                crmValue[0] = k;
+                crmValue[1] = CROPID;
+                isHave[0] = true;
+            }
+        });
+        if (!isHave[0]) {
+            Map crmToken = getToken();
+            crmValue[0] = UtilMap.getString(crmToken,"token");
+            crmValue[1] = UtilMap.getString(crmToken,"corpId");
+        }
+
+        String userFormByNumber = getUserFormByNumber(trim, crmValue[1], crmValue[0]);
+
+        updateCRMForm(userFormByNumber,crmValue[1],crmValue[0],objectData);
+
+    }
+
+    /**
+     * 获取token
+     * @return
+     */
+    public Map getToken() {
+        Map<String, String> body = new HashMap<>();
+        body.put("appId", appId);
+        body.put("appSecret", appSecret);
+        body.put("permanentCode", permanentCode);
+        Map<String, String> header = UtilMap.map("Content-Type", "application/json");
+        DDR ddr = DDR.doPost(getRequestUrl(API_TOKEN), header, null, body);
+        addToken(ddr.getCorpAccessToken(),  110);
+        CROPID = ddr.getCorpId();
+        return UtilMap.map("token, corpId",ddr.getCorpAccessToken(),ddr.getCorpId());
+    }
+
+    /**
+     * token存储
+     * @param token
+     * @param expireMinutes
+     */
+    public void addToken(String token, int expireMinutes) {
+        LocalDateTime expirationTime = LocalDateTime.now().plusMinutes(expireMinutes);
+        tokenStore.put(token, expirationTime);
+    }
+
+    /**
+     * 清理过期token
+     * @param token
+     * @return
+     */
+    public boolean validateToken(String token) {
+        LocalDateTime expirationTime = tokenStore.get(token);
+        if (expirationTime == null || expirationTime.isBefore(LocalDateTime.now())) {
+            tokenStore.remove(token); // 清理过期 Token
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void testToken() {
+        Map processData = ddClient_workflow.getProcessInstanceId(ddClient.getAccessToken(APP_EKY, APP_SECRET), "9aUyg-MhQOaWMIu6KIMt3A03891762254188");
+        List<Map> operationRecords = (List<Map>) processData.get("operationRecords");
+        final String[] remark = {""};
+        operationRecords.forEach(o->{
+            if(!"NONE".equals(o.get("result"))){
+                remark[0] = UtilMap.getString(o,"remark");
+            }
+        });
+        approveUpdateCRM("QHtdZ0DyTQq-elLLjmic9g03891762329654","DXCPXS","审批拒绝", false);
+        System.out.println(remark[0]);
+//        Map token = getToken();
+//        List<String> mapList = new ArrayList<>();
+//        mapList.add("manager3269");
+//        List<String> numberByUserId = getNumberByUserId(mapList);
+//        numberByUserId.forEach(e->{
+//            String trim = e;
+//            if (e.contains("-")){
+//                String[] split = e.split("-");
+//                trim = split[1].trim();
+//            }
+//            String userFormByNumber = getUserFormByNumber(trim, token.get("corpId").toString(), token.get("token").toString());
+//
+//        });
+    }
+
+    @Override
+    public void testGet(String id) {
+        approveUpdateCRM("K977PURCR0SATrmOoPfTAg03891761641697","KS","审批拒绝",false);
+    }
+
+    /**
+     * 获取钉钉OA单据数据
+     * @param processInstanceId 钉钉OA流程单号(URL上)
+     * @return
+     */
+    public Map queryOAById(String processInstanceId) {
+        String[] compsId_main = {"发票抬头", "备注", "出差天数", "出差事由", "出差备注", "出行人(同行人)", "外部人员", "出差流水号", "日程", "出差类型"};
+        String[] compsId_itinerary = {"交通工具", "单程往返", "出发城市", "目的城市", "开始时间", "结束时间", "时长"};
+//        syncYD(processInstanceId, "FORM-210DA087671044F8A5CD72F0E9E89060SZ8Q", compsId_main, compsId_itinerary, "itinerary");
+        String token = ddClient.getAccessToken(APP_EKY, APP_SECRET);
+
+        Map processData = ddClient_workflow.getProcessInstanceId(token, processInstanceId);
+        List<Map> formComponentValues = (List<Map>) processData.get("formComponentValues");
+        log.info("开始获取OA数据:{}",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("userId, depId, createTime, textField_lygnetw9", userId, processData.get("originatorDeptId"), cDate, UtilMap.getString(processData, "businessId"));
+
+        for (String name : compsId_main) {
+            String compId = name;
+            // 判定是否子表 [宜搭]
+            if (compId.endsWith("日程")) {
+                List<Map> details = new ArrayList<>();
+                // 兼容明细组件, 存在多条情况 [加班跨天才有有明细]
+                Optional optional = formComponentValues.stream().filter(item -> "itinerary".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) {
+                        log.info("子表字段, {}", subName);
+                        //  加班单跨天 [子表label为空]
+                        rowData.put(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;
+            }
+            try {
+                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);
+            } catch (Exception e) {
+                log.info("缺失字段:{}", name);
+            }
+        }
+        return formData;
+    }
+
+    /**
+     * 获取钉钉员工手机号
+     * @param workingEmployeeIds 钉钉人员id
+     * @return
+     */
+    public List<String> getNumberByUserId(List<String> workingEmployeeIds) {
+        log.info("根据钉钉人员id获取员工工号:{}",workingEmployeeIds.toString());
+        List<String> filterList = new ArrayList<>();
+        filterList.add("sys00-mobile");
+        List<Map> employeeInfos = ddClientPersonnel.getEmployeeInfos(ddClient.getAccessToken(), workingEmployeeIds, agentId, filterList);
+        List<String> personList = new ArrayList<>();
+        employeeInfos.forEach(e -> {
+            List<Map> mapList = (List<Map>) e.get("field_data_list");
+            mapList.forEach(m -> {
+                List<Map> valueList = (List<Map>) m.get("field_value_list");
+                String value = valueList.get(0).get("value").toString();
+                personList.add(value);
+            });
+        });
+        return personList;
+    }
+
+        /**
+     * 获取钉钉员工手机号
+     * @param workingEmployeeIds 钉钉人员id
+     * @return
+     */
+    public List<String> getDeptIdByUserId(List<String> workingEmployeeIds) {
+        log.info("根据钉钉人员id获取员工部门id:{}",workingEmployeeIds.toString());
+        List<String> filterList = new ArrayList<>();
+        filterList.add("sys00-mainDeptId");
+        List<Map> employeeInfos = ddClientPersonnel.getEmployeeInfos(ddClient.getAccessToken(), workingEmployeeIds, agentId, filterList);
+        List<String> personList = new ArrayList<>();
+        employeeInfos.forEach(e -> {
+            List<Map> mapList = (List<Map>) e.get("field_data_list");
+            mapList.forEach(m -> {
+                List<Map> valueList = (List<Map>) m.get("field_value_list");
+                String value = valueList.get(0).get("value").toString();
+                personList.add(value);
+            });
+        });
+        return personList;
+    }
+
+
+
+    /**
+     * 获取crm平台员工信息
+     * @param mobile 钉钉手机号
+     * @param corpId crm企业id
+     * @param token token
+     * @return
+     */
+    public String getUserFormByNumber(String mobile, String corpId, String token) {
+        log.info("获取crm平台员工信息:{}",mobile);
+        final String[] openUserId = {""};
+        Map<String, Object> body = UtilMap.map("corpAccessToken, corpId, mobile", token, corpId, mobile);
+        DDR ddr = DDR.doPost(getRequestUrl(POST_GET_USER), null, null, body);
+        log.info("员工信息返回:{}",ddr.toString());
+        if (ObjectUtil.isNotNull(ddr.getEmpList())) {
+            JSONArray jsonArray = JSONArray.parseArray(ddr.getEmpList().toString());
+            jsonArray.forEach(e->{
+                JSONObject jsonObject = JSONObject.parseObject(e.toString());
+                openUserId[0] = jsonObject.get("openUserId").toString();
+            });
+        }
+
+        return openUserId[0];
+    }
+
+    /**
+     * 修改crm自定义对象
+     * @param currentOpenUserId crm员工号
+     * @param corpId crm企业id
+     * @param token token
+     * @param object_data 修改参数
+     * @return
+     */
+    public String updateCRMForm(String currentOpenUserId, String corpId, String token, Map object_data) {
+        log.info("修改crm自定义对象:{}",object_data);
+        Map<String, Object> objectData = UtilMap.map("object_data", object_data);
+        Map<String, Object> body = UtilMap.map("corpAccessToken, corpId, currentOpenUserId, data, triggerWorkFlow, triggerApprovalFlow",
+                token, corpId, currentOpenUserId, objectData, false, false);
+        JSON.toJSONString(body);
+        DDR ddr = DDR.doPost(getRequestUrl(POST_DATA_UPDATE), null, null, body);
+        log.info("修改crm自定义对象返回:{}",ddr.toString());
+        if ("0".equals(ddr.getErrorCode())){
+            return "success";
+        }else {
+            return "error";
+        }
+    }
+
+    /**
+     * 递归查询人员一级部门(B0001除外)
+     * @param depId 人员所属部门id
+     * @param token token
+     * @return
+     */
+    public String getCompany(String depId, String token) {
+        String uuid = UUID.randomUUID().toString();
+        Map<String, Object> param = UtilMap.map("bizId, timestamp, code, access_token", uuid, new Date().getTime(), depId, token);
+        DDR ddr = DDR.doGet(getRequestUrl(API_TOKEN), null, param);
+        String company = depId;
+        if (ObjectUtil.isNotNull(ddr.getData())) {
+            List<Map> data = (List<Map>) ddr.getData();
+            JSONObject jsonObject = JSONObject.parseObject(data.get(0).get("parent_vo").toString());
+            if (!"B0001".equals(depId) && !"B0001".equals(jsonObject.getString("code"))){
+                company = jsonObject.getString("code");
+                company = getCompany(company, token);
+            }
+        }
+        return company;
+    }
+
+    /**
+     * 获取拼接后接口地址
+     * @param url
+     * @return
+     */
+    public String getRequestUrl(String url){
+        log.info("请求CRM地址接口:{}",crmHost+url);
+        return crmHost + url;
+    }
+}

+ 26 - 0
mjava-aosikang/src/main/java/com/malk/aosikang/service/PayService.java

@@ -0,0 +1,26 @@
+package com.malk.aosikang.service;
+
+import com.malk.server.common.McR;
+
+import java.util.Map;
+
+public interface PayService {
+
+    void approveUpdateCRM(String processInstanceId, String code, String result, boolean isAgree);
+
+    void testToken();
+    void testGet(String id);
+
+    void synchronousAndPush(String processInstanceId);
+
+    /**
+     * 通用流程发起
+     */
+    Map<String, Object> startProcess(String code, Map data);
+
+    Map terminateProcess(Map data);
+
+
+    boolean updateProcess(String code, Map data);
+
+}

+ 60 - 0
mjava-aosikang/src/main/resources/application-dev.yml

@@ -0,0 +1,60 @@
+# 环境配置
+server:
+  port: 8108
+  servlet:
+    context-path: /api/aosikang
+
+# condition
+spel:
+  scheduling: false        # 定时任务是否执行
+  multiSource: false       # 是否多数据源配置
+
+spring:
+  # database
+  datasource:
+    hikari:
+      connection-init-sql: SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci           # SqlServer, Oracle 无需设置类型
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    username: root
+    password: cp-root@2022++
+    url: jdbc:mysql://47.110.74.198:3306/dingtalk?serverTimezone=Asia/Shanghai&useUnicode=yes&characterEncoding=UTF-8&useSSL=true
+  jpa:
+    hibernate:
+      ddl-auto: none      # JPA对表没有任何操作
+    show-sql: true
+    database: MYSQL
+    database-platform: org.hibernate.dialect.MySQL57Dialect
+
+# filepath
+file:
+  path:
+    file: /Users/malk/server/_Tool/var/mjava/tmp/file/
+    image: /Users/malk/server/_Tool/var/mjava/tmp/image/
+    tmp: /Users/malk/server/_Tool/var/mjava/tmp/
+  source:
+    fonts: /Users/malk/server/_Tool/fonts/simsun.ttc
+#logging:
+#  file:
+#    path: /Users/malk/server/_Tool/var/mjava/log
+
+# dingtalk
+dingtalk:
+  agentId: 4043200193
+  appKey: dingspvmfolrjzhak6ge
+  appSecret: tcBzkR5fnRwaiJoWEpl9SFOChcBBPtCU5Nr0W9_dE1cF7V8UcB2AX7gz_3p9AGZa
+  corpId:
+  aesKey: C7Eau5BAem351bOvcvPwDjIElOZv4y62jpL9ep4OPH2
+  token: jtTq2ISmZkBJdL6wwq2r7iOpOAKifMDTOeGKE7DgTkzMiKfFV4pW66MQx
+  operator: ""   # OA管理员账号
+
+# aliwork
+#aliwork:
+#  appType: "APP_LNBUWW7ZBXRUWE29PCOV"
+#  systemToken: "WC866ZA1PI1B2L1JDNL334F44KI03GREIV1ILZ13"
+crm:
+  url: https://open.fxiaoke.com/cgi
+  appId: FSAID_1321943
+  appSecret: 8e4858d3de0549c48eebd0d0d3ecabad
+  permanentCode: ECF5F462BFD86418730399FCC465C25D
+
+

+ 46 - 0
mjava-aosikang/src/main/resources/application-prod.yml

@@ -0,0 +1,46 @@
+# 环境配置
+server:
+  port: 8108
+  servlet:
+    context-path: /api/aosikang
+
+# condition
+spel:
+  scheduling: true        # 定时任务是否执行
+  multiSource: false       # 是否多数据源配置
+
+spring:
+  # database
+  datasource:
+    hikari:
+      connection-init-sql: SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci           # SqlServer, Oracle 无需设置类型
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    username: root
+    password: cp-root@2022++
+    url: jdbc:mysql://127.0.0.1:3306/dingtalk?serverTimezone=Asia/Shanghai&useUnicode=yes&characterEncoding=UTF-8&useSSL=true
+#  jpa:
+#    hibernate:
+#      ddl-auto: none      # JPA对表没有任何操作
+#    show-sql: true
+#    database: MYSQL
+#    database-platform: org.hibernate.dialect.MySQL57Dialect
+
+# dingtalk
+dingtalk:
+  agentId: 4043200193
+  appKey: dingspvmfolrjzhak6ge
+  appSecret: tcBzkR5fnRwaiJoWEpl9SFOChcBBPtCU5Nr0W9_dE1cF7V8UcB2AX7gz_3p9AGZa
+  corpId:
+  aesKey: C7Eau5BAem351bOvcvPwDjIElOZv4y62jpL9ep4OPH2
+  token: jtTq2ISmZkBJdL6wwq2r7iOpOAKifMDTOeGKE7DgTkzMiKfFV4pW66MQx
+  operator: ""   # OA管理员账号
+
+# aliwork
+#aliwork:
+#  appType: "APP_LNBUWW7ZBXRUWE29PCOV"
+#  systemToken: "WC866ZA1PI1B2L1JDNL334F44KI03GREIV1ILZ13"
+crm:
+  url: https://open.fxiaoke.com/cgi
+  appId: FSAID_1321943
+  appSecret: 8e4858d3de0549c48eebd0d0d3ecabad
+  permanentCode: ECF5F462BFD86418730399FCC465C25D

+ 106 - 0
mjava-aosikang/src/main/resources/static/json/form.json

@@ -0,0 +1,106 @@
+{
+  "DXCPXS": {
+    "processCode1": "PROC-3F7AC816-B41B-4A48-8CFD-CE9FF92B05AB",
+    "processCode": "PROC-BA558102-6DA9-4F02-9E0A-BE4272362C06",
+    "creator": "proposerCode",
+    "attachmentUrl": {
+      "downloadUrl": "downloadUrl",
+      "name": "name"
+    },
+    "compIds": {
+      "crmId": "crmId",
+      "合同描述": "contractContent",
+      "名称": "name",
+      "签署法人实体": "signingLegal",
+      "区域": "region",
+      "合同事宜": "contractIssue",
+      "合同编号": "contractCode",
+      "甲方信息": "partyAInfo",
+      "乙方信息": "partyBInfo",
+      "合同签订日期": "contractSignDate",
+      "是否涉及法人章": "involveCorporateSeal",
+      "是否涉密": "isConfidential",
+      "合同总价是否为固定金额": "isFixed",
+      "原币类型": "currencyType",
+      "原币金额": "currencyAmount",
+      "人民币金额(元)": "RMBAmount"
+    }
+  },
+  "NDKJXS": {
+    "processCode1": "PROC-A9F9C430-5947-4B4F-9D8A-2591207E6153",
+    "processCode": "PROC-452744BC-BDA4-4641-AA5B-B5D68A47242B",
+    "creator": "proposerCode",
+    "attachmentUrl": {
+      "downloadUrl": "downloadUrl",
+      "name": "name"
+    },
+    "compIds": {
+      "crmId": "crmId",
+      "名称": "name",
+      "签署法人实体": "signingLegal",
+      "是否加盖公章": "isSealUsed",
+      "合同描述": "contractContent",
+      "合同编号": "contractCode",
+      "甲方信息": "partyAInfo",
+      "乙方信息": "partyBInfo",
+      "合同签订日期": "contractSignDate",
+      "合同事宜": "contractIssue",
+      "是否涉及法人章": "involveCorporateSeal",
+      "是否涉密": "isConfidential"
+    }
+  },
+  "YPSQ": {
+    "processCode1": "PROC-B6CCD0BE-E74D-4FCD-8B3A-B68F7827C5AC",
+    "processCode": "PROC-82FE4028-CA69-47A6-8D9C-16239BC4006F",
+    "creator": "proposerCode",
+    "attachmentUrl": {
+      "downloadUrl": "downloadUrl",
+      "name": "name"
+    },
+    "products" : "样品信息",
+    "cellLines" : "细胞株信息",
+    "compIds": {
+      "crmId": "crmId",
+      "区域": "region",
+      "申请原因": "reason",
+      "申请类型": "type",
+      "客户名称": "customerName",
+      "收货人": "receiverName",
+      "收货地址": "address",
+      "收货人电话": "receiverPhone",
+      "产品数量": "productNum",
+      "实际发出数量": "actualNum",
+      "tableField_products": {
+        "产品名称": "productName",
+        "产品规格": "productSpecs",
+        "有效期": "validityPeriod",
+        "产品批号": "productLot",
+        "包装数量": "quantity"
+      },
+      "tableField_cellLines": {
+        "细胞株名称": "cellLineName",
+        "细胞株类型": "cellLineType",
+        "数量": "quantity",
+        "温度": "temperature",
+        "储存和运输条件": "condition"
+      }
+    }
+  },
+  "KS": {
+    "processCode1": "PROC-B5937B5F-7883-4BAD-932A-F54A5113576C",
+    "processCode": "PROC-89069560-F910-4048-9DC2-8F82DE724F4C",
+    "creator": "proposerCode",
+    "attachmentUrl": {
+      "downloadUrl": "downloadUrl",
+      "name": "name"
+    },
+    "compIds": {
+      "crmId": "crmId",
+      "售后管理": "afterSales",
+      "详情": "issueDescription",
+      "投诉等级": "level",
+      "客户是否要求赔偿": "isNeedCompensation",
+      "赔偿金额(元)": "amount"
+    }
+  }
+}

+ 47 - 0
mjava-aosikang/src/test/java/test.java

@@ -0,0 +1,47 @@
+import com.malk.aosikang.Boot;
+import com.malk.aosikang.service.Impl.PayServiceImpl;
+import com.malk.server.dingtalk.DDConf;
+import com.malk.server.dingtalk.crypto.DingCallbackCrypto;
+import com.malk.service.dingtalk.DDClient;
+import com.malk.service.dingtalk.DDClient_Event;
+
+import com.malk.service.dingtalk.DDClient_Workflow;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Boot.class)
+public class test {
+
+    @Autowired
+    private DDConf ddConf;
+    @Autowired
+    private DDClient_Event ddClient_event;
+    @Autowired
+    private DDClient ddClient;
+    @Autowired
+    private PayServiceImpl payService;
+    @Autowired
+    private DDClient_Workflow ddClient_workflow;
+    @Value("${dingtalk.appKey}")
+    private String APP_EKY;
+    @Value("${dingtalk.appSecret}")
+    private String APP_SECRET;
+    @Test
+    public void sonUpdateTest(){
+
+        Map processData = ddClient_workflow.getProcessInstanceId(ddClient.getAccessToken(APP_EKY, APP_SECRET), "uSyw345fTtqr8ZiY5eNDbw03891762224757");
+
+        System.out.println(processData);
+    }
+}

+ 39 - 0
mjava-aosikang/src/test/resource/server.sh

@@ -0,0 +1,39 @@
+#!/bin/bash
+
+appname='mjava-dongfangxinhua'
+
+if [ "$1" == "dev" ]; then
+  java -Xms256m -Xmx256m -jar $appname.jar --spring.profiles.active=dev
+else
+  if [ "$1" == "start" ]; then
+    nohup java -Xms256m -Xmx256m -jar $appname.jar &
+    echo "server prod is starting"
+    tail -f log/info.log
+  else
+    if [ "$1" == "test" ]; then
+      nohup java -Xms256m -Xmx256m -jar $appname.jar --spring.profiles.active=test &
+      echo "server test is starting"
+      tail -f log/info.log
+    else
+      if [ "$1" == "stop" ]; then
+        PID=$(ps -ef | grep $appname.jar | grep -v grep | awk '{ print $2 }')
+        if [ -z "$PID" ]; then
+          echo "server is already stopped"
+        else
+          echo kill $PID
+          kill $PID
+        fi
+      else
+        if [ "$1" == "status" ]; then
+          PID=$(ps -ef | grep $appname.jar | grep -v grep | awk '{ print $2 }')
+          if [ -z "$PID" ]; then
+            echo "server is stopped"
+          else
+            echo "server is running"
+            echo $PID
+          fi
+        fi
+      fi
+    fi
+  fi
+fi

+ 448 - 0
pom.xml

@@ -0,0 +1,448 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.malk</groupId>
+    <artifactId>java-mcli</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <modules>
+        <module>mjava-aosikang</module>
+    </modules>
+    <packaging>pom</packaging>
+
+    <name>java-mcli</name>
+    <description>mjava framework</description>
+
+    <!-- 版本管理 Management -->
+    <properties>
+        <!-- mjava版本: 修改mjava pom配置 -->
+        <mjava.version>0.0.3</mjava.version>
+        <!-- 全局配置 -->
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>1.8</java.version>
+        <!-- 公共依赖 -->
+        <spring-boot-dependencies.version>2.2.13.RELEASE</spring-boot-dependencies.version>
+        <junit.verson>4.12</junit.verson>
+        <lombok.version>1.18.8</lombok.version>
+        <validation-api.version>2.0.1.Final</validation-api.version>
+        <fastjson.version>1.2.83</fastjson.version>
+        <commons-lang3.version>3.10</commons-lang3.version>
+        <guava.version>30.1.1-jre</guava.version>
+        <hutool-all.version>5.6.0</hutool-all.version>
+        <spring-boot-starter-data-jpa.version>2.1.3.RELEASE</spring-boot-starter-data-jpa.version>
+        <querydsl-apt.version>4.2.1</querydsl-apt.version>
+        <querydsl-jpa.version>4.2.1</querydsl-jpa.version>
+        <spring-boot-starter-jdbc.version>2.2.13.RELEASE</spring-boot-starter-jdbc.version>
+        <easyexcel.version>2.2.7</easyexcel.version>
+        <!-- 数据库连接 [仅mysql为全局依赖] -->
+        <mysql-connector-java.version>8.0.22</mysql-connector-java.version>
+        <mssql-jdbc.version>6.4.0.jre8</mssql-jdbc.version>
+        <ojdbc6.version>11.2.0.4</ojdbc6.version>
+        <mongo-java-driver.version>3.12.7</mongo-java-driver.version>
+        <spring-boot-starter-data-mongodb.version>2.2.13.RELEASE</spring-boot-starter-data-mongodb.version>
+        <!-- jsp [非全局依赖] -->
+        <tomcat-embed-jasper.version>9.0.41</tomcat-embed-jasper.version>
+        <jstl.version>1.2</jstl.version>
+        <javax.servlet-api.version>4.0.1</javax.servlet-api.version>
+        <javax.servlet.jsp-api.version>2.3.1</javax.servlet.jsp-api.version>
+        <!-- jwt [非全局依赖] -->
+        <java-jwt.version>3.4.0</java-jwt.version>
+        <!-- swagger3: todo -->
+        <springfox-boot-starter.version>3.0.0</springfox-boot-starter.version>
+        <!-- 网页转pdf [非全局依赖] -->
+        <flying-saucer-pdf-itext5.version>9.0.3</flying-saucer-pdf-itext5.version>
+        <!-- 腾讯云[发票识别] [非全局依赖] -->
+        <tencentcloud-sdk-java.version>3.1.778</tencentcloud-sdk-java.version>
+        <!-- 不执行单元测试,也不编译测试类 -->
+        <skipTests>true</skipTests>
+        <!-- 不执行单元测试,但会编译测试类,并在target/test-classes目录下生成相应的class -->
+        <maven.test.skip>true</maven.test.skip>
+        <mybatis-plus.version>3.5.1</mybatis-plus.version>
+        <durid.version>1.1.18</durid.version>
+    </properties>
+
+    <!-- 依赖声明 & 版本 -->
+    <dependencyManagement>
+        <dependencies>
+            <!-- SpringBoot 依赖 -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot-dependencies.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <!-- 单元测试 -->
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>${junit.verson}</version>
+                <scope>test</scope>
+            </dependency>
+
+            <!-- lombok -->
+            <dependency>
+                <groupId>org.projectlombok</groupId>
+                <artifactId>lombok</artifactId>
+                <version>${lombok.version}</version>
+                <scope>provided</scope>
+            </dependency>
+
+            <!-- validation 参数校验 -->
+            <dependency>
+                <groupId>javax.validation</groupId>
+                <artifactId>validation-api</artifactId>
+                <version>${validation-api.version}</version>
+            </dependency>
+
+            <!-- 阿里巴巴 json -->
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>fastjson</artifactId>
+                <version>${fastjson.version}</version>
+            </dependency>
+
+            <!-- 通用的工具类集 -->
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-lang3</artifactId>
+                <version>${commons-lang3.version}</version>
+            </dependency>
+            <!-- ppExt: 23.10.26 钉钉新方式以Steam接入, HTTP形式commonsc-codec在升级之后,其内部做了一个validateCharacter校验. 使用 guava 替代-->
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>${guava.version}</version>
+            </dependency>
+            <!-- 国产工具集 -->
+            <dependency>
+                <groupId>cn.hutool</groupId>
+                <artifactId>hutool-all</artifactId>
+                <version>${hutool-all.version}</version>
+            </dependency>
+
+            <!-- data-jpa 数据库操作 -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-data-jpa</artifactId>
+                <version>${spring-boot-starter-data-jpa.version}</version>
+            </dependency>
+
+            <!-- QueryDSL 4.x 支持-->
+            <dependency>
+                <groupId>com.querydsl</groupId>
+                <artifactId>querydsl-apt</artifactId>
+                <scope>provided</scope>
+                <version>${querydsl-apt.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.querydsl</groupId>
+                <artifactId>querydsl-jpa</artifactId>
+                <version>${querydsl-jpa.version}</version>
+            </dependency>
+
+            <!-- AOP多数据源切换 -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-jdbc</artifactId>
+                <version>${spring-boot-starter-jdbc.version}</version>
+            </dependency>
+
+            <!-- easyExcel 优化 poi [不影响单独引入poi, 会冲突] -->
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>easyexcel</artifactId>
+                <version>${easyexcel.version}</version>
+            </dependency>
+
+            <!-- mySql 驱动 -->
+            <dependency>
+                <groupId>mysql</groupId>
+                <artifactId>mysql-connector-java</artifactId>
+                <version>${mysql-connector-java.version}</version>
+            </dependency>
+
+            <!-- sqlserver依赖 -->
+            <dependency>
+                <groupId>com.microsoft.sqlserver</groupId>
+                <artifactId>mssql-jdbc</artifactId>
+                <scope>runtime</scope>
+                <version>${mssql-jdbc.version}</version>
+            </dependency>
+
+            <!-- Oracle 依赖 -->
+            <dependency>
+                <groupId>com.oracle.database.jdbc</groupId>
+                <artifactId>ojdbc6</artifactId>
+                <version>${ojdbc6.version}</version>
+            </dependency>
+
+            <!-- MongoDB 驱动 -->
+            <dependency>
+                <groupId>org.mongodb</groupId>
+                <artifactId>mongo-java-driver</artifactId>
+                <version>${mongo-java-driver.version}</version>
+            </dependency>
+            <!-- MongoDB jpa 操作 -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-data-mongodb</artifactId>
+                <version>${spring-boot-starter-data-mongodb.version}</version>
+            </dependency>
+
+            <!-- url转pdf -->
+            <dependency>
+                <groupId>org.xhtmlrenderer</groupId>
+                <artifactId>flying-saucer-pdf-itext5</artifactId>
+                <version>${flying-saucer-pdf-itext5.version}</version>
+            </dependency>
+
+            <!-- jsp: tomcat-embed-jasper 需要添加到子项目内 -->
+            <dependency>
+                <groupId>org.apache.tomcat.embed</groupId>
+                <artifactId>tomcat-embed-jasper</artifactId>
+                <version>${tomcat-embed-jasper.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>javax.servlet</groupId>
+                <artifactId>jstl</artifactId>
+                <version>${jstl.version}</version>
+            </dependency>
+            <!-- servlet -->
+            <dependency>
+                <groupId>javax.servlet</groupId>
+                <artifactId>javax.servlet-api</artifactId>
+                <version>${javax.servlet-api.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>javax.servlet.jsp</groupId>
+                <artifactId>javax.servlet.jsp-api</artifactId>
+                <version>${javax.servlet.jsp-api.version}</version>
+            </dependency>
+
+            <!-- jwt -->
+            <dependency>
+                <groupId>com.auth0</groupId>
+                <artifactId>java-jwt</artifactId>
+                <version>${java-jwt.version}</version>
+            </dependency>
+
+            <!-- 腾讯云 -->
+            <dependency>
+                <groupId>com.tencentcloudapi</groupId>
+                <artifactId>tencentcloud-sdk-java</artifactId>
+                <version>${tencentcloud-sdk-java.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <!-- 子项目需要与 mjava 相同的依赖, 否则调试可运行, 打包后会运行报错. 为了避免重复引入 pom, 将 mjava 依赖直接在全局 pom 引入 -->
+    <dependencies>
+        <!-- spring boot -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-tomcat</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <!-- spring-boot-devtools [热部署] -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <optional>true</optional> <!-- 表示依赖不会传递 -->
+        </dependency>
+        <!-- 单元测试 -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- validation 参数校验 -->
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+
+        <!-- 阿里巴巴 json -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+
+        <!-- 通用的工具类集 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- 国产工具集 -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+
+        <!-- data-jpa 数据库操作 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+
+        <!-- QueryDSL 4.x 支持-->
+        <dependency>
+            <groupId>com.querydsl</groupId>
+            <artifactId>querydsl-apt</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.querydsl</groupId>
+            <artifactId>querydsl-jpa</artifactId>
+        </dependency>
+
+        <!-- AOP多数据源切换 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
+
+        <!-- easyExcel 优化 poi [不影响单独引入poi, 会冲突] -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+        </dependency>
+
+        <!-- mySql 驱动 -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <!--        引入mybatisPlus 包含了 jdbc -->
+<!--        <dependency>-->
+<!--            <groupId>com.baomidou</groupId>-->
+<!--            <artifactId>mybatis-plus-boot-starter</artifactId>-->
+<!--            <version>${mybatis-plus.version}</version>-->
+<!--        </dependency>-->
+
+        <!--        引入durid數據源-->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+            <version>${durid.version}</version>
+        </dependency>
+
+        <!-- Oracle 依赖 -->
+<!--                <dependency>-->
+<!--                    <groupId>com.oracle.database.jdbc</groupId>-->
+<!--                    <artifactId>ojdbc6</artifactId>-->
+<!--                </dependency>-->
+
+<!--         sqlserver 依赖 -->
+<!--                <dependency>-->
+<!--                    <groupId>com.microsoft.sqlserver</groupId>-->
+<!--                    <artifactId>mssql-jdbc</artifactId>-->
+<!--                    <scope>runtime</scope>-->
+<!--                </dependency>-->
+
+
+<!--         MongoDB 驱动 -->
+<!--                <dependency>-->
+<!--                    <groupId>org.mongodb</groupId>-->
+<!--                    <artifactId>mongo-java-driver</artifactId>-->
+<!--                </dependency>-->
+<!--         MongoDB jpa 操作 -->
+<!--                <dependency>-->
+<!--                    <groupId>org.springframework.boot</groupId>-->
+<!--                    <artifactId>spring-boot-starter-data-mongodb</artifactId>-->
+<!--                    <version>${spring-boot-starter-data-mongodb.version}</version>-->
+<!--                </dependency>-->
+
+<!--         url转pdf -->
+<!--                <dependency>-->
+<!--                    <groupId>org.xhtmlrenderer</groupId>-->
+<!--                    <artifactId>flying-saucer-pdf-itext5</artifactId>-->
+<!--                </dependency>-->
+
+<!--         jsp: tomcat-embed-jasper 需要在子项目内引用才有效 -->
+<!--                <dependency>-->
+<!--                    <groupId>javax.servlet</groupId>-->
+<!--                    <artifactId>jstl</artifactId>-->
+<!--                </dependency>-->
+<!--         servlet -->
+                <dependency>
+                    <groupId>javax.servlet</groupId>
+                    <artifactId>javax.servlet-api</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>javax.servlet.jsp</groupId>
+                    <artifactId>javax.servlet.jsp-api</artifactId>
+                </dependency>
+
+<!--         jwt -->
+                <dependency>
+                    <groupId>com.auth0</groupId>
+                    <artifactId>java-jwt</artifactId>
+                </dependency>
+
+<!--         腾讯云 [go to https://search.maven.org/search?q=tencentcloud-sdk-java and get the latest version.] -->
+<!--                <dependency>-->
+<!--                    <groupId>com.tencentcloudapi</groupId>-->
+<!--                    <artifactId>tencentcloud-sdk-java</artifactId>-->
+<!--                </dependency>-->
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                    <!-- jva使用了未经检查或不安全的操作,编译会有打印,通过插件显示具体报错警告位置, 如 T, Map, List 也会报警告 -->
+                    <compilerArgument>-Xlint:unchecked</compilerArgument>
+                </configuration>
+            </plugin>
+            <!-- QueryDSL 插件: 因为QueryDsl是类型安全的,所以还需要加上Maven APT plugin,使用 APT 自动生成Q类 -->
+            <plugin>
+                <groupId>com.mysema.maven</groupId>
+                <artifactId>apt-maven-plugin</artifactId>
+                <version>1.1.3</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>process</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>target/generated-sources/java</outputDirectory>
+                            <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>