Procházet zdrojové kódy

Track Maven source directory normally

zhangkan před 1 týdnem
rodič
revize
eb1ab6bc8a
21 změnil soubory, kde provedl 2448 přidání a 1 odebrání
  1. 0 1
      src
  2. 21 0
      src/main/java/com/muzhi/tianpai/TianpaiApplication.java
  3. 77 0
      src/main/java/com/muzhi/tianpai/common/R.java
  4. 12 0
      src/main/java/com/muzhi/tianpai/common/ResultCode.java
  5. 69 0
      src/main/java/com/muzhi/tianpai/common/dingtalk/DingtalkEmployee.java
  6. 52 0
      src/main/java/com/muzhi/tianpai/common/dingtalk/DingtalkUserV2.java
  7. 209 0
      src/main/java/com/muzhi/tianpai/common/dingtalk/RosterSystemField.java
  8. 39 0
      src/main/java/com/muzhi/tianpai/common/dingtalkwebhook/DingtalkWebhook.java
  9. 82 0
      src/main/java/com/muzhi/tianpai/common/dingtalkyida/YiDaFormData.java
  10. 465 0
      src/main/java/com/muzhi/tianpai/common/dingtalkyida/YiDaFormDataV2.java
  11. 40 0
      src/main/java/com/muzhi/tianpai/common/time/BeijingToUtcConverter.java
  12. 122 0
      src/main/java/com/muzhi/tianpai/common/utilityclass/PackageFlie.java
  13. 48 0
      src/main/java/com/muzhi/tianpai/controller/AccessTokenController.java
  14. 52 0
      src/main/java/com/muzhi/tianpai/controller/DingTalkController.java
  15. 17 0
      src/main/java/com/muzhi/tianpai/service/AccessTokenService.java
  16. 21 0
      src/main/java/com/muzhi/tianpai/service/DingTalkService.java
  17. 109 0
      src/main/java/com/muzhi/tianpai/service/impl/AccessTokenServiceImpl.java
  18. 176 0
      src/main/java/com/muzhi/tianpai/service/impl/DingTalkServiceImpl.java
  19. 72 0
      src/main/resources/application.properties
  20. 752 0
      src/test/java/com/muzhi/tianpai/APITest/Test1.java
  21. 13 0
      src/test/java/com/muzhi/tianpai/TestApplicationTests.java

+ 0 - 1
src

@@ -1 +0,0 @@
-Subproject commit 684b371ebbbbbaeda6eae2df73a86804f873113c

+ 21 - 0
src/main/java/com/muzhi/tianpai/TianpaiApplication.java

@@ -0,0 +1,21 @@
+package com.muzhi.tianpai;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+
+//@MapperScan("com.muzhi.huaxiao.mapper")
+@EnableAsync//开启异步注解
+@SpringBootApplication(exclude= DataSourceAutoConfiguration.class)//启动时不加载数据库(exclude= DataSourceAutoConfiguration.class)
+@EnableScheduling//开启定时任务
+//@ComponentScan(basePackages = {"com.muzhi"})
+public class TianpaiApplication {
+
+    public static void main(String[] args){
+        SpringApplication.run(TianpaiApplication.class, args);
+    }
+
+}

+ 77 - 0
src/main/java/com/muzhi/tianpai/common/R.java

@@ -0,0 +1,77 @@
+package com.muzhi.tianpai.common;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author ZhangKan
+ * @Data 11:20
+ */
+
+//统一返回结果类
+@Data
+public class R {
+
+    @ApiModelProperty(value = "是否成功")
+    private Boolean success;
+
+    @ApiModelProperty(value = "返回码")
+    private Integer code;
+
+    @ApiModelProperty(value = "返回消息")
+    private String message;
+
+    @ApiModelProperty(value = "返回数据")
+    private Map<String, Object> data = new HashMap<String, Object>();
+
+    //把构造方法私有
+    private R(){}
+
+    //成功静态方法
+    public static R ok(){
+
+        R r = new R();
+        r.setSuccess(true);
+        r.setCode(ResultCode.SUCCESS);
+        r.setMessage("成功");
+        return r;
+    }
+
+    //失败静态方法
+    public static R error(){
+
+        R r = new R();
+        r.setSuccess(false);
+        r.setCode(ResultCode.ERROR);
+        r.setMessage("失败");
+        return r;
+    }
+
+    public R message(String message){
+        this.setMessage(message);
+        return this;
+    }
+
+    public R code(Integer code){
+        this.setCode(code);
+        return this;
+    }
+
+    public R data(String key, Object value){
+        this.data.put(key, value);
+        return this;
+    }
+
+    public R data(String key, String value){
+        this.data.put(key, value);
+        return this;
+    }
+
+    public R data(Map<String, Object> map){
+        this.setData(map);
+        return this;
+    }
+}

+ 12 - 0
src/main/java/com/muzhi/tianpai/common/ResultCode.java

@@ -0,0 +1,12 @@
+package com.muzhi.tianpai.common;
+
+/**
+ * @Author ZhangKan
+ * @Data 11:15
+ */
+public interface ResultCode {
+
+    public static Integer SUCCESS = 200;//成功
+
+    public static Integer ERROR = 201;//失败
+}

+ 69 - 0
src/main/java/com/muzhi/tianpai/common/dingtalk/DingtalkEmployee.java

@@ -0,0 +1,69 @@
+package com.muzhi.tianpai.common.dingtalk;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.OapiSmartworkHrmEmployeeV2UpdateRequest;
+import com.dingtalk.api.response.OapiSmartworkHrmEmployeeV2UpdateResponse;
+import com.taobao.api.ApiException;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+@Component
+public class DingtalkEmployee {
+
+    /**
+     * 更新钉钉花名册字段信息
+     * REMARK 接口文档地址:https://open.dingtalk.com/document/development/create-dingtalk-user-created-dedicated-account
+     *
+     * @param userid 钉钉人员userid
+     * @param fieldCode 花名册字段code
+     * @param fieldId 花名册字段id
+     * @param fieldValue 花名册字段值
+     * @param accessToken     钉钉accessToken
+     * @return String
+     */
+    public static String updateHuamingce(String userid, String fieldCode, String fieldId, String fieldValue, String accessToken) {
+        log.info("传参钉钉人员userid:" + userid);
+        log.info("传参花名册字段code:" + fieldCode);
+        log.info("传参花名册字段id:" + fieldId);
+        log.info("传参花名册字段值:" + fieldValue);
+
+        try {
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/smartwork/hrm/employee/v2/update");
+            OapiSmartworkHrmEmployeeV2UpdateRequest req = new OapiSmartworkHrmEmployeeV2UpdateRequest();
+            req.setAgentid(4574746416L);
+            OapiSmartworkHrmEmployeeV2UpdateRequest.EmpUpdateByCustomParam obj1 = new OapiSmartworkHrmEmployeeV2UpdateRequest.EmpUpdateByCustomParam();
+            List<OapiSmartworkHrmEmployeeV2UpdateRequest.EmpGroupFieldVo> list3 = new ArrayList<OapiSmartworkHrmEmployeeV2UpdateRequest.EmpGroupFieldVo>();
+            OapiSmartworkHrmEmployeeV2UpdateRequest.EmpGroupFieldVo obj4 = new OapiSmartworkHrmEmployeeV2UpdateRequest.EmpGroupFieldVo();
+            list3.add(obj4);
+            List<OapiSmartworkHrmEmployeeV2UpdateRequest.EmpListFieldVo> list7 = new ArrayList<OapiSmartworkHrmEmployeeV2UpdateRequest.EmpListFieldVo>();
+            OapiSmartworkHrmEmployeeV2UpdateRequest.EmpListFieldVo obj8 = new OapiSmartworkHrmEmployeeV2UpdateRequest.EmpListFieldVo();
+            list7.add(obj8);
+            List<OapiSmartworkHrmEmployeeV2UpdateRequest.EmpFieldVo> list11 = new ArrayList<OapiSmartworkHrmEmployeeV2UpdateRequest.EmpFieldVo>();
+            OapiSmartworkHrmEmployeeV2UpdateRequest.EmpFieldVo obj12 = new OapiSmartworkHrmEmployeeV2UpdateRequest.EmpFieldVo();
+            list11.add(obj12);
+            obj12.setFieldCode(fieldId);
+            obj12.setValue(fieldValue);
+            obj8.setSection(list11);
+            obj8.setOldIndex(0L);
+            obj4.setSections(list7);
+            obj4.setGroupId(fieldCode);
+            obj1.setGroups(list3);
+            obj1.setUserid(userid);
+            req.setParam(obj1);
+            OapiSmartworkHrmEmployeeV2UpdateResponse rsp = client.execute(req, accessToken);
+            System.out.println(rsp.getBody());
+        } catch (ApiException e) {
+            e.printStackTrace();
+        }
+
+        return "===字段id【" + fieldId + "】更新花名册值,执行结束===";
+    }
+}

+ 52 - 0
src/main/java/com/muzhi/tianpai/common/dingtalk/DingtalkUserV2.java

@@ -0,0 +1,52 @@
+package com.muzhi.tianpai.common.dingtalk;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.dingtalkyida_1_0.models.*;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+public class DingtalkUserV2 {
+
+    /**
+     * 创建钉钉自建企业账号
+     * REMARK 接口文档地址:https://open.dingtalk.com/document/development/create-dingtalk-user-created-dedicated-account
+     *
+     * @param bodyStr         请求体信息
+     * @param accessToken     钉钉accessToken
+     * @return jsonObject{}
+     */
+    public static JSONObject createDingtalkEnterpirseUser(String bodyStr, String accessToken) {
+
+        JSONObject jsonObject = new JSONObject();
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("application/json");
+            RequestBody body = RequestBody.create(mediaType, bodyStr);
+            Request request = new Request.Builder()
+                    .url("https://oapi.dingtalk.com/topapi/v2/user/create?access_token=" + accessToken)
+                    .method("POST", body)
+                    .addHeader("Content-Type", "application/json")
+                    .build();
+            Response response = client.newCall(request).execute();
+            String resBody = response.body().string();
+            jsonObject = JSON.parseObject(resBody);
+        }catch (Exception e){
+            log.error("创建钉钉企业账号异常:{}",e);
+        }
+        return jsonObject;
+    }
+}

+ 209 - 0
src/main/java/com/muzhi/tianpai/common/dingtalk/RosterSystemField.java

