Procházet zdrojové kódy

整理并迁移 shuchan 项目

zhangkan před 3 týdny
rodič
revize
5a8fe3f624
30 změnil soubory, kde provedl 3441 přidání a 6544 odebrání
  1. 4 0
      .gitignore
  2. 0 0
      README.md
  3. 0 43
      main/java/com/muzhi/huanshi/controller/DingOaController.java
  4. 0 14
      main/java/com/muzhi/huanshi/service/AccessTokenService.java
  5. 0 14
      main/java/com/muzhi/huanshi/service/DingOaService.java
  6. 0 2921
      main/java/com/muzhi/huanshi/service/impl/DingOaServiceImpl.java
  7. 407 0
      pom.xml
  8. 4 4
      main/java/com/muzhi/huanshi/HuanshiApplication.java
  9. 77 77
      main/java/com/muzhi/huanshi/common/R.java
  10. 12 12
      main/java/com/muzhi/huanshi/common/ResultCode.java
  11. 120 0
      src/main/java/com/muzhi/shuchan/common/dingtalkyida/YiDaFormData.java
  12. 37 0
      src/main/java/com/muzhi/shuchan/common/fastjson/Util.java
  13. 218 0
      src/main/java/com/muzhi/shuchan/common/teambition/Idmap.java
  14. 387 0
      src/main/java/com/muzhi/shuchan/common/teambition/Project.java
  15. 36 0
      src/main/java/com/muzhi/shuchan/common/universal/Time.java
  16. 39 43
      main/java/com/muzhi/huanshi/controller/AccessTokenController.java
  17. 39 0
      src/main/java/com/muzhi/shuchan/controller/DingTalkController.java
  18. 46 0
      src/main/java/com/muzhi/shuchan/controller/TeambitionController.java
  19. 36 0
      src/main/java/com/muzhi/shuchan/dingtalk/dingtalkWebhook.java
  20. 40 0
      src/main/java/com/muzhi/shuchan/entity/InvoiceItem.java
  21. 14 0
      src/main/java/com/muzhi/shuchan/service/AccessTokenService.java
  22. 18 0
      src/main/java/com/muzhi/shuchan/service/DingTalkService.java
  23. 22 0
      src/main/java/com/muzhi/shuchan/service/TeambitionService.java
  24. 89 86
      main/java/com/muzhi/huanshi/service/impl/AccessTokenServiceImpl.java
  25. 651 0
      src/main/java/com/muzhi/shuchan/service/impl/DingTalkServiceImpl.java
  26. 584 0
      src/main/java/com/muzhi/shuchan/service/impl/TeambitionServiceImpl.java
  27. 14 8
      main/resources/application.properties
  28. 546 0
      src/test/java/com/muzhi/shuchan/APITest/Test1.java
  29. 1 1
      test/java/com/muzhi/huanshi/TestApplicationTests.java
  30. 0 3321
      test/java/com/muzhi/huanshi/APITest/Test1.java

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+.DS_Store
+.idea/
+target/
+*.iml

+ 0 - 0
README.md


+ 0 - 43
main/java/com/muzhi/huanshi/controller/DingOaController.java

@@ -1,43 +0,0 @@
-package com.muzhi.huanshi.controller;
-
-import com.muzhi.huanshi.service.DingOaService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.web.bind.annotation.*;
-
-
-@Api(description = "钉钉")
-@RestController
-@RequestMapping("/dingservice")
-//@CrossOrigin//解决跨域问题
-public class DingOaController {
-
-    @Autowired
-    private DingOaService dingOaService;
-
-    //获取钉钉人员-同步至宜搭人员信息表
-    @ApiOperation(value = "获取钉钉人员-同步至宜搭人员信息表")
-    @PostMapping("addDingdingRenyuan")
-//    @Scheduled(cron = "0 10 23 * * ?")//每天23:10触发(也就是凌晨23点10分触发)
-//    @Scheduled(cron = "0 0/10 * * * ?")//每10分钟执行一次
-//    @Scheduled(cron = "0 */10 * * * ?")//每隔10分钟执行一次
-    public String getKaoqin(){
-
-        String addDingdingRenyuan = dingOaService.addDingdingRenyuan();
-        return addDingdingRenyuan;
-    }
-
-    //统计钉钉考勤、请假、TB项目/任务工时
-    @ApiOperation(value = "统计钉钉考勤、请假、TB项目/任务工时表")
-    @PostMapping("allTongji")
-//    @Scheduled(cron = "0 30 10 * * ?")//每天01:30触发(也就是凌晨01点30分触发)
-//    @Scheduled(cron = "0 0/10 * * * ?")//每10分钟执行一次
-//    @Scheduled(cron = "0 */10 * * * ?")//每隔10分钟执行一次
-    public String allTongji(int type){
-
-        String allTongji = dingOaService.allTongji(type);
-        return allTongji;
-    }
-}

+ 0 - 14
main/java/com/muzhi/huanshi/service/AccessTokenService.java

@@ -1,14 +0,0 @@
-package com.muzhi.huanshi.service;
-
-/**
- * @Author ZhangKan
- * @Data 15:34
- */
-public interface AccessTokenService {
-
-    //获取access_token的方法(环世)
-    String getAccessToken();
-
-    //获取access_token的方法(环世TB应用)
-    String getAppToken();
-}

+ 0 - 14
main/java/com/muzhi/huanshi/service/DingOaService.java

@@ -1,14 +0,0 @@
-package com.muzhi.huanshi.service;
-
-/**
- * @Author ZhangKan
- * @Data 15:34
- */
-public interface DingOaService {
-
-    //获取钉钉人员-同步至宜搭人员信息表
-    String addDingdingRenyuan();
-
-    //统计钉钉考勤、请假、TB项目/任务工时
-    String allTongji(int type);
-}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 2921
main/java/com/muzhi/huanshi/service/impl/DingOaServiceImpl.java


+ 407 - 0
pom.xml

@@ -0,0 +1,407 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.3.3.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.muzhi.shuchan</groupId>
+    <artifactId>springboot</artifactId>
+<!--    <packaging>jar</packaging>-->
+    <version>shuchan</version>
+    <name>shuchan</name>
+    <description>Demo project for Spring Boot</description>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <fastjson.version>1.2.33</fastjson.version>
+        <gson.version>2.8.2</gson.version>
+        <json.version>20170516</json.version>
+        <poi.version>3.17</poi.version>
+        <jodatime.version>2.10.1</jodatime.version>
+        <swagger.version>2.7.0</swagger.version>
+        <commons-fileupload.version>1.3.1</commons-fileupload.version>
+        <!--        <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>-->
+        <!-- 布局功能的支持程序 thymeleaf3主程序 layout2以上版本-->
+        <!--        <thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version>-->
+    </properties>
+
+    <repositories>
+        <repository>
+            <id>sonatype-nexus-staging</id>
+            <name>Sonatype Nexus Staging</name>
+            <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>true</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <!-- JAXB API (Java 11+ 需要手动添加) -->
+        <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>2.3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jaxb</groupId>
+            <artifactId>jaxb-runtime</artifactId>
+            <version>2.3.3</version>
+        </dependency>
+
+        <!-- 用于发送 HTTP 请求 (Java 11+ 可选,也可用内置 HttpClient) -->
+        <!-- 如果你用 Java 8,推荐用 Apache HttpClient -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.14</version>
+        </dependency>
+
+        <!-- Apache HttpClient (Java 8 最常用的 HTTP 客户端) -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.14</version>
+        </dependency>
+
+        <!-- JAXB API (Java 8 内置,但显式声明避免问题) -->
+        <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>2.3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jaxb</groupId>
+            <artifactId>jaxb-runtime</artifactId>
+            <version>2.3.3</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>3.4.0</version>
+        </dependency>
+
+        <!-- httpClient -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
+            <version>4.4.13</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <version>3.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>4.4.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.konghq</groupId>
+            <artifactId>unirest-java</artifactId>
+            <version>2.3.17</version>
+        </dependency>
+
+<!--        <dependency>-->
+<!--            <groupId>org.slf4j</groupId>-->
+<!--            <artifactId>slf4j-api</artifactId>-->
+<!--        </dependency>-->
+
+<!--        <dependency>-->
+<!--            <groupId>commons-io</groupId>-->
+<!--            <artifactId>commons-io</artifactId>-->
+<!--        </dependency>-->
+
+        <!--json 组件 -->
+<!--        <dependency>-->
+<!--            <groupId>com.alibaba</groupId>-->
+<!--            <artifactId>fastjson</artifactId>-->
+<!--            <scope>provided</scope>-->
+<!--        </dependency>-->
+
+        <!--        <dependency>-->
+        <!--            <groupId>org.apache.cxf</groupId>-->
+        <!--            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>-->
+        <!--            <version>3.3.1</version>-->
+        <!--        </dependency>-->
+
+        <!--        <dependency>-->
+        <!--            <groupId>org.springframework.boot</groupId>-->
+        <!--            <artifactId>spring-boot-starter-thymeleaf</artifactId>-->
+        <!--&lt;!&ndash;            <version>2.3.3.RELEASE</version>&ndash;&gt;-->
+        <!--        </dependency>-->
+
+        <!--钉钉相关-->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>alibaba-dingtalk-service-sdk</artifactId>
+            <version>2.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dingtalk</artifactId>
+            <version>2.2.34</version>
+        </dependency>
+
+        <!--json相关-->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>${fastjson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.json</groupId>
+            <artifactId>json</artifactId>
+            <version>${json.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>${gson.version}</version>
+        </dependency>
+
+        <!--        <dependency>-->
+        <!--            <groupId>net.sf.json-lib</groupId>-->
+        <!--            <artifactId>json-lib</artifactId>-->
+        <!--            <version>2.4</version>-->
+        <!--        </dependency>-->
+
+
+        <!-- JSONObject对象依赖的jar包 -->
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+            <version>1.9.3</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <version>3.2.1</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.6</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.ezmorph</groupId>
+            <artifactId>ezmorph</artifactId>
+            <version>1.0.6</version>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.json-lib</groupId>
+            <artifactId>json-lib</artifactId>
+            <version>2.2.3</version>
+            <classifier>jdk15</classifier><!-- 指定jdk版本 -->
+        </dependency>
+
+
+        <!--xls-->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+        <!--xlsx-->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+        <!--文件上传-->
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>${commons-fileupload.version}</version>
+        </dependency>
+
+        <!--        <dependency>-->
+        <!--            <groupId>org.springframework.boot</groupId>-->
+        <!--            <artifactId>spring-boot-starter-thymeleaf</artifactId>-->
+        <!--        </dependency>-->
+
+        <!--引入web模块-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <version>2.3.3.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+            <version>1.18.10</version>
+        </dependency>
+
+        <!--MD5加密-->
+        <dependency>
+            <groupId>org.apache.directory.studio</groupId>
+            <artifactId>org.apache.commons.codec</artifactId>
+            <version>1.8</version>
+        </dependency>
+
+        <!--Map缓存方法:可设置Map中的Entry在一段时间后自动过期-->
+        <dependency>
+            <groupId>net.jodah</groupId>
+            <artifactId>expiringmap</artifactId>
+            <version>0.5.8</version>
+        </dependency>
+
+<!--        &lt;!&ndash;mybatis-plus数据库连接&ndash;&gt;-->
+<!--        <dependency>-->
+<!--            <groupId>com.baomidou</groupId>-->
+<!--            <artifactId>mybatis-plus-boot-starter</artifactId>-->
+<!--            <version>3.2.0</version>-->
+<!--        </dependency>-->
+<!--        &lt;!&ndash;引入mysql:数据库驱动&ndash;&gt;-->
+<!--        <dependency>-->
+<!--            <groupId>mysql</groupId>-->
+<!--            <artifactId>mysql-connector-java</artifactId>-->
+<!--            <scope>runtime</scope>-->
+<!--        </dependency>-->
+<!--        &lt;!&ndash;引入druid数据源&ndash;&gt;-->
+<!--        &lt;!&ndash; https://mvnrepository.com/artifact/com.alibaba/druid &ndash;&gt;-->
+<!--        <dependency>-->
+<!--            <groupId>com.alibaba</groupId>-->
+<!--            <artifactId>druid</artifactId>-->
+<!--            <version>1.1.9</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>com.alibaba</groupId>-->
+<!--            <artifactId>druid-spring-boot-starter</artifactId>-->
+<!--            <version>1.1.9</version>-->
+<!--        </dependency>-->
+
+        <!--引入jpa模块-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+
+        <!--日期时间工具-->
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+            <version>${jodatime.version}</version>
+        </dependency>
+
+        <!--日期工具-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>4.6.8</version>
+        </dependency>
+
+        <!--log4j-->
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>1.2.17</version>
+        </dependency>
+
+        <!--swagger-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>${swagger.version}</version>
+        </dependency>
+        <!--swagger ui-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>${swagger.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.vintage</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <fork>true</fork>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>utf8</encoding>
+                </configuration>
+            </plugin>
+
+            <!--打包跳过测试-->
+<!--            <plugin>-->
+<!--                <groupId>org.apache.maven.plugins</groupId>-->
+<!--                <artifactId>maven-surefire-plugin</artifactId>-->
+<!--                <version>2.4.2</version>-->
+<!--                <configuration>-->
+<!--                    <skipTests>true</skipTests>-->
+<!--                </configuration>-->
+<!--            </plugin>-->
+        </plugins>
+
+        <!--映射mapper中对应的xml配置文件,如果没有mapper.xml文件则将下列注释-->
+<!--        <resources>-->
+<!--            <resource>-->
+<!--                <directory>src/main/java</directory>-->
+<!--                <includes>-->
+<!--                    <include>**/*.xml</include>-->
+
+<!--                    <include>**/*.properties</include>-->
+<!--                    <include>**/*.tld</include>-->
+<!--                    <include>**/*.xls</include>-->
+<!--                    <include>**/*.xlsx</include>-->
+<!--                </includes>-->
+<!--                <filtering>false</filtering>-->
+<!--            </resource>-->
+<!--        </resources>-->
+    </build>
+
+</project>

+ 4 - 4
main/java/com/muzhi/huanshi/HuanshiApplication.java

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

+ 77 - 77
main/java/com/muzhi/huanshi/common/R.java

@@ -1,77 +1,77 @@
-package com.muzhi.huanshi.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;
-    }
-}
+package com.muzhi.shuchan.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 - 12
main/java/com/muzhi/huanshi/common/ResultCode.java

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

+ 120 - 0
src/main/java/com/muzhi/shuchan/common/dingtalkyida/YiDaFormData.java

@@ -0,0 +1,120 @@
+package com.muzhi.shuchan.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 com.muzhi.shuchan.common.teambition.Project;
+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 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 void 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 属性,可帮助开发定位问题
+            }
+        }
+
+        try {
+            //新增或更新的表单实例id
+            String slid = jsonObject.getJSONArray("result").getString(0);
+            System.out.println("新增或更新的表单实例id:" + slid);
+
+            /**
+             * 更新表单数据「按照数据版本」
+             * 注:非表单字段调整不开启
+             * */
+
+            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(slid)
+                    .setUserId("yida_pub_account")
+                    .setUseLatestVersion(true)
+                    .setAppType(appType)
+                    .setUpdateFormDataJson(formDataJson);
+
+            client.updateFormDataWithOptions(updateFormDataRequest, updateFormDataHeaders, 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 属性,可帮助开发定位问题
+            }
+        }
+    }
+}