@@ -0,0 +1,209 @@
+package com.muzhi.tianpai.common.dingtalk;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public enum RosterSystemField {
+    NAME("姓名", "sys00", "sys00-name"),
+    EMAIL("邮箱", "sys00", "sys00-email"),
+    DEPARTMENT_IDS("部门id列表", "sys00", "sys00-deptIds"),
+    MAIN_DEPARTMENT_ID("主部门id", "sys00", "sys00-mainDeptId"),
+    DEPARTMENT("部门", "sys00", "sys00-dept"),
+    MAIN_DEPARTMENT("主部门", "sys00", "sys00-mainDept"),
+    POSITION("职位", "sys00", "sys00-position"),
+    MOBILE("手机号", "sys00", "sys00-mobile"),
+    JOB_NUMBER("工号", "sys00", "sys00-jobNumber"),
+    TELEPHONE_EXTENSION("分机号", "sys00", "sys00-tel"),
+    WORK_PLACE("办公地点", "sys00", "sys00-workPlace"),
+    REMARK("备注", "sys00", "sys00-remark"),
+    CONFIRM_JOIN_TIME("入职时间", "sys00", "sys00-confirmJoinTime"),
+
+    DUTY("zhiwu", "sys00", "0e3aed7e-2812-4851-87e6-aa6244deb5a9"),//NOTE 自定义字段
+    RANK("zhiji", "sys01", "419d7ae9-1d42-4b55-aae4-7248afb1b936"),//NOTE 自定义字段
+
+    EMPLOYEE_TYPE("yuangongleixing", "sys01", "sys01-employeeType"),
+    EMPLOYEE_STATUS("yuangongzhuangtai", "sys01", "sys01-employeeStatus"),
+    PROBATION_PERIOD_TYPE("shiyongqi", "sys01", "sys01-probationPeriodType"),
+    REGULAR_TIME("zhuanzhengriqi", "sys01", "sys01-regularTime"),
+    POSITION_LEVEL("岗位职级", "sys01", "sys01-positionLevel"),
+
+    REAL_NAME("shenfenzhengxingming", "sys02", "sys02-realName"),
+    CERTIFICATE_NUMBER("zhengjianhaoma", "sys02", "sys02-certNo"),
+    BIRTH_TIME("chushengriqi", "sys02", "sys02-birthTime"),
+    SEX_TYPE("xingbie", "sys02", "sys02-sexType"),
+    NATION_TYPE("minzu", "sys02", "sys02-nationType"),
+    CERTIFICATE_ADDRESS("shenfenzhengdizhi", "sys02", "sys02-certAddress"),
+    CERTIFICATE_END_TIME("zhengjianyouxiaoqi", "sys02", "sys02-certEndTime"),
+    MARRIAGE("婚姻状况", "sys02", "sys02-marriage"),
+    JOIN_WORKING_TIME("首次参加工作时间", "sys02", "sys02-joinWorkingTime"),
+    RESIDENCE_TYPE("hujileixing", "sys02", "sys02-residenceType"),
+    ADDRESS("zhuzhi", "sys02", "sys02-address"),
+    POLITICAL_STATUS("zhengzhimianmao", "sys02", "sys02-politicalStatus"),
+
+    HIGHEST_EDUCATION("xueli", "sys03", "sys03-highestEdu"),
+    GRADUATE_SCHOOL("biyeyuanxiao", "sys03", "sys03-graduateSchool"),
+    GRADUATION_TIME("biyeshijian", "sys03", "sys03-graduationTime"),
+    MAJOR("suoxuezhuanye", "sys03", "sys03-major"),
+
+    BANK_ACCOUNT_NUMBER("yinhangkahao", "sys04", "sys04-bankAccountNo"),
+    ACCOUNT_BANK("kaihuhang", "sys04", "sys04-accountBank"),
+
+    CONTRACT_COMPANY_NAME("合同公司", "sys05", "sys05-contractCompanyName"),
+    CONTRACT_TYPE("hetongleixing", "sys05", "sys05-contractType"),
+    FIRST_CONTRACT_START_TIME(
+            "首次合同起始日",
+            "sys05",
+            "sys05-firstContractStartTime"
+    ),
+    FIRST_CONTRACT_END_TIME(
+            "首次合同到期日",
+            "sys05",
+            "sys05-firstContractEndTime"
+    ),
+    CURRENT_CONTRACT_START_TIME(
+            "hetongkaishiriqi",
+            "sys05",
+            "sys05-nowContractStartTime"
+    ),
+    CURRENT_CONTRACT_END_TIME(
+            "hetongjieshuriqi",
+            "sys05",
+            "sys05-nowContractEndTime"
+    ),
+    CONTRACT_PERIOD_TYPE("合同期限", "sys05", "sys05-contractPeriodType"),
+    CONTRACT_RENEW_COUNT("续签次数", "sys05", "sys05-contractRenewCount"),
+
+    URGENT_CONTACT_NAME(
+            "jinjilianxirenxingming",
+            "sys06",
+            "sys06-urgentContactsName"
+    ),
+    URGENT_CONTACT_RELATION(
+            "lianxirenguanxi",
+            "sys06",
+            "sys06-urgentContactsRelation"
+    ),
+    URGENT_CONTACT_PHONE(
+            "lianxirendianhua",
+            "sys06",
+            "sys06-urgentContactsPhone"
+    ),
+
+    HAVE_CHILD("有无子女", "sys07", "sys07-haveChild"),
+    CHILD_NAME("子女姓名", "sys07", "sys07-childName"),
+    CHILD_SEX("子女性别", "sys07", "sys07-childSex"),
+    CHILD_BIRTH_DATE("子女出生日期", "sys07", "sys07-childBirthDate"),
+
+    PERSONAL_SOCIAL_INSURANCE(
+            "个人社保账号",
+            "sys09",
+            "sys09-personalSi"
+    ),
+    PERSONAL_HOUSING_FUND(
+            "个人公积金账号",
+            "sys09",
+            "sys09-personalHf"
+    );
+
+    private final String fieldName;
+    private final String fieldId;
+    private final String fieldCode;
+
+    private static final Map<String, RosterSystemField> NAME_INDEX;
+    private static final Map<String, RosterSystemField> CODE_INDEX;
+
+    static {
+        Map<String, RosterSystemField> nameIndex = new HashMap<>();
+        Map<String, RosterSystemField> codeIndex = new HashMap<>();
+
+        for (RosterSystemField field : values()) {
+            nameIndex.put(field.fieldName, field);
+            codeIndex.put(field.fieldCode, field);
+        }
+
+        NAME_INDEX = Collections.unmodifiableMap(nameIndex);
+        CODE_INDEX = Collections.unmodifiableMap(codeIndex);
+    }
+
+    RosterSystemField(
+            String fieldName,
+            String fieldId,
+            String fieldCode
+    ) {
+        this.fieldName = fieldName;
+        this.fieldId = fieldId;
+        this.fieldCode = fieldCode;
+    }
+
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    public String getFieldId() {
+        return fieldId;
+    }
+
+    public String getFieldCode() {
+        return fieldCode;
+    }
+
+    /**
+     * 根据中文字段名称查找字段定义。
+     */
+    public static Optional<RosterSystemField> findByName(String fieldName) {
+        if (fieldName == null || fieldName.trim().isEmpty()) {
+            return Optional.empty();
+        }
+
+        return Optional.ofNullable(NAME_INDEX.get(fieldName.trim()));
+    }
+
+    /**
+     * 根据完整字段 code 查找字段定义。
+     */
+    public static Optional<RosterSystemField> findByCode(String fieldCode) {
+        if (fieldCode == null || fieldCode.trim().isEmpty()) {
+            return Optional.empty();
+        }
+
+        return Optional.ofNullable(CODE_INDEX.get(fieldCode.trim()));
+    }
+
+    /**
+     * 根据中文字段名称获取字段 ID,即“-”左侧部分。
+     */
+    public static Optional<String> findFieldIdByName(String fieldName) {
+        return findByName(fieldName)
+                .map(RosterSystemField::getFieldId);
+    }
+
+    /**
+     * 根据中文字段名称获取完整字段 code。
+     */
+    public static Optional<String> findFieldCodeByName(String fieldName) {
+        return findByName(fieldName)
+                .map(RosterSystemField::getFieldCode);
+    }
+
+    /**
+     * 根据中文字段名称获取字段 ID,未找到时抛出异常。
+     */
+    public static String requireFieldIdByName(String fieldName) {
+        return findFieldIdByName(fieldName)
+                .orElseThrow(() -> new IllegalArgumentException(
+                        "未找到系统标准字段:" + fieldName
+                ));
+    }
+
+    /**
+     * 根据中文字段名称获取完整字段 code,未找到时抛出异常。
+     */
+    public static String requireFieldCodeByName(String fieldName) {
+        return findFieldCodeByName(fieldName)
+                .orElseThrow(() -> new IllegalArgumentException(
+                        "未找到系统标准字段:" + fieldName
+                ));
+    }
+}

+ 39 - 0
src/main/java/com/muzhi/tianpai/common/dingtalkwebhook/DingtalkWebhook.java

@@ -0,0 +1,39 @@
+package com.muzhi.tianpai.common.dingtalkwebhook;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+public class DingtalkWebhook {
+
+
+    public static JSONObject dingtalkWebhookConnection(String webhookUrl, String mapStr){
+        log.info("钉钉webhookUrl:" + webhookUrl);
+        log.info("钉钉mapStr:" + mapStr);
+
+        OkHttpClient client = new OkHttpClient().newBuilder()
+                .build();
+        MediaType mediaType = MediaType.parse("text/plain");
+        RequestBody body = RequestBody.create(mediaType, mapStr);
+        Request request = new Request.Builder()
+                .url(webhookUrl)
+                .method("POST", body)
+                .build();
+        JSONObject jsonObject = null;
+        try {
+            Response response = client.newCall(request).execute();
+            String bodyStr = response.body().string();
+            jsonObject = JSON.parseObject(bodyStr);
+            System.out.println("返回体body{}:" + jsonObject);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+
+        return jsonObject;
+    }
+
+}

+ 82 - 0
src/main/java/com/muzhi/tianpai/common/dingtalkyida/YiDaFormData.java

@@ -0,0 +1,82 @@
+package com.muzhi.tianpai.common.dingtalkyida;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.dingtalkyida_1_0.models.CreateOrUpdateFormDataResponse;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class YiDaFormData {
+
+    private static String appType;
+    private static String appSecret;
+
+    // 创建一个非静态的 Setter 方法,加上 @Value
+    // Spring 启动时会调用这个方法,将配置文件值传入
+    @Value("${myapp.yida.appType}")
+    public void setAppType(String value) {
+        YiDaFormData.appType = value; // 赋值给静态变量
+    }
+    @Value("${myapp.yida.appSecret}")
+    public void setAppSecret(String value) {
+        YiDaFormData.appSecret = value; // 赋值给静态变量
+    }
+
+    //宜搭接口调用
+    public static com.aliyun.dingtalkyida_1_0.Client createClient() throws Exception {
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        return new com.aliyun.dingtalkyida_1_0.Client(config);
+    }
+
+    /**
+     * 新增或更新表单实例
+     * REMARK 接口文档地址:https://open.dingtalk.com/document/development/api-createorupdateformdata-v2
+     *
+     * @param formUuid        表单编码
+     * @param searchCondition 查询条件
+     * @param formDataJson    表单字段
+     * @param accessToken     钉钉accessToken
+     * @return jsonObject{}
+     */
+    public static JSONObject CreateOrUpdateFormData(String formUuid, String searchCondition, String formDataJson, String accessToken) {
+
+        JSONObject jsonObject = new JSONObject();
+        try {
+            com.aliyun.dingtalkyida_1_0.Client client11 = createClient();
+            com.aliyun.dingtalkyida_1_0.models.CreateOrUpdateFormDataHeaders createOrUpdateFormDataHeaders = new com.aliyun.dingtalkyida_1_0.models.CreateOrUpdateFormDataHeaders();
+            createOrUpdateFormDataHeaders.xAcsDingtalkAccessToken = accessToken;
+            com.aliyun.dingtalkyida_1_0.models.CreateOrUpdateFormDataRequest createOrUpdateFormDataRequest = new com.aliyun.dingtalkyida_1_0.models.CreateOrUpdateFormDataRequest()
+                    //应用秘钥
+                    .setSystemToken(appSecret)
+                    //表单编码
+                    .setFormUuid(formUuid)
+                    .setUserId("yida_pub_account")//发起人userId,宜搭平台发起默认:yida_pub_account
+                    .setSearchCondition(searchCondition)
+                    //应用编码
+                    .setAppType(appType)
+                    .setFormDataJson(formDataJson);
+
+            CreateOrUpdateFormDataResponse orUpdateFormDataWithOptions = client11.createOrUpdateFormDataWithOptions(createOrUpdateFormDataRequest, createOrUpdateFormDataHeaders, new RuntimeOptions());
+            String s = JSON.toJSONString(orUpdateFormDataWithOptions.getBody());
+            jsonObject = JSON.parseObject(s);
+            System.out.println("【创建或更新】表单object对象:" + jsonObject);
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+        return jsonObject;
+    }
+}

+ 465 - 0
src/main/java/com/muzhi/tianpai/common/dingtalkyida/YiDaFormDataV2.java

@@ -0,0 +1,465 @@
+package com.muzhi.tianpai.common.dingtalkyida;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.dingtalkyida_1_0.models.*;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+public class YiDaFormDataV2 {
+
+    private static String appType;
+    private static String appSecret;
+
+    // 创建一个非静态的 Setter 方法,加上 @Value
+    // Spring 启动时会调用这个方法,将配置文件值传入
+    @Value("${myapp.yida.appType}")
+    public void setAppType(String value) {
+        YiDaFormDataV2.appType = value; // 赋值给静态变量
+    }
+    @Value("${myapp.yida.appSecret}")
+    public void setAppSecret(String value) {
+        YiDaFormDataV2.appSecret = value; // 赋值给静态变量
+    }
+
+    //宜搭接口调用
+    public static com.aliyun.dingtalkyida_1_0.Client createClient() throws Exception {
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        return new com.aliyun.dingtalkyida_1_0.Client(config);
+    }
+
+    public static com.aliyun.dingtalkyida_2_0.Client createClient2() throws Exception {
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        return new com.aliyun.dingtalkyida_2_0.Client(config);
+    }
+
+    /**
+     * 查询和更新表单实例
+     * REMARK 接口文档地址:
+     *  【查询表单实例数据】https://open.dingtalk.com/document/development/api-searchformdatas-v2
+     *  【更新表单数据】https://open.dingtalk.com/document/development/api-updateformdata-v2
+     *
+     * @param formUuid        表单编码
+     * @param serialNumber    EIC流水号
+     * @param state           EIC报销付款状态
+     * @param message         EIC回传信息
+     * @param accessToken     钉钉accessToken
+     * @return jsonObject{}
+     */
+    public static String searchAndUpdateFormData(String formUuid, String serialNumber, String state, String message, String accessToken) {
+
+        String shiliID = null;
+
+        //查询表单实例数据
+        Map mapAll = new HashMap();
+        //EIC流水号
+        mapAll.put("textField_mnh2jodr", serialNumber);
+        String mapAllStr = JSON.toJSONString(mapAll);
+        System.out.println("封装查询的字段参数:" + mapAllStr);
+
+        int size = 100;
+        int page = 1;
+        try {
+            com.aliyun.dingtalkyida_1_0.Client client = createClient();
+            com.aliyun.dingtalkyida_1_0.models.SearchFormDatasHeaders searchFormDatasHeaders = new com.aliyun.dingtalkyida_1_0.models.SearchFormDatasHeaders();
+            searchFormDatasHeaders.xAcsDingtalkAccessToken = accessToken;
+            com.aliyun.dingtalkyida_1_0.models.SearchFormDatasRequest searchFormDatasRequest = new com.aliyun.dingtalkyida_1_0.models.SearchFormDatasRequest()
+                    .setSystemToken(appSecret)
+
+                    .setSearchFieldJson(mapAllStr)
+
+                    .setCurrentPage(page)
+                    .setPageSize(size)
+                    .setFormUuid(formUuid)
+                    .setUserId("yida_pub_account")
+                    .setAppType(appType);
+
+            SearchFormDatasResponse searchFormDatasResponse = client.searchFormDatasWithOptions(searchFormDatasRequest, searchFormDatasHeaders, new RuntimeOptions());
+//            System.out.println(JSON.toJSONString(searchFormDatasResponse.getBody()));
+            String s = JSON.toJSONString(searchFormDatasResponse.getBody());
+            JSONObject jsonObject = JSON.parseObject(s);
+            System.out.println("宜搭数据高级条件查询【报销档案】接口object对象:" + jsonObject);
+            JSONArray data = jsonObject.getJSONArray("data");
+            if (data.isEmpty()) {
+                System.out.println("查询【报销档案】返回数据为空!");
+            } else {
+                //实例ID
+                shiliID = data.getJSONObject(0).getString("formInstanceId");
+                System.out.println("实例ID===>" + shiliID);
+            }
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        }
+
+        if (shiliID != null){
+            try {
+                /**
+                 * 更新表单数据「按照数据版本」
+                 * 注:非表单字段调整不开启
+                 * */
+
+                Map formMap = new HashMap<>();
+                formMap.put("radioField_mm5zuw4o", state);//EIC付款单状态
+                formMap.put("textField_mm5zuw4m", message);//EIC回传信息
+                String formDataJson = JSON.toJSONString(formMap);
+                System.out.println("mapJson:" + formDataJson);
+
+                com.aliyun.dingtalkyida_1_0.Client client = createClient();
+                com.aliyun.dingtalkyida_1_0.models.UpdateFormDataHeaders updateFormDataHeaders = new com.aliyun.dingtalkyida_1_0.models.UpdateFormDataHeaders();
+                updateFormDataHeaders.xAcsDingtalkAccessToken = accessToken;
+                com.aliyun.dingtalkyida_1_0.models.UpdateFormDataRequest updateFormDataRequest = new com.aliyun.dingtalkyida_1_0.models.UpdateFormDataRequest()
+                        .setSystemToken(appSecret)
+                        .setFormInstanceId(shiliID)
+                        .setUserId("yida_pub_account")
+                        .setUseLatestVersion(true)
+                        .setAppType(appType)
+                        .setUpdateFormDataJson(formDataJson);
+
+                client.updateFormDataWithOptions(updateFormDataRequest, updateFormDataHeaders, new RuntimeOptions());
+            } catch (TeaException err) {
+                if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                    // err 中含有 code 和 message 属性,可帮助开发定位问题
+                }
+            } catch (Exception _err) {
+                TeaException err = new TeaException(_err.getMessage(), _err);
+                if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                    // err 中含有 code 和 message 属性,可帮助开发定位问题
+                }
+            }
+        }
+
+        return shiliID;
+    }
+
+    /**
+     * 更新表单实例
+     * REMARK 接口文档地址:https://open.dingtalk.com/document/development/api-updateformdata-v2
+     *
+     * @param shiliID         数据实例id
+     * @param accessToken     钉钉accessToken
+     * @return jsonObject{}
+     */
+    public static void updateFormData(String shiliID, String formDataJson, String accessToken) {
+        try {
+            /**
+             * 更新表单数据「按照数据版本」
+             * 注:非表单字段调整不开启
+             * */
+
+            com.aliyun.dingtalkyida_1_0.Client client = createClient();
+            com.aliyun.dingtalkyida_1_0.models.UpdateFormDataHeaders updateFormDataHeaders = new com.aliyun.dingtalkyida_1_0.models.UpdateFormDataHeaders();
+            updateFormDataHeaders.xAcsDingtalkAccessToken = accessToken;
+            com.aliyun.dingtalkyida_1_0.models.UpdateFormDataRequest updateFormDataRequest = new com.aliyun.dingtalkyida_1_0.models.UpdateFormDataRequest()
+                    .setSystemToken(appSecret)
+                    .setFormInstanceId(shiliID)
+                    .setUserId("yida_pub_account")
+                    .setUseLatestVersion(true)
+                    .setAppType(appType)
+                    .setUpdateFormDataJson(formDataJson);
+
+            client.updateFormDataWithOptions(updateFormDataRequest, updateFormDataHeaders, new RuntimeOptions());
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+    }
+
+    /**
+     * 根据实例id查询表单实例数据
+     * REMARK 接口文档地址:https://open.dingtalk.com/document/development/api-getformdatabyid-v2
+     *
+     * @param shiliID         数据实例id
+     * @param accessToken     钉钉accessToken
+     * @return jsonObject{}
+     */
+    public static JSONObject getYidaFormData(String shiliID, String accessToken){
+
+        JSONObject jsonObject = null;
+        try {
+            com.aliyun.dingtalkyida_2_0.Client client = createClient2();
+            com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDHeaders getFormDataByIDHeaders = new com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDHeaders();
+            getFormDataByIDHeaders.xAcsDingtalkAccessToken = accessToken;
+            com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDRequest getFormDataByIDRequest = new com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDRequest()
+                    .setSystemToken(appSecret)
+                    .setUserId("yida_pub_account")
+                    .setAppType(appType);
+            com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDResponse formDataByIDWithOptions = client.getFormDataByIDWithOptions(shiliID, getFormDataByIDRequest, getFormDataByIDHeaders, new RuntimeOptions());
+            String s = JSON.toJSONString(formDataByIDWithOptions.getBody());
+            jsonObject = JSON.parseObject(s);
+//            System.out.println("获取宜搭表单数据,表单object对象:" + jsonObject);
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        }
+        System.out.println("获取宜搭表单数据,表单object对象:" + jsonObject);
+
+        return jsonObject;
+    }
+
+    /**
+     * 根据实例id查询表单实例明细组件的数据
+     * REMARK 接口文档地址:https://open.dingtalk.com/document/development/obtain-child-table-component-data
+     *
+     * @param formUuid        表单uuid
+     * @param shiliID         数据实例id
+     * @param tabeId          明细组件id
+     * @param accessToken     钉钉accessToken
+     * @return jsonObject{}
+     */
+    public static JSONArray getYidaMX(String formUuid, String shiliID, String tabeId, String accessToken){
+
+        int pageNumber = 1;
+        int pageSize = 50;
+
+        float nowNum = 0;
+        float totalCount = 0;
+        List listAll = new ArrayList();
+
+        do {
+            JSONObject jsonObject = null;
+            try {
+                com.aliyun.dingtalkyida_1_0.Client client = createClient();
+                com.aliyun.dingtalkyida_1_0.models.ListTableDataByFormInstanceIdTableIdHeaders listTableDataByFormInstanceIdTableIdHeaders = new com.aliyun.dingtalkyida_1_0.models.ListTableDataByFormInstanceIdTableIdHeaders();
+                listTableDataByFormInstanceIdTableIdHeaders.xAcsDingtalkAccessToken = accessToken;
+                com.aliyun.dingtalkyida_1_0.models.ListTableDataByFormInstanceIdTableIdRequest listTableDataByFormInstanceIdTableIdRequest = new com.aliyun.dingtalkyida_1_0.models.ListTableDataByFormInstanceIdTableIdRequest()
+                        .setFormUuid(formUuid)
+                        .setPageNumber(pageNumber)
+                        .setAppType(appType)
+                        .setSystemToken(appSecret)
+                        .setPageSize(pageSize)
+                        .setTableFieldId(tabeId)
+                        .setUserId("yida_pub_account");
+
+                ListTableDataByFormInstanceIdTableIdResponse listTableDataByFormInstanceIdTableIdResponse = client.listTableDataByFormInstanceIdTableIdWithOptions(shiliID, listTableDataByFormInstanceIdTableIdRequest, listTableDataByFormInstanceIdTableIdHeaders, new RuntimeOptions());
+                String s = JSON.toJSONString(listTableDataByFormInstanceIdTableIdResponse.getBody());
+                jsonObject = JSON.parseObject(s);
+            } catch (TeaException err) {
+                if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                    // err 中含有 code 和 message 属性,可帮助开发定位问题
+                }
+            } catch (Exception _err) {
+                TeaException err = new TeaException(_err.getMessage(), _err);
+                if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                    // err 中含有 code 和 message 属性,可帮助开发定位问题
+                }
+            }
+            System.out.println("获取宜搭表单数据,明细数据object对象:" + jsonObject);
+
+            JSONArray data = jsonObject.getJSONArray("data");
+            System.out.println("返回的宜搭明细列表data:" + data);
+            for (int j = 0; j < data.size(); j++) {
+                JSONObject jsonObject1 = data.getJSONObject(j);
+                listAll.add(jsonObject1);
+            }
+
+            totalCount = jsonObject.getFloat("totalCount");
+            System.out.println("明细总数:" + totalCount);
+            nowNum = pageNumber * pageSize;
+            System.out.println("当前分页应查询出来的总数:" + nowNum);
+            pageNumber += 1;
+            System.out.println("下次查询的分页:" + pageNumber);
+        }while (nowNum <= totalCount);
+
+        //封装所有的明细分页数据
+        JSONArray jsonArray = JSON.parseArray(listAll.toString());
+        System.out.println("封装所有的明细分页数据jsonArray:" + jsonArray);
+
+        return jsonArray;
+    }
+
+    /**
+     * 查询表单实例数据
+     * REMARK 接口文档地址:【查询表单实例数据】https://open.dingtalk.com/document/development/api-searchformdatas-v2
+     *
+     * @param formUuid        表单编码
+     * @param queryMapAllStr  封装查询的字段参数
+     * @param accessToken     钉钉accessToken
+     * @return jsonObject{}
+     */
+    public static JSONObject getQueryYidaForm(String formUuid, String queryMapAllStr, String accessToken){
+
+        JSONObject jsonObject = new JSONObject();
+
+        int size = 100;
+        int page = 1;
+        try {
+            com.aliyun.dingtalkyida_1_0.Client client = createClient();
+            com.aliyun.dingtalkyida_1_0.models.SearchFormDatasHeaders searchFormDatasHeaders = new com.aliyun.dingtalkyida_1_0.models.SearchFormDatasHeaders();
+            searchFormDatasHeaders.xAcsDingtalkAccessToken = accessToken;
+            com.aliyun.dingtalkyida_1_0.models.SearchFormDatasRequest searchFormDatasRequest = new com.aliyun.dingtalkyida_1_0.models.SearchFormDatasRequest()
+                    .setSystemToken(appSecret)
+
+                    .setSearchFieldJson(queryMapAllStr)
+
+                    .setCurrentPage(page)
+                    .setPageSize(size)
+                    .setFormUuid(formUuid)
+                    .setUserId("yida_pub_account")
+                    .setAppType(appType);
+
+            SearchFormDatasResponse searchFormDatasResponse = client.searchFormDatasWithOptions(searchFormDatasRequest, searchFormDatasHeaders, new RuntimeOptions());
+//            System.out.println(JSON.toJSONString(searchFormDatasResponse.getBody()));
+            String s = JSON.toJSONString(searchFormDatasResponse.getBody());
+            jsonObject = JSON.parseObject(s);
+            System.out.println("宜搭数据高级条件查询到接口object对象:" + jsonObject);
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+        return jsonObject;
+    }
+
+    /**
+     * 获取宜搭附件免登地址
+     * REMARK 接口文档地址:https://open.dingtalk.com/document/development/obtain-the-temporary-free-access-address-of-yixian-accessories
+     *
+     * @param url       宜搭附件地址
+     * @return String   临时免登地址URL
+     */
+    public static String getYidaTemporaryFileURL(String url, String accessToken) {
+        String fileUrl = "";
+        JSONObject jsonObject = null;
+        try {
+            com.aliyun.dingtalkyida_1_0.Client client = createClient();
+            com.aliyun.dingtalkyida_1_0.models.GetOpenUrlHeaders getOpenUrlHeaders = new com.aliyun.dingtalkyida_1_0.models.GetOpenUrlHeaders();
+            getOpenUrlHeaders.xAcsDingtalkAccessToken = accessToken;
+            com.aliyun.dingtalkyida_1_0.models.GetOpenUrlRequest getOpenUrlRequest = new com.aliyun.dingtalkyida_1_0.models.GetOpenUrlRequest()
+                    .setSystemToken(appSecret)
+                    .setFileUrl(url)//宜搭附件url
+                    .setUserId("yida_pub_account")
+                    .setTimeout(86400000L);//最大值: 86400000 , 即24小时。不填则默认60000,即1分钟。填0或者负数无效。
+
+            GetOpenUrlResponse appA1U3R89IHBD21BJZVX06 = client.getOpenUrlWithOptions(appType, getOpenUrlRequest, getOpenUrlHeaders, new RuntimeOptions());
+            String s = JSON.toJSONString(appA1U3R89IHBD21BJZVX06.getBody());
+            jsonObject = JSON.parseObject(s);
+            System.out.println("获取宜搭附件免登地址:" + jsonObject);
+
+            fileUrl = jsonObject.getString("result");
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+        System.out.println("获取宜搭附件免登地址:" + fileUrl);
+        return fileUrl;
+    }
+
+    /**
+     * 根据表单实例ID和子表组ID获取子表组件数据
+     * REMARK 接口文档地址:https://open.dingtalk.com/document/development/obtain-child-table-component-data
+     *
+     * @param shiliID         数据实例id
+     * @param tableField      表单明细id
+     * @param accessToken     钉钉accessToken
+     * @return jsonObject{}
+     */
+    public static JSONArray getYidaTable(String shiliID, String formId, String tableField, String accessToken){
+
+        int pageNumber = 1;
+        int pageSize = 50;
+
+        float nowNum = 0;
+        float totalCount = 0;
+        List listAll = new ArrayList();
+
+        do {
+            JSONObject jsonObject = null;
+            try {
+                com.aliyun.dingtalkyida_1_0.Client client = createClient();
+                com.aliyun.dingtalkyida_1_0.models.ListTableDataByFormInstanceIdTableIdHeaders listTableDataByFormInstanceIdTableIdHeaders = new com.aliyun.dingtalkyida_1_0.models.ListTableDataByFormInstanceIdTableIdHeaders();
+                listTableDataByFormInstanceIdTableIdHeaders.xAcsDingtalkAccessToken = accessToken;
+                com.aliyun.dingtalkyida_1_0.models.ListTableDataByFormInstanceIdTableIdRequest listTableDataByFormInstanceIdTableIdRequest = new com.aliyun.dingtalkyida_1_0.models.ListTableDataByFormInstanceIdTableIdRequest()
+                        .setFormUuid(formId)
+                        .setPageNumber(pageNumber)
+                        .setAppType(appType)
+                        .setSystemToken(appSecret)
+                        .setPageSize(pageSize)
+                        .setTableFieldId(tableField)
+                        .setUserId("yida_pub_account");
+
+                ListTableDataByFormInstanceIdTableIdResponse listTableDataByFormInstanceIdTableIdResponse = client.listTableDataByFormInstanceIdTableIdWithOptions(shiliID, listTableDataByFormInstanceIdTableIdRequest, listTableDataByFormInstanceIdTableIdHeaders, new RuntimeOptions());
+                String s = JSON.toJSONString(listTableDataByFormInstanceIdTableIdResponse.getBody());
+                jsonObject = JSON.parseObject(s);
+            } catch (TeaException err) {
+                if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                    // err 中含有 code 和 message 属性,可帮助开发定位问题
+                }
+            } catch (Exception _err) {
+                TeaException err = new TeaException(_err.getMessage(), _err);
+                if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                    // err 中含有 code 和 message 属性,可帮助开发定位问题
+                }
+            }
+            System.out.println("获取宜搭表单数据明细数据object对象:" + jsonObject);
+
+            JSONArray data = jsonObject.getJSONArray("data");
+            System.out.println("返回的宜搭明细列表data:" + data);
+            for (int j = 0; j < data.size(); j++) {
+                JSONObject jsonObject1 = data.getJSONObject(j);
+                listAll.add(jsonObject1);
+            }
+
+            totalCount = jsonObject.getFloat("totalCount");
+            System.out.println("明细总数:" + totalCount);
+            nowNum = pageNumber * pageSize;
+            System.out.println("当前分页应查询出来的总数:" + nowNum);
+            pageNumber += 1;
+            System.out.println("下次查询的分页:" + pageNumber);
+        }while (nowNum <= totalCount);
+
+        //封装所有的明细分页数据
+        JSONArray jsonArray = JSON.parseArray(listAll.toString());
+        System.out.println("封装所有的明细分页数据jsonArray:" + jsonArray);
+
+        return jsonArray;
+    }
+}