+ 37 - 0
src/main/java/com/muzhi/shuchan/common/fastjson/Util.java

@@ -0,0 +1,37 @@
+package com.muzhi.shuchan.common.fastjson;
+
+import com.alibaba.fastjson.JSON;
+
+import java.util.List;
+
+public class Util {
+
+    /**
+     * 获取数组内单个字符串内的文本
+     *
+     * @param strList 格式:["xxx"]
+     * @return String[0] 数组内的文本
+     */
+    public static String getStringToList(String strList) {
+        List<String> list = JSON.parseArray(strList, String.class);
+
+        String str = list.get(0);
+        System.out.println("获取到的数组内文本: " + str);
+        return str;
+    }
+
+    /**
+     * 获取数组内指定下标的字符串文本
+     *
+     * @param strList 格式:["xxx1","xxx2"]
+     * @param index 数组下标
+     * @return String[index] 数组内的文本
+     */
+    public static String getStringToIndexList(String strList, int index) {
+        List<String> list = JSON.parseArray(strList, String.class);
+
+        String str = list.get(index);
+        System.out.println("获取到的数组内文本: " + str);
+        return str;
+    }
+}

+ 218 - 0
src/main/java/com/muzhi/shuchan/common/teambition/Idmap.java

@@ -0,0 +1,218 @@
+package com.muzhi.shuchan.common.teambition;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@Component
+public class Idmap {
+
+    private static String tbOrganizationId;
+
+    // 创建一个非静态的 Setter 方法,加上 @Value
+    // Spring 启动时会调用这个方法,将配置文件值传入
+    @Value("${myapp.teambition.tbOrganizationId}")
+    public void setTbOrganizationId(String value) {
+        Idmap.tbOrganizationId = value; // 赋值给静态变量
+    }
+
+    /**
+     * 钉钉 userId 查询 Teambition userId
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/66792847912d20d3b5160f10
+     *
+     * @param dingUserIds 钉钉用户ID
+     * @param token 令牌
+     *
+     * @return String[tbUserId] 项目id
+     */
+    public static String getTbUserId(String dingUserIds, String token) {
+
+
+        int code = 0;
+        JSONArray result = null;
+        String tbUserId = null;
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            RequestBody body = RequestBody.create(mediaType, "");
+            Request request = new Request.Builder()
+                    .url("https://open.teambition.com/api/idmap/dingtalk/getTbUserId?dingUserIds=" + dingUserIds)
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + token)
+                    .addHeader("X-Tenant-Id", tbOrganizationId)
+                    .addHeader("X-Tenant-Type", "organization")
+                    .build();
+            Response response = client.newCall(request).execute();
+
+            String bodyStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject);
+            code = jsonObject.getInteger("code");
+            System.out.println("TB获取人员接口返回状态码:" + code);
+
+            result = jsonObject.getJSONArray("result");
+            System.out.println("TB获取人员接口返回result:" + result);
+            if (result.size() > 0 && result != null && !result.isEmpty()){
+                tbUserId = result.getJSONObject(0).getString("tbUserId");
+            }
+        }catch (IOException e){
+            e.printStackTrace();
+        }
+        System.out.println("根据钉钉userid匹配的TBuserId:" + tbUserId);
+
+        return tbUserId;
+    }
+
+    /**
+     * 根据钉钉部门ID查询Teambition部门ID
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/66792847912d20d3b5160f10
+     *
+     * @param dingTeamId 钉钉部门ID
+     * @param token 令牌
+     *
+     * @return String[teamId] tb部门id
+     */
+    public static String getTeamIdByDingTeamId(String dingTeamId, String token) {
+
+        int code = 0;
+        JSONArray teamIds = null;
+        String teamId = null;
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            RequestBody body = RequestBody.create(mediaType, "");
+            Request request = new Request.Builder()
+                    .url("https://open.teambition.com/api/org/teams/getTeamIdByDingTeamId?dingTeamId=" + dingTeamId)
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + token)
+                    .addHeader("X-Tenant-Id", tbOrganizationId)
+                    .addHeader("X-Tenant-Type", "organization")
+                    .build();
+            Response response = client.newCall(request).execute();
+
+            String bodyStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject);
+            code = jsonObject.getInteger("code");
+            System.out.println("TB获取部门id接口返回状态码:" + code);
+
+            JSONObject result = jsonObject.getJSONObject("result");
+            System.out.println("TB获取部门id接口返回result:" + result);
+            teamIds = result.getJSONArray("teamIds");
+            if (teamIds.size() > 0 && teamIds != null && !teamIds.isEmpty()){
+                teamId = teamIds.getString(0);
+            }
+        }catch (IOException e){
+            e.printStackTrace();
+        }
+        System.out.println("根据钉钉部门id匹配的TB部门id:" + teamId);
+
+        return teamId;
+    }
+
+    /**
+     * 根据Teambition部门ID获取部门详情
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/632828c3912d20d3b5aca571
+     *
+     * @param teamId Teambition部门ID
+     * @param token 令牌
+     *
+     * @return String[teamName] tb部门名称
+     */
+    public static String getTeamNameByTeamId(String teamId, String token) {
+
+        int code = 0;
+        JSONArray teamIds = null;
+        String teamName = null;
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            RequestBody body = RequestBody.create(mediaType, "");
+            Request request = new Request.Builder()
+                    .url("https://open.teambition.com/api/org/department/info?deptId=" + teamId)
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + token)
+                    .addHeader("X-Tenant-Id", tbOrganizationId)
+                    .addHeader("X-Tenant-Type", "organization")
+                    .build();
+            Response response = client.newCall(request).execute();
+
+            String bodyStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject);
+            code = jsonObject.getInteger("code");
+            System.out.println("TB获取部门详情接口返回状态码:" + code);
+
+            JSONObject result = jsonObject.getJSONObject("result");
+            System.out.println("TB获取部门详情接口返回result:" + result);
+            teamName = result.getString("name");
+        }catch (IOException e){
+            e.printStackTrace();
+        }
+        System.out.println("根据Teambition部门ID获取部门详情的teamName:" + teamName);
+
+        return teamName;
+    }
+
+    /**
+     * 根据Teambition部门名称获取项目集id
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/6501ad17912d20d3b5086630
+     *
+     * @param teamName Teambition部门名称
+     * @param token 令牌
+     *
+     * @return String[teamName] tb部门名称
+     */
+    public static String getProgramIdByTeamName(String teamName, String token) {
+
+        String parentId = "69ccab946970d23e8d30ad55";//父项目集id,默认:上海数字产业发展有限公司
+
+        int code = 0;
+        JSONArray result = null;
+        String xmjId = null;
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            RequestBody body = RequestBody.create(mediaType, "");
+            Request request = new Request.Builder()
+                    .url("https://open.teambition.com/api/v3/program/query?parentId=" + parentId + "&q=" + teamName)
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + token)
+                    .addHeader("X-Tenant-Id", tbOrganizationId)
+                    .addHeader("X-Tenant-Type", "organization")
+                    .build();
+            Response response = client.newCall(request).execute();
+
+            String bodyStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject);
+            code = jsonObject.getInteger("code");
+            System.out.println("TB接口返回状态码:" + code);
+
+            result = jsonObject.getJSONArray("result");
+            System.out.println("TB接口返回result:" + result);
+            if (result.size() > 0 && result != null && !result.isEmpty()){
+                xmjId = result.getJSONObject(0).getString("id");
+            }
+        }catch (IOException e){
+            e.printStackTrace();
+        }
+        System.out.println("根据部门名称匹配TB项目集id:" + xmjId);
+
+        return xmjId;
+    }
+}

+ 387 - 0
src/main/java/com/muzhi/shuchan/common/teambition/Project.java