+ 40 - 0
src/main/java/com/muzhi/tianpai/common/time/BeijingToUtcConverter.java

@@ -0,0 +1,40 @@
+package com.muzhi.tianpai.common.time;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+
+public class BeijingToUtcConverter {
+
+    /**
+     * 将指定的北京时间日期(如 "2026-01-30")转换为对应的 UTC 起止时间(ISO 8601 + Z)
+     *
+     * @param beijingDateStr 格式:yyyy-MM-dd(表示北京时间的某一天)
+     * @return String[2] {startUtc, endUtc}
+     */
+    public static String[] convertBeijingDateToUtcRange(String beijingDateStr) {
+        // 1. 解析输入日期(视为北京时间的日期)
+        LocalDate date = LocalDate.parse(beijingDateStr);
+
+        // 2. 构造北京时间的 00:00:00 和 23:59:59
+        LocalDateTime startBeijing = date.atStartOfDay(); // 00:00:00
+        LocalDateTime endBeijing = date.atTime(23, 59, 59); // 23:59:59
+
+        // 3. 指定时区为北京
+        ZoneId beijingZone = ZoneId.of("Asia/Shanghai");
+
+        // 4. 转换为 UTC Instant
+        Instant startUtc = startBeijing.atZone(beijingZone).toInstant();
+        Instant endUtc = endBeijing.atZone(beijingZone).toInstant();
+
+        // 5. 格式化为 ISO 8601(带 Z)
+        DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
+
+        return new String[]{
+                formatter.format(startUtc),
+                formatter.format(endUtc)
+        };
+    }
+}

+ 122 - 0
src/main/java/com/muzhi/tianpai/common/utilityclass/PackageFlie.java

@@ -0,0 +1,122 @@
+package com.muzhi.tianpai.common.utilityclass;
+
+import com.github.junrar.Archive;
+import com.github.junrar.rarfile.FileHeader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class PackageFlie {
+
+    // === 从 ZIP 流中提取 PDF ===
+    public static String extractPdfFromZipStream(InputStream zipStream, Path targetDir) throws IOException {
+
+        String flieName = null;
+
+        Files.createDirectories(targetDir);
+        try (ZipInputStream zis = new ZipInputStream(zipStream)) {
+            ZipEntry entry;
+            while ((entry = zis.getNextEntry()) != null) {
+                String name = entry.getName().toLowerCase();
+                if (entry.isDirectory() || !name.endsWith(".pdf")) {
+                    zis.closeEntry();
+                    continue;
+                }
+
+                // 防路径穿越:只取文件名
+                String safeName = Paths.get(entry.getName()).getFileName().toString();
+                Path destPath = targetDir.resolve(safeName);
+
+                System.out.println("Found PDF: " + safeName);
+                flieName = safeName;
+
+                // 避免覆盖(可选)
+                if (Files.exists(destPath)) {
+                    System.out.println("Skip existing: " + safeName);
+                    zis.close();
+                    continue;
+                }
+
+                try (OutputStream os = Files.newOutputStream(destPath)) {
+                    byte[] buffer = new byte[8192];
+                    int len;
+                    while ((len = zis.read(buffer)) > 0) {
+                        os.write(buffer, 0, len);
+                    }
+                    System.out.println("✅ Extracted PDF: " + destPath);
+                }
+                zis.closeEntry();
+            }
+        }
+        return flieName;
+    }
+
+    // === 从 RAR 流中提取 PDF ===
+    public static String extractPdfFromRarStream(InputStream rarStream, Path targetDir) throws IOException {
+
+        String flieName = null;
+
+        Files.createDirectories(targetDir);
+
+        // junrar 需要 RandomAccessFile,但我们可以用 ByteArrayOutputStream 模拟
+        // 更简单方式:先写入临时文件(因 junrar 不支持纯 InputStream)
+        Path tempRar = Files.createTempFile("temp_", ".rar");
+        try {
+            Files.copy(rarStream, tempRar, StandardCopyOption.REPLACE_EXISTING);
+
+            try (Archive archive = new Archive(tempRar.toFile())) {
+                FileHeader fileHeader;
+                while ((fileHeader = archive.nextFileHeader()) != null) {
+                    String name = fileHeader.getFileNameString().toLowerCase();
+                    if (fileHeader.isDirectory() || !name.endsWith(".pdf")) {
+                        continue;
+                    }
+
+                    String safeName = Paths.get(fileHeader.getFileNameString()).getFileName().toString();
+                    Path destPath = targetDir.resolve(safeName);
+
+                    System.out.println("Found PDF: " + safeName);
+                    flieName = safeName;
+
+                    if (Files.exists(destPath)) {
+                        System.out.println("Skip existing: " + safeName);
+                        continue;
+                    }
+
+                    try (OutputStream os = Files.newOutputStream(destPath)) {
+                        archive.extractFile(fileHeader, os);
+                        System.out.println("✅ Extracted PDF: " + destPath);
+                    }
+                }
+            }catch (Exception e){
+                e.printStackTrace();
+            }
+        } finally {
+            // 清理临时文件
+            try {
+                Files.deleteIfExists(tempRar);
+            } catch (IOException e) {
+                System.err.println("Failed to delete temp RAR file: " + e.getMessage());
+            }
+        }
+        return flieName;
+    }
+
+    // === 从 Content-Disposition 或 URL 提取文件名 ===
+    public static String extractFileName(String contentDisposition, String url) {
+        if (contentDisposition != null && contentDisposition.contains("filename=")) {
+            String filename = contentDisposition.substring(contentDisposition.indexOf("filename=") + 9);
+            filename = filename.replaceAll("\"", "").trim();
+            return filename;
+        }
+        // 从 URL 最后一段取
+        return Paths.get(url).getFileName().toString();
+    }
+}

+ 48 - 0
src/main/java/com/muzhi/tianpai/controller/AccessTokenController.java

@@ -0,0 +1,48 @@
+package com.muzhi.tianpai.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @Author ZhangKan
+ * @Data 15:03
+ */
+
+@Api(description = "钉钉授权AccessToken")
+@RestController
+@RequestMapping("/dingservice")
+@CrossOrigin//解决跨域问题
+public class AccessTokenController {
+
+    @Autowired
+    private com.muzhi.tianpai.service.AccessTokenService AccessTokenService;
+
+    //获取钉钉中的access_token(钉钉应用)
+    @ApiOperation(value = "获取钉钉中的access_token")
+    @GetMapping("getAccessToken_YunpuPOC")
+    public String getAccessToken_YunpuPOC(){
+
+        String accessToken_yunpuPOC = AccessTokenService.getAccessToken_YunpuPOC();
+        return accessToken_yunpuPOC;
+    }
+
+    //获取钉钉中的access_token(钉钉应用)
+    @ApiOperation(value = "获取钉钉的access_token")
+    @GetMapping("getAccessToken")
+    public String getAccessToken(){
+
+        String accessToken = AccessTokenService.getAccessToken();
+        return accessToken;
+    }
+
+    //获取阿里企业邮箱的access_token
+    @ApiOperation(value = "获取阿里企业邮箱的access_token")
+    @GetMapping("getAlibabaMailboxAccessToken")
+    public String getAlibabaMailboxAccessToken(){
+
+        String mailboxAccessToken = AccessTokenService.getAlibabaMailboxAccessToken();
+        return mailboxAccessToken;
+    }
+}