@@ -0,0 +1,387 @@
+package com.muzhi.shuchan.common.teambition;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+public class Project {
+
+    private static String tbOrganizationId;
+
+    // 创建一个非静态的 Setter 方法,加上 @Value
+    // Spring 启动时会调用这个方法,将配置文件值传入
+    @Value("${myapp.teambition.tbOrganizationId}")
+    public void setTbOrganizationId(String value) {
+        Project.tbOrganizationId = value; // 赋值给静态变量
+    }
+
+    /**
+     * 从模板创建项目
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/6321c6d0912d20d3b5a49753
+     *
+     * @param projectName 项目名称
+     * @param templateId  模板id
+     * @param operatorId  操作人员id
+     * @param startDate 项目开始时间
+     * @param endDate 项目结束时间
+     * @param token 令牌
+     *
+     * @return String[projectId] 项目id
+     */
+    public static String createFromTemplate(String projectName, String templateId, String startDate, String endDate, String operatorId, String token) {
+
+        Map mapA = new HashMap();
+        mapA.put("name", projectName);
+        mapA.put("templateId", templateId);
+        mapA.put("startDate", startDate);
+        mapA.put("endDate", endDate);
+        String mapJsonA = JSON.toJSONString(mapA);
+        System.out.println("封装接口动态参数mapJson:" + mapJsonA);
+
+        OkHttpClient client = new OkHttpClient().newBuilder()
+                .build();
+        MediaType mediaType = MediaType.parse("application/json");
+        RequestBody body = RequestBody.create(mediaType, mapJsonA);
+        Request request = new Request.Builder()
+                .url("https://open.teambition.com/api/v3/project/create-from-template")
+                .method("POST", body)
+                .addHeader("x-operator-id", operatorId)
+                .addHeader("Authorization", "Bearer " + token)
+                .addHeader("X-Tenant-Id", tbOrganizationId)
+                .addHeader("X-Tenant-Type", "organization")
+                .addHeader("Content-Type", "application/json")
+                .build();
+
+        int code = 0;
+        String errorMessage = "";
+        String projectId = null;
+        try {
+            Response response = client.newCall(request).execute();
+            String bodyStr = response.body().string();
+            JSONObject jsonObject11 = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject11);
+            if (!jsonObject11.isEmpty() && jsonObject11 != null){
+                code = jsonObject11.getInteger("code");
+                errorMessage = jsonObject11.getString("errorMessage");
+                JSONObject jsonObjectResult = jsonObject11.getJSONObject("result");
+                if (!jsonObjectResult.isEmpty() && jsonObjectResult != null){
+                    projectId = jsonObjectResult.getString("id");
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        System.out.println("【根据模板创建项目】接口返回的状态码(200成功,400异常):" + code);
+        System.out.println("【根据模板创建项目】接口返回的异常状态信息(errorMessage):" + errorMessage);
+        System.out.println("【根据模板创建项目】接口返回的项目id:" + projectId);
+
+        return projectId;
+    }
+
+    /**
+     * 更新项目概览自定义字段值
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/6321c6d1912d20d3b5a49d3f
+     *
+     * @param operatorId 创建人id
+     * @param xmid  项目id
+     * @param customfieldName  自定义字段名称
+     * @param title  自定义字段值
+     * @param token 令牌
+     *
+     * @return String[projectId] 项目id
+     */
+    public static void updateProjectCustomField(String type, String operatorId, String xmid, String customfieldName, String title, String token) {
+
+        Map mapTitle = new HashMap();
+        if (type != null){
+            mapTitle.put("id", title);
+        }
+        mapTitle.put("title", title);
+
+        List listTitle = new ArrayList();
+        listTitle.add(mapTitle);
+
+        Map mapAll = new HashMap();
+        mapAll.put("customfieldName", customfieldName);
+        mapAll.put("value", listTitle);
+
+        String mapStr = JSON.toJSONString(mapAll);
+        System.out.println("封装更新的字段参数:" + mapStr);
+
+
+        OkHttpClient client = new OkHttpClient().newBuilder()
+                .build();
+        MediaType mediaType = MediaType.parse("application/json");
+        RequestBody body = RequestBody.create(mediaType, mapStr);
+        Request request = new Request.Builder()
+                .url("https://open.teambition.com/api/v3/project/"+ xmid +"/status/customfield/create")
+                .method("POST", body)
+                .addHeader("x-operator-id", operatorId)
+                .addHeader("Authorization", "Bearer " + token)
+                .addHeader("X-Tenant-Id", tbOrganizationId)
+                .addHeader("X-Tenant-Type", "organization")
+                .addHeader("Content-Type", "application/json")
+                .build();
+
+        int code1 = 0;
+        try {
+            Response response1 = client.newCall(request).execute();
+            String bodyStr1 = response1.body().string();
+            JSONObject jsonObject1 = JSON.parseObject(bodyStr1);
+            System.out.println("返回体body:" + jsonObject1);
+            code1 = jsonObject1.getInteger("code");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        System.out.println("【更新项目概览自定义字段值】接口返回的状态码(200成功,400异常):" + code1);
+    }
+
+    /**
+     * 通过TQL搜索项目
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/6728570f912d20d3b53ed2ea
+     *
+     * @param tql 项目搜索TQL语句
+     * @param token 令牌
+     *
+     * @return JSONArray[projectId] 项目id
+     */
+    public static JSONArray searchProjectIds(String tql, String token) {
+
+        int code = 0;
+        JSONArray projectIdArray = new JSONArray();
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            RequestBody body = RequestBody.create(mediaType, "");
+            Request request = new Request.Builder()
+                    .url("https://open.teambition.com/api/project/search?tql=" + tql)
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + token)
+                    .addHeader("X-Tenant-Id", tbOrganizationId)
+                    .addHeader("X-Tenant-Type", "organization")
+                    .build();
+            Response response = client.newCall(request).execute();
+
+            String bodyStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject);
+            code = jsonObject.getInteger("code");
+            System.out.println("通过TQL搜索项目接口返回状态码:" + code);
+
+            projectIdArray = jsonObject.getJSONArray("result");
+            System.out.println("通过TQL搜索项目接口返回result:" + projectIdArray);
+        }catch (IOException e){
+            e.printStackTrace();
+        }
+        System.out.println("通过TQL搜索项目返回的项目id数组:" + projectIdArray);
+
+        return projectIdArray;
+    }
+
+    /**
+     * 创建项目成员 v2
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/6363bcfa912d20d3b56faf07
+     *
+     * @param operatorId 创建人id
+     * @param xmid  tb项目id
+     * @param listAllTbUserId  待更新的项目成员TBuserid数组
+     * @param token 令牌
+     *
+     */
+    public static void createProjectMember(String operatorId, String xmid, List listAllTbUserId, String token) {
+
+        Map mapAll = new HashMap();
+        mapAll.put("userIds", listAllTbUserId);
+
+        String mapStr = JSON.toJSONString(mapAll);
+        System.out.println("封装更新的字段参数:" + mapStr);
+
+
+        OkHttpClient client = new OkHttpClient().newBuilder()
+                .build();
+        MediaType mediaType = MediaType.parse("application/json");
+        RequestBody body = RequestBody.create(mediaType, mapStr);
+        Request request = new Request.Builder()
+                .url("https://open.teambition.com/api/v3/project/"+ xmid +"/member/create-v2")
+                .method("POST", body)
+                .addHeader("x-operator-id", operatorId)
+                .addHeader("Authorization", "Bearer " + token)
+                .addHeader("X-Tenant-Id", tbOrganizationId)
+                .addHeader("X-Tenant-Type", "organization")
+                .addHeader("Content-Type", "application/json")
+                .build();
+
+        int code1 = 0;
+        try {
+            Response response1 = client.newCall(request).execute();
+            String bodyStr1 = response1.body().string();
+            JSONObject jsonObject1 = JSON.parseObject(bodyStr1);
+            System.out.println("返回体body:" + jsonObject1);
+            code1 = jsonObject1.getInteger("code");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        System.out.println("【创建项目成员 v2】接口返回的状态码(200成功,400异常):" + code1);
+    }
+
+    /**
+     * 获取项目角色列表
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/6321c6d1912d20d3b5a49b8f
+     *
+     * @param projectId 项目id
+     * @param token 令牌
+     *
+     * @return array[object] result项目角色列表
+     */
+    public static JSONArray getProjectRoleList(String projectId, String token) {
+
+        int code = 0;
+        JSONArray result = null;
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            RequestBody body = RequestBody.create(mediaType, "");
+            Request request = new Request.Builder()
+                    .url("https://open.teambition.com/api/v3/project/" + projectId + "/role")
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + token)
+                    .addHeader("X-Tenant-Id", tbOrganizationId)
+                    .addHeader("X-Tenant-Type", "organization")
+                    .build();
+            Response response = client.newCall(request).execute();
+
+            String bodyStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject);
+            code = jsonObject.getInteger("code");
+            System.out.println("TB接口返回状态码:" + code);
+
+            result = jsonObject.getJSONArray("result");
+            System.out.println("TB接口返回result:" + result);
+        }catch (IOException e){
+            e.printStackTrace();
+        }
+        System.out.println("根据项目id查询的项目角色列表:" + result);
+
+        return result;
+    }
+
+    /**
+     * 创建项目成员 v2
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/6363bcfa912d20d3b56faf07
+     *
+     * @param operatorId 创建人id
+     * @param xmid  tb项目id
+     * @param tbUserId  tb用户id
+     * @param roleId  tb角色id
+     * @param token 令牌
+     *
+     */
+    public static void updateProjectRole(String operatorId, String xmid, String tbUserId, String roleId, String token) {
+
+        Map mapAll = new HashMap();
+        List listTbUserId = new ArrayList();
+        listTbUserId.add(tbUserId);
+        List listRoleId = new ArrayList();
+        listRoleId.add(roleId);
+        mapAll.put("userIds", listTbUserId);
+        mapAll.put("roleIds", listRoleId);
+
+        String mapStr = JSON.toJSONString(mapAll);
+        System.out.println("封装更新的字段参数:" + mapStr);
+
+        OkHttpClient client = new OkHttpClient().newBuilder()
+                .build();
+        MediaType mediaType = MediaType.parse("application/json");
+        RequestBody body = RequestBody.create(mediaType, mapStr);
+        Request request = new Request.Builder()
+                .url("https://open.teambition.com/api/v3/project/"+ xmid +"/member/role/assign")
+                .method("POST", body)
+                .addHeader("x-operator-id", operatorId)
+                .addHeader("Authorization", "Bearer " + token)
+                .addHeader("X-Tenant-Id", tbOrganizationId)
+                .addHeader("X-Tenant-Type", "organization")
+                .addHeader("Content-Type", "application/json")
+                .build();
+
+        int code1 = 0;
+        try {
+            Response response1 = client.newCall(request).execute();
+            String bodyStr1 = response1.body().string();
+            JSONObject jsonObject1 = JSON.parseObject(bodyStr1);
+            System.out.println("返回体body:" + jsonObject1);
+            code1 = jsonObject1.getInteger("code");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        System.out.println("【修改项目成员的角色】接口返回的状态码(200成功,400异常):" + code1);
+    }
+
+    /**
+     * 批量添加项目到项目集
+     * REMARK 接口文档地址:https://open.teambition.com/docs/apis/64e810ff912d20d3b58fa74a
+     *
+     * @param programId 项目集id
+     * @param projectId 项目id
+     * @param operatorId 创建人id
+     * @param token 令牌
+     *
+     * @return code 相应状态码,200为成功
+     */
+    public static int createProjectToProjectSet(String programId, String projectId, String operatorId, String token) {
+
+        //封装待添加项目集的项目id
+        Map map = new HashMap();
+        List list = new ArrayList();
+        list.add(projectId);
+        map.put("projectIds",list);
+
+        String mapJson = JSON.toJSONString(map);
+        System.out.println("封装待添加项目集的项目id:" + mapJson);
+
+        OkHttpClient client = new OkHttpClient().newBuilder()
+                .build();
+        MediaType mediaType = MediaType.parse("application/json");
+        RequestBody body = RequestBody.create(mediaType, mapJson);
+        Request request = new Request.Builder()
+                .url("https://open.teambition.com/api/v3/program/"+ programId +"/project/create")
+                .method("POST", body)
+                .addHeader("x-operator-id", operatorId)
+                .addHeader("Authorization", "Bearer " + token)
+                .addHeader("X-Tenant-Id", tbOrganizationId)
+                .addHeader("X-Tenant-Type", "organization")
+                .addHeader("Content-Type", "application/json")
+                .build();
+
+        int code = 0;
+        String errorMessage = "";
+        try {
+            Response response = client.newCall(request).execute();
+            String bodyStr = response.body().string();
+            JSONObject jsonObject11 = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject11);
+            code = jsonObject11.getInteger("code");
+            errorMessage = jsonObject11.getString("errorMessage");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        System.out.println("【批量添加项目到项目集】接口返回的状态码(200成功,400异常):" + code);
+        System.out.println("【批量添加项目到项目集】接口返回的异常状态信息(errorMessage):" + errorMessage);
+
+        return code;
+    }
+}

+ 36 - 0
src/main/java/com/muzhi/shuchan/common/universal/Time.java

@@ -0,0 +1,36 @@
+package com.muzhi.shuchan.common.universal;
+
+import com.alibaba.fastjson.JSON;
+
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+public class Time {
+
+    /**
+     * 获取数组内单个字符串内的文本
+     *
+     * @param timestamp 毫秒级时间戳
+     * @return String 转换后的UTC时间
+     */
+    public static String getUtcToTimestamp(long timestamp) {
+        /**按照时区转换时间格式*/
+        // 使用DateTimeFormatter进行格式化
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+
+        String timeUTC = null;
+        if (timestamp > 0) {
+            // 将时间戳转换为Instant
+            Instant instant = Instant.ofEpochMilli(timestamp);
+            // 转换为OffsetDateTime,使用UTC时区
+            OffsetDateTime dateTime = instant.atOffset(ZoneOffset.UTC);
+            // 输出格式化后的日期时间字符串
+            timeUTC = dateTime.format(formatter);
+            System.out.println("转换后的时间(Utc):" + timeUTC);
+        }
+        return timeUTC;
+    }
+}

+ 39 - 43
main/java/com/muzhi/huanshi/controller/AccessTokenController.java

@@ -1,43 +1,39 @@
-package com.muzhi.huanshi.controller;
-
-import com.muzhi.huanshi.service.AccessTokenService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.CrossOrigin;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * @Author ZhangKan
- * @Data 15:03
- */
-
-@Api(description = "钉钉授权AccessToken")
-@RestController
-@RequestMapping("/dingservice")
-@CrossOrigin//解决跨域问题
-public class AccessTokenController {
-
-    @Autowired
-    private AccessTokenService AccessTokenService;
-
-    //获取钉钉中的access_token(斯曼尔)
-    @ApiOperation(value = "获取钉钉中的access_token")
-    @GetMapping("getAccessToken")
-    public String getAccessToken(){
-
-        String accessToken = AccessTokenService.getAccessToken();
-        return accessToken;
-    }
-
-    //获取钉钉中的access_token(环世TB应用)
-    @ApiOperation(value = "获取钉钉中的access_token")
-    @GetMapping("getAppToken")
-    public String getAppToken(){
-
-        String AppToken = AccessTokenService.getAppToken();
-        return AppToken;
-    }
-}
+package com.muzhi.shuchan.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.shuchan.service.AccessTokenService AccessTokenService;
+
+    //获取钉钉中的access_token(钉钉应用)
+    @ApiOperation(value = "获取钉钉的access_token")
+    @GetMapping("getAccessToken")
+    public String getAccessToken(){
+
+        String accessToken = AccessTokenService.getAccessToken();
+        return accessToken;
+    }
+
+    //获取TB中的access_token(数产TB应用)
+    @ApiOperation(value = "获取TB中的access_token")
+    @GetMapping("getAppToken")
+    public String getAppToken(){
+
+        String AppToken = AccessTokenService.getAppToken();
+        return AppToken;
+    }
+}

+ 39 - 0
src/main/java/com/muzhi/shuchan/controller/DingTalkController.java

@@ -0,0 +1,39 @@
+package com.muzhi.shuchan.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.muzhi.shuchan.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.shuchan.common.R;
+
+
+@Api(description = "钉钉")
+@RestController
+@RequestMapping("/dingservice")
+@CrossOrigin//解决跨域问题
+public class DingTalkController {
+
+    @Autowired
+    private DingTalkService dingTalkService;
+
+    /**通过宜搭附件url地址获取上传的XML格式发票明细*/
+    @ApiOperation(value = "通过宜搭附件url地址获取上传的XML格式发票明细")
+    @PostMapping("getInvoiceList")
+    public R getInvoiceList(@RequestBody JSONObject obj){
+
+        R invoiceList = dingTalkService.getInvoiceList(obj);
+        return invoiceList;
+    }
+
+    /**工时填报触发创建工时审核*/
+    @ApiOperation(value = "工时填报触发创建工时审核")
+    @PostMapping("createGongshishenhe")
+    public R createGongshishenhe(String slid){
+
+        R createGongshishenhe = dingTalkService.createGongshishenhe(slid);
+        return createGongshishenhe;
+    }
+}

+ 46 - 0
src/main/java/com/muzhi/shuchan/controller/TeambitionController.java

@@ -0,0 +1,46 @@
+package com.muzhi.shuchan.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.muzhi.shuchan.common.R;
+import com.muzhi.shuchan.service.DingTalkService;
+import com.muzhi.shuchan.service.TeambitionService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+
+@Api(description = "钉钉")
+@RestController
+@RequestMapping("/tbservice")
+@CrossOrigin//解决跨域问题
+public class TeambitionController {
+
+    @Autowired
+    private TeambitionService teambitionService;
+
+    /**项目立项通过后创建TB项目信息*/
+    @ApiOperation(value = "项目立项通过后创建TB项目信息")
+    @PostMapping("createTeambitionProject")
+    public R createTeambitionProject(String tbmbid,
+                                     String xmbh, String xmmc, String xmzq,
+                                     String xmjlDingUserId, String ssbmDingDeptId, String pmoDingUserId,
+                                     String sjfzrDingUserId, String jjfafzrDingUserId,
+                                     String ssfazDingUserId, String ssjszDingUserId, String zlcszDingUserId, String ywwbzDingUserId,
+                                     String bzgfzDingUserId, String sjyyzDingUserId, String xqdjzDingUserId, String zlsszDingUserId,
+                                     String sjaqzDingUserId, String ptaqzDingUserId, String wlaqzDingUserId,
+                                     String sfwndlxjhxm, String xmlx, String xmbq, String xmxqly, String sfkslx,
+                                     String slid){
+
+        R teambitionProject = teambitionService.createTeambitionProject(tbmbid,
+                xmbh, xmmc, xmzq,
+                xmjlDingUserId, ssbmDingDeptId, pmoDingUserId,
+                sjfzrDingUserId, jjfafzrDingUserId,
+                ssfazDingUserId, ssjszDingUserId, zlcszDingUserId, ywwbzDingUserId,
+                bzgfzDingUserId, sjyyzDingUserId, xqdjzDingUserId, zlsszDingUserId,
+                sjaqzDingUserId, ptaqzDingUserId, wlaqzDingUserId,
+                sfwndlxjhxm, xmlx, xmbq, xmxqly, sfkslx,
+                slid);
+        return teambitionProject;
+    }
+}

+ 36 - 0
src/main/java/com/muzhi/shuchan/dingtalk/dingtalkWebhook.java

@@ -0,0 +1,36 @@
+package com.muzhi.shuchan.dingtalk;
+
+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 void dingtalkWebhook(String webhookUrl, String mapStr){
+        log.info("钉钉webhookUrl:" + webhookUrl);
+
+        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();
+        }
+    }
+
+}

+ 40 - 0
src/main/java/com/muzhi/shuchan/entity/InvoiceItem.java

@@ -0,0 +1,40 @@
+package com.muzhi.shuchan.entity;
+
+public class InvoiceItem {
+    private String itemName;
+    private String quantity;
+    private String unitPrice;
+    private String amount;
+    private String taxRate;
+    private String taxAmount;
+    private String totalTaxIncluded;
+
+    // 构造方法
+    public InvoiceItem(String itemName, String quantity, String unitPrice,
+                       String amount, String taxRate, String taxAmount, String totalTaxIncluded) {
+        this.itemName = itemName;
+        this.quantity = quantity;
+        this.unitPrice = unitPrice;
+        this.amount = amount;
+        this.taxRate = taxRate;
+        this.taxAmount = taxAmount;
+        this.totalTaxIncluded = totalTaxIncluded;
+    }
+
+    // Getter 方法(用于输出)
+    public String getItemName() { return itemName; }
+    public String getQuantity() { return quantity; }
+    public String getUnitPrice() { return unitPrice; }
+    public String getAmount() { return amount; }
+    public String getTaxRate() { return taxRate; }
+    public String getTaxAmount() { return taxAmount; }
+    public String getTotalTaxIncluded() { return totalTaxIncluded; }
+
+    @Override
+    public String toString() {
+        return String.format(
+                "商品: %s | 数量: %s | 单价: %s | 金额: %s | 税率: %s | 税额: %s | 价税合计: %s",
+                itemName, quantity, unitPrice, amount, taxRate, taxAmount, totalTaxIncluded
+        );
+    }
+}

+ 14 - 0
src/main/java/com/muzhi/shuchan/service/AccessTokenService.java

@@ -0,0 +1,14 @@
+package com.muzhi.shuchan.service;
+
+/**
+ * @Author ZhangKan
+ * @Data 15:34
+ */
+public interface AccessTokenService {
+
+    //获取access_token的方法(钉钉应用)
+    String getAccessToken();
+
+    //获取access_token的方法(数产TB应用)
+    String getAppToken();
+}

+ 18 - 0
src/main/java/com/muzhi/shuchan/service/DingTalkService.java

@@ -0,0 +1,18 @@
+package com.muzhi.shuchan.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.muzhi.shuchan.common.R;
+import org.springframework.web.bind.annotation.RequestBody;
+
+/**
+ * @Author ZhangKan
+ * @Data 15:34
+ */
+public interface DingTalkService {
+
+    /**通过宜搭附件url地址获取上传的XML格式发票明细*/
+    R getInvoiceList(JSONObject obj);
+
+    /**工时填报触发创建工时审核*/
+    R createGongshishenhe(String slid);
+}

+ 22 - 0
src/main/java/com/muzhi/shuchan/service/TeambitionService.java

@@ -0,0 +1,22 @@
+package com.muzhi.shuchan.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.muzhi.shuchan.common.R;
+
+/**
+ * @Author ZhangKan
+ * @Data 15:34
+ */
+public interface TeambitionService {
+
+    /**项目立项通过后创建TB项目信息*/
+    R createTeambitionProject(String tbmbid,
+                              String xmbh, String xmmc, String xmzq,
+                              String xmjlDingUserId, String ssbmDingDeptId, String pmoDingUserId,
+                              String sjfzrDingUserId, String jjfafzrDingUserId,
+                              String ssfazDingUserId, String ssjszDingUserId, String zlcszDingUserId, String ywwbzDingUserId,
+                              String bzgfzDingUserId, String sjyyzDingUserId, String xqdjzDingUserId, String zlsszDingUserId,
+                              String sjaqzDingUserId, String ptaqzDingUserId, String wlaqzDingUserId,
+                              String sfwndlxjhxm, String xmlx, String xmbq, String xmxqly, String sfkslx,
+                              String slid);
+}

+ 89 - 86
main/java/com/muzhi/huanshi/service/impl/AccessTokenServiceImpl.java

@@ -1,86 +1,89 @@
-package com.muzhi.huanshi.service.impl;
-
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.algorithms.Algorithm;
-import com.dingtalk.api.DefaultDingTalkClient;
-import com.dingtalk.api.request.OapiGettokenRequest;
-import com.dingtalk.api.response.OapiGettokenResponse;
-import com.muzhi.huanshi.service.AccessTokenService;
-import com.taobao.api.ApiException;
-import org.springframework.stereotype.Service;
-import org.springframework.util.StringUtils;
-
-import java.util.Date;
-
-
-/**
- * @Author ZhangKan
- * @Data 15:39
- */
-
-@Service
-public class AccessTokenServiceImpl implements AccessTokenService {
-
-    //环世
-    //APPKEY
-    private static String APPKEY = "dingobpyhlo3vioasyo9";
-    //APPSECRET
-    private static String APPSECRET = "RNLnat9hiz8zQGSyOhLf1GEAx9qS66_VoFZFYJTwT_uy2aB_5sEoByTdd7oWc3Y0";
-    //AgenthId:2769663130
-
-    public static String accessToken = null;
-
-
-
-    //获取access_token的方法(环世)
-    @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);
-            accessToken = response.getAccessToken();
-            System.out.println("环世AccessToken:" + accessToken);
-        } catch (ApiException e) {
-            e.printStackTrace();
-        }
-        return accessToken;
-    }
-
-    public static final Long   EXPIRES_IN = 1 * 3600 * 1000L;
-    public static final String TOKEN_APPID = "_appId";
-
-    public static String appId = "6555d03b73aa2fd874f4cc12";
-    public static String appSecret = "HDMqRuUY08eY1FtEzxs0hNB6A2Tq58aL";
-    //获取access_token的方法(环世TB应用)
-    @Override
-    public String getAppToken() {
-        if (StringUtils.isEmpty(appId) || StringUtils.isEmpty(appSecret)) {
-            return null;
-        }
-
-        Algorithm algorithm = Algorithm.HMAC256(appSecret);
-        long timestamp = System.currentTimeMillis();
-        Date issuedAt = new Date(timestamp);
-        Date expiresAt = new Date(timestamp + EXPIRES_IN);
-
-//        return JWT.create()
-//                .withClaim(TOKEN_APPID, appId)
-//                .withIssuedAt(issuedAt)
-//                .withExpiresAt(expiresAt)
-//                .sign(algorithm);
-
-        String sign = JWT.create()
-                .withClaim(TOKEN_APPID, appId)
-                .withIssuedAt(issuedAt)
-                .withExpiresAt(expiresAt)
-                .sign(algorithm);
-        System.out.println("环世TB应用的AccessToken:" + sign);
-
-        return sign;
-
-    }
-}
+package com.muzhi.shuchan.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.request.OapiGettokenRequest;
+import com.dingtalk.api.response.OapiGettokenResponse;
+import com.muzhi.shuchan.service.AccessTokenService;
+import com.taobao.api.ApiException;
+import okhttp3.*;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.io.IOException;
+import java.util.Date;
+
+
+/**
+ * @Author ZhangKan
+ * @Data 15:39
+ */
+
+@Service
+public class AccessTokenServiceImpl implements AccessTokenService {
+
+    /**钉钉应用*/
+    //APPKEY
+    private static String APPKEY = "dinghuwaif7hbom2won4";
+    //APPSECRET
+    private static String APPSECRET = "XPdl3aQ7Hd4SNetnq7LEXTyaON2W1ziUX3WwTdhKS28rKbUj2NYXZhdb0EAc5Htj";
+    //AgenthId:4119463990
+    //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;
+    }
+
+    //TB企业id:6008e33803f7c491c204d08a
+    public static final Long   EXPIRES_IN = 1 * 3600 * 1000L;
+    public static final String TOKEN_APPID = "_appId";
+
+    public static String appId = "693b8c56e7358413d33806ec";
+    public static String appSecret = "xdVyLe3RXtHbjTNsrMaIL1RRwBeosf8y";
+    //获取access_token的方法(数产TB应用)
+    @Override
+    public String getAppToken() {
+        if (StringUtils.isEmpty(appId) || StringUtils.isEmpty(appSecret)) {
+            return null;
+        }
+
+        Algorithm algorithm = Algorithm.HMAC256(appSecret);
+        long timestamp = System.currentTimeMillis();
+        Date issuedAt = new Date(timestamp);
+        Date expiresAt = new Date(timestamp + EXPIRES_IN);
+
+//        return JWT.create()
+//                .withClaim(TOKEN_APPID, appId)
+//                .withIssuedAt(issuedAt)
+//                .withExpiresAt(expiresAt)
+//                .sign(algorithm);
+
+        String sign = JWT.create()
+                .withClaim(TOKEN_APPID, appId)
+                .withIssuedAt(issuedAt)
+                .withExpiresAt(expiresAt)
+                .sign(algorithm);
+        System.out.println("数产TB应用的AccessToken:" + sign);
+
+        return sign;
+    }
+}

+ 651 - 0
src/main/java/com/muzhi/shuchan/service/impl/DingTalkServiceImpl.java