+ 52 - 0
src/main/java/com/muzhi/tianpai/controller/DingTalkController.java

@@ -0,0 +1,52 @@
+package com.muzhi.tianpai.controller;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.muzhi.tianpai.service.DingTalkService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import com.muzhi.tianpai.common.R;
+
+
+@Api(description = "钉钉")
+@RestController
+@RequestMapping("/dingservice")
+@CrossOrigin//解决跨域问题
+public class DingTalkController {
+
+    @Autowired
+    private DingTalkService dingTalkService;
+
+    /**员工入职创建钉钉企业账号*/
+    @ApiOperation(value = "员工入职创建钉钉企业账号")
+    @PostMapping("createUserEntryAccountNumber")
+    public R createUserEntryAccountNumber(@RequestBody JSONObject obj){
+
+        R createUserEntryAccountNumber = dingTalkService.createUserEntryAccountNumber(obj);
+        return createUserEntryAccountNumber;
+    }
+
+    /**更新花名册信息*/
+    @ApiOperation(value = "更新花名册信息")
+    @PostMapping("updateHuamingce")
+    public R updateHuamingce(@RequestBody JSONObject obj){
+
+        R updateHuamingce = dingTalkService.updateHuamingce(obj);
+        return updateHuamingce;
+    }
+
+    /**
+     * 【钉钉OA连接器】
+     * 表单打开时加载
+     */
+    @ApiOperation(value = "【钉钉连接器使用】获取员工职级")
+    @GetMapping("getUserRank")
+    public R getUserRank(String userId){
+
+        R getUserRank = dingTalkService.getUserRank(userId);
+        return getUserRank;
+    }
+}

+ 17 - 0
src/main/java/com/muzhi/tianpai/service/AccessTokenService.java

@@ -0,0 +1,17 @@
+package com.muzhi.tianpai.service;
+
+/**
+ * @Author ZhangKan
+ * @Data 15:34
+ */
+public interface AccessTokenService {
+
+    //获取access_token的方法(钉钉应用)
+    String getAccessToken_YunpuPOC();
+
+    //获取access_token的方法(钉钉应用)
+    String getAccessToken();
+
+    //获取阿里企业邮箱的access_token
+    String getAlibabaMailboxAccessToken();
+}

+ 21 - 0
src/main/java/com/muzhi/tianpai/service/DingTalkService.java

@@ -0,0 +1,21 @@
+package com.muzhi.tianpai.service;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.muzhi.tianpai.common.R;
+
+/**
+ * @Author ZhangKan
+ * @Data 15:34
+ */
+public interface DingTalkService {
+
+    /**员工入职创建钉钉企业账号*/
+    R createUserEntryAccountNumber(JSONObject obj);
+
+    /**更新花名册信息*/
+    R updateHuamingce(JSONObject obj);
+
+    /**【钉钉连接器使用】获取员工职级*/
+    R getUserRank(String userId);
+}

+ 109 - 0
src/main/java/com/muzhi/tianpai/service/impl/AccessTokenServiceImpl.java

@@ -0,0 +1,109 @@
+package com.muzhi.tianpai.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.request.OapiGettokenRequest;
+import com.dingtalk.api.response.OapiGettokenResponse;
+import com.muzhi.tianpai.service.AccessTokenService;
+import com.taobao.api.ApiException;
+import okhttp3.*;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * @Author ZhangKan
+ * @Data 15:39
+ */
+
+@Service
+public class AccessTokenServiceImpl implements AccessTokenService {
+
+    /**钉钉应用_云璞POC*/
+    //APPKEY
+    private static String APPKEY_YunpuPOC = "";
+    //APPSECRET
+    private static String APPSECRET_YunpuPOC = "";
+    //AgenthId:1963716187
+    //corpId:
+
+    public static String DingtalkAccessToken_YunpuPOC = null;
+
+    //获取dingtalk授权码(钉钉应用)
+    @Override
+    public String getAccessToken_YunpuPOC() {
+        DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
+        OapiGettokenRequest request = new OapiGettokenRequest();
+        request.setAppkey(APPKEY_YunpuPOC);
+        request.setAppsecret(APPSECRET_YunpuPOC);
+        request.setHttpMethod("GET");
+        try {
+            OapiGettokenResponse response = client.execute(request);
+            DingtalkAccessToken_YunpuPOC = response.getAccessToken();
+            System.out.println("云璞POC钉钉AccessToken:" + DingtalkAccessToken_YunpuPOC);
+        } catch (ApiException e) {
+            e.printStackTrace();
+        }
+        return DingtalkAccessToken_YunpuPOC;
+    }
+
+    /**钉钉应用*/
+    //APPKEY
+    private static String APPKEY = "ding4fn4g3vzglff2bxy";
+    //APPSECRET
+    private static String APPSECRET = "9IebAXjr0wSUfSj5LWq5nlf85GbJmC9hFSWAWq78YEOg1VEwhFV8D-i8cZWX5EWS";
+    //AgenthId:
+    //corpId:
+
+    public static String DingtalkAccessToken = null;
+
+    //获取dingtalk授权码(钉钉应用)
+    @Override
+    public String getAccessToken() {
+        DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
+        OapiGettokenRequest request = new OapiGettokenRequest();
+        request.setAppkey(APPKEY);
+        request.setAppsecret(APPSECRET);
+        request.setHttpMethod("GET");
+        try {
+            OapiGettokenResponse response = client.execute(request);
+            DingtalkAccessToken = response.getAccessToken();
+            System.out.println("天派钉钉AccessToken:" + DingtalkAccessToken);
+        } catch (ApiException e) {
+            e.printStackTrace();
+        }
+        return DingtalkAccessToken;
+    }
+
+    //获取阿里企业邮箱的access_token
+    @Override
+    public String getAlibabaMailboxAccessToken() {
+
+        String accessToken = null;
+
+        String grant_type = "client_credentials";//固定值
+        String client_id = "";//应用id
+        String client_secret = "";//应用密钥
+        String bodyStr = "grant_type="+ grant_type +"&client_id="+ client_id +"&client_secret=" + client_secret;
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
+            RequestBody body = RequestBody.create(mediaType, bodyStr);
+            Request request = new Request.Builder()
+                    .url("https://alimail-cn.aliyuncs.com/oauth2/v2.0/token")
+                    .method("POST", body)
+                    .addHeader("Content-Type", "application/x-www-form-urlencoded")
+                    .build();
+            Response response = client.newCall(request).execute();
+            String resStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(resStr);
+            accessToken = jsonObject.getString("access_token");
+            System.out.println("阿里企业邮箱的access_token:" + accessToken);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+
+        return accessToken;
+    }
+}

+ 176 - 0
src/main/java/com/muzhi/tianpai/service/impl/DingTalkServiceImpl.java