@@ -0,0 +1,651 @@
+package com.muzhi.shuchan.service.impl;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.dingtalkyida_1_0.models.GetOpenUrlResponse;
+import com.aliyun.dingtalkyida_1_0.models.StartInstanceResponse;
+import com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDResponse;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import com.muzhi.shuchan.entity.InvoiceItem;
+import com.muzhi.shuchan.service.AccessTokenService;
+import com.muzhi.shuchan.service.DingTalkService;
+import com.muzhi.shuchan.common.R;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.*;
+import java.math.BigDecimal;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+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.*;
+
+import org.springframework.web.bind.annotation.RequestBody;
+import org.w3c.dom.*;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+import org.xml.sax.InputSource;
+
+
+@Service
+@Slf4j
+public class DingTalkServiceImpl implements DingTalkService {
+
+    @Autowired
+    private AccessTokenService accessTokenService;
+
+    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);
+    }
+
+    public static void parseEInvoice(String xmlContent) throws Exception {
+        // 创建 DocumentBuilder
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        Document doc = builder.parse(new InputSource(new StringReader(xmlContent)));
+        doc.getDocumentElement().normalize();
+
+        System.out.println("开始解析电子发票...");
+
+        // 获取根节点
+        Element root = doc.getDocumentElement();
+
+        // 1. 解析 Header 信息
+        NodeList headerList = root.getElementsByTagName("Header");
+        if (headerList.getLength() > 0) {
+            Element header = (Element) headerList.item(0);
+            String eiid = getTextContent(header, "EIid");
+            String version = getTextContent(header, "Version");
+            System.out.println("发票ID: " + eiid);
+            System.out.println("版本: " + version);
+        }
+
+        // 2. 卖方信息
+        NodeList sellerList = root.getElementsByTagName("SellerInformation");
+        if (sellerList.getLength() > 0) {
+            Element seller = (Element) sellerList.item(0);
+            System.out.println("卖方名称: " + getTextContent(seller, "SellerName"));
+            System.out.println("卖方税号: " + getTextContent(seller, "SellerIdNum"));
+            System.out.println("卖方地址: " + getTextContent(seller, "SellerAddr"));
+        }
+
+        // 3. 买方信息
+        NodeList buyerList = root.getElementsByTagName("BuyerInformation");
+        if (buyerList.getLength() > 0) {
+            Element buyer = (Element) buyerList.item(0);
+            System.out.println("买方名称: " + getTextContent(buyer, "BuyerName"));
+            System.out.println("买方税号: " + getTextContent(buyer, "BuyerIdNum"));
+        }
+
+        // 4. 基本信息(总金额等)
+        NodeList basicInfoList = root.getElementsByTagName("BasicInformation");
+        if (basicInfoList.getLength() > 0) {
+            Element basic = (Element) basicInfoList.item(0);
+            System.out.println("不含税金额: " + getTextContent(basic, "TotalAmWithoutTax"));
+            System.out.println("税额: " + getTextContent(basic, "TotalTaxAm"));
+            System.out.println("价税合计: " + getTextContent(basic, "TotalTax-includedAmount"));
+            System.out.println("开票时间(请求): " + getTextContent(basic, "RequestTime"));
+        }
+
+        // 5. 发货项信息(可能多个)
+        NodeList itemNodes = root.getElementsByTagName("IssuItemInformation");
+        System.out.println("\n--- 发票明细 ---");
+        for (int i = 0; i < itemNodes.getLength(); i++) {
+            Element item = (Element) itemNodes.item(i);
+            System.out.println("商品名称: " + getTextContent(item, "ItemName"));
+            System.out.println("规格型号: " + getTextContent(item, "SpecMod"));
+            System.out.println("单位: " + getTextContent(item, "MeaUnits"));
+            System.out.println("数量: " + getTextContent(item, "Quantity"));
+            System.out.println("单价: " + getTextContent(item, "UnPrice"));
+            System.out.println("金额: " + getTextContent(item, "Amount"));
+            System.out.println("税率: " + getTextContent(item, "TaxRate"));
+            System.out.println("税额: " + getTextContent(item, "ComTaxAm"));
+            System.out.println("含税总额: " + getTextContent(item, "TotaltaxIncludedAmount"));
+            System.out.println("税收分类编码: " + getTextContent(item, "TaxClassificationCode"));
+            System.out.println("------------------------");
+        }
+
+        // 6. 备注信息
+        NodeList remarkList = root.getElementsByTagName("Remark");
+        if (remarkList.getLength() > 0) {
+            System.out.println("备注: " + remarkList.item(0).getTextContent());
+        }
+
+        // 7. 税务监管信息
+        NodeList taxInfoList = root.getElementsByTagName("TaxSupervisionInfo");
+        if (taxInfoList.getLength() > 0) {
+            Element taxInfo = (Element) taxInfoList.item(0);
+            System.out.println("发票号码: " + getTextContent(taxInfo, "InvoiceNumber"));
+            System.out.println("开票时间: " + getTextContent(taxInfo, "IssueTime"));
+            System.out.println("税务局名称: " + getTextContent(taxInfo, "TaxBureauName"));
+        }
+    }
+
+    // 辅助方法:安全获取子元素文本内容
+    private static String getTextContent(Element parent, String tagName) {
+        NodeList list = parent.getElementsByTagName(tagName);
+        if (list.getLength() > 0 && list.item(0).getFirstChild() != null) {
+            return list.item(0).getFirstChild().getNodeValue().trim();
+        }
+        return "";
+    }
+
+    /**
+     * 从指定URL下载文件到本地路径
+     */
+    public static File downloadFile(String urlPath, String downloadDir, String fileName, String destinationDir) {
+        File file = null;
+        try {
+
+            URL url = new URL(urlPath);
+
+            URLConnection urlConnection = url.openConnection();
+
+            HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;// http的连接类
+
+            //String contentType = httpURLConnection.getContentType();//请求类型,可用来过滤请求,
+
+            httpURLConnection.setConnectTimeout(1000*5);//设置超时
+
+            httpURLConnection.setRequestMethod("GET");//设置请求方式,默认是GET
+
+            httpURLConnection.setRequestProperty("Charset", "UTF-8");// 设置字符编码
+
+
+            httpURLConnection.connect();// 打开连接
+
+            BufferedInputStream bin = new BufferedInputStream(httpURLConnection.getInputStream());
+
+            //TODO 修改成动态文件名称:当前审批单的文件名称
+//            String fileName = "ceshi.pdf";// 指定文件名称
+
+            String path = destinationDir + File.separatorChar + downloadDir + File.separatorChar + fileName;// 指定存放位置,此处使用的是全局变量的文件名称
+            file = new File(path);
+            // 校验文件夹目录是否存在,不存在就创建一个目录
+            if (!file.getParentFile().exists()) {
+                file.getParentFile().mkdirs();
+            }
+
+            OutputStream out = new FileOutputStream(file);
+            int size = 0;
+
+            byte[] b = new byte[2048];
+            //把输入流的文件读取到字节数据b中,然后输出到指定目录的文件
+            while ((size = bin.read(b)) != -1) {
+                out.write(b, 0, size);
+            }
+            // 关闭资源
+            bin.close();
+            out.close();
+            System.out.println("文件下载成功!");
+        } catch (MalformedURLException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+            System.out.println("文件下载失败!");
+        } finally {
+            return file;
+        }
+
+    }
+
+    public static void downloadXmlInvoice(String fileUrl, String localPath) {
+        try {
+            // 创建目标目录(如果不存在)
+            Path targetPath = Paths.get(localPath);
+            Files.createDirectories(targetPath.getParent());
+
+            // 从 URL 下载并保存到本地
+            URL url = new URL(fileUrl);
+            Files.copy(url.openStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);
+
+            System.out.println("✅ 发票 XML 已成功下载到: " + localPath);
+        } catch (IOException e) {
+            System.err.println("❌ 下载失败: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    //获取宜搭附件免登地址
+    public String getYidafile(String url) {
+
+        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 = 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);
+        return fileUrl;
+    }
+
+    /**
+     * 通过宜搭附件url地址获取上传的XML格式发票明细
+     */
+    @Override
+    public R getInvoiceList(JSONObject obj){
+
+        /**通过宜搭附件url地址获取上传的XML格式发票明细*/
+        log.info("宜搭传参--->" + obj);
+        log.info("开始解析参数......");
+        JSONArray jsonArray = obj.getJSONArray("urlList");
+
+        List list = new ArrayList<>();
+        JSONArray jsonArray_item = new JSONArray();
+        for (int x = 0; x < jsonArray.size(); x++) {
+            JSONObject jsonObject = jsonArray.getJSONObject(x);
+
+            String fileUrlDownload = jsonObject.getString("fileUrlDownload");
+            log.info("宜搭附件地址:" + fileUrlDownload);
+            String fileDownloadName = jsonObject.getString("fileDownloadName");
+            log.info("宜搭附件名称:" + fileDownloadName);
+
+            /**通过宜搭附件url地址获取上传的XML格式发票下载至本地服务器*/
+//        fileUrlDownload = "https://xyqvbp.aliwork.com/inst/preview?appType=APP_XV6ZGK1162TI1Z1FE17D&fileName=APP_XV6ZGK1162TI1Z1FE17D_MTU3MzExMjA5XzA3QjY2T0QxSE0wMUozSkhLNzlSSzY5NEpNRlEyUkdZUlFMSU02NA%24%24.xml&fileSize=3965&downloadUrl=APP_XV6ZGK1162TI1Z1FE17D_MTU3MzExMjA5XzA3QjY2T0QxSE0wMUozSkhLNzlSSzY5NEpNRlEyUkdZUlFMSU02NA$$.xml";
+//        fileDownloadName = "dzfp_25317000002107213624_20250902105324.xml";
+
+            /**获取宜搭附件免登授权地址*/
+            String fileUrlDownloadToken = getYidafile(fileUrlDownload);
+            System.out.println("宜搭附件免登授权地址:" + fileUrlDownloadToken);
+
+            //TODO 【云璞测试服务器-临时存放】xml发票文件下载地址
+            String filePath = null;
+            try {
+                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()为获取当前系统时间
+
+                /**通过宜搭授权免登地址下载文件至服务器*/
+                downloadFile(fileUrlDownloadToken, format, fileDownloadName, destinationDir);
+
+                String savePath = destinationDir + File.separatorChar + format + File.separatorChar + fileDownloadName; // Linux
+                // String savePath = "D:\\invoices\\downloaded_invoice.xml"; // Windows
+
+                /**【该下载附件方法也可以用,比较简洁】通过宜搭授权免登地址下载文件至服务器*/
+//                downloadXmlInvoice(fileUrlDownloadToken, savePath);
+
+                String fileNewUrl = "https://poc.cloudpure.cn/invoice/" + format + File.separator + fileDownloadName;
+                System.out.println("服务器访问全地址:" + fileNewUrl);
+
+                //【Linux下面的路径】
+                filePath = destinationDir + File.separatorChar + format + File.separator + fileDownloadName;
+                System.out.println("服务器直接访问进行发票解析的地址:" + filePath);
+            } catch (Exception e) {
+                System.err.println("下载失败: " + e.getMessage());
+                e.printStackTrace();
+            }
+
+            //测试使用
+//        filePath = "invoice" + File.separatorChar + "2025-11-30" + File.separator + fileDownloadName;
+
+            //测试使用
+//        String fileName = "dzfp_25317000002107213624_20250902105324";
+//        String filePath = "/Users/zhangkan/Documents/发票/"+ fileName +".xml";  // macOS/Linux
+
+            //读取本地服务器存储的XML格式发票进行解析
+            try {
+                // 替换为你的本地 XML 文件路径
+                File xmlFile = new File(filePath);
+
+                // 1. 解析 XML 文档
+                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+                // 无需 setNamespaceAware(true),因为无命名空间
+                DocumentBuilder builder = factory.newDocumentBuilder();
+                Document doc = builder.parse(xmlFile);
+                doc.getDocumentElement().normalize();
+
+                // 2. 创建 XPath 对象
+                XPath xpath = XPathFactory.newInstance().newXPath();
+
+                // 3. 提取关键字段
+                String invoiceNumber = (String) xpath.evaluate("/EInvoice/TaxSupervisionInfo/InvoiceNumber", doc, XPathConstants.STRING);
+                String issueTime = (String) xpath.evaluate("/EInvoice/TaxSupervisionInfo/IssueTime", doc, XPathConstants.STRING);
+
+                String sellerName = (String) xpath.evaluate("/EInvoice/EInvoiceData/SellerInformation/SellerName", doc, XPathConstants.STRING);
+                String buyerName = (String) xpath.evaluate("/EInvoice/EInvoiceData/BuyerInformation/BuyerName", doc, XPathConstants.STRING);
+
+                String totalTaxIncluded = (String) xpath.evaluate("/EInvoice/EInvoiceData/BasicInformation/TotalTax-includedAmount", doc, XPathConstants.STRING);
+                String totalWithoutTax = (String) xpath.evaluate("/EInvoice/EInvoiceData/BasicInformation/TotalAmWithoutTax", doc, XPathConstants.STRING);
+                String totalTax = (String) xpath.evaluate("/EInvoice/EInvoiceData/BasicInformation/TotalTaxAm", doc, XPathConstants.STRING);
+
+                String drawer = (String) xpath.evaluate("/EInvoice/EInvoiceData/BasicInformation/Drawer", doc, XPathConstants.STRING);
+                String requestTime = (String) xpath.evaluate("/EInvoice/EInvoiceData/BasicInformation/RequestTime", doc, XPathConstants.STRING);
+
+                // 商品明细(多行处理)
+                // ✅ 关键:获取所有 IssuItemInformation 节点
+                NodeList itemNodes = (NodeList) xpath.evaluate(
+                        "/EInvoice/EInvoiceData/IssuItemInformation",
+                        doc,
+                        XPathConstants.NODESET
+                );
+                List<InvoiceItem> items = new ArrayList<>();
+
+                String itemName = null;
+                String specMod = null;
+                String meaUnits = null;
+                String quantity = null;
+                String unitPrice = null;
+                String amount = null;
+                String taxRate = null;
+                String taxAmount = null;
+
+                for (int i = 0; i < itemNodes.getLength(); i++) {
+                    Element item = (Element) itemNodes.item(i);
+
+                    itemName = getTextContent(item, "ItemName");
+                    specMod = getTextContent(item, "SpecMod");
+                    meaUnits = getTextContent(item, "MeaUnits");
+                    quantity = getTextContent(item, "Quantity");
+                    unitPrice = getTextContent(item, "UnPrice");
+                    amount = getTextContent(item, "Amount");
+                    taxRate = getTextContent(item, "TaxRate");
+                    taxAmount = getTextContent(item, "ComTaxAm");
+
+                    InvoiceItem invoiceItem = new InvoiceItem(
+                            itemName, quantity, unitPrice, amount, taxRate, taxAmount, totalTaxIncluded
+                    );
+                    items.add(invoiceItem);
+                    System.out.println((i + 1) + ". " + invoiceItem);
+
+                    //税率转成数值并乘100
+                    BigDecimal taxRateBig = new BigDecimal(taxRate).multiply(new BigDecimal(100));
+
+                    Map map = new HashMap<>();
+                    map.put("itemName", itemName);
+                    map.put("specMod", specMod);
+                    map.put("meaUnits", meaUnits);
+                    map.put("quantity", quantity);
+                    map.put("unitPrice", unitPrice);
+                    map.put("amount", amount);
+                    map.put("taxRate", taxRateBig);
+                    map.put("taxAmount", taxAmount);
+                    list.add(map);
+                }
+                //转成jsonArray
+                String jsonString = JSON.toJSONString(list);
+                jsonArray_item = JSON.parseArray(jsonString);
+
+                // 4. 输出结果
+                System.out.println("发票号码: " + invoiceNumber);
+                System.out.println("开票日期: " + issueTime);
+                System.out.println("销售方: " + sellerName);
+                System.out.println("购买方: " + buyerName);
+                System.out.println("价税合计: " + totalTaxIncluded + " 元");
+                System.out.println("不含税金额: " + totalWithoutTax + " 元");
+                System.out.println("税额: " + totalTax + " 元");
+                System.out.println("开票人: " + drawer);
+                System.out.println("请求时间: " + requestTime);
+                System.out.println("\n--- 商品明细 ---");
+                System.out.println("商品明细jsonArray: " + jsonArray_item);
+
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        if (jsonArray_item != null && jsonArray_item.size() > 0){
+            log.info("xml发票解析成功!" + jsonArray_item);
+            return R.ok().message("").data("items", jsonArray_item);
+        }else {
+            log.info("xml发票解析失败!");
+            return R.error().message("xml发票解析失败!");
+        }
+    }
+
+    //根据实例id获取宜搭表单数据
+    public JSONObject getYidaform(String instanceId) {
+        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 属性,可帮助开发定位问题
+            }
+        }
+        return jsonObject;
+    }
+
+    /**
+     * 工时填报触发创建工时审核
+     */
+    @Override
+    public R createGongshishenhe(String slid) {
+
+        /**工时填报触发创建工时审核*/
+        log.info("宜搭传参「实例id」--->" + slid);
+
+        /**根据实例id查询宜搭工时填报表单内容*/
+        JSONObject yidaform = getYidaform(slid);
+        JSONObject formData = yidaform.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);
+            //填报日期文本
+            String tianbaoriqiStr = formData.getString("textField_mrww26ab") != null ? formData.getString("textField_mrww26ab") : "";
+            System.out.println("填报日期文本:" + tianbaoriqiStr);
+            //填报总工时
+            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("当日已填报总工时:" + dangriyitianbaozonggongshi);
+            //工时填报明细
+            JSONArray jsonArray = formData.getJSONArray("tableField_mhbs93eh");
+            int all = jsonArray.size();//当前工时填报所有的明细条数
+            System.out.println("工时填报明细条数:" + all);
+            int now = 0;
+            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);
+                //工时填报明细-名称
+                String mx_mingcheng = jsonObject1.getString("textField_mhbs93eg");
+                System.out.println("工时填报明细-名称:" + mx_mingcheng);
+
+                /**获取宜搭工时填报表单内容进行创建工时填报审核流程*/
+                /**发起宜搭审批流程*/
+                Map map = new HashMap();
+                //填报单号
+                map.put("textField_miptigyy", tianbaodanhao);
+                //填报日期
+                map.put("dateField_mgt1sluh", tianbaoriqi);
+                //填报日期文本
+                map.put("textField_mrww26ab", tianbaoriqiStr);
+                //填报总工时
+                map.put("numberField_llunq7e2", tianbaozonggongshi);
+                //填报人员userid
+                map.put("employeeField_mhiv3pjk", tianbaorenyuanUserId);
+                //当日已填报总工时
+                map.put("numberField_mhiun888", dangriyitianbaozonggongshi);
+
+                //最新动态工时
+                map.put("numberField_mioiczps", 0);//TODO 数据联动条件【匹配项目工时填报中间表:按照「填报单号+填报日期+编号+成员」,匹配"已填报工时"】
+                //最新工时
+                map.put("numberField_mire06h5", 0);//TODO 计算规则:最新动态工时+工时
+
+                //类型
+                map.put("radioField_mhbs93e6", mx_leixing);
+                //名称
+                map.put("textField_mhbs93eg", mx_mingcheng);
+                //编号
+                map.put("textField_mhbs93ea", mx_bianhao);
+                //负责人
+                map.put("employeeField_mhiyyx13", mx_fuzerenUserId);
+                //客户名称
+                map.put("textField_mhbs93eb", mx_kehumingcheng);
+                //工时
+                map.put("numberField_mhbs93ec", mx_gongshi);
+                //工作内容
+                map.put("textareaField_mhbs93ed", mx_gongzuoneirong);
+                //备注
+                map.put("textareaField_mhbs93ee", mx_beizhu);
+
+                //填报总工时
+                map.put("numberField_mhbs93e5", mx_tianbaozonggongshi);
+                //工时填报审核状态
+                map.put("selectField_mhbs93ef", "未审核");
+                //总工时
+                map.put("numberField_mhbs93e3", mx_zonggongshi);
+                //已填报工时
+                map.put("numberField_mhbs93e4", mx_yitianbaogongshi);
+
+                String mapJson = JSON.toJSONString(map);
+                System.out.println("mapJson:" + mapJson);
+
+                //新增或更新表单实例【工时填报审核】
+                JSONObject jsonObject2 = null;
+                try {
+                    com.aliyun.dingtalkyida_1_0.Client client = createClient();
+                    com.aliyun.dingtalkyida_1_0.models.StartInstanceHeaders startInstanceHeaders = new com.aliyun.dingtalkyida_1_0.models.StartInstanceHeaders();
+                    startInstanceHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+                    com.aliyun.dingtalkyida_1_0.models.StartInstanceRequest startInstanceRequest = new com.aliyun.dingtalkyida_1_0.models.StartInstanceRequest()
+                            .setSystemToken("USC66V61YG8ZMBM5E8JQFBZQ6W0G3V1L6J0GMXE")
+                            .setFormUuid("FORM-B461B4A7B7DC4F1FB9AA4D5837CD8C2C1LUH")
+                            .setUserId(tianbaorenyuanUserId)//发起人身份yida_pub_account
+                            .setAppType("APP_XV6ZGK1162TI1Z1FE17D")
+                            .setFormDataJson(mapJson);
+
+                    StartInstanceResponse startInstanceResponse = client.startInstanceWithOptions(startInstanceRequest, startInstanceHeaders, new RuntimeOptions());
+                    String s = JSON.toJSONString(startInstanceResponse.getBody());
+                    jsonObject2 = JSON.parseObject(s);
+                    System.out.println("【创建工时填报审核】流程表单object对象:" + jsonObject2);
+                    String instanceId = jsonObject2.getString("result");
+                    if (!instanceId.equals("") && instanceId.length() > 0){
+                        now = now + 1;
+                    }
+                } 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 属性,可帮助开发定位问题
+                    }
+                }
+
+                //延时2秒
+                try {
+                    System.out.println("延时2秒循环下一条,避免调用频率过高===");
+                    Thread.sleep(2000);
+                }catch (InterruptedException e){
+                    e.printStackTrace();
+                }
+            }
+            if (all == now){
+                return R.ok().message("创建工时填报审核成功!");
+            }else {
+                return R.error().message("创建工时填报审核失败,未按照明细全部创建,请检查!");
+            }
+        }else {
+            return R.error().message("根据实例id获取工时填报数据为空,创建工时填报审核失败!");
+        }
+    }
+}

+ 584 - 0
src/main/java/com/muzhi/shuchan/service/impl/TeambitionServiceImpl.java

@@ -0,0 +1,584 @@
+package com.muzhi.shuchan.service.impl;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.dingtalkyida_1_0.models.GetOpenUrlResponse;
+import com.aliyun.dingtalkyida_1_0.models.StartInstanceResponse;
+import com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDResponse;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import com.muzhi.shuchan.common.R;
+import com.muzhi.shuchan.common.dingtalkyida.YiDaFormData;
+import com.muzhi.shuchan.common.fastjson.Util;
+import com.muzhi.shuchan.common.teambition.Idmap;
+import com.muzhi.shuchan.common.teambition.Project;
+import com.muzhi.shuchan.common.universal.Time;
+import com.muzhi.shuchan.entity.InvoiceItem;
+import com.muzhi.shuchan.service.AccessTokenService;
+import com.muzhi.shuchan.service.DingTalkService;
+import com.muzhi.shuchan.service.TeambitionService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+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.*;
+
+
+@Service
+@Slf4j
+public class TeambitionServiceImpl implements TeambitionService {
+
+    @Autowired
+    private AccessTokenService accessTokenService;
+
+    // 通用工具方法:如果不存在则添加
+    public static <T> void addIfAbsent(List<T> list, T element) {
+        if (!list.contains(element)) {
+            list.add(element);
+        }
+    }
+
+    /**
+     * 项目立项通过后创建TB项目信息
+     * 通过匹配项目模板创建
+     *
+     * @param tbmbid TB模板id
+     * @param xmbh 项目编号
+     * @param xmmc 项目名称
+     * @param xmzq 项目周期
+     *
+     * @param xmjlDingUserId 项目经理钉钉用户id
+     * @param ssbmDingDeptId 实施部门钉钉部门id
+     * @param pmoDingUserId PMO钉钉用户id
+     *
+     * @param sjfzrDingUserId 商机负责人钉钉用户id
+     * @param jjfafzrDingUserId 解决方案负责人钉钉用户id
+     * @param ssfazDingUserId 【实施方案组】钉钉用户id
+     * @param ssjszDingUserId 【实施建设组】钉钉用户id
+     * @param zlcszDingUserId 【质量测试组】钉钉用户id
+     * @param ywwbzDingUserId 【运维维保组】钉钉用户id
+     * @param bzgfzDingUserId 【标准规范组】钉钉用户id
+     * @param sjyyzDingUserId 【数据运营组】钉钉用户id
+     * @param xqdjzDingUserId 【需求对接组】钉钉用户id
+     * @param zlsszDingUserId 【治理实施组】钉钉用户id
+     * @param sjaqzDingUserId 【数据安全组】钉钉用户id
+     * @param ptaqzDingUserId 【平台安全组】钉钉用户id
+     * @param wlaqzDingUserId 【网络安全组】钉钉用户id
+     *
+     * @param sfwndlxjhxm 是否为年度立项计划项目
+     * @param xmlx 项目类型
+     * @param xmbq 项目标签
+     * @param xmxqly (项目)需求来源
+     * @param sfkslx 是否快速立项
+     * @param slid 实例id
+     *
+     * NOTE(TB模板id映射):
+     *     【数据运营 - 项目模板】:69031cec741ee887ba0aaa85
+     *     【等保服务 - 项目模板】:6904543acd44bbef88936163
+     *     【安全服务 - 项目模板】:690455ea7b2a84bef265dfa9
+     *     【开发类 - 项目模板】:690458c7c4e0c1ed239f22d1
+     *     【运维服务 - 项目模板】:69080c557899d5670762f632
+     */
+    @Override
+    public R createTeambitionProject(String tbmbid,
+                                     String xmbh, String xmmc, String xmzq,
+                                     String xmjlDingUserId, String ssbmDingDeptId, String pmoDingUserId,
+                                     String sjfzrDingUserId, String jjfafzrDingUserId,
+                                     String ssfazDingUserId, String ssjszDingUserId, String zlcszDingUserId, String ywwbzDingUserId,
+                                     String bzgfzDingUserId, String sjyyzDingUserId, String xqdjzDingUserId, String zlsszDingUserId,
+                                     String sjaqzDingUserId, String ptaqzDingUserId, String wlaqzDingUserId,
+                                     String sfwndlxjhxm, String xmlx, String xmbq, String xmxqly, String sfkslx,
+                                     String slid) {
+
+        /**项目立项通过后创建TB项目信息*/
+        log.info("项目立项通过后创建TB项目信息===");
+
+        log.info("TB模板id--->" + tbmbid);
+        log.info("项目编号--->" + xmbh);
+        log.info("项目名称--->" + xmmc);
+        log.info("项目周期--->" + xmzq);
+        log.info("项目经理钉钉用户id--->" + xmjlDingUserId);
+        log.info("实施部门钉钉部门id--->" + ssbmDingDeptId);
+        log.info("PMO钉钉用户id--->" + pmoDingUserId);
+
+        log.info("商机负责人钉钉用户id--->" + sjfzrDingUserId);//单人
+        log.info("解决方案负责人钉钉用户id--->" + jjfafzrDingUserId);//单人
+
+        log.info("【实施方案组】钉钉用户id--->" + ssfazDingUserId);//多人
+        log.info("【实施建设组】钉钉用户id--->" + ssjszDingUserId);//多人
+        log.info("【质量测试组】钉钉用户id--->" + zlcszDingUserId);//多人
+        log.info("【运维维保组】钉钉用户id--->" + ywwbzDingUserId);//多人
+        log.info("【标准规范组】钉钉用户id--->" + bzgfzDingUserId);//多人
+        log.info("【数据运营组】钉钉用户id--->" + sjyyzDingUserId);//多人
+        log.info("【需求对接组】钉钉用户id--->" + xqdjzDingUserId);//多人
+        log.info("【治理实施组】钉钉用户id--->" + zlsszDingUserId);//多人
+        log.info("【数据安全组】钉钉用户id--->" + sjaqzDingUserId);//多人
+        log.info("【平台安全组】钉钉用户id--->" + ptaqzDingUserId);//多人
+        log.info("【网络安全组】钉钉用户id--->" + wlaqzDingUserId);//多人
+
+        log.info("是否为年度立项计划项目--->" + sfwndlxjhxm);
+        log.info("项目类型--->" + xmlx);
+        log.info("项目标签--->" + xmbq);
+        log.info("项目需求来源--->" + xmxqly);
+        log.info("是否快速立项--->" + sfkslx);
+        log.info("宜搭传参「实例id」--->" + slid);
+
+        String adminTBuserId = "68dd29dceeb2af5b9da833d6";
+
+        /*根据项目编号判断项目是否已经存在TB*/
+        //企业级自定义字段,项目编号:69b7a841922144cd6e83acc0
+        String tql = "isArchived = false AND cf:69b7a841922144cd6e83acc0 = " + xmbh;
+        JSONArray projectIdArray = Project.searchProjectIds(tql, accessTokenService.getAppToken());
+
+        if (xmbh.length() < 11){
+            log.error("项目编号长度小于11位,同步中断,请检查项目编号是否正确!");
+            return R.error().message("项目编号长度小于11位,同步中断,请检查项目编号是否正确!");
+        }else {
+            if (projectIdArray.size() > 0){
+                log.error("项目已存在TB,开始更新项目======");
+
+                /*更新项目时间周期*/
+                //TODO
+
+                /*更新项目概览字段*/
+                //TODO
+
+                /*更新项目成员*/
+                //TODO
+
+            }else {
+                log.info("项目不存在TB,开始创建项目======");
+                if (tbmbid == null || tbmbid.equals("")){
+                    if (xmbq == null || xmbq.equals("")){
+                        log.error("项目标签为空,请检查项目标签是否正确!");
+                        /*更新项目档案-写入日志记录*/
+                        Map formMap = new HashMap<>();
+                        formMap.put("selectField_mn33fn3i", "推送失败");//推送TB状态
+                        formMap.put("textField_mn33fn3n", "项目标签为空,请检查项目标签是否正确!");//推送日志信息
+                        String formDataJson = JSON.toJSONString(formMap);
+                        System.out.println("mapJson:" + formDataJson);
+
+                        Map searchMap = new HashMap<>();
+                        searchMap.put("key", "textField_mhvfugv3");
+                        searchMap.put("value", xmbh);
+                        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);
+                        YiDaFormData.createOrUpdateFormData("FORM-E82C76C732F34F8FBA318095ABA0F4DF1L3W", searchCondition, formDataJson, accessTokenService.getAccessToken());
+
+                        return R.error().message("项目标签为空,请检查项目标签是否正确!");
+                    }else {
+                        if(xmbq.equals("数据运营")){
+                            tbmbid = "69031cec741ee887ba0aaa85";
+                        } else if (xmbq.equals("等保服务")){
+                            tbmbid = "6904543acd44bbef88936163";
+                        } else if (xmbq.equals("安全服务")) {
+                            tbmbid = "690455ea7b2a84bef265dfa9";
+                        } else if (xmbq.equals("软件开发")) {
+                            tbmbid = "690458c7c4e0c1ed239f22d1";
+                        } else if (xmbq.equals("运维服务")) {
+                            tbmbid = "69080c557899d5670762f632";
+                        }
+                    }
+                }
+
+                /**通过IDMap查询TBuserId*/
+                log.info("调用【通过IDMap查询TBuserId】接口......");
+                //项目经理
+                String xmjlTbUserId = Idmap.getTbUserId(Util.getStringToList(xmjlDingUserId), accessTokenService.getAppToken());
+                System.out.println("通过IDMap查询TBuserId(项目经理):" + xmjlTbUserId);
+                //PMO
+                String pmoTbUserId = Idmap.getTbUserId(Util.getStringToList(pmoDingUserId), accessTokenService.getAppToken());
+                System.out.println("通过IDMap查询TBuserId(PMO):" + pmoTbUserId);
+                //实施部门
+                String ssbmTbDeptId = Idmap.getTeamIdByDingTeamId(Util.getStringToList(ssbmDingDeptId), accessTokenService.getAppToken());
+                System.out.println("通过IDMap查询TBteamId(实施部门id):" + ssbmTbDeptId);
+                String teamName = Idmap.getTeamNameByTeamId(ssbmTbDeptId, accessTokenService.getAppToken());
+                System.out.println("通过IDMap查询TBteamName(实施部门名称):" + teamName);
+
+                //提取项目周期的开始和结束时间戳
+                //项目开始时间戳
+                String xmkssjStrLong = Util.getStringToIndexList(xmzq, 0);
+                long xmkssjLong = Long.parseLong(xmkssjStrLong);
+                String xmjssjStrLong = Util.getStringToIndexList(xmzq, 1);
+                long xmjssjLong = Long.parseLong(xmjssjStrLong);
+
+                if (tbmbid == null){
+                    log.error("根据当前项目标签未匹配到TB模板id,同步中断!!!");
+                    /*更新项目档案-写入日志记录*/
+                    Map formMap = new HashMap<>();
+                    formMap.put("selectField_mn33fn3i", "推送失败");//推送TB状态
+                    formMap.put("textField_mn33fn3n", "❌根据当前项目标签未匹配到TB模板id,同步中断!!!");//推送日志信息
+                    String formDataJson = JSON.toJSONString(formMap);
+                    System.out.println("mapJson:" + formDataJson);
+
+                    Map searchMap = new HashMap<>();
+                    searchMap.put("key", "textField_mhvfugv3");
+                    searchMap.put("value", xmbh);
+                    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);
+                    YiDaFormData.createOrUpdateFormData("FORM-E82C76C732F34F8FBA318095ABA0F4DF1L3W", searchCondition, formDataJson, accessTokenService.getAccessToken());
+
+                    return R.error().message("根据当前项目标签未匹配到TB模板id,同步中断!!!");
+                }else {
+                    if (xmjlTbUserId == null){
+                        log.error("通过IDMap查询TBuserId(项目经理)为空,同步中断!!!");
+                        /*更新项目档案-写入日志记录*/
+                        Map formMap = new HashMap<>();
+                        formMap.put("selectField_mn33fn3i", "推送失败");//推送TB状态
+                        formMap.put("textField_mn33fn3n", "❌通过IDMap查询TBuserId(项目经理)为空,同步中断!!!");//推送日志信息
+                        String formDataJson = JSON.toJSONString(formMap);
+                        System.out.println("mapJson:" + formDataJson);
+
+                        Map searchMap = new HashMap<>();
+                        searchMap.put("key", "textField_mhvfugv3");
+                        searchMap.put("value", xmbh);
+                        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);
+                        YiDaFormData.createOrUpdateFormData("FORM-E82C76C732F34F8FBA318095ABA0F4DF1L3W", searchCondition, formDataJson, accessTokenService.getAccessToken());
+
+                        return R.error().message("通过IDMap查询TBuserId(项目经理)为空,同步中断!!!");
+                    }else {
+                        /*根据模板创建项目*/
+                        String projectId = Project.createFromTemplate(xmmc, tbmbid, Time.getUtcToTimestamp(xmkssjLong), Time.getUtcToTimestamp(xmjssjLong), xmjlTbUserId, accessTokenService.getAppToken());
+                        if (projectId != null){
+                            log.info("【根据模板创建项目】成功,开始更新项目概览字段===");
+                            /*更新项目概览字段*/
+                            Project.updateProjectCustomField(null, xmjlTbUserId, projectId, "项目编号", xmbh, accessTokenService.getAppToken());
+                            Project.updateProjectCustomField("成员组件", xmjlTbUserId, projectId, "项目经理", xmjlTbUserId, accessTokenService.getAppToken());
+                            Project.updateProjectCustomField(null, xmjlTbUserId, projectId, "实施部门", teamName, accessTokenService.getAppToken());
+                            Project.updateProjectCustomField("成员组件", xmjlTbUserId, projectId, "PMO", pmoTbUserId, accessTokenService.getAppToken());
+                            Project.updateProjectCustomField(null, xmjlTbUserId, projectId, "是否为年度立项计划项目", sfwndlxjhxm, accessTokenService.getAppToken());
+                            Project.updateProjectCustomField(null, xmjlTbUserId, projectId, "项目类型", xmlx, accessTokenService.getAppToken());
+                            Project.updateProjectCustomField(null, xmjlTbUserId, projectId, "项目标签", xmbq, accessTokenService.getAppToken());
+                            Project.updateProjectCustomField(null, xmjlTbUserId, projectId, "(项目)需求来源", xmxqly, accessTokenService.getAppToken());
+                            Project.updateProjectCustomField(null, xmjlTbUserId, projectId, "是否快速立项", sfkslx, accessTokenService.getAppToken());
+
+                            /*创建项目成员*/
+                            log.info("【创建项目成员】开始===");
+                            List listEmpAll = new ArrayList();
+                            //数发子管理员//REMARK 固定每个项目中该账号都需要加入,并在创建项目后更新该账号为该项目【管理员】角色
+                            addIfAbsent(listEmpAll, adminTBuserId);
+
+                            //PMO
+                            if (pmoTbUserId != null){
+                                if (pmoTbUserId != xmjlTbUserId){
+                                    addIfAbsent(listEmpAll, pmoTbUserId);
+                                }
+                            }
+                            //商机负责人
+                            String sjfzrTbUserId = Idmap.getTbUserId(Util.getStringToList(sjfzrDingUserId), accessTokenService.getAppToken());
+                            System.out.println("通过IDMap查询TBuserId(商机负责人):" + sjfzrTbUserId);
+                            if (sjfzrTbUserId != null){
+                                if (sjfzrTbUserId != xmjlTbUserId){
+                                    addIfAbsent(listEmpAll, sjfzrTbUserId);
+                                }
+                            }
+                            //解决方案负责人
+                            String jjfafzrTbUserId = Idmap.getTbUserId(Util.getStringToList(jjfafzrDingUserId), accessTokenService.getAppToken());
+                            System.out.println("通过IDMap查询TBuserId(解决方案负责人):" + jjfafzrTbUserId);
+                            if (jjfafzrTbUserId != null){
+                                if (jjfafzrTbUserId != xmjlTbUserId){
+                                    addIfAbsent(listEmpAll, jjfafzrTbUserId);
+                                }
+                            }
+                            //实施方案组
+                            if (ssfazDingUserId != null){
+                                List<String> list1 = JSON.parseArray(ssfazDingUserId, String.class);
+                                for (int i = 0; i < list1.size(); i++) {
+                                    String str = list1.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String ssfazTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(实施方案组):" + ssfazTbUserId);
+                                    if (ssfazTbUserId != null){
+                                        if (ssfazTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, ssfazTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            //实施建设组
+                            if (ssjszDingUserId != null){
+                                List<String> list2 = JSON.parseArray(ssjszDingUserId, String.class);
+                                for (int i = 0; i < list2.size(); i++) {
+                                    String str = list2.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String ssjszTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(实施建设组):" + ssjszTbUserId);
+                                    if (ssjszTbUserId != null){
+                                        if (ssjszTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, ssjszTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            //质量测试组
+                            if (zlcszDingUserId != null){
+                                List<String> list3 = JSON.parseArray(zlcszDingUserId, String.class);
+                                for (int i = 0; i < list3.size(); i++) {
+                                    String str = list3.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String zlcszTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(质量测试组):" + zlcszTbUserId);
+                                    if (zlcszTbUserId != null){
+                                        if (zlcszTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, zlcszTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            //运维维保组
+                            if (ywwbzDingUserId != null){
+                                List<String> list4 = JSON.parseArray(ywwbzDingUserId, String.class);
+                                for (int i = 0; i < list4.size(); i++) {
+                                    String str = list4.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String ywwbzTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(运维维保组):" + ywwbzTbUserId);
+                                    if (ywwbzTbUserId != null){
+                                        if (ywwbzTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, ywwbzTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            //标准规范组
+                            if (bzgfzDingUserId != null){
+                                List<String> list5 = JSON.parseArray(bzgfzDingUserId, String.class);
+                                for (int i = 0; i < list5.size(); i++) {
+                                    String str = list5.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String bzgfzTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(标准规范组):" + bzgfzTbUserId);
+                                    if (bzgfzTbUserId != null){
+                                        if (bzgfzTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, bzgfzTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            //数据运营组
+                            if (sjyyzDingUserId != null){
+                                List<String> list6 = JSON.parseArray(sjyyzDingUserId, String.class);
+                                for (int i = 0; i < list6.size(); i++) {
+                                    String str = list6.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String sjyyzTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(数据运营组):" + sjyyzTbUserId);
+                                    if (sjyyzTbUserId != null){
+                                        if (sjyyzTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, sjyyzTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            //需求对接组
+                            if (xqdjzDingUserId != null){
+                                List<String> list7 = JSON.parseArray(xqdjzDingUserId, String.class);
+                                for (int i = 0; i < list7.size(); i++) {
+                                    String str = list7.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String xqdjzTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(需求对接组):" + xqdjzTbUserId);
+                                    if (xqdjzTbUserId != null){
+                                        if (xqdjzTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, xqdjzTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            //治理实施组
+                            if (zlsszDingUserId != null){
+                                List<String> list8 = JSON.parseArray(zlsszDingUserId, String.class);
+                                for (int i = 0; i < list8.size(); i++) {
+                                    String str = list8.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String zlsszTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(治理实施组):" + zlsszTbUserId);
+                                    if (zlsszTbUserId != null){
+                                        if (zlsszTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, zlsszTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            //数据安全组
+                            if (sjaqzDingUserId != null){
+                                List<String> list9 = JSON.parseArray(sjaqzDingUserId, String.class);
+                                for (int i = 0; i < list9.size(); i++) {
+                                    String str = list9.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String sjaqzTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(数据安全组):" + sjaqzTbUserId);
+                                    if (sjaqzTbUserId != null){
+                                        if (sjaqzTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, sjaqzTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            //平台安全组
+                            if (ptaqzDingUserId != null){
+                                List<String> list10 = JSON.parseArray(ptaqzDingUserId, String.class);
+                                for (int i = 0; i < list10.size(); i++) {
+                                    String str = list10.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String ptaqzTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(平台安全组):" + ptaqzTbUserId);
+                                    if (ptaqzTbUserId != null){
+                                        if (ptaqzTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, ptaqzTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            //网络安全组
+                            if (wlaqzDingUserId != null){
+                                List<String> list11 = JSON.parseArray(wlaqzDingUserId, String.class);
+                                for (int i = 0; i < list11.size(); i++) {
+                                    String str = list11.get(i);
+                                    System.out.println("获取到的多人数组内人员的钉钉id: " + str);
+                                    String wlaqzTbUserId = Idmap.getTbUserId(str, accessTokenService.getAppToken());
+                                    System.out.println("通过IDMap查询TBuserId(网络安全组):" + wlaqzTbUserId);
+                                    if (wlaqzTbUserId != null){
+                                        if (wlaqzTbUserId != xmjlTbUserId){
+                                            addIfAbsent(listEmpAll, wlaqzTbUserId);
+                                        }
+                                    }
+                                }
+                            }
+                            Project.createProjectMember(xmjlTbUserId, projectId, listEmpAll, accessTokenService.getAppToken());
+
+                            /*获取该项目角色列表*/
+                            JSONArray projectRoleList = Project.getProjectRoleList(projectId, accessTokenService.getAppToken());
+                            String roleId = null;
+                            if (projectRoleList.size() > 0 && projectRoleList != null && !projectRoleList.isEmpty()){
+                                for (int i = 0; i < projectRoleList.size(); i++) {
+                                    JSONObject item = projectRoleList.getJSONObject(i);
+                                    if ("项目管理员".equals(item.getString("name"))) {
+                                        roleId = item.getString("id");
+                                    }
+                                }
+                            }
+                            System.out.println("该项目管理员角色的 id:" + roleId);
+                            if (roleId != null){
+                                /*修改项目成员的角色-将「数发子管理员」的账号在该项目中更新为该项目的【管理员】角色*/
+                                Project.updateProjectRole(xmjlTbUserId,projectId,adminTBuserId,roleId,accessTokenService.getAppToken());
+                            }
+
+                            /*根据部门名称获取TB项目集id*/
+                            String xmjMessage = null;
+                            String programId = Idmap.getProgramIdByTeamName(teamName, accessTokenService.getAppToken());
+                            if (programId != null && programId != ""){
+                                log.info("根据部门名称获取TB项目集id成功,项目集ID:" + programId);
+                                log.info("开始将当前项目添加至该项目集===");
+                                /*添加项目到项目集*/
+                                int projectToProjectSet = Project.createProjectToProjectSet(programId, projectId, adminTBuserId, accessTokenService.getAppToken());
+                                if (projectToProjectSet == 200){
+                                    log.info("项目添加至项目集成功!");
+                                    xmjMessage = "✅项目添加至【"+teamName+"】项目集-->成功!";
+                                }else {
+                                    log.info("项目添加至项目集失败!");
+                                    xmjMessage = "❌项目添加至【"+teamName+"】项目集-->失败!";
+                                }
+                            }else {
+                                log.info("根据部门名称获取TB项目集id失败,请检查部门名称是否正确!现在将项目添加至默认项目集===");
+                                String parentId = "69ccab946970d23e8d30ad55";//父项目集id,默认:上海数字产业发展有限公司
+                                int projectToProjectSet = Project.createProjectToProjectSet(parentId, projectId, adminTBuserId, accessTokenService.getAppToken());
+                                if (projectToProjectSet == 200){
+                                    log.info("项目添加至默认项目集成功!");
+                                    xmjMessage = "⚠️项目添加至【上海数字产业发展有限公司】默认项目集-->成功!";
+                                }else {
+                                    log.info("项目添加至默认项目集失败!");
+                                    xmjMessage = "❌项目添加至【上海数字产业发展有限公司】默认项目集-->失败!";
+                                }
+                            }
+
+                            log.info("项目立项通过后创建TB项目信息-->成功!");
+                            /*更新项目档案-写入日志记录*/
+                            Map formMap = new HashMap<>();
+                            formMap.put("selectField_mn33fn3i", "推送成功");//推送TB状态
+                            formMap.put("textField_mn33fn3n", "✅项目启动通过后创建TB项目信息-->成功!" + xmjMessage);//推送日志信息
+                            formMap.put("textField_mn3cf6t4", projectId);//TB项目ID
+                            String formDataJson = JSON.toJSONString(formMap);
+                            System.out.println("mapJson:" + formDataJson);
+
+                            Map searchMap = new HashMap<>();
+                            searchMap.put("key", "textField_mhvfugv3");
+                            searchMap.put("value", xmbh);
+                            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);
+                            YiDaFormData.createOrUpdateFormData("FORM-E82C76C732F34F8FBA318095ABA0F4DF1L3W", searchCondition, formDataJson, accessTokenService.getAccessToken());
+
+                            return R.ok().message("项目立项通过后创建TB项目信息-->成功!");
+                        }else {
+                            log.error("【根据模板创建项目】失败,同步中断!!!");
+                            /*更新项目档案-写入日志记录*/
+                            Map formMap = new HashMap<>();
+                            formMap.put("selectField_mn33fn3i", "推送失败");//推送TB状态
+                            formMap.put("textField_mn33fn3n", "❌【根据模板创建项目】失败,同步中断!!!");//推送日志信息
+                            String formDataJson = JSON.toJSONString(formMap);
+                            System.out.println("mapJson:" + formDataJson);
+
+                            Map searchMap = new HashMap<>();
+                            searchMap.put("key", "textField_mhvfugv3");
+                            searchMap.put("value", xmbh);
+                            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);
+                            YiDaFormData.createOrUpdateFormData("FORM-E82C76C732F34F8FBA318095ABA0F4DF1L3W", searchCondition, formDataJson, accessTokenService.getAccessToken());
+
+                            return R.error().message("【根据模板创建项目】失败,同步中断!!!");
+                        }
+                    }
+                }
+            }
+        }
+
+        return R.ok().message("项目立项通过后创建/更新TB项目信息-->执行结束!");
+    }
+}

+ 14 - 8
main/resources/application.properties

@@ -1,8 +1,20 @@
-server.port=8107
-server.servlet.context-path=/huanshi
+server.port=8100
+server.servlet.context-path=/shuchan
 
 server.tomcat.uri-encoding=UTF-8
 
+#TB
+myapp.teambition.tbOrganizationId=6008e33803f7c491c204d08a
+
+#YIDA
+myapp.yida.appType=APP_XV6ZGK1162TI1Z1FE17D
+myapp.yida.appSecret=USC66V61YG8ZMBM5E8JQFBZQ6W0G3V1L6J0GMXE
+
+#?????????
+ribbon:
+    ReadTimeoutMillis: 5000
+    ConnectTimeoutMillis: 5000
+
 ## 服务名
 #spring.application.name=simaner-yd
 ## 环境设置:dev、test、prod(开发环境、测试环境、生产环境)
@@ -15,12 +27,6 @@ server.tomcat.uri-encoding=UTF-8
 #spring.datasource.username=root
 #spring.datasource.password=123456
 
-# 云璞【47.98.130.40服务器】测试数据库
-#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
-#spring.datasource.url=jdbc:mysql://47.97.181.40:3306/dingtalk?serverTimezone=GMT%2B8
-#spring.datasource.username=root
-#spring.datasource.password=cp-root@2022++
-
 #定时同步
 
 #配置日志,当前为默认的控制台输出,也可以用log4j

+ 546 - 0
src/test/java/com/muzhi/shuchan/APITest/Test1.java

@@ -0,0 +1,546 @@
+package com.muzhi.shuchan.APITest;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.dingtalkyida_1_0.models.GetOpenUrlResponse;
+import com.aliyun.dingtalkyida_2_0.models.CreateOrUpdateFormDataResponse;
+import com.aliyun.dingtalkyida_2_0.models.GetFormDataByIDResponse;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import com.muzhi.shuchan.service.AccessTokenService;
+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.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@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);
+    }
+
+    @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);
+            }
+        }
+    }
+
+    @Test
+    public void test7(){
+        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfYXBwSWQiOiI2OTNiOGM1NmU3MzU4NDEzZDMzODA2ZWMiLCJleHAiOjE3NzQyNzc3MDgsImlhdCI6MTc3NDI3NDEwOH0.gNhjQOVbztEpiH5ZMGuUUtY9BecEIX1-o2oZh-eEC-U";
+        String xmid = "69c14c13702bcba476738e35";
+        String operatorId = "65a61a48d21f06c4b5885f43";
+        String tbOrganizationId = "6008e33803f7c491c204d08a";
+        List<String> listAllTbUserId = new ArrayList<>();
+        listAllTbUserId.add("65a61a48d21f06c4b5885f43");
+        listAllTbUserId.add("68b165a2411974f65da630c9");
+        listAllTbUserId.add("61a6b2c3e466a93dd8f444a4");
+
+        Map mapAll = new HashMap();
+        mapAll.put("userIds", listAllTbUserId);
+
+        String mapStr = JSON.toJSONString(mapAll);
+        System.out.println("封装更新的字段参数:" + mapStr);
+
+
+        OkHttpClient client = new OkHttpClient().newBuilder()
+                .build();
+        MediaType mediaType = MediaType.parse("application/json");
+        RequestBody body = RequestBody.create(mediaType, mapStr);
+        Request request = new Request.Builder()
+                .url("https://open.teambition.com/api/v3/project/"+ xmid +"/member/create-v2")
+                .method("POST", body)
+                .addHeader("x-operator-id", operatorId)
+                .addHeader("Authorization", "Bearer " + token)
+                .addHeader("X-Tenant-Id", tbOrganizationId)
+                .addHeader("X-Tenant-Type", "organization")
+                .addHeader("Content-Type", "application/json")
+                .build();
+
+        int code1 = 0;
+        try {
+            Response response1 = client.newCall(request).execute();
+            String bodyStr1 = response1.body().string();
+            JSONObject jsonObject1 = JSON.parseObject(bodyStr1);
+            System.out.println("返回体body:" + jsonObject1);
+            code1 = jsonObject1.getInteger("code");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        System.out.println("【创建项目成员 v2】接口返回的状态码(200成功,400异常):" + code1);
+    }
+
+    @Test
+    public void test8(){
+
+        String dingUserIds = "157311209";
+        String tbOrganizationId = "6008e33803f7c491c204d08a";
+
+        int code = 0;
+        JSONArray result = null;
+        String tbUserId = null;
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            RequestBody body = RequestBody.create(mediaType, "");
+            Request request = new Request.Builder()
+                    .url("https://open.teambition.com/api/idmap/dingtalk/getTbUserId?dingUserIds=" + dingUserIds)
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + accessTokenService.getAppToken())
+                    .addHeader("X-Tenant-Id", tbOrganizationId)
+                    .addHeader("X-Tenant-Type", "organization")
+                    .build();
+            Response response = client.newCall(request).execute();
+
+            String bodyStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject);
+            code = jsonObject.getInteger("code");
+            System.out.println("TB获取人员接口返回状态码:" + code);
+
+            result = jsonObject.getJSONArray("result");
+            System.out.println("TB获取人员接口返回result:" + result);
+            if (result.size() > 0 && result != null && !result.isEmpty()){
+                tbUserId = result.getJSONObject(0).getString("tbUserId");
+            }
+        }catch (IOException e){
+            e.printStackTrace();
+        }
+        System.out.println("根据钉钉userid匹配的TBuserId:" + tbUserId);
+    }
+
+    @Test
+    public void test9(){
+        String bumenName = "测试项目集";
+        String tbOrganizationId = "6008e33803f7c491c204d08a";
+
+        int code = 0;
+        JSONArray result = null;
+        String xmjId = null;
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            RequestBody body = RequestBody.create(mediaType, "");
+            Request request = new Request.Builder()
+                    .url("https://open.teambition.com/api/v3/program/query?q=" + bumenName)
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + accessTokenService.getAppToken())
+                    .addHeader("X-Tenant-Id", tbOrganizationId)
+                    .addHeader("X-Tenant-Type", "organization")
+                    .build();
+            Response response = client.newCall(request).execute();
+
+            String bodyStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject);
+            code = jsonObject.getInteger("code");
+            System.out.println("TB接口返回状态码:" + code);
+
+            result = jsonObject.getJSONArray("result");
+            System.out.println("TB接口返回result:" + result);
+            if (result.size() > 0 && result != null && !result.isEmpty()){
+                xmjId = result.getJSONObject(0).getString("id");
+            }
+        }catch (IOException e){
+            e.printStackTrace();
+        }
+        System.out.println("根据部门名称匹配TB项目集id:" + xmjId);
+    }
+
+    @Test
+    public void test10(){
+        String tbOrganizationId = "6008e33803f7c491c204d08a";
+
+        String projectId = "6a6b1daf068f4cbde450e3c5";
+        String programId = "6a6f5a34b3e2a1425a32d55e";
+        String operatorId = "58eae4f25dd5232434e197fa";
+
+        //封装待添加项目集的项目id
+        Map map = new HashMap();
+        List list = new ArrayList();
+        list.add(projectId);
+        map.put("projectIds",list);
+
+        String mapJson = JSON.toJSONString(map);
+        System.out.println("封装待添加项目集的项目id:" + mapJson);
+
+        OkHttpClient client = new OkHttpClient().newBuilder()
+                .build();
+        MediaType mediaType = MediaType.parse("application/json");
+        RequestBody body = RequestBody.create(mediaType, mapJson);
+        Request request = new Request.Builder()
+                .url("https://open.teambition.com/api/v3/program/"+ programId +"/project/create")
+                .method("POST", body)
+                .addHeader("x-operator-id", operatorId)
+                .addHeader("Authorization", "Bearer " + accessTokenService.getAppToken())
+                .addHeader("X-Tenant-Id", tbOrganizationId)
+                .addHeader("X-Tenant-Type", "organization")
+                .addHeader("Content-Type", "application/json")
+                .build();
+
+        int code = 0;
+        String errorMessage = "";
+        try {
+            Response response = client.newCall(request).execute();
+            String bodyStr = response.body().string();
+            JSONObject jsonObject11 = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject11);
+            code = jsonObject11.getInteger("code");
+            errorMessage = jsonObject11.getString("errorMessage");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        System.out.println("【批量添加项目到项目集】接口返回的状态码(200成功,400异常):" + code);
+        System.out.println("【批量添加项目到项目集】接口返回的异常状态信息(errorMessage):" + errorMessage);
+    }
+
+    @Test
+    public void test11(){
+        String projectId = "6a6b1daf068f4cbde450e3c5";
+
+        int code = 0;
+        JSONArray result = null;
+        try {
+            OkHttpClient client = new OkHttpClient().newBuilder()
+                    .build();
+            MediaType mediaType = MediaType.parse("text/plain");
+            RequestBody body = RequestBody.create(mediaType, "");
+            Request request = new Request.Builder()
+                    .url("https://open.teambition.com/api/v3/project/" + projectId + "/role")
+                    .method("GET", null)
+                    .addHeader("Authorization", "Bearer " + accessTokenService.getAppToken())
+                    .addHeader("X-Tenant-Id", "6008e33803f7c491c204d08a")
+                    .addHeader("X-Tenant-Type", "organization")
+                    .build();
+            Response response = client.newCall(request).execute();
+
+            String bodyStr = response.body().string();
+            JSONObject jsonObject = JSON.parseObject(bodyStr);
+            System.out.println("返回体body:" + jsonObject);
+            code = jsonObject.getInteger("code");
+            System.out.println("TB接口返回状态码:" + code);
+
+            result = jsonObject.getJSONArray("result");
+            System.out.println("TB接口返回result:" + result);
+        }catch (IOException e){
+            e.printStackTrace();
+        }
+        System.out.println("根据项目id查询的项目角色列表:" + result);
+
+        String id = null;
+        if (result.size() > 0 && result != null && !result.isEmpty()){
+            for (int i = 0; i < result.size(); i++) {
+                JSONObject item = result.getJSONObject(i);
+                if ("项目管理员".equals(item.getString("name"))) {
+                    id = item.getString("id");
+                    break;
+                }
+            }
+        }
+        System.out.println("项目管理员的 id:" + id);
+    }
+
+    @Test
+    public void test12(){
+        String operatorId = "68b165a2411974f65da630c9";
+
+        String xmid = "6a6afb1dcfcd439e91b5a76a";
+        String tbUserId = "68b165a2411974f65da630c9";
+        String roleId = "6a6afb1d357f23bd75c24165";
+
+        Map mapAll = new HashMap();
+        List listTbUserId = new ArrayList();
+        listTbUserId.add(tbUserId);
+        List listRoleId = new ArrayList();
+        listRoleId.add(roleId);
+        mapAll.put("userIds", listTbUserId);
+        mapAll.put("roleIds", listRoleId);
+
+        String mapStr = JSON.toJSONString(mapAll);
+        System.out.println("封装更新的字段参数:" + mapStr);
+
+        OkHttpClient client = new OkHttpClient().newBuilder()
+                .build();
+        MediaType mediaType = MediaType.parse("application/json");
+        RequestBody body = RequestBody.create(mediaType, mapStr);
+        Request request = new Request.Builder()
+                .url("https://open.teambition.com/api/v3/project/"+ xmid +"/member/role/assign")
+                .method("POST", body)
+                .addHeader("x-operator-id", operatorId)
+                .addHeader("Authorization", "Bearer " + accessTokenService.getAppToken())
+                .addHeader("X-Tenant-Id", "6008e33803f7c491c204d08a")
+                .addHeader("X-Tenant-Type", "organization")
+                .addHeader("Content-Type", "application/json")
+                .build();
+
+        int code1 = 0;
+        try {
+            Response response1 = client.newCall(request).execute();
+            String bodyStr1 = response1.body().string();
+            JSONObject jsonObject1 = JSON.parseObject(bodyStr1);
+            System.out.println("返回体body:" + jsonObject1);
+            code1 = jsonObject1.getInteger("code");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        System.out.println("【修改项目成员的角色】接口返回的状态码(200成功,400异常):" + code1);
+    }
+}

+ 1 - 1
test/java/com/muzhi/huanshi/TestApplicationTests.java

@@ -1,4 +1,4 @@
-package com.muzhi.huanshi;
+package com.muzhi.shuchan;
 
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 3321
test/java/com/muzhi/huanshi/APITest/Test1.java