@@ -0,0 +1,176 @@
+package com.muzhi.tianpai.service.impl;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.dingtalkhrm_1_0.models.GetEmployeeRosterByFieldResponse;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.OapiV2UserGetRequest;
+import com.dingtalk.api.response.OapiV2UserGetResponse;
+import com.muzhi.tianpai.common.dingtalk.DingtalkEmployee;
+import com.muzhi.tianpai.common.dingtalk.DingtalkUserV2;
+import com.muzhi.tianpai.common.dingtalk.RosterSystemField;
+import com.muzhi.tianpai.common.dingtalkyida.YiDaFormDataV2;
+import com.muzhi.tianpai.service.AccessTokenService;
+import com.muzhi.tianpai.service.DingTalkService;
+import com.muzhi.tianpai.common.R;
+import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
+import com.taobao.api.ApiException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.*;
+import java.lang.reflect.Method;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+
+@Service
+@Slf4j
+public class DingTalkServiceImpl implements DingTalkService {
+
+    @Autowired
+    private AccessTokenService accessTokenService;
+
+    public static com.aliyun.dingtalkhrm_1_0.Client createClient() throws Exception {
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        return new com.aliyun.dingtalkhrm_1_0.Client(config);
+    }
+
+    /**
+     * 员工入职创建钉钉企业账号
+     */
+    @Override
+    public R createUserEntryAccountNumber(JSONObject obj) {
+        log.info("进入方法,开始获取宜搭数据......");
+        log.info("接收宜搭传参--->" + obj);
+        log.info("开始进行创建钉钉企业账号===");
+
+        /*调用钉钉创建企业账号*/
+        JSONObject dingtalkEnterpirseUser = DingtalkUserV2.createDingtalkEnterpirseUser(obj.toJSONString(), accessTokenService.getAccessToken());
+        log.info("钉钉创建企业账号结果--->" + dingtalkEnterpirseUser);
+        if (dingtalkEnterpirseUser.getInteger("errcode") != 0) {
+            log.error("创建钉钉企业账号异常!");
+            return R.error().message("创建钉钉企业账号异常!").data(dingtalkEnterpirseUser);
+        }else {
+            log.info("钉钉创建企业账号成功!");
+            return R.ok().message("钉钉创建企业账号成功!").data(dingtalkEnterpirseUser);
+        }
+    }
+
+    /**
+     * 员工入职创建钉钉企业账号
+     */
+    @Override
+    public R updateHuamingce(JSONObject obj) {
+        log.info("进入方法,开始获取宜搭数据......");
+        log.info("接收宜搭传参--->" + obj);
+        log.info("开始进行更新该人员钉钉花名册字段值===");
+
+        //更新匹配字段
+        String userid = obj.getString("userid");//钉钉userId
+
+        for (Map.Entry<String, Object> entry : obj.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue() == null
+                    ? null
+                    : entry.getValue().toString();
+
+            System.out.println("key:" + key);
+            System.out.println("value:" + value);
+
+            if (!key.equals("userid")){
+                //通过key去匹配字段
+                Optional<RosterSystemField> optional = RosterSystemField.findByName(key);
+                if (optional.isPresent()) {
+                    RosterSystemField field = optional.get();
+
+                    System.out.println("字段名称:" + field.getFieldName());
+                    System.out.println("字段 code:" + field.getFieldId());
+                    System.out.println("字段 ID:" + field.getFieldCode());
+
+                    /*调用钉钉更新花名册*/
+                    log.info("开始进行更新该人员钉钉花名册字段值===");
+                    DingtalkEmployee.updateHuamingce(userid, field.getFieldId(), field.getFieldCode(), value, accessTokenService.getAccessToken());
+                    log.info("钉钉更新花名册结束!开始下一个字段===");
+                }
+            }
+        }
+
+
+        log.info("钉钉更新花名册结束!");
+        return R.ok().message("钉钉更新花名册结束!");
+    }
+
+    /**
+     * 【钉钉OA连接器】
+     * 表单打开时加载
+     */
+    @Override
+    public R getUserRank(String userId){
+        log.info("【钉钉OA连接器】表单打开时加载...");
+        System.out.println("连接器传参钉钉员工userId:" + userId);
+
+        String userRank = null;
+        String targetFieldCode = "419d7ae9-1d42-4b55-aae4-7248afb1b936";//钉钉花名册自定义字段职级id
+        try {
+            com.aliyun.dingtalkhrm_1_0.Client client = createClient();
+            com.aliyun.dingtalkhrm_1_0.models.GetEmployeeRosterByFieldHeaders getEmployeeRosterByFieldHeaders = new com.aliyun.dingtalkhrm_1_0.models.GetEmployeeRosterByFieldHeaders();
+            getEmployeeRosterByFieldHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+            com.aliyun.dingtalkhrm_1_0.models.GetEmployeeRosterByFieldRequest getEmployeeRosterByFieldRequest = new com.aliyun.dingtalkhrm_1_0.models.GetEmployeeRosterByFieldRequest()
+                    .setUserIdList(java.util.Arrays.asList(
+                            userId
+                    ))
+                    .setAppAgentId(4574746416L)
+                    .setText2SelectConvert(true);
+            GetEmployeeRosterByFieldResponse employeeRosterByFieldWithOptions = client.getEmployeeRosterByFieldWithOptions(getEmployeeRosterByFieldRequest, getEmployeeRosterByFieldHeaders, new RuntimeOptions());
+            String jsonString = JSON.toJSONString(employeeRosterByFieldWithOptions.getBody());
+            JSONObject jsonObject = JSON.parseObject(jsonString);
+//            System.out.println("获取钉钉花名册接口返回:" + jsonObject);
+            JSONArray jsonArray = jsonObject.getJSONArray("result");
+            JSONObject jsonObject1 = jsonArray.getJSONObject(0);
+            JSONArray jsonArray1 = jsonObject1.getJSONArray("fieldDataList");
+            userRank = jsonArray1.stream()
+                    .map(JSONObject.class::cast)
+                    .filter(field -> targetFieldCode.equals(field.getString("fieldCode")))
+                    .flatMap(field -> field.getJSONArray("fieldValueList").stream())
+                    .map(JSONObject.class::cast)
+                    .map(item -> item.getString("label"))
+                    .findFirst()
+                    .orElse(null);
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+        System.out.println("该人员钉钉花名册职级:" + userRank);
+        Map map = new HashMap();
+        map.put("userId", userId);
+        map.put("userRank", userRank);
+
+        if (userRank == null){
+            return R.error().message("未获取该人员钉钉花名册职级!").data(map);
+        }else {
+            return R.ok().message("已获取该人员钉钉花名册职级!").data(map);
+        }
+    }
+}

+ 72 - 0
src/main/resources/application.properties

@@ -0,0 +1,72 @@
+server.port=8104
+server.servlet.context-path=/api/tianpai
+
+server.tomcat.uri-encoding=UTF-8
+
+#YIDA
+myapp.yida.appType=APP_LP7KZD7WKJH43RK4HW5G
+myapp.yida.appSecret=0MD66P814X16B2I4NZL8V5QHN5RG23YLTFMPMKE8
+
+#ALIYUN
+myapp.aliyun.accessKeyId=
+myapp.aliyun.accessKeySecret=
+
+#TENGXUN
+myapp.tengxun.secretId=
+myapp.tengxun.secretKey=
+myapp.tengxun.region=
+
+#EIC
+myapp.eic.eicUrlAddress=
+
+#?????????
+ribbon:
+    ReadTimeoutMillis: 5000
+    ConnectTimeoutMillis: 5000
+
+## 服务名
+#spring.application.name=simaner-yd
+## 环境设置:dev、test、prod(开发环境、测试环境、生产环境)
+#spring.profiles.active=dev
+
+# mysql数据库连接
+# 本地测试数据库
+#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+#spring.datasource.url=jdbc:mysql://localhost:3306/dingtalk?serverTimezone=GMT%2B8
+#spring.datasource.username=root
+#spring.datasource.password=123456
+
+#定时同步
+
+#配置日志,当前为默认的控制台输出,也可以用log4j
+#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
+
+#控制台显示sql
+#spring.jpa.show-sql=true
+#更新或者创建数据表结构
+#spring.jpa.hibernate.ddl-auto=update
+
+#返回json的全局时间格式
+spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=GMT+8
+#配置mapper xml文件的路径
+#mybatis-plus.mapper-locations=classpath:com/muzhi/lz/mapper/xml/*.xml
+
+#mybatis-plus.config-location=classpath:mybatis/mybatis-config.xml
+
+#禁用缓存
+spring.thymeleaf.cache=false
+#spring.thymeleaf.mode= LEGACYHTML5
+#spring.resources.chain.strategy.content.enabled=true
+#spring.resources.chain.strategy.content.paths=/**
+#spring.thymeleaf.check-template = true
+#spring.thymeleaf.servlet.content-type=text/html
+#spring.thymeleaf.enabled = true
+#spring.thymeleaf.encoding = UTF-8
+#spring.thymeleaf.prefix = classpath:/static/
+#spring.thymeleaf.suffix = .html
+
+#定义日期提交的格式(日期的格式化):SpingMVC将页面提交的值需要转换为指定类型;默认的是yyyy/MM/dd
+#spring.mvc.date-format=yyyy-MM-dd
+
+spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/templates/

+ 752 - 0
src/test/java/com/muzhi/tianpai/APITest/Test1.java

@@ -0,0 +1,752 @@
+package com.muzhi.tianpai.APITest;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.dingtalkhrm_1_0.models.GetEmployeeRosterByFieldResponse;
+import com.aliyun.dingtalkyida_1_0.models.GetOpenUrlResponse;
+import com.aliyun.dingtalkyida_1_0.models.SearchFormDatasResponse;
+import com.aliyun.dingtalkyida_2_0.models.CreateOrUpdateFormDataResponse;
+import com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDResponse;
+import com.aliyun.ocr_api20210707.Client;
+import com.aliyun.ocr_api20210707.models.RecognizeAllTextRequest;
+import com.aliyun.ocr_api20210707.models.RecognizeAllTextResponse;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teaopenapi.models.Config;
+import com.aliyun.teautil.models.RuntimeOptions;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.request.OapiGettokenRequest;
+import com.dingtalk.api.response.OapiGettokenResponse;
+import com.muzhi.tianpai.common.dingtalkyida.YiDaFormData;
+import com.muzhi.tianpai.service.AccessTokenService;
+import com.taobao.api.ApiException;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+@SpringBootTest
+@Slf4j
+public class Test1 {
+
+    @Autowired
+    private AccessTokenService accessTokenService;
+
+    public static com.aliyun.dingtalkworkflow_1_0.Client createClient() throws Exception {
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        return new com.aliyun.dingtalkworkflow_1_0.Client(config);
+    }
+
+    public static com.aliyun.dingtalkyida_2_0.Client createClient2() throws Exception {
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        return new com.aliyun.dingtalkyida_2_0.Client(config);
+    }
+
+    public static com.aliyun.dingtalkyida_1_0.Client createClient3() throws Exception {
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        return new com.aliyun.dingtalkyida_1_0.Client(config);
+    }
+
+    public static com.aliyun.dingtalkhrm_1_0.Client createClient4() throws Exception {
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        return new com.aliyun.dingtalkhrm_1_0.Client(config);
+    }
+
+    @Test
+    public void test1(){
+        JSONObject jsonObject2 = null;
+        try {
+            com.aliyun.dingtalkyida_2_0.Client client = createClient2();
+            com.aliyun.dingtalkyida_2_0.models.CreateOrUpdateFormDataHeaders createOrUpdateFormDataHeaders = new com.aliyun.dingtalkyida_2_0.models.CreateOrUpdateFormDataHeaders();
+            createOrUpdateFormDataHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+            com.aliyun.dingtalkyida_2_0.models.CreateOrUpdateFormDataRequest createOrUpdateFormDataRequest = new com.aliyun.dingtalkyida_2_0.models.CreateOrUpdateFormDataRequest()
+                    .setSystemToken("JB966E91RQZUKPPFFNOKBB86G3GH38VSN42AMGV")
+                    .setFormUuid("FORM-BD31E2C07C7448DD931F686A5D881E96DQV8")
+                    .setUserId("yida_pub_account")
+                    .setSearchCondition("[{\"componentName\":\"TextField\",\"type\":\"TEXT\",\"value\":\"BC2000029\",\"key\":\"textField_maw7b9k0\",\"operator\":\"eq\"}]")
+                    .setAppType("APP_FJSLK9BJ20K1NL3G2PUW")
+                    .setFormDataJson("{\"selectField_lqesy4t1\":\"D\",\"textField_maw7b9k0\":\"BC2000029\",\"textField_lqesy4sy\":\"KETTLER Alu-Rad GmbH\",\"textField_mh8j17hy\":\"袁铷玥\"}");
+
+            CreateOrUpdateFormDataResponse orUpdateFormDataWithOptions = client.createOrUpdateFormDataWithOptions(createOrUpdateFormDataRequest, createOrUpdateFormDataHeaders, new RuntimeOptions());
+            String s = JSON.toJSONString(orUpdateFormDataWithOptions.getBody());
+            jsonObject2 = JSON.parseObject(s);
+            System.out.println("【创建或更新】表单object对象:" + jsonObject2);
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+    }
+
+    @Test
+    public void test2(){
+        try {
+            com.aliyun.dingtalkyida_2_0.Client client = createClient2();
+            com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDHeaders getFormDataByIDHeaders = new com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDHeaders();
+            getFormDataByIDHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+            com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDRequest getFormDataByIDRequest = new com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDRequest()
+                    .setSystemToken("JB966E91RQZUKPPFFNOKBB86G3GH38VSN42AMGV")
+                    .setUserId("yida_pub_account")
+                    .setAppType("APP_FJSLK9BJ20K1NL3G2PUW");
+
+            GetFormDataByIDResponse formDataByIDWithOptions = client.getFormDataByIDWithOptions("FINST-R8E66381FS30I83TKR2W4DO14O8V35TY6QBHM5NH", getFormDataByIDRequest, getFormDataByIDHeaders, new RuntimeOptions());
+            String s = JSON.toJSONString(formDataByIDWithOptions.getBody());
+            JSONObject jsonObject = JSON.parseObject(s);
+            System.out.println("【查询】表单object对象:" + jsonObject);
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        }
+    }
+
+    @Test
+    public void test3(){
+        try {
+            com.aliyun.dingtalkyida_2_0.Client client = createClient2();
+            com.aliyun.dingtalkyida_2_0.models.UpdateSubTableHeaders updateSubTableHeaders = new com.aliyun.dingtalkyida_2_0.models.UpdateSubTableHeaders();
+            updateSubTableHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+            com.aliyun.dingtalkyida_2_0.models.UpdateSubTableRequest updateSubTableRequest = new com.aliyun.dingtalkyida_2_0.models.UpdateSubTableRequest()
+                    .setSystemToken("JB966E91RQZUKPPFFNOKBB86G3GH38VSN42AMGV")
+                    .setFormInstanceId("FINST-R8E66381FS30I83TKR2W4DO14O8V35TY6QBHM5NH")
+                    .setUserId("yida_pub_account")
+                    .setTableFieldIds("tableField_lqesy4t4")
+                    .setUpdateFormDataJson("{     \"tableField_lqesy4t4\": [         {             \"textField_makozait\": \"0519-68198299\",             \"textField_lqesy4t7\": \"\",             \"textField_lqesy4t6\": \"\"         }     ] }")
+                    .setAppType("APP_FJSLK9BJ20K1NL3G2PUW");
+
+            client.updateSubTableWithOptions(updateSubTableRequest, updateSubTableHeaders, new com.aliyun.teautil.models.RuntimeOptions());
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        }
+    }
+
+    @Test
+    public void test4(){
+        try {
+            com.aliyun.dingtalkyida_1_0.Client client = createClient3();
+            com.aliyun.dingtalkyida_1_0.models.GetOpenUrlHeaders getOpenUrlHeaders = new com.aliyun.dingtalkyida_1_0.models.GetOpenUrlHeaders();
+            getOpenUrlHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+            com.aliyun.dingtalkyida_1_0.models.GetOpenUrlRequest getOpenUrlRequest = new com.aliyun.dingtalkyida_1_0.models.GetOpenUrlRequest()
+                    .setSystemToken("333")
+                    .setFileUrl("555")
+                    .setUserId("444");
+
+            client.getOpenUrlWithOptions("222", getOpenUrlRequest, getOpenUrlHeaders, new com.aliyun.teautil.models.RuntimeOptions());
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+    }
+
+    @Test
+    public void test5(){
+
+        String url = "https://www.aliwork.com/ossFileHandle?appType=APP_XV6ZGK1162TI1Z1FE17D&fileName=APP_XV6ZGK1162TI1Z1FE17D_MTU3MzExMjA5XzMyRDY2Q0ExNEY2MUtLT0RIMDRIWTNDM0pLRjIySVhLOUtSSU1NNA$$.xml&instId=&type=download";
+
+        String fileUrl = "";
+        JSONObject jsonObject = null;
+        try {
+            com.aliyun.dingtalkyida_1_0.Client client = createClient3();
+            com.aliyun.dingtalkyida_1_0.models.GetOpenUrlHeaders getOpenUrlHeaders = new com.aliyun.dingtalkyida_1_0.models.GetOpenUrlHeaders();
+            getOpenUrlHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+            com.aliyun.dingtalkyida_1_0.models.GetOpenUrlRequest getOpenUrlRequest = new com.aliyun.dingtalkyida_1_0.models.GetOpenUrlRequest()
+                    .setSystemToken("USC66V61YG8ZMBM5E8JQFBZQ6W0G3V1L6J0GMXE")
+                    .setFileUrl(url)//宜搭附件url
+                    .setUserId("yida_pub_account")
+                    .setTimeout(86400000L);//最大值: 86400000 , 即24小时。不填则默认60000,即1分钟。填0或者负数无效。
+
+            GetOpenUrlResponse appA1U3R89IHBD21BJZVX06 = client.getOpenUrlWithOptions("APP_XV6ZGK1162TI1Z1FE17D", getOpenUrlRequest, getOpenUrlHeaders, new RuntimeOptions());
+            String s = JSON.toJSONString(appA1U3R89IHBD21BJZVX06.getBody());
+            jsonObject = JSON.parseObject(s);
+            System.out.println("获取宜搭附件免登地址:" + jsonObject);
+
+            fileUrl = jsonObject.getString("result");
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+        System.out.println("获取宜搭附件免登地址:" + fileUrl);
+    }
+
+    @Test
+    public void test6(){
+        String instanceId = "FINST-L3D66Z8197X1EX7NHEMMCDBF7JJV26UGWBZJMOWH";
+        JSONObject jsonObject = null;
+        try {
+            com.aliyun.dingtalkyida_2_0.Client client = createClient2();
+            com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDHeaders getFormDataByIDHeaders = new com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDHeaders();
+            getFormDataByIDHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+            com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDRequest getFormDataByIDRequest = new com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDRequest()
+                    .setSystemToken("USC66V61YG8ZMBM5E8JQFBZQ6W0G3V1L6J0GMXE")
+                    .setUserId("yida_pub_account")
+                    .setAppType("APP_XV6ZGK1162TI1Z1FE17D");
+            GetFormDataByIDResponse formDataByIDWithOptions = client.getFormDataByIDWithOptions(instanceId, getFormDataByIDRequest, getFormDataByIDHeaders, new RuntimeOptions());
+            String s = JSON.toJSONString(formDataByIDWithOptions.getBody());
+            jsonObject = JSON.parseObject(s);
+//            System.out.println("宜搭实例id查询【工时填报】接口object对象:" + jsonObject);
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+        JSONObject formData = jsonObject.getJSONObject("formData");
+        System.out.println("宜搭实例id查询【工时填报】接口formData对象:" + formData);
+        if (formData != null){
+            //填报单号
+            String tianbaodanhao = formData.getString("serialNumberField_lmiltutx");
+            System.out.println("填报单号:" + tianbaodanhao);
+            //填报日期
+            long tianbaoriqi = formData.getLong("dateField_mgt1sluh");
+            System.out.println("填报日期:" + tianbaoriqi);
+            //填报总工时
+            float tianbaozonggongshi = formData.getFloat("numberField_llunq7e2") != null ? formData.getFloat("numberField_llunq7e2") : 0;
+            System.out.println("填报总工时:" + tianbaozonggongshi);
+            //填报人员userid
+            String tianbaorenyuanUserId = formData.getJSONArray("employeeField_mhiv3pjk_id").getString(0);
+            System.out.println("填报人员userid:" + tianbaorenyuanUserId);
+            //当日已填报总工时
+            float dangriyitianbaozonggongshi = formData.getFloat("numberField_mhiun888") != null ? formData.getFloat("numberField_mhiun888") : 0;
+            System.out.println("填报人员userid:" + dangriyitianbaozonggongshi);
+            //工时填报明细
+            JSONArray jsonArray = formData.getJSONArray("tableField_mhbs93eh");
+            for (int i = 0; i < jsonArray.size(); i++) {
+                JSONObject jsonObject1 = jsonArray.getJSONObject(i);
+                //工时填报明细-总工时
+                float mx_zonggongshi = jsonObject1.getFloat("numberField_mhbs93e3") != null ? jsonObject1.getFloat("numberField_mhbs93e3") : 0;
+                System.out.println("工时填报明细-总工时:" + mx_zonggongshi);
+                //工时填报明细-已填报工时
+                float mx_yitianbaogongshi = jsonObject1.getFloat("numberField_mhbs93e4") != null ? jsonObject1.getFloat("numberField_mhbs93e4") : 0;
+                System.out.println("工时填报明细-已填报工时:" + mx_yitianbaogongshi);
+                //工时填报明细-填报总工时
+                float mx_tianbaozonggongshi = jsonObject1.getFloat("numberField_mhbs93e5") != null ? jsonObject1.getFloat("numberField_mhbs93e5") : 0;
+                System.out.println("工时填报明细-填报总工时:" + mx_tianbaozonggongshi);
+
+                //工时填报明细-类型
+                String mx_leixing = jsonObject1.getString("radioField_mhbs93e6");
+                System.out.println("工时填报明细-类型:" + mx_leixing);
+                //工时填报明细-负责人userId
+                String mx_fuzerenUserId = jsonObject1.getJSONArray("employeeField_mhbs93e9_id").getString(0);
+                System.out.println("工时填报明细-负责人userId:" + mx_fuzerenUserId);
+                //工时填报明细-编号
+                String mx_bianhao = jsonObject1.getString("textField_mhbs93ea");
+                System.out.println("工时填报明细-编号:" + mx_bianhao);
+                //工时填报明细-客户名称
+                String mx_kehumingcheng = jsonObject1.getString("textField_mhbs93eb");
+                System.out.println("工时填报明细-客户名称:" + mx_kehumingcheng);
+                //工时填报明细-工时
+                float mx_gongshi = jsonObject1.getFloat("numberField_mhbs93ec") != null ? jsonObject1.getFloat("numberField_mhbs93ec") : 0;
+                System.out.println("工时填报明细-工时:" + mx_gongshi);
+                //工时填报明细-工作内容
+                String mx_gongzuoneirong = jsonObject1.getString("textareaField_mhbs93ed");
+                System.out.println("工时填报明细-工作内容:" + mx_gongzuoneirong);
+                //工时填报明细-备注
+                String mx_beizhu = jsonObject1.getString("textareaField_mhbs93ee");
+                System.out.println("工时填报明细-备注:" + mx_beizhu);
+            }
+        }
+    }
+
+    //TODO 指定代收邮箱地址
+    public static String emailAddress = "postmaster@fnsx.xyz";
+
+    /*阿里邮箱列出邮件的全部附件*/
+    @Test
+    public void test000() {
+        String emailId = "DzzzzzzNqZC";//邮件id
+        JSONArray jsonArray = new JSONArray();
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            Request request = new Request.Builder()
+                    .url("https://alimail-cn.aliyuncs.com/v2/users/"+ emailAddress +"/messages/"+ emailId +"/attachments")
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + accessTokenService.getAlibabaMailboxAccessToken())
+                    .build();
+            Response response = client.newCall(request).execute();
+            String resStr = response.body().string();
+            JSONObject jsonObject = JSONObject.parseObject(resStr);
+            jsonArray = jsonObject.getJSONArray("attachments") != null ? jsonObject.getJSONArray("attachments") : new JSONArray();
+            System.out.println("列出邮件的发票附件列表[]:" + jsonArray);
+        }catch (Exception e){
+            log.error("该邮件的发票附件获取异常:{}", e);
+        }
+    }
+
+    /*阿里邮箱文件下载*/
+    @Test
+    public void test7() {
+
+        String emailId = "DzzzzzzNqZC";//邮件id
+        String emailFileId = "8319daf3-7388-49f9-87fc-2bd8818179f5";//邮件附件id
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            Request request = new Request.Builder()
+                    .url("https://alimail-cn.aliyuncs.com/v2/users/"+ emailAddress +"/messages/"+ emailId +"/attachments/"+ emailFileId +"/$value")
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + accessTokenService.getAlibabaMailboxAccessToken())
+                    .build();
+            try (Response response = client.newCall(request).execute()) {
+                if (!response.isSuccessful()) {
+                    System.err.println("下载失败: " + response.code() + " - " + response.message());
+                    // 打印错误 body(可能是 JSON 错误信息)
+                    if (response.body() != null) {
+                        System.err.println("Error body: " + response.body().string());
+                    }
+                    return;
+                }
+
+                String destinationDir = "invoice"; // Linux 路径
+
+                //获取当前日期,用于创建文件保存目录中增加当前日期的文件夹作区分
+                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");//设置日期格式 yyyy-MM-dd HH:mm:ss
+                String format = df.format(new Date());
+                System.out.println(format);// new Date()为获取当前系统时间
+
+                String fileName = "测试发票-001.pdf";
+
+                String path = destinationDir + File.separatorChar + format + File.separatorChar + fileName;// 指定存放位置,此处使用的是全局变量的文件名称
+
+                // 确保目录存在
+                Path dir = Paths.get(path);
+                Files.createDirectories(dir);
+
+                // 构建完整文件路径
+                Path filePath = dir.resolve("测试发票-001.pdf");
+
+                // 从响应流直接写入文件(高效,不加载全内存)
+                try (ResponseBody body = response.body()) {
+                    if (body == null) {
+                        throw new IOException("响应体为空");
+                    }
+                    Files.copy(body.byteStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
+                }
+                System.out.println("✅ 附件已保存至: " + filePath.toAbsolutePath());
+            } catch (IOException e) {
+                System.err.println("下载异常: " + e.getMessage());
+                e.printStackTrace();
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test8() {
+
+        String email = "test@qq.com";
+        String fileName = "测试发票-001.pdf";
+        float fileSize = 1024;
+        String fileNewUrl = "https://poc.cloudpure.cn/changguchuan/invoice/2026-02-05/25317000003383776933-pdf.pdf";
+
+        Map formMap = new HashMap<>();
+        formMap.put("textField_ml9154m5", fileName);//发票文件名称
+        formMap.put("textField_ml7hkag3", email);//收件人邮箱地址
+        formMap.put("textField_ml9154ma", fileNewUrl);//文件转存url地址
+
+        Map fileMap = new HashMap();
+        fileMap.put("previewUrl",fileNewUrl);
+        fileMap.put("size",fileSize);
+        fileMap.put("name",fileName);
+        fileMap.put("downloadUrl",fileNewUrl);
+        fileMap.put("url",fileNewUrl);
+        List fileList = new ArrayList();
+        fileList.add(fileMap);
+        formMap.put("attachmentField_ml9154mb",fileList);
+
+        String formDataJson = JSON.toJSONString(formMap);
+        System.out.println("mapJson:" + formDataJson);
+
+        Map searchMap = new HashMap<>();
+        searchMap.put("key", "textField_ml9154m5");
+        searchMap.put("value", fileName);
+        searchMap.put("type", "TEXT");
+        searchMap.put("operator", "eq");
+        searchMap.put("componentName", "TextField");
+        List searchList = new ArrayList();
+        searchList.add(searchMap);
+        String searchCondition = JSON.toJSONString(searchList);
+        System.out.println("查询条件searchCondition:" + searchCondition);
+        /*新增或更新到宜搭*/
+        log.info("开始新增或更新到宜搭「票夹」表单--->");
+        YiDaFormData.CreateOrUpdateFormData(
+                "FORM-036D3F7CB4CC44E7AE7846A789C3DA65EOJN",
+                searchCondition,
+                formDataJson,
+                accessTokenService.getAccessToken());
+    }
+
+    /**钉钉应用*/
+    //APPKEY
+    private static String APPKEY = "dingzupxqpzcczaaddcw";
+    //APPSECRET
+    private static String APPSECRET = "EpND2Oj4adk2Od9ZwAszSYGC8WL_OWYPq1Uj7b5jR1DI6rCsWTdxzvqTHM4H3Dz6";
+    //AgenthId:
+    //corpId:
+
+    public static String DingtalkAccessToken = null;
+    @Test
+    public void test9() {
+
+        DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
+        OapiGettokenRequest request = new OapiGettokenRequest();
+        request.setAppkey(APPKEY);
+        request.setAppsecret(APPSECRET);
+        request.setHttpMethod("GET");
+        try {
+            OapiGettokenResponse response = client.execute(request);
+            DingtalkAccessToken = response.getAccessToken();
+            System.out.println("乐檬钉钉AccessToken:" + DingtalkAccessToken);
+        } catch (ApiException e) {
+            e.printStackTrace();
+        }
+    }
+
+//    /**
+//     * <b>description</b> :
+//     * <p>使用凭据初始化账号Client</p>
+//     * @return Client
+//     *
+//     * @throws Exception
+//     */
+//    public static com.aliyun.ocr_api20210707.Client createClientOCR() throws Exception {
+//        // 工程代码建议使用更安全的无AK方式,凭据配置方式请参见:https://help.aliyun.com/document_detail/378657.html。
+//        com.aliyun.credentials.Client credential = new com.aliyun.credentials.Client();
+//        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
+//                .setCredential(credential);
+//        // Endpoint 请参考 https://api.aliyun.com/product/ocr-api
+//        config.endpoint = "ocr-api.cn-hangzhou.aliyuncs.com";
+//        return new com.aliyun.ocr_api20210707.Client(config);
+//    }
+//
+//    //OCR识别
+//    @Test
+//    public void test10() {
+//        try {
+//            com.aliyun.ocr_api20210707.Client client = createClientOCR();
+//            com.aliyun.ocr_api20210707.models.RecognizeAllTextRequest recognizeAllTextRequest = new com.aliyun.ocr_api20210707.models.RecognizeAllTextRequest()
+//                    .setType("Advanced")
+//                    .setUrl("https://poc.cloudpure.cn/changguchuan/invoice/2026-02-05/26329216804000097816.pdf");
+//            com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
+//            com.aliyun.ocr_api20210707.models.RecognizeAllTextResponse resp = client.recognizeAllTextWithOptions(recognizeAllTextRequest, runtime);
+//            System.out.println(new com.google.gson.Gson().toJson(resp));
+//        } catch (TeaException error) {
+//            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
+//            // 错误 message
+//            System.out.println(error.getMessage());
+//            // 诊断地址
+//            System.out.println(error.getData().get("Recommend"));
+//        } catch (Exception _error) {
+//            TeaException error = new TeaException(_error.getMessage(), _error);
+//            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
+//            // 错误 message
+//            System.out.println(error.getMessage());
+//            // 诊断地址
+//            System.out.println(error.getData().get("Recommend"));
+//        }
+//    }
+
+    /**
+     * 创建 OCR 客户端
+     * @param accessKeyId 阿里云 AccessKey ID
+     * @param accessKeySecret 阿里云 AccessKey Secret
+     * @return Client
+     * @throws Exception
+     */
+    public static Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
+        Config config = new Config()
+                // 必填,您的 AccessKey ID
+                .setAccessKeyId(accessKeyId)
+                // 必填,您的 AccessKey Secret
+                .setAccessKeySecret(accessKeySecret);
+        // Endpoint (地域节点),OCR 统一识别通常使用 cn-shanghai
+        config.endpoint = "ocr-api.cn-hangzhou.aliyuncs.com";
+        return new Client(config);
+    }
+
+    @Test
+    public void test11() {
+        // 1. 替换为你的阿里云密钥
+        String accessKeyId = "LTAI5tRXcYFKYuBV3EriBAKm";
+        String accessKeySecret = "dWPdO22hTwkH4UoK734J53FLwwvBsY";
+
+        try {
+            // 2. 初始化客户端
+            Client client = createClient(accessKeyId, accessKeySecret);
+
+            // 3. 构建请求对象
+            RecognizeAllTextRequest request = new RecognizeAllTextRequest();
+
+            // 设置请求参数
+            // 方式一:传图片 URL (推荐,简单)
+            request.setUrl("https://poc.cloudpure.cn/changguchuan/invoice/2026-02-05/26329216804000097816.pdf");
+
+            // 方式二:传本地图片二进制流 (需要自行读取文件)
+            // byte[] imageBytes = Files.readAllBytes(Paths.get("path/to/your/image.jpg"));
+            // request.setBody(TeaUtil.toReadableByteChannel(imageBytes));
+
+            // 必填参数:指定图片类型
+            // Advanced = 通用文字识别高精版
+            request.setType("Advanced");
+
+            // 可选参数:是否返回坐标
+            // request.setOutputCoordinate("points");
+
+            // 4. 设置运行时选项(如超时)
+            RuntimeOptions runtime = new RuntimeOptions();
+            runtime.readTimeout = 10000; // 10秒读超时
+            runtime.connectTimeout = 5000; // 5秒连接超时
+
+            // 5. 发起调用
+            // RecognizeAllText 是 API 方法名
+            RecognizeAllTextResponse response = client.recognizeAllTextWithOptions(request, runtime);
+
+            // 6. 处理结果
+            if (response.getBody() != null && response.getBody().getData() != null) {
+                System.out.println("识别成功!");
+                System.out.println("图片宽度: " + response.getBody().getData().getWidth());
+                System.out.println("图片高度: " + response.getBody().getData().getHeight());
+                System.out.println("识别内容: " + response.getBody().getData().getContent());
+
+                // 详细结果在 SubImages 或 BlockInfo 中
+                // 具体结构请参考文档中的 JSON 结构
+            } else {
+                System.out.println("识别失败,无数据返回");
+            }
+
+        } catch (TeaException e) {
+            // 阿里云 SDK 异常
+            System.err.println("ErrorCode: " + e.getCode());
+            System.err.println("Message: " + e.getMessage());
+            System.err.println("Data: " + e.getData());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test12() {
+
+    }
+
+    @Test
+    public void test13(){
+        String accessToken = null;
+
+        String grant_type = "client_credentials";//固定值
+        String client_id = "tlHiqXxwX6jVFEKs";//应用id
+        String client_secret = "BI9O3IvEBJVYuKC55hEuxkwkpHTdheh8PUSipwBreHkEWq0xDc0rOUQ9pM9lktuE";//应用密钥
+        String bodyStr = "grant_type="+ grant_type +"&client_id="+ client_id +"&client_secret=" + client_secret;
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
+            RequestBody body = RequestBody.create(mediaType, bodyStr);
+            Request request = new Request.Builder()
+                    .url("https://alimail-cn.aliyuncs.com/oauth2/v2.0/token")
+                    .method("POST", body)
+                    .addHeader("Content-Type", "application/x-www-form-urlencoded")
+                    .build();
+            Response response = client.newCall(request).execute();
+            String resStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(resStr);
+            accessToken = jsonObject.getString("access_token");
+            System.out.println("阿里企业邮箱的access_token:" + accessToken);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test14(){
+
+        String email = "zhuxiaojian@cnlemon.net";
+
+        //查询表单实例数据
+        Map mapAll = new HashMap();
+        //邮箱地址
+        mapAll.put("textField_mob3qfjb", email);
+        String mapAllStr = JSON.toJSONString(mapAll);
+        System.out.println("封装查询的字段参数:" + mapAllStr);
+
+        JSONObject jsonObject = new JSONObject();
+
+        int size = 100;
+        int page = 1;
+        try {
+            com.aliyun.dingtalkyida_1_0.Client client = createClient3();
+            com.aliyun.dingtalkyida_1_0.models.SearchFormDatasHeaders searchFormDatasHeaders = new com.aliyun.dingtalkyida_1_0.models.SearchFormDatasHeaders();
+            searchFormDatasHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+            com.aliyun.dingtalkyida_1_0.models.SearchFormDatasRequest searchFormDatasRequest = new com.aliyun.dingtalkyida_1_0.models.SearchFormDatasRequest()
+                    .setSystemToken("3Z966P91XA1FTYVQ7UTVLDTGJ4613UJUR7ONLT7C")
+
+                    .setSearchFieldJson(mapAllStr)
+
+                    .setCurrentPage(page)
+                    .setPageSize(size)
+                    .setFormUuid("FORM-E489F03D805D4E169BA6A8755B4E72E6TN6N")
+                    .setUserId("yida_pub_account")
+                    .setAppType("APP_YPF7S88D9O139KRVM83J");
+
+            SearchFormDatasResponse searchFormDatasResponse = client.searchFormDatasWithOptions(searchFormDatasRequest, searchFormDatasHeaders, new RuntimeOptions());
+//            System.out.println(JSON.toJSONString(searchFormDatasResponse.getBody()));
+            String s = JSON.toJSONString(searchFormDatasResponse.getBody());
+            jsonObject = JSON.parseObject(s);
+            System.out.println("宜搭数据高级条件查询到接口object对象:" + jsonObject);
+            JSONArray data = jsonObject.getJSONArray("data");
+            if (data.isEmpty()) {
+                System.out.println("根据邮箱地址查询【用户信息】返回数据为空!");
+            } else {
+                JSONObject formData = data.getJSONObject(0).getJSONObject("formData");
+                //成员userId
+                String emailUserId = formData.getJSONArray("employeeField_mm1g5703_id").getString(0);
+                System.out.println("成员userId===>" + emailUserId);
+            }
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+    }
+
+    @Test
+    public void test15(){
+
+        String shiliID = "FINST-GOD667C13RN57V1XM8AC995FKPW42K8Q495PMNOA";
+
+        JSONObject jsonObject = null;
+        try {
+            com.aliyun.dingtalkyida_2_0.Client client = createClient2();
+            com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDHeaders getFormDataByIDHeaders = new com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDHeaders();
+            getFormDataByIDHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken_YunpuPOC();
+            com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDRequest getFormDataByIDRequest = new com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDRequest()
+                    .setSystemToken("R9666ED1IRN57Y9RGFEGQ742QXID2MH74S3PM6Y4")
+                    .setUserId("yida_pub_account")
+                    .setAppType("APP_IJXSY7YQ7O3U23759WV8");
+            GetFormDataByIDResponse formDataByIDWithOptions = client.getFormDataByIDWithOptions(shiliID, getFormDataByIDRequest, getFormDataByIDHeaders, new RuntimeOptions());
+            String s = JSON.toJSONString(formDataByIDWithOptions.getBody());
+            jsonObject = JSON.parseObject(s);
+//            System.out.println("获取宜搭表单数据,表单object对象:" + jsonObject);
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        }
+        System.out.println("获取宜搭表单数据,表单object对象:" + jsonObject);
+    }
+
+    @Test
+    public void test16(){
+
+        String userId = "011033176057927940";
+
+        String userRank = null;
+        String targetFieldCode = "419d7ae9-1d42-4b55-aae4-7248afb1b936";//钉钉花名册自定义字段职级id
+        try {
+            com.aliyun.dingtalkhrm_1_0.Client client = createClient4();
+            com.aliyun.dingtalkhrm_1_0.models.GetEmployeeRosterByFieldHeaders getEmployeeRosterByFieldHeaders = new com.aliyun.dingtalkhrm_1_0.models.GetEmployeeRosterByFieldHeaders();
+            getEmployeeRosterByFieldHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+            com.aliyun.dingtalkhrm_1_0.models.GetEmployeeRosterByFieldRequest getEmployeeRosterByFieldRequest = new com.aliyun.dingtalkhrm_1_0.models.GetEmployeeRosterByFieldRequest()
+                    .setUserIdList(java.util.Arrays.asList(
+                            userId
+                    ))
+                    .setAppAgentId(4574746416L)
+                    .setText2SelectConvert(true);
+            GetEmployeeRosterByFieldResponse employeeRosterByFieldWithOptions = client.getEmployeeRosterByFieldWithOptions(getEmployeeRosterByFieldRequest, getEmployeeRosterByFieldHeaders, new RuntimeOptions());
+            String jsonString = JSON.toJSONString(employeeRosterByFieldWithOptions.getBody());
+            JSONObject jsonObject = JSON.parseObject(jsonString);
+            System.out.println("获取钉钉花名册接口返回:" + jsonObject);
+            JSONArray jsonArray = jsonObject.getJSONArray("result");
+            JSONObject jsonObject1 = jsonArray.getJSONObject(0);
+            JSONArray jsonArray1 = jsonObject1.getJSONArray("fieldDataList");
+            userRank = jsonArray1.stream()
+                    .map(JSONObject.class::cast)
+                    .filter(field -> targetFieldCode.equals(field.getString("fieldCode")))
+                    .flatMap(field -> field.getJSONArray("fieldValueList").stream())
+                    .map(JSONObject.class::cast)
+                    .map(item -> item.getString("label"))
+                    .findFirst()
+                    .orElse(null);
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+        }
+        System.out.println("该人员钉钉花名册职级:" + userRank);
+    }
+}

+ 13 - 0
src/test/java/com/muzhi/tianpai/TestApplicationTests.java

@@ -0,0 +1,13 @@
+package com.muzhi.tianpai;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class TestApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}