lfx 1 year ago
parent
commit
a0bb498838
31 changed files with 3513 additions and 0 deletions
  1. 26 0
      src/main/java/com/muzhi/tianhe/TianHeApplication.java
  2. 27 0
      src/main/java/com/muzhi/tianhe/config/AsyncConfig.java
  3. 20 0
      src/main/java/com/muzhi/tianhe/config/MybatisPlusConfig.java
  4. 41 0
      src/main/java/com/muzhi/tianhe/controller/AccessTokenController.java
  5. 58 0
      src/main/java/com/muzhi/tianhe/controller/DingController.java
  6. 89 0
      src/main/java/com/muzhi/tianhe/controller/TbController.java
  7. 38 0
      src/main/java/com/muzhi/tianhe/controller/YiDaController.java
  8. 103 0
      src/main/java/com/muzhi/tianhe/entity/Tianhe.java
  9. 13 0
      src/main/java/com/muzhi/tianhe/entity/User.java
  10. 14 0
      src/main/java/com/muzhi/tianhe/entity/vo/TianheDataVo.java
  11. 36 0
      src/main/java/com/muzhi/tianhe/entity/vo/TianheQuery.java
  12. 72 0
      src/main/java/com/muzhi/tianhe/exceptionhander/R.java
  13. 12 0
      src/main/java/com/muzhi/tianhe/exceptionhander/ResultCode.java
  14. 11 0
      src/main/java/com/muzhi/tianhe/mapper/TianheMapper.java
  15. 5 0
      src/main/java/com/muzhi/tianhe/mapper/xml/TianheMapper.xml
  16. 14 0
      src/main/java/com/muzhi/tianhe/service/AccessTokenService.java
  17. 16 0
      src/main/java/com/muzhi/tianhe/service/DingService.java
  18. 7 0
      src/main/java/com/muzhi/tianhe/service/TbProjectZbService.java
  19. 35 0
      src/main/java/com/muzhi/tianhe/service/TbService.java
  20. 10 0
      src/main/java/com/muzhi/tianhe/service/YiDaService.java
  21. 88 0
      src/main/java/com/muzhi/tianhe/service/impl/AccessTokenServiceImpl.java
  22. 1072 0
      src/main/java/com/muzhi/tianhe/service/impl/DingServiceImpl.java
  23. 146 0
      src/main/java/com/muzhi/tianhe/service/impl/TbApiService.java
  24. 93 0
      src/main/java/com/muzhi/tianhe/service/impl/TbProjectZbServiceImpl.java
  25. 667 0
      src/main/java/com/muzhi/tianhe/service/impl/TbServiceImpl.java
  26. 146 0
      src/main/java/com/muzhi/tianhe/service/impl/YiDaServiceImpl.java
  27. 26 0
      src/main/java/com/muzhi/tianhe/util/PublicUtil.java
  28. 274 0
      src/main/java/com/muzhi/tianhe/util/UtilExcel.java
  29. 239 0
      src/main/java/com/muzhi/tianhe/util/UtilFile.java
  30. 57 0
      src/main/resources/application.properties
  31. 58 0
      src/test/java/com/muzhi/tianhe/TbTest.java

+ 26 - 0
src/main/java/com/muzhi/tianhe/TianHeApplication.java

@@ -0,0 +1,26 @@
+package com.muzhi.tianhe;
+
+//import org.mybatis.spring.annotation.MapperScan;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+
+@MapperScan("com.muzhi.tianhe.mapper")
+@EnableAsync
+@SpringBootApplication//(exclude= DataSourceAutoConfiguration.class)
+@EnableScheduling//开启定时任务
+//@ComponentScan(basePackages = {"com.muzhi"})
+public class TianHeApplication {
+
+    public static void main(String[] args){
+        try {
+            SpringApplication.run(TianHeApplication.class, args);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+}

+ 27 - 0
src/main/java/com/muzhi/tianhe/config/AsyncConfig.java

@@ -0,0 +1,27 @@
+package com.muzhi.tianhe.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.task.TaskExecutor;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+@Configuration
+@EnableAsync // 开启异步任务支持
+public class AsyncConfig {
+    @Bean
+    public TaskExecutor taskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        // 设置核心线程数
+        executor.setCorePoolSize(5);
+        // 最大线程数
+        executor.setMaxPoolSize(10);
+        // 队列容量
+        executor.setQueueCapacity(20);
+        // 线程池名前缀
+        executor.setThreadNamePrefix("Async-");
+        // 初始化
+        executor.initialize();
+        return executor;
+    }
+}

+ 20 - 0
src/main/java/com/muzhi/tianhe/config/MybatisPlusConfig.java

@@ -0,0 +1,20 @@
+package com.muzhi.tianhe.config;
+
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Author ZhangKan
+ * @Data 21:30
+ */
+
+//配置拦截器
+@Configuration
+public class MybatisPlusConfig {
+
+    @Bean
+    public PaginationInterceptor paginationInterceptor(){
+        return new PaginationInterceptor();
+    }
+}

+ 41 - 0
src/main/java/com/muzhi/tianhe/controller/AccessTokenController.java

@@ -0,0 +1,41 @@
+package com.muzhi.tianhe.controller;
+
+import com.muzhi.tianhe.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.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")
+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;
+    }
+}

+ 58 - 0
src/main/java/com/muzhi/tianhe/controller/DingController.java

@@ -0,0 +1,58 @@
+package com.muzhi.tianhe.controller;
+
+import com.muzhi.tianhe.service.DingService;
+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 = "钉钉接口")
+@CrossOrigin
+@RestController
+@RequestMapping("/dingOAservice")
+public class DingController {
+
+    @Autowired
+    private DingService dingService;
+
+    //获取钉钉OA审批信息(任务)
+    @ApiOperation(value = "获取钉钉OA审批信息(任务)")
+    @PostMapping("getDingOA")
+    public String getDingOA(String processInstances){
+
+        String dingOA = dingService.getDingOA(processInstances);
+        return dingOA;
+    }
+
+    //获取钉钉OA审批信息(变更)
+    @ApiOperation(value = "获取钉钉OA审批信息(变更)")
+    @PostMapping("getDingOAbiangeng")
+    public String getDingOAbiangeng(String processInstances, String shenheyijian){
+
+        String dingOAbiangeng = dingService.getDingOAbiangeng(processInstances, shenheyijian);
+        return dingOAbiangeng;
+    }
+
+    //获取钉钉OA审批信息(关键任务变更)
+    @ApiOperation(value = "获取钉钉OA审批信息(关键任务变更)")
+    @PostMapping("getDingOAbiangeng2")
+    public String getDingOAbiangeng2(String processInstances, String shenheyijian){
+
+        String dingOAbiangeng2 = dingService.getDingOAbiangeng2(processInstances, shenheyijian);
+        return dingOAbiangeng2;
+    }
+
+    //获取钉钉OA审批信息(额外增加1:会议决议完成情况审核)
+    @ApiOperation(value = "获取钉钉OA审批信息(额外增加1:会议决议完成情况审核)")
+    @PostMapping("getDingOAbiangeng3")
+    public String getDingOAbiangeng3(String processInstances){
+
+        String dingOAbiangeng3 = dingService.getDingOAbiangeng3(processInstances);
+        return dingOAbiangeng3;
+    }
+}

+ 89 - 0
src/main/java/com/muzhi/tianhe/controller/TbController.java

@@ -0,0 +1,89 @@
+package com.muzhi.tianhe.controller;
+
+import com.muzhi.tianhe.entity.Tianhe;
+import com.muzhi.tianhe.entity.vo.TianheDataVo;
+import com.muzhi.tianhe.entity.vo.TianheQuery;
+import com.muzhi.tianhe.exceptionhander.R;
+import com.muzhi.tianhe.service.TbService;
+import com.muzhi.tianhe.util.UtilExcel;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.http.HttpResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @Author ZhangKan
+ * @Data 15:03
+ */
+
+@Api(description = "TB接口")
+@EnableScheduling
+@CrossOrigin
+@RestController
+@RequestMapping("/tbservice")
+public class TbController {
+
+    @Autowired
+    private TbService tbService;
+
+    //获取项目信息
+    @Scheduled(cron = "0 0 0,12 * * ? ")
+    @ApiOperation(value = "获取项目信息")
+    @PostMapping("getXiangmu")
+    public String getXiangmu(){
+        tbService.asyncXm();
+        return "success";
+    }
+
+    @GetMapping("syncProject")
+    public String syncProject(String id){
+        tbService.asyncXm(id);
+        return "success";
+    }
+
+    //查询数据库中tianhe表的所有数据
+    @ApiOperation(value = "查询数据库中tianhe表的所有数据")
+    @GetMapping("getTianhe")
+    public List<Tianhe> getTianhe(){
+        List<Tianhe> tianheList = tbService.getTianhe();
+        return tianheList;
+    }
+
+    //删除tianhe表中的所有数据
+    @ApiOperation(value = "删除tianhe表中的所有数据")
+    @PostMapping("removeTianhe")
+    public R removeTianhe(){
+        String result = tbService.removeTianhe();
+        return R.ok().data("result", result);
+    }
+
+    //获取数据库中tianhe实体类表数据并放入vo对象进行前端接口调用展示数据
+    @ApiOperation(value = "获取天合TB项目信息")
+    @GetMapping("getTianheTB")
+    public TianheDataVo getTianheTB(Integer page, Integer limit,
+                                    TianheQuery tianheQuery){
+
+//        @RequestBody是使用json传递数据,把json数据封装到对应的对象里面,注意:需要用Post提交方式,Get取不到值
+//        (required = false)表示参数值可以为空
+//        @ApiParam(name = "teacherQuery", value = "自定义讲师查询条件", required = true)
+//        @RequestBody(required = false) EmployeeQuery employeeVoQuery
+
+        TianheDataVo tianheTB = tbService.getTianheTB(page,limit,tianheQuery);
+        return tianheTB;
+    }
+
+    //获取数据库中tianhe实体类表数据并导出
+    @ApiOperation(value = "导出天合TB项目信息")
+    @GetMapping("exportTianheTB")
+    public void exportTianheTB(HttpServletResponse response,TianheQuery tianheQuery) throws IOException {
+        TianheDataVo tianheTB = tbService.getTianheTB(1,Integer.MAX_VALUE,tianheQuery);
+        UtilExcel.exportListByTemplate(response,tianheTB.getData(),Tianhe.class,"周报");
+    }
+}

+ 38 - 0
src/main/java/com/muzhi/tianhe/controller/YiDaController.java

@@ -0,0 +1,38 @@
+package com.muzhi.tianhe.controller;
+
+import com.muzhi.tianhe.service.YiDaService;
+import io.swagger.annotations.Api;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @Author ZhangKan
+ * @Data 15:03
+ */
+
+@Api(description = "TB接口")
+@RestController
+@RequestMapping("/yidaservice")
+public class YiDaController {
+
+    @Autowired
+    private YiDaService yiDaService;
+
+    //创建宜搭项目表单
+    @PostMapping("createYiDaXM")
+    public String createYiDaXM(String mapJson,String xmid) throws Exception{
+
+        String yiDaXM = yiDaService.createYiDaXM(mapJson,xmid);
+        System.out.println(yiDaXM);
+        return yiDaXM;
+    }
+
+    //创建宜搭任务表单
+    @PostMapping("createYiDaRW")
+    public String createYiDaRW(String dingUserid,String mapJson) throws Exception{
+
+        String yiDaRW = yiDaService.createYiDaRW(dingUserid,mapJson);
+        System.out.println(yiDaRW);
+        return yiDaRW;
+    }
+}

+ 103 - 0
src/main/java/com/muzhi/tianhe/entity/Tianhe.java

@@ -0,0 +1,103 @@
+package com.muzhi.tianhe.entity;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value = "yikongTB信息", description = "易控TB信息")
+public class Tianhe {
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "项目编号")
+    private String xiangmubianhao;
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "任务编号")
+    private String taskid;
+
+    @ExcelProperty("项目名称")
+    @ApiModelProperty(value = "项目名称")
+    private String xiangmumingcheng;
+
+    @ExcelProperty("项目类别")
+    @ApiModelProperty(value = "项目类别")
+    private String xiangmuleibie;
+
+    @ExcelProperty("里程碑")
+    @ApiModelProperty(value = "里程碑")//任务字段
+    private String lichengbei;
+
+    @ExcelProperty("所属周")
+    @ApiModelProperty(value = "所属周")//任务字段
+    private String suoshuzhou;
+
+    @ExcelProperty("本周工作内容")
+    @ApiModelProperty(value = "本周工作内容")//任务字段
+    private String benzhougongzuoneirong;
+
+    @ExcelProperty("下周工作计划")
+    @ApiModelProperty(value = "下周工作计划")//任务字段
+    private String xiazhougongzuojihua;
+
+    @ExcelProperty("项目事项清单")
+    @ApiModelProperty(value = "项目事项清单")//任务字段
+    private String xiangmushixiangqingdan;
+
+    @ExcelProperty("备注")
+    @ApiModelProperty(value = "备注")//任务字段
+    private String beizhu;
+
+    @ExcelProperty("项目状态")
+    @ApiModelProperty(value = "项目状态")
+    private String xiangmuzhuangtai;
+
+    @ExcelProperty("项目负责人")
+    @ApiModelProperty(value = "项目负责人")
+    private String xiangmufuzeren;
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "项目负责人编号")
+    private String xiangmufuzerenid;
+
+    @ExcelProperty("项目创建时间")
+    @ApiModelProperty(value = "项目创建时间")
+    private String xiangmuchuangjianshijian;
+
+    @ExcelProperty("任务开始时间")
+    @ApiModelProperty(value = "任务开始时间")//任务字段
+    private String renwukaishishijian;
+
+    @ExcelProperty("任务结束时间")
+    @ApiModelProperty(value = "任务结束时间")//任务字段
+    private String renwujieshushijian;
+
+    @ExcelProperty("任务执行人")
+    @ApiModelProperty(value = "任务执行人")//任务字段
+    private String renwuzhixingren;
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "任务执行人编号")//任务字段
+    private String renwuzhixingrenid;
+
+
+    // 此字段不映射到数据库表中
+    @TableField(exist = false)
+    @ExcelProperty("是否归档")
+    private String shifouguidangStr;
+
+    public String getShifouguidangStr() {
+        return shifouguidang==0?"未归档":"已归档";
+    }
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "是否归档(0未归档;1已归档)")
+    private int shifouguidang;
+}

+ 13 - 0
src/main/java/com/muzhi/tianhe/entity/User.java

@@ -0,0 +1,13 @@
+package com.muzhi.tianhe.entity;
+
+import lombok.Data;
+
+@Data
+public class User {
+
+    private String tbUserId;
+    private String ddUserId;
+    private String userName;
+    private int role;// 项目角色,0=成员;1=管理员;2=拥有者
+
+}

+ 14 - 0
src/main/java/com/muzhi/tianhe/entity/vo/TianheDataVo.java

@@ -0,0 +1,14 @@
+package com.muzhi.tianhe.entity.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class TianheDataVo<T> {
+
+    private Integer code;
+    private String msg;
+    private Long count;
+    private List<T> data;
+}

+ 36 - 0
src/main/java/com/muzhi/tianhe/entity/vo/TianheQuery.java

@@ -0,0 +1,36 @@
+package com.muzhi.tianhe.entity.vo;
+
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class TianheQuery {
+
+    // https://mc.cloudpure.cn/tianhe/tbservice/getTianheTB?page=1&limit=10
+    // &name=&xmbh&xmfzr=&xmzt&sfgd=&rwStart=&rwEnd=
+
+    @ApiModelProperty(value = "项目名称,模糊查询")
+    private String name;
+
+    @ApiModelProperty(value = "项目编号,模糊查询")
+    private String xmbh;
+
+    @ApiModelProperty(value = "项目负责人,模糊查询")
+    private String xmfzr;
+
+    @ApiModelProperty(value = "项目状态,模糊查询")
+    private String xmzt;
+
+    @ApiModelProperty(value = "是否归档 0未归档 1已归档")
+    private Integer sfgd;
+
+    @ApiModelProperty(value = "任务时间(开始)", example = "2019-01-01 10:10:10")
+    private String rwStart;
+
+    @ApiModelProperty(value = "任务时间(结束)", example = "2019-01-01 10:10:10")
+    private String rwEnd;
+
+    @ApiModelProperty(value = "用户编号")
+    private String userId;
+}

+ 72 - 0
src/main/java/com/muzhi/tianhe/exceptionhander/R.java

@@ -0,0 +1,72 @@
+package com.muzhi.tianhe.exceptionhander;
+
+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(true);
+        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(Map<String, Object> map){
+        this.setData(map);
+        return this;
+    }
+}

+ 12 - 0
src/main/java/com/muzhi/tianhe/exceptionhander/ResultCode.java

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

+ 11 - 0
src/main/java/com/muzhi/tianhe/mapper/TianheMapper.java

@@ -0,0 +1,11 @@
+package com.muzhi.tianhe.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.muzhi.tianhe.entity.Tianhe;
+import org.springframework.stereotype.Repository;
+
+//在对应的Mapper上面继承基本的类BaseMapper
+@Repository//代表持久层,或者加入@Mapper注解
+public interface TianheMapper extends BaseMapper<Tianhe> {
+    //所有的CRUD操作都已经编写完成了
+}

+ 5 - 0
src/main/java/com/muzhi/tianhe/mapper/xml/TianheMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+        <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.muzhi.tianhe.mapper.TianheMapper">
+
+</mapper>

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

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

+ 16 - 0
src/main/java/com/muzhi/tianhe/service/DingService.java

@@ -0,0 +1,16 @@
+package com.muzhi.tianhe.service;
+
+public interface DingService{
+
+    //获取钉钉OA审批信息(任务)
+    String getDingOA(String processInstances);
+
+    //获取钉钉OA审批信息(变更)
+    String getDingOAbiangeng(String processInstances,String shenheyijian);
+
+    //获取钉钉OA审批信息(关键任务变更)
+    String getDingOAbiangeng2(String processInstances,String shenheyijian);
+
+    //获取钉钉OA审批信息(额外增加1:会议决议完成情况审核)
+    String getDingOAbiangeng3(String processInstances);
+}

+ 7 - 0
src/main/java/com/muzhi/tianhe/service/TbProjectZbService.java

@@ -0,0 +1,7 @@
+package com.muzhi.tianhe.service;
+
+public interface TbProjectZbService {
+
+    void sync();
+
+}

+ 35 - 0
src/main/java/com/muzhi/tianhe/service/TbService.java

@@ -0,0 +1,35 @@
+package com.muzhi.tianhe.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.muzhi.tianhe.entity.Tianhe;
+import com.muzhi.tianhe.entity.User;
+import com.muzhi.tianhe.entity.vo.TianheDataVo;
+import com.muzhi.tianhe.entity.vo.TianheQuery;
+
+import java.util.List;
+
+public interface TbService extends IService<Tianhe> {
+
+    //获取项目信息
+    String getXiangmu() throws Exception;
+
+    //查询数据库中tianhe表的所有数据
+    List<Tianhe> getTianhe();
+
+    //删除tianhe表中的所有数据
+    String removeTianhe();
+
+    //获取数据库中员工花名册employee实体类表数据进行前端接口调用展示数据
+    public TianheDataVo<Tianhe> getTianheTB(Integer page, Integer limit, TianheQuery tianheQuery);
+
+    public User getDingUserId(String tbUserId);
+
+    public List<User> projectMember(String projectId);
+
+
+    void asyncXm();
+
+    void asyncXm(String id);
+
+    void test();
+}

+ 10 - 0
src/main/java/com/muzhi/tianhe/service/YiDaService.java

@@ -0,0 +1,10 @@
+package com.muzhi.tianhe.service;
+
+public interface YiDaService {
+
+    //创建宜搭项目表单
+    String createYiDaXM(String mapJson,String xmid) throws Exception;
+
+    //创建宜搭任务表单
+    String createYiDaRW(String dingUserid,String mapJson) throws Exception;
+}

+ 88 - 0
src/main/java/com/muzhi/tianhe/service/impl/AccessTokenServiceImpl.java

@@ -0,0 +1,88 @@
+package com.muzhi.tianhe.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.tianhe.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 {
+    //AccessTokenService
+
+    //易控
+    //AgentId:
+    //APPKEY
+    private static String APPKEY = "";
+    //APPSECRET
+    private static String APPSECRET = "";
+
+    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 = "65f92ac8046a09f810107919";
+    public static String appSecret = "fy9OcKys3ZMdPnj0jvATrni5LvEn8ksk";
+    //获取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;
+
+    }
+
+}

File diff suppressed because it is too large
+ 1072 - 0
src/main/java/com/muzhi/tianhe/service/impl/DingServiceImpl.java


+ 146 - 0
src/main/java/com/muzhi/tianhe/service/impl/TbApiService.java

@@ -0,0 +1,146 @@
+package com.muzhi.tianhe.service.impl;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.muzhi.tianhe.service.AccessTokenService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+@Slf4j
+public class TbApiService {
+
+    @Autowired
+    private AccessTokenService accessTokenService;
+    //私有化
+    public static String PRIVATE_API_URL = "https://tb.trinasolar.com/gateway";
+//    public static String PRIVATE_API_URL = "https://tb.trinasolar.com/gateway";
+
+    public static String DING_CORP_ID = "dinga41ec6a58a4911d0f2c783f7214b6d69";
+    public static String tbOrganizationId = "65b1dca21ab0fa13be993595";
+
+    public JSONArray getProjectList(){
+        JSONObject result=header(HttpRequest.get(PRIVATE_API_URL + "/v3/project/query?pageSize=1000"));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getProjectInfo(String projectId){
+        JSONObject result=header(HttpRequest.get(PRIVATE_API_URL + "/v3/project/query?projectIds=" + projectId + "&pageSize=1000"));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getTasksType(String xmid){
+        JSONObject result=header(HttpRequest.get(PRIVATE_API_URL + "/v3/project/" + xmid + "/scenariofieldconfig/search"));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getTasksByType(String xmid,String rwlxid){
+        JSONObject result=header(HttpRequest.get(PRIVATE_API_URL + "/v3/project/" + xmid + "/task/query?q=sfcId = " + rwlxid + "&pageSize=1000"));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getProjectCust(String projectId){
+        JSONObject result=header(HttpRequest.get(PRIVATE_API_URL + "/v3/project/"+ projectId +"/status/customfield/list"));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getProjectRoleMember(String projectId,String id){
+        JSONObject result=header(HttpUtil.createGet(PRIVATE_API_URL + "/v3/project/"+ projectId +"/member").form("projectRoleId",id));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getProjectTag(String projectId){
+        JSONObject result=header(HttpUtil.createGet(PRIVATE_API_URL + "/v3/project/{projectId}/project-tag".replace("{projectId}",projectId)));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getProjecGrouptTag(String id){
+        JSONObject result=header(HttpUtil.createGet(PRIVATE_API_URL + "/v3/project-tag/query").form(new JSONObject().fluentPut("ids",id)));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getProjectState(String projectId){
+        Map param = new HashMap();
+        param.put("pageSize", 1000);
+        JSONObject result=header(HttpUtil.createGet(PRIVATE_API_URL + "/v3/project/{projectId}/status/list".replace("{projectId}",projectId))
+                .form(param));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getProjecTagUser(String projectTagId){
+        JSONObject result=header(HttpUtil.createGet(PRIVATE_API_URL + "/v3/project-tag/{projectTagId}/project/query".replace("{projectTagId}",projectTagId)));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getProjectRole(String projectId){
+        Map param = new HashMap();
+        param.put("pageSize", 1000);
+        JSONObject result=header(HttpUtil.createGet(PRIVATE_API_URL + "/v3/project/" + projectId + "/role")
+                .form(param));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getProjectMemberRole(String projectId,String userIds){
+        Map param = new HashMap();
+        param.put("userIds", userIds);
+        JSONObject result=header(HttpUtil.createGet(PRIVATE_API_URL + "/v3/project/"+projectId+"/member-role")
+                .form(param));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray getProjectMember(String projectId){
+        Map param = new HashMap();
+        param.put("pageSize", 1000);
+        JSONObject result=header(HttpUtil.createGet(PRIVATE_API_URL + "/v3/project/"+projectId+"/member")
+                .form(param));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray userSearch(String userId){
+        Map param = new HashMap();
+        param.put("orgId",tbOrganizationId);
+        param.put("query",userId);
+        param.put("pageSize", 1000);
+        JSONObject result=header(HttpUtil.createGet(PRIVATE_API_URL + "/org/member/search")
+                .form(param));
+        return result.getJSONArray("result");
+    }
+
+    public JSONArray idMapQuery(String userId, boolean isTbID){
+        Map param = new HashMap();
+        param.put("refer","dingTalk-user");
+        param.put("refId",DING_CORP_ID);
+        param.put(isTbID ? "tbId" : "extraUserId", userId);
+        JSONObject result=header(HttpUtil.createGet(PRIVATE_API_URL + "/idmap/query")
+                .form(param));
+        return result.getJSONArray("result");
+    }
+
+
+
+    private JSONObject header(HttpRequest request){
+        try {
+            request.header("Authorization","Bearer " + accessTokenService.getAppToken())
+                    .header("X-Tenant-Id", tbOrganizationId)
+                    .header("X-Tenant-Type", "organization");
+            String resultStr=request.execute().body();
+            JSONObject result=JSONObject.parseObject(resultStr);
+            if(!result.getString("code").equals("200")){
+                log.error("请求失败!返回数据:{}",resultStr);
+                return new JSONObject();
+            }
+            return result;
+        }catch (Exception e){
+            e.printStackTrace();
+            log.error("请求异常!错误信息:{}",e);
+            return new JSONObject();
+        }
+    }
+
+}

+ 93 - 0
src/main/java/com/muzhi/tianhe/service/impl/TbProjectZbServiceImpl.java

@@ -0,0 +1,93 @@
+package com.muzhi.tianhe.service.impl;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.muzhi.tianhe.entity.User;
+import com.muzhi.tianhe.mapper.TianheMapper;
+import com.muzhi.tianhe.service.AccessTokenService;
+import com.muzhi.tianhe.service.TbProjectZbService;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@Service
+public class TbProjectZbServiceImpl implements TbProjectZbService {
+
+    @Autowired
+    private TianheMapper tianheMapper;
+    @Autowired
+    private AccessTokenService accessTokenService;
+    private static String PRIVATE_API_URL = "https://tb.trinasolar.com/gateway";
+    private static String tbOrganizationId = "65b1dca21ab0fa13be993595";
+    //缓存用户信息
+    private static Map<String,User> USER_MAP=new HashMap<>();
+    //缓存项目里程碑
+    private static Map<String,String> TB_LICHENGBEI_MAP=new HashMap<>();
+    //缓存项目分组成员
+    private static Map<String,String> TB_PROJECT_GROUP_MAP=new HashMap<>();
+
+    @Override
+    @Async
+    public void sync() {
+        //先删除清空tianhe表中的所有数据
+        tianheMapper.delete(null);
+        //再执行插入TB统计数据库表的接口
+        getXiangmu();
+    }
+
+    private void getXiangmu(){
+        HttpRequest request=HttpUtil.createGet(PRIVATE_API_URL + "/v3/project/query?pageSize=1000").header(header());
+        JSONArray result2 = null;
+        try {
+            String bodyStr = request.execute().body();
+            JSONObject jsonObject = JSON.parseObject(bodyStr);
+            result2 = jsonObject.getJSONArray("result");
+        }catch (Exception e){
+            e.printStackTrace();
+            log.error("同步异常!");
+            return;
+        }
+        for (int ii = 0; ii < result2.size(); ii++) {
+            //项目id
+            String xmid = result2.getJSONObject(ii).getString("id");
+            syncData(xmid);
+        }
+    }
+
+    private void syncData(String xmid){
+//        /**数据库表*/
+//        JSONArray xiangmuRenwuLeixing = getXiangmuRenwuLeixing(xmid);
+//        for (int j = 0; j < xiangmuRenwuLeixing.size(); j++) {
+//            JSONObject jsonObject1 = xiangmuRenwuLeixing.getJSONObject(j);
+//            String renwuleixingName = jsonObject1.getString("name");
+//            if (renwuleixingName.equals("周报")){
+//                // 获取属于【周报】的任务
+//                getZhoubao(jsonObject1.getString("id"),xmid);
+//            }else if (renwuleixingName.equals("里程碑")){
+//                // 获取项目里程碑
+//                getLichengbei(jsonObject1.getString("id"),xmid);
+//            }
+//        }
+    }
+
+    private Map header(){
+        Map map=new HashMap();
+        map.put("Authorization","Bearer " + accessTokenService.getAppToken());
+        map.put("X-Tenant-Id", tbOrganizationId);
+        map.put("X-Tenant-Type", "organization");
+        return map;
+    }
+
+}

+ 667 - 0
src/main/java/com/muzhi/tianhe/service/impl/TbServiceImpl.java

@@ -0,0 +1,667 @@
+package com.muzhi.tianhe.service.impl;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.muzhi.tianhe.entity.Tianhe;
+import com.muzhi.tianhe.entity.User;
+import com.muzhi.tianhe.entity.vo.TianheDataVo;
+import com.muzhi.tianhe.entity.vo.TianheQuery;
+import com.muzhi.tianhe.mapper.TianheMapper;
+import com.muzhi.tianhe.service.TbService;
+import com.muzhi.tianhe.util.PublicUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+
+@Service
+@Slf4j
+public class TbServiceImpl extends ServiceImpl<TianheMapper, Tianhe> implements TbService{
+
+    //【云璞POC TB】企业ID:6034c885e71842e1e5bb5218
+
+    @Autowired
+    private TbApiService tbApiService;
+    @Autowired
+    private TianheMapper tianheMapper;
+
+    //缓存用户信息
+    public static Map<String,User> USER_MAP=new HashMap<>();
+    //缓存项目里程碑
+    public static Map<String,String> TB_LICHENGBEI_MAP=new HashMap<>();
+    //缓存项目分组成员
+    public static Map<String,String> TB_PROJECT_GROUP_MAP=new HashMap<>();
+
+    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS Z");
+    SimpleDateFormat defaultFormat = new SimpleDateFormat("yyyy-MM-dd");
+
+    //获取项目任务类型
+    public JSONArray getXiangmuRenwuLeixing(String xmid){
+        JSONArray array=tbApiService.getTasksType(xmid);
+        JSONArray result = new JSONArray();
+        if (array != null){
+            for (int i = 0; i < array.size(); i++) {
+                Map map = new HashMap();
+                String name = array.getJSONObject(i).getString("name");
+                String id = array.getJSONObject(i).getString("id");
+                map.put("name",name);
+                map.put("id",id);
+                result.add(map);
+            }
+        }
+        return result;
+    }
+
+    //获取项目信息
+    @Override
+    public String getXiangmu(){
+        JSONArray array=tbApiService.getProjectList();
+        for (int ii = 0; ii < array.size(); ii++) {
+            String xmid = array.getJSONObject(ii).getString("id");
+            log.info("封装项目数据,项目编号:[{}]进度:[{}/{}]",xmid,ii+1,array.size());
+            syncData(xmid);
+        }
+        return "同步完成";
+    }
+
+    // 单个项目同步
+    private void syncData(String xmid){
+        /**数据库表*/
+        JSONArray array = getXiangmuRenwuLeixing(xmid);
+        List<String> list=new ArrayList<>();
+        for (int j = 0; j < array.size(); j++) {
+            JSONObject jsonObject1 = array.getJSONObject(j);
+            String renwuleixingName = jsonObject1.getString("name");
+            if (renwuleixingName.equals("周报")){
+                // 获取属于【周报】的任务
+                list.add(jsonObject1.getString("id"));
+            }else if (renwuleixingName.equals("里程碑")){
+                // 获取项目里程碑
+                getLichengbei(jsonObject1.getString("id"),xmid);
+            }
+        }
+        for (int i = 0; i < list.size(); i++) {
+            log.info("封装周报类型项目数据,项目编号:[{}]进度:[{}/{}]",list.get(i),i+1,list.size());
+            getZhoubao(list.get(i),xmid);
+        }
+    }
+
+    // 获取项目里程碑信息
+    private void getLichengbei(String rwlxid,String xmid){
+        JSONArray array=tbApiService.getTasksByType(xmid,rwlxid);
+        if (array != null){
+            int uniqueId=1000000;
+            String content="";
+            for (int i = 0; i < array.size(); i++) {
+                JSONObject jsonObject1 = array.getJSONObject(i);
+                if(!jsonObject1.getBoolean("isDone")){
+                    // 未完成的里程碑
+                    if(uniqueId>jsonObject1.getIntValue("uniqueId")){
+                        uniqueId=jsonObject1.getIntValue("uniqueId");
+                        content=array.getJSONObject(i).getString("content");
+                    }
+                }else{
+                    // 已完成的里程碑
+                    TB_LICHENGBEI_MAP.put(xmid,content);
+                    return;
+                }
+            }
+            TB_LICHENGBEI_MAP.put(xmid,content);// 如果全部未完成,显示第一个里程碑
+        }
+    }
+
+    private String timeFormat(String startDate){
+        String result = "";
+        Date rwStateTime = null;
+        long timeState = 0;
+        if (startDate == null) {
+            System.out.println("该项目没有获取到任务开始时间!");
+        } else {
+            startDate = startDate.replace("Z", " UTC");
+            try {
+                rwStateTime = format.parse(startDate);
+                result = defaultFormat.format(rwStateTime);
+                timeState = rwStateTime.getTime();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    // 同步项目下所有周报任务
+    private void getZhoubao(String rwlxid,String projectId){
+        Tianhe tianhe = new Tianhe();
+        JSONArray tasks=tbApiService.getTasksByType(projectId,rwlxid);
+        if (tasks != null){
+            //里程碑
+            tianhe.setLichengbei(TB_LICHENGBEI_MAP.get(projectId));
+            //项目类别
+            tianhe.setXiangmuleibie(getProjectCustVule(projectId,"65bb984c7bbdd92f48e7264a"));
+            tianhe.setXiangmubianhao(projectId);
+            tianhe.setXiangmuzhuangtai(getProjectState(projectId));
+            JSONArray projects=tbApiService.getProjectInfo(projectId);
+            JSONObject project = projects.getJSONObject(0);
+//            JSONArray customfields = project.getJSONArray("customfields");
+            // 项目名称
+            tianhe.setXiangmumingcheng(project.getString("name"));
+            //项目负责人
+//                List<User> xmfzr = projectMember(projectId);// 单项目
+            List<User> xmfzrId = projectAllMember(projectId);// 包含项目分组
+            List<User> xmfzr = _getProjectRoleId(projectId,"项目经理(业务数字化/集团QPD)");
+            xmfzrId.addAll(xmfzr);// 添加项目经理
+            tianhe.setXiangmufuzeren((getUser(true,xmfzr)).replaceAll("null",""));
+            tianhe.setXiangmufuzerenid((getUser(false,xmfzrId)));
+            if (project.getString("isArchived").equals("false") && project.getString("isTemplate").equals("false")) {
+                //项目创建时间
+                tianhe.setXiangmuchuangjianshijian(timeFormat(project.getString("created")));
+                //是否归档
+                tianhe.setShifouguidang(project.getString("isSuspended").equals("false")?0:1);
+            }
+            // 循环处理每个任务
+            for (int i = 0; i < tasks.size(); i++) {
+                JSONObject task = tasks.getJSONObject(i);
+                log.info("封装任务数据,任务编号:[{}]进度:[{}/{}]",task.getString("id"),i+1,tasks.size());
+                tianhe.setTaskid(task.getString("id"));
+                tianhe.setBeizhu(task.getString("note"));
+                //任务开始时间
+                tianhe.setRenwukaishishijian(timeFormat(task.getString("startDate")));
+                //任务截止时间
+                tianhe.setRenwujieshushijian(timeFormat(task.getString("dueDate")));
+                //所属周
+                String ssz = "";
+                //本周工作内容
+                String bzgznr = "";
+                //下周工作计划
+                String xzgzjh = "";
+                //项目事项清单
+                String xmsxqd = "";
+                //获取自定义任务集合
+                JSONArray rwArray = task.getJSONArray("customfields");
+                if (!rwArray.isEmpty()){
+                    for (int j = 0; j < rwArray.size(); j++) {
+                        //任务-自定义字段id
+                        String cfId = rwArray.getJSONObject(j).getString("cfId");
+                        if (cfId.equals("65bcac474d4dc9286d42afdd")) {//所属周
+                            JSONArray value = rwArray.getJSONObject(j).getJSONArray("value");
+                            if (!value.isEmpty()) {
+                                for (int z = 0; z < value.size(); z++) {
+                                    String title = value.getJSONObject(z).getString("title");
+                                    ssz = title;
+                                }
+                            }
+                        } else if (cfId.equals("65bcac510bb530c9d7d04fe8")) {//本周工作内容
+                            JSONArray value = rwArray.getJSONObject(j).getJSONArray("value");
+                            if (!value.isEmpty()) {
+                                for (int z = 0; z < value.size(); z++) {
+                                    String title = value.getJSONObject(z).getString("title");
+                                    bzgznr = title;
+                                }
+                            }
+                        } else if (cfId.equals("65bcac5a7bbdd92f48e74927")) {//下周工作计划
+                            JSONArray value = rwArray.getJSONObject(j).getJSONArray("value");
+                            if (!value.isEmpty()) {
+                                for (int z = 0; z < value.size(); z++) {
+                                    String title = value.getJSONObject(z).getString("title");
+                                    xzgzjh = title;
+                                }
+                            }
+                        } else if (cfId.equals("65bcacf85b255d16573fe47f")) {//项目事项清单
+                            JSONArray value = rwArray.getJSONObject(j).getJSONArray("value");
+                            if (!value.isEmpty()) {
+                                for (int z = 0; z < value.size(); z++) {
+                                    String title = value.getJSONObject(z).getString("title");
+                                    xmsxqd = title;
+                                }
+                            }
+                        }
+                    }
+                }
+                tianhe.setSuoshuzhou(ssz);
+                tianhe.setBenzhougongzuoneirong(bzgznr);
+                tianhe.setXiazhougongzuojihua(xzgzjh);
+                tianhe.setXiangmushixiangqingdan(xmsxqd);
+                //项目执行人
+                User xmzxr = getDingUserId(task.getString("executorId"));
+                tianhe.setRenwuzhixingren(xmzxr.getUserName());
+                tianhe.setRenwuzhixingrenid(xmzxr.getTbUserId());
+                QueryWrapper<Tianhe> wrapper = new QueryWrapper<>();
+                wrapper.lambda().eq(Tianhe::getXiangmubianhao,tianhe.getXiangmubianhao()).eq(Tianhe::getTaskid,tianhe.getTaskid());
+                List<Tianhe> list=tianheMapper.selectList(wrapper);
+                if(list!=null&&list.size()>0){
+                    tianheMapper.update(tianhe,wrapper);
+                }else{
+                    tianheMapper.insert(tianhe);
+                }
+            }
+        }
+    }
+
+    //查询数据库中employee表的所有数据
+    @Override
+    public List<Tianhe> getTianhe(){
+        List<Tianhe> tianheList = tianheMapper.selectList(null);
+        return tianheList;
+    }
+
+    //删除employee表中的所有数据
+    @Override
+    public String removeTianhe(){
+//        //获取当前时间
+//        Date Date = new Date(System.currentTimeMillis());
+//        System.out.println("当前时间:" + Date);
+//        //截取当天本月的年月
+//        Calendar calendar123 = Calendar.getInstance();
+//        calendar123.setTime(Date);
+//        calendar123.add(Calendar.MONTH,0);
+//        String lastMonthNow = new SimpleDateFormat("yyyy-MM").format(calendar123.getTime());
+//        System.out.println("本月日期,文本格式(月)" + lastMonthNow);
+
+//        //构造查询条件
+//        QueryWrapper<Yikong> queryWrapper = new QueryWrapper();
+//        try {
+//            queryWrapper.eq("tongbutime", lastMonthNow);
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+//
+//        yikongMapper.delete(queryWrapper);
+        tianheMapper.delete(null);
+        return "清空天合项目表数据成功!";
+    }
+
+    //获取数据库中yikong实体类表数据并放入vo对象进行前端接口调用展示数据
+    @Override
+    public TianheDataVo<Tianhe> getTianheTB(Integer page, Integer limit, TianheQuery tianheQuery){
+        log.info("查询条件:{}",tianheQuery.toString());
+        //写法1:调用写好的接口(查询数据库中employee表的所有数据)
+//        EmployeeDataVo employeeDataVo = new EmployeeDataVo();
+//        employeeDataVo.setCode(0);
+//        employeeDataVo.setMsg("");
+//        employeeDataVo.setCount(tongbuService.getEmployee().size());
+//
+//        employeeDataVo.setData(tongbuService.getEmployee());
+//        return employeeDataVo;
+
+        //写法2:直接在该实现类查询数据库实体类的数据
+        TianheDataVo tianheDataVo = new TianheDataVo();
+        tianheDataVo.setCode(0);
+        tianheDataVo.setMsg("");
+        //查询总数方法1
+//        employeeDataVo.setCount(employeeMapper.selectCount(null));
+
+        System.out.println("page===" + page + "--->limit===" + limit);
+
+        //分页查询,创建page对象
+        Page<Tianhe> tianhePage = new Page<>(page,limit);
+
+        //构建查询条件
+        QueryWrapper<Tianhe> wrapper = new QueryWrapper<>();
+        //多条件组合查询(类似mybatis中动态sql)
+        String name = tianheQuery.getName();
+        String xmbh = tianheQuery.getXmbh();
+        String xmfzr = tianheQuery.getXmfzr();
+        String xmzt = tianheQuery.getXmzt();
+        Integer sfgd = tianheQuery.getSfgd();
+        String userId = tianheQuery.getUserId();
+
+        String rwStart = tianheQuery.getRwStart();//TODO 2024-03-18+00:00
+        String rwEnd = tianheQuery.getRwEnd();//TODO 2024-03-22+00:00
+
+
+        //判断条件值是否为空,如果不为空则拼接条件
+        if (!StringUtils.isEmpty(name)) {
+            //构建条件
+            wrapper.like("xiangmumingcheng", name);
+        }
+        if (!StringUtils.isEmpty(xmbh)) {
+            //构建条件
+            wrapper.like("xiangmubianhao", xmbh);
+        }
+        if (!StringUtils.isEmpty(xmfzr)) {
+            //构建条件
+            wrapper.like("xiangmufuzeren", xmfzr);
+        }
+        if (!StringUtils.isEmpty(xmzt)) {
+            wrapper.eq("xiangmuzhuangtai", xmzt);
+        }
+        if (!StringUtils.isEmpty(sfgd)) {
+            wrapper.eq("shifouguidang", sfgd);
+        }
+        if (!StringUtils.isEmpty(rwStart) && !StringUtils.isEmpty(rwEnd)) {
+            System.out.println("前端传参任务开始时间:" + rwStart);
+            System.out.println("前端传参任务截止时间:" + rwEnd);
+
+            wrapper.ge("renwukaishishijian", rwStart);
+            wrapper.le("renwujieshushijian", rwEnd);
+        }
+        if (!StringUtils.isEmpty(userId)) {
+            //构建条件
+            wrapper.and(i->i.like("xiangmufuzerenid", userId).or().eq("renwuzhixingrenid",userId));
+        }
+//        if (!StringUtils.isEmpty(shijiStart) && !StringUtils.isEmpty(shijiEnd)) {
+//            wrapper.between("shijiwanchengshijian", shijiStart,shijiEnd);
+//        }
+//        //排序:按照入职日期进行排序
+//        wrapper.orderByDesc("confirmjointime");
+
+        //排序:按照项目序号进行排序
+        wrapper.orderByDesc("xiangmubianhao");
+
+//        List<Employee> employeeList = employeePage.getRecords();
+
+        IPage<Tianhe> result = tianheMapper.selectPage(tianhePage, wrapper);
+
+        //查询实体类表中所有的数据(全部)
+//        List<Employee> employeeList = employeeMapper.selectList(null);
+        //查询分页信息记录(根据分页要求来查)
+        List<Tianhe> tianheList = result.getRecords();
+        //查询总数方法2
+        tianheDataVo.setCount(result.getTotal());
+        System.out.println("总数:" + result.getTotal());
+
+//        List<EmployeeVo> employeeVoList = new ArrayList<>();
+//        for (Employee employee : employeeList) {
+//            EmployeeVo employeeVo = new EmployeeVo();
+//            //将前面实体类表中的数据,一一对应赋值给后面的vo对象
+//            BeanUtils.copyProperties(employee,employeeVo);
+//
+//            employeeVoList.add(employeeVo);
+//        }
+
+        tianheDataVo.setData(tianheList);
+        return tianheDataVo;
+    }
+
+    @Override
+    public User getDingUserId(String tbUserId) {
+        if(USER_MAP.containsKey(tbUserId)){
+            return USER_MAP.get(tbUserId);
+        }
+        User user=new User();
+        String ddUserId=idMapQuery(tbUserId,true);
+        if(ddUserId!=null){
+            user.setDdUserId(ddUserId);
+            user.setUserName(userSearch(ddUserId));
+            user.setTbUserId(tbUserId);
+            USER_MAP.put(tbUserId,user);
+        }
+        return user;
+    }
+
+
+    /***
+     * 获取钉钉userId
+     * @param userId
+     * @param isTbID
+     * @return
+     */
+    public String idMapQuery(String userId, boolean isTbID) {
+
+        try {
+            JSONArray array=tbApiService.idMapQuery(userId,isTbID);
+            if(array!=null&&array.size()>0){
+                return array.getJSONObject(0).getJSONObject("extra").getString("userId");
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public String userSearch(String userId) {
+        try {
+            JSONArray array=tbApiService.userSearch(userId);
+            if(array!=null&&array.size()>0){
+                return array.getJSONObject(0).getString("name");
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        // 暂不需要钉钉userId
+        return null;
+    }
+
+    @Override
+    public List<User> projectMember(String projectId) {
+        try {
+            JSONArray array=tbApiService.getProjectMember(projectId);
+            if(array!=null&&array.size()>0){
+                List<User> users=new ArrayList<>();
+                for (int i = 0; i < array.size(); i++) {
+                    User user=new User();
+                    user.setTbUserId(array.getJSONObject(i).getString("userId"));// id? userId
+                    user.setRole(array.getJSONObject(i).getInteger("role"));
+                    users.add(user);
+                }
+                return users;
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    @Async
+    public void asyncXm() {
+        getXiangmu();
+    }
+
+    @Override
+    public void asyncXm(String id) {
+        syncData(id);
+    }
+
+    public String getUser(boolean isNeedName,List<User> users){
+        if(users==null||users.size()<1){
+            return "";
+        }
+        if(isNeedName){
+            // 筛选只显示拥有者  .filter(user -> user.getRole() == 2)
+            return String.join(",",Arrays.stream(users.toArray(new User[0])).map(User::getUserName).toArray(String[]::new));
+        }else{
+            return String.join(",",Arrays.stream(users.toArray(new User[0])).map(User::getTbUserId).toArray(String[]::new));
+        }
+    }
+
+    @Override
+    public void test() {
+//        getProjectState("65eefd95fc0ff5ef94bd7863");
+        syncData("65efed344f2eb1fe4e88c81f");
+//        List<User> list=projectAllMember("65eefd4b5a93ac0240065a4e");
+//        User user=getDingUserId("65c09f4155d0f38026a1e218");
+//        String list=userSearch("孙雯");
+//        System.out.println(JSONObject.toJSONString(list));
+
+    }
+
+    /***
+     * 查询项目用户
+     * @param projectId
+     * @return
+     */
+    public List<User> projectAllMember(String projectId) {
+        String users=getAllUserIds(projectId);
+        List<User> list=new ArrayList<>();
+        if(PublicUtil.isNull(users)){
+            return list;
+        }
+        String[] user=users.split(",");
+        for (int i = 0; i < user.length; i++) {
+            list.addAll(projectAllMemberId(projectId,user[i]));// 默认不要名称
+        }
+        return list;
+    }
+
+    /***
+     * 查询项目所有权限的用户ID 及 角色
+     * @param projectId
+     * @param userIds
+     * @return
+     */
+    public List<User> projectAllMemberId(String projectId,String userIds) {
+        try {
+            JSONArray array=tbApiService.getProjectMemberRole(projectId,userIds);
+            if(array!=null&&array.size()>0){
+                List<User> users=new ArrayList<>();
+                for (int i = 0; i < array.size(); i++) {
+                    if(array.getJSONObject(i).getInteger("role")==-2){
+                        continue;
+                    }
+                    User user=getDingUserId(array.getJSONObject(i).getString("userId"));// id? userId
+                    if(user!=null){
+                        user.setRole(array.getJSONObject(i).getInteger("role"));
+                        users.add(user);
+                    }
+                }
+                return users;
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return new ArrayList<>();
+    }
+
+    /***
+     * 获取项目状态
+     * @param projectId
+     * @return
+     */
+    public String getProjectState(String projectId) {
+        try {
+            JSONArray array=tbApiService.getProjectState(projectId);
+            if(array!=null&&array.size()>0){
+                return array.getJSONObject(0).getString("name");
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /***
+     * 获取项目所有成员信息
+     * @param projectId
+     * @return
+     */
+    public String getAllUserIds(String projectId){
+        String projectTagId=getProjectGroupId(projectId,true);// 父级项目分组ID
+        if(PublicUtil.isNull(projectTagId)){
+            return null;
+        }
+        String ids=getProjectGroupUserIds(projectTagId);
+//        getProjectGroupId(projectTagId,false);// 父分组
+        return ids;
+    }
+
+    /***
+     * 获取项目分组下所有项目的项目成员
+     * @param projectTagId
+     * @return
+     */
+    public String getProjectGroupUserIds(String projectTagId){
+        if(TB_PROJECT_GROUP_MAP.containsKey(projectTagId)){
+            return TB_PROJECT_GROUP_MAP.get(projectTagId);
+        }
+        JSONArray array2=tbApiService.getProjecTagUser(projectTagId);
+        List<String> list=new ArrayList<>();
+        for (int i = 0; i < array2.size(); i++) {
+            JSONArray array3=tbApiService.getProjectMember(array2.getString(i));
+            if(array3!=null&&array3.size()>0){
+                for (int j = 0; j < array3.size(); j++) {
+                    list.add(array3.getJSONObject(j).getString("userId"));
+                }
+            }
+        }
+        Set<String> set=new HashSet(list);
+        String[] ids = set.toArray(new String[0]);
+        TB_PROJECT_GROUP_MAP.put(projectTagId,String.join(",",ids));
+        return TB_PROJECT_GROUP_MAP.get(projectTagId);
+    }
+
+    /***
+     * 查询项目分组ID
+     * @param id 编号
+     * @param isProjectId true 项目编号 false 项目分组编号
+     * @return
+     */
+    public String getProjectGroupId(String id,boolean isProjectId){
+        JSONArray array;
+        if(isProjectId){
+            array=tbApiService.getProjectTag(id);
+        }else{
+            array=tbApiService.getProjecGrouptTag(id);
+        }
+        if(array!=null&&array.size()>0){
+            if(isProjectId){
+                return array.getString(0);
+            }else{
+                System.out.println("==============");
+                // todo 查询项目分组接口的返回值缺少一个ancestorIds参数。公有云是有的
+                System.out.println(array.toJSONString());
+            }
+        }
+        return null;
+    }
+
+    /***
+     * 查询项目指定角色下的成员
+     * @param projectId
+     * @param roleName
+     * @return
+     */
+    private List<User> _getProjectRoleId(String projectId, String roleName) {
+        try {
+            JSONArray roleArray=tbApiService.getProjectRole(projectId);
+            List<Map> roles = roleArray.toJavaList(Map.class);
+            if(roles!=null&&roles.size()>0){
+                for (Map map:roles){
+                    if(roleName.equals(String.valueOf(map.get("name")))){
+                        String id=String.valueOf(map.get("id"));
+//                        String id="65eef969b6adf46ff2c974a1";
+                        JSONArray array=tbApiService.getProjectRoleMember(projectId,id);
+                        if(array!=null&&array.size()>0){
+                            List<User> users=new ArrayList<>();
+                            for (int i = 0; i < array.size(); i++) {
+                                users.add(getDingUserId(array.getJSONObject(i).getString("userId")));
+                            }
+                            return users;
+                        }
+                    }
+                }
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return new ArrayList<>();
+    }
+
+    /***
+     * 查询项目概览自定义字段数据
+     */
+    private String getProjectCustVule(String projectId,String cfId){
+        try {
+            JSONArray array = tbApiService.getProjectCust(projectId);
+            List<JSONObject> list=array.toJavaList(JSONObject.class);
+            Optional optional = list.stream().filter(item -> cfId.equals(item.get("cfId"))).findAny();
+            JSONArray valueArray=((JSONObject) optional.get()).getJSONArray("value");
+            if(valueArray!=null&&valueArray.size()>0){
+                String value=valueArray.getJSONObject(0).getString("title");
+                return value;
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return "";
+    }
+}

+ 146 - 0
src/main/java/com/muzhi/tianhe/service/impl/YiDaServiceImpl.java

@@ -0,0 +1,146 @@
+package com.muzhi.tianhe.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.dingtalkyida_1_0.models.CreateOrUpdateFormDataResponse;
+import com.aliyun.dingtalkyida_1_0.models.StartInstanceHeaders;
+import com.aliyun.dingtalkyida_1_0.models.StartInstanceRequest;
+import com.aliyun.tea.TeaException;
+import com.aliyun.teautil.models.RuntimeOptions;
+import com.muzhi.tianhe.service.AccessTokenService;
+import com.muzhi.tianhe.service.YiDaService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class YiDaServiceImpl implements YiDaService {
+
+    @Autowired
+    private AccessTokenService accessTokenService;
+
+    public static String body = null;
+    public static String result = null;
+
+
+    /**
+     * 使用 Token 初始化账号Client
+     * @return Client
+     * @throws Exception
+     */
+    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);
+    }
+
+    @Override
+    public String createYiDaXM(String mapJson,String xmid) throws Exception{
+
+        //创建或更新宜搭表单
+        /*测试数据*/
+//        mapJson = "{\"textField_ljzik5xb\":\"64acf5950f2e439900ff109d\",\"textField_ljzik5xd\":\"PM2023-030 门厅漆面工程\",\"textField_lk0qrye4\":\"客户现场产品(软件)调试的标准业务流程、规范、交付物\"}\n";
+//
+//        xmid = "111";
+
+        /*同步至宜搭*/
+
+        //宜搭表单判断查询条件
+        Map map1 = new HashMap();
+        //字段编号
+        map1.put("key","textField_ljzik5xb");
+        //项目ID编号
+        map1.put("value", xmid);
+        //控件类型
+        map1.put("type","TEXT");
+        //逻辑判断符
+        map1.put("operator","eq");
+        //控件名称
+        map1.put("componentName","TextField");
+
+        List list11 = new ArrayList();
+        list11.add(map1);
+
+        String listJson = JSON.toJSONString(list11);
+        System.out.println("查询条件listJson:" + listJson);
+
+
+        //新增或更新表单实例【宜搭】
+        JSONObject jsonObject = null;
+        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 = accessTokenService.getAccessToken();
+            com.aliyun.dingtalkyida_1_0.models.CreateOrUpdateFormDataRequest createOrUpdateFormDataRequest = new com.aliyun.dingtalkyida_1_0.models.CreateOrUpdateFormDataRequest()
+                    //应用秘钥
+                    .setSystemToken("9H666R71GOFCQDI3BG6LQ8P85P0B3N3LJIZJL3")
+                    //表单编码
+                    .setFormUuid("FORM-XHA66881VDFCHYS7A3FG57P8U0D02H5PJIZJLK")
+                    .setUserId("yida_pub_account")
+                    .setSearchCondition(listJson)
+                    //应用编码
+                    .setAppType("APP_OT7X1YCX3FL4OUCVI1SS")
+                    .setFormDataJson(mapJson);
+
+            CreateOrUpdateFormDataResponse orUpdateFormDataWithOptions = client11.createOrUpdateFormDataWithOptions(createOrUpdateFormDataRequest, createOrUpdateFormDataHeaders, new RuntimeOptions());
+            String s = JSON.toJSONString(orUpdateFormDataWithOptions.getBody());
+            jsonObject = JSON.parseObject(s);
+            System.out.println("【创建或更新】表单object对象:" + jsonObject);
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        }
+        /*同步至宜搭*/
+
+        return "创建宜搭项目表单成功!";
+    }
+
+    @Override
+    public String createYiDaRW(String dingUserid,String mapJson) throws Exception{
+
+        com.aliyun.dingtalkyida_1_0.Client client = createClient();
+        StartInstanceHeaders startInstanceHeaders = new StartInstanceHeaders();
+        startInstanceHeaders.xAcsDingtalkAccessToken = accessTokenService.getAccessToken();
+        StartInstanceRequest startInstanceRequest = new StartInstanceRequest()
+                .setAppType("APP_T3TFV4N6RKNJJE2G78NY")
+                .setSystemToken("N4A66IB1II3A3OXXEG08F7WBXJV72G9ACWFHL7Q")
+                .setUserId(dingUserid)
+                .setLanguage("zh_CN")
+                .setFormUuid("FORM-OS566L918B9A5CO89BLL3857BAWJ2R3ICWFHLE")
+                .setFormDataJson(mapJson);
+//                //流程编码
+//                .setProcessCode("TPROC--EF6Y4xxx")
+//                //发起人所在部门ID
+//                .setDepartmentId("18295");
+        try {
+            client.startInstanceWithOptions(startInstanceRequest, startInstanceHeaders, new RuntimeOptions());
+        } catch (TeaException err) {
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        } catch (Exception _err) {
+            TeaException err = new TeaException(_err.getMessage(), _err);
+            if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
+                // err 中含有 code 和 message 属性,可帮助开发定位问题
+            }
+
+        }
+
+        return "创建宜搭任务表单成功!";
+    }
+
+}

+ 26 - 0
src/main/java/com/muzhi/tianhe/util/PublicUtil.java

@@ -0,0 +1,26 @@
+package com.muzhi.tianhe.util;
+
+import java.util.Map;
+
+public class PublicUtil {
+    public PublicUtil() {
+    }
+
+    public static boolean isNull(Object obj) {
+        return obj == null || "".equals(obj) || "null".equals(obj);
+    }
+
+    public static boolean isNull(Map param, String... keys) {
+        String[] var2 = keys;
+        int var3 = keys.length;
+
+        for(int var4 = 0; var4 < var3; ++var4) {
+            String key = var2[var4];
+            if (!param.containsKey(key) || param.get(key) == null || String.valueOf(param.get(key)).trim().equals("") || String.valueOf(param.get(key)).trim().equals("null")) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 274 - 0
src/main/java/com/muzhi/tianhe/util/UtilExcel.java

@@ -0,0 +1,274 @@
+package com.muzhi.tianhe.util;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.fastjson.JSON;
+import com.google.common.base.Strings;
+import lombok.Builder;
+import lombok.Data;
+import lombok.SneakyThrows;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.xssf.usermodel.*;
+import org.joda.time.DateTimeUtils;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * excel 导出工具 [poi & easy excel]
+ */
+@Builder
+@Data
+public class UtilExcel {
+
+    /**
+     * 设置响应流
+     */
+    @SneakyThrows
+    public static void setResponseHeader(HttpServletResponse response,String fileName, String extension) {
+        // 后缀: poi, xlsx下预览无详情; EasyExcel 需要使用xlsx, 否则不能预览
+        if (StringUtils.isBlank(extension)) {
+            extension = ".xls";
+        }
+        String date = new SimpleDateFormat("yyyy-MM-dd HH_mm_ss").format(new Date());
+        fileName = fileName + "_" + date + extension;
+        // 设置导出流信息
+        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
+        response.setCharacterEncoding("utf-8");
+        // 文件名兼容中文
+        fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
+        response.setHeader("Content-Disposition", "attachment;filename*=utf-8" + fileName);
+        // 想要让客户端CellStyleModel可以访问到其他的首部信息,服务器不仅要在header里加入该首部,还要将它们在 Access-Control-Expose-Headers 里面列出来
+        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+    }
+
+    /////////////////// Poi ///////////////////
+
+    // 各个列的表头
+    private String[] heardList;
+    // 各个列的元素key值
+    private String[] heardKey;
+    // 需要填充的数据信息
+    private List<Map> data;
+    // 导出文件名称
+    private String fileName;
+
+    // 字体大小
+    @Builder.Default
+    private int fontSize = 12;
+    @Builder.Default
+    private String fontName = "微软雅黑";
+    // 行高
+    @Builder.Default
+    private int rowHeight = 30;
+    // 列宽
+    @Builder.Default
+    private int columnWidth = 20;
+    // 工作表
+    @Builder.Default
+    private String sheetName = "sheet1";
+
+    /**
+     * 回调单元格样式
+     * -
+     * cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); // 是设置前景色不是背景色
+     * cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+     */
+    @FunctionalInterface
+    public interface UpdateCellStyle {
+        void invoke(String value, XSSFCellStyle cellStyle);
+    }
+
+    public void exportExcelByPoi(HttpServletResponse response) throws IOException {
+        exportExcelByPoi(response, null);
+    }
+
+    /**
+     * Poi 导出功能
+     * -
+     * - java中的强制类型转换只是针对单个对象的,想要偷懒将整个数组转换成另外一种类型的数组是不行的. toArray 运行中已经转为 Object, 需要指定初始化类型
+     * - 4.x打包引用会异常: HSSFCellStyle.BORDER_THIN 指向的是 CellStyle.BORDER_THIN. 另若引入 easyExcel, 无需再引入 poi [其依赖是低版本 poi]
+     * - 数量限制: HSSF改为XSSF后,导出1000000条数据. HSSF最大容量为65535 [最大65536(2的16次方)行;即横向256个单元格,竖向65536个单元格]
+     */
+    public void exportExcelByPoi(HttpServletResponse response, UpdateCellStyle lambda) throws IOException {
+        // 检查参数配置信息
+        checkConfig();
+        // 创建工作簿
+        XSSFWorkbook wb = new XSSFWorkbook();
+        // 创建工作表
+        XSSFSheet wbSheet = wb.createSheet(sheetName);
+        // 设置默认行宽
+        wbSheet.setDefaultColumnWidth(columnWidth);
+
+        // 设置表格样式
+        XSSFCellStyle styleHeader = createCellStyle(wb);
+        // 设置表头字体
+        XSSFFont fontHeader = wb.createFont();
+        fontHeader.setFontHeightInPoints((short) fontSize);
+        fontHeader.setFontName(fontName);
+        fontHeader.setBold(true);
+        styleHeader.setFont(fontHeader);
+
+        //设置列头元素
+        XSSFRow row = wbSheet.createRow(0);
+        XSSFCell cellHead = null;
+        for (int i = 0; i < heardList.length; i++) {
+            cellHead = row.createCell(i);
+            cellHead.setCellValue(heardList[i]);
+            cellHead.setCellStyle(styleHeader);
+        }
+
+        // 设置表格样式
+        XSSFCellStyle bodyStyle = createCellStyle(wb);
+        // 设置表格字体
+        XSSFFont fontBody = wb.createFont();
+        fontBody.setFontHeightInPoints((short) fontSize);
+        fontBody.setFontName(fontName);
+        fontBody.setBold(false);
+        bodyStyle.setFont(fontBody);
+
+        //开始写入实体数据信息
+        int a = 1;
+        for (int i = 0; i < data.size(); i++) {
+            XSSFRow roww = wbSheet.createRow(a);
+            Map map = data.get(i);
+            XSSFCell cell = null;
+            for (int j = 0; j < heardKey.length; j++) {
+                cell = roww.createCell(j);
+                cell.setCellStyle(bodyStyle);
+                Object valueObject = map.get(heardKey[j]);
+                String value = null;
+                if (valueObject == null) {
+                    valueObject = "";
+                }
+                if (valueObject instanceof String) {
+                    //取出的数据是字符串直接赋值
+                    value = (String) map.get(heardKey[j]);
+                } else if (valueObject instanceof Integer) {
+                    //取出的数据是Integer
+                    value = String.valueOf(((Integer) (valueObject)).floatValue());
+                } else if (valueObject instanceof BigDecimal) {
+                    //取出的数据是BigDecimal
+                    value = String.valueOf(((BigDecimal) (valueObject)).floatValue());
+                } else {
+                    value = valueObject.toString();
+                }
+                cell.setCellValue(Strings.isNullOrEmpty(value) ? "" : value);
+                // 独立设置单元格样式
+                if (ObjectUtil.isNotNull(lambda)) {
+                    XSSFCellStyle newBodyStyle = createCellStyle(wb);
+                    newBodyStyle.setFont(fontBody);
+                    lambda.invoke(value, newBodyStyle);
+                    cell.setCellStyle(newBodyStyle);
+                }
+            }
+            a++;
+        }
+        // 导出文件
+        setResponseHeader(response, fileName, ".xls");
+        // 取得输出流
+        OutputStream os = response.getOutputStream();
+        wb.write(os);
+        os.flush();
+        os.close();
+    }
+
+    // 不同的 CellStyle 需要独立创建
+    private XSSFCellStyle createCellStyle(XSSFWorkbook wb) {
+        XSSFCellStyle style = wb.createCellStyle();
+        style.setBorderBottom(BorderStyle.THIN);     //下边框
+        style.setBorderLeft(BorderStyle.THIN);       //左边框
+        style.setBorderTop(BorderStyle.THIN);        //上边框
+        style.setBorderRight(BorderStyle.THIN);      //右边框
+        return style;
+    }
+
+    // 检查数据配置问题
+    protected void checkConfig() {
+        if (heardKey == null || heardList.length == 0) {
+//            McException.exceptionParam("列名数组不能为空或者为NULL");
+        }
+        if (fontSize < 0 || rowHeight < 0 || columnWidth < 0) {
+//            McException.exceptionParam("字体、宽度或者高度不能为负值");
+        }
+        if (Strings.isNullOrEmpty(fileName)) {
+//            McException.exceptionParam("导出文件名称不能为NULL");
+        }
+    }
+
+    /////////////////// EasyExcel ///////////////////
+
+    /**
+     * 导出功能 EasyExcel [class支持map]
+     * -
+     * - Mac下, 若报错 Times 字体找不到, 下载安装一下, 不影响功能使用
+     * - ClassPathResource, 需要打包/编译后才能访问到. 识别不是架包内内容
+     */
+    @SneakyThrows
+    public static void exportListByTemplate(HttpServletResponse response, List dataList, Class dtoClass,String fileName, String templateName) {
+        InputStream inputStream = UtilFile.readPackageResource("templates/" + templateName);
+        UtilExcel.setResponseHeader(response, fileName, ".xlsx");
+        // 模板导出
+        EasyExcel.write(response.getOutputStream(), dtoClass).withTemplate(inputStream).sheet().doFill(dataList);
+    }
+
+    /**
+     * 列表与主表进行填充 [格式: 模板主表 {字段}, 列表 {.字段}]
+     */
+    @SneakyThrows
+    public static void exportMapAndListByTemplate(HttpServletResponse response, Object dataMain, List dataList, Class dtoClass, String fileName, String templateName) {
+        InputStream inputStream = UtilFile.readPackageResource("templates/" + templateName);
+        UtilExcel.setResponseHeader(response, fileName, ".xlsx");
+        ExcelWriter workBook = EasyExcel.write(response.getOutputStream(), dtoClass).withTemplate(inputStream).build();
+        WriteSheet sheet = EasyExcel.writerSheet().build();
+        // 先单组数据填充,再多组数据填充
+        workBook.fill(dataMain, sheet);
+        workBook.fill(dataList, sheet);
+        workBook.finish();
+    }
+
+
+    /**
+     * 导出功能 EasyExcel [class支持map]
+     * -
+     * - Mac下, 若报错 Times 字体找不到, 下载安装一下, 不影响功能使用
+     * - ClassPathResource, 需要打包/编译后才能访问到. 识别不是架包内内容
+     */
+    public static void exportListByTemplate(HttpServletResponse response, List dataList, Class dtoClass,String fileName) throws IOException {
+//        UtilExcel.setResponseHeader(response, fileName, ".xlsx");
+        String date = new SimpleDateFormat("yyyy-MM-dd HH_mm_ss").format(new Date());
+        fileName = fileName + "_" + date;
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        try {
+            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
+            fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
+            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            // 模板导出
+            EasyExcel.write(response.getOutputStream(), dtoClass).sheet().doWrite(dataList);
+        } catch (Exception e) {
+            // 重置response
+            response.reset();
+            response.setContentType("application/json");
+            response.setCharacterEncoding("utf-8");
+            Map<String, String> map = new HashMap<>();
+            map.put("status", "failure");
+            map.put("message", "下载文件失败" + e.getMessage());
+            response.getWriter().println(JSON.toJSONString(map));
+        }
+    }
+
+}

+ 239 - 0
src/main/java/com/muzhi/tianhe/util/UtilFile.java

@@ -0,0 +1,239 @@
+package com.muzhi.tianhe.util;
+
+import com.alibaba.fastjson.JSON;
+import lombok.SneakyThrows;
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.core.io.ClassPathResource;
+
+import java.io.*;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public abstract class UtilFile {
+
+    ////////////////////////////// File Path //////////////////////////////
+
+    /**
+     * 匹配路径: 自动追加年月日作为目录
+     * -
+     * 文件上传存储,必须是绝对路径. 日志的配置地址可以是相对路径
+     */
+    public static File mkdirIfNot(String fileName,String dirName) {
+        String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
+        String outputFile = dirName + "/" + date + "/" + fileName;
+        File file = new File(outputFile);
+        // 创建 mkdirIfNot
+        if (!file.getParentFile().exists()) {
+            file.getParentFile().mkdirs();
+        }
+        return file;
+    }
+
+    /**
+     * 根据路径删除指定的目录或文件,无论存在与否
+     */
+    public static boolean DeleteFolder(String sPath) {
+        boolean flag = false;
+        File file = new File(sPath);
+        // 判断目录或文件是否存在
+        if (!file.exists()) {  // 不存在返回 false
+            return flag;
+        } else {
+            // 判断是否为文件
+            if (file.isFile()) {  // 为文件时调用删除文件方法
+                return deleteFile(sPath);
+            } else {  // 为目录时调用删除目录方法
+                return deleteDirectory(sPath);
+            }
+        }
+    }
+
+    /**
+     * 删除单个文件
+     */
+    public static boolean deleteFile(String sPath) {
+        boolean flag = false;
+        File file = new File(sPath);
+        // 路径为文件且不为空则进行删除
+        if (file.isFile() && file.exists()) {
+            file.delete();
+            flag = true;
+        }
+        return flag;
+    }
+
+    /**
+     * 删除目录(文件夹)以及目录下的文件
+     */
+    public static boolean deleteDirectory(String sPath) {
+        // 如果sPath不以文件分隔符结尾,自动添加文件分隔符
+        if (!sPath.endsWith(File.separator)) {
+            sPath = sPath + File.separator;
+        }
+        File dirFile = new File(sPath);
+        // 如果dir对应的文件不存在,或者不是一个目录,则退出
+        if (!dirFile.exists() || !dirFile.isDirectory()) {
+            return false;
+        }
+        boolean flag = true;
+        // 删除文件夹下的所有文件(包括子目录)
+        File[] files = dirFile.listFiles();
+        for (int i = 0; i < files.length; i++) {
+            // 删除子文件
+            if (files[i].isFile()) {
+                flag = deleteFile(files[i].getAbsolutePath());
+                if (!flag) break;
+            } // 删除子目录
+            else {
+                flag = deleteDirectory(files[i].getAbsolutePath());
+                if (!flag) break;
+            }
+        }
+        if (!flag) return false;
+        // 删除当前目录
+        if (dirFile.delete()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    ////////////////////////////// File & 字节/流 //////////////////////////////
+
+    /**
+     * 文件转化为byte字节数组
+     */
+    public static byte[] fileToByteArray(File file) {
+        byte[] data = null;
+        try {
+            FileInputStream fis = new FileInputStream(file);
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            int len;
+            byte[] buffer = new byte[1024];
+            while ((len = fis.read(buffer)) != -1) {
+                baos.write(buffer, 0, len);
+            }
+            data = baos.toByteArray();
+            fis.close();
+            baos.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return data;
+    }
+
+    /**
+     * 文件全路径转Base64
+     */
+    public static String fileToBase64(String path) {
+        String base64 = null;
+        InputStream in = null;
+        try {
+            File file = new File(path);
+            in = new FileInputStream(file);
+            byte[] bytes = new byte[(int) file.length()];
+            in.read(bytes);
+            base64 = new String(Base64.encodeBase64(bytes), "UTF-8");
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return base64;
+    }
+
+    /**
+     * Base64转文件储存
+     */
+    public static void base64ToFile(String outFilePath, String base64, String outFileName) {
+        File file = null;
+        //创建文件目录
+        String filePath = outFilePath;
+        File dir = new File(filePath);
+        if (!dir.exists() && !dir.isDirectory()) {
+            dir.mkdirs();
+        }
+        BufferedOutputStream bos = null;
+        FileOutputStream fos = null;
+        try {
+            byte[] bytes = Base64.decodeBase64(base64);
+            file = new File(filePath + "/" + outFileName);
+            fos = new FileOutputStream(file);
+            bos = new BufferedOutputStream(fos);
+            bos.write(bytes);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (bos != null) {
+                try {
+                    bos.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    ////////////////////////////// Resource 读取 //////////////////////////////
+
+    /**
+     * 项目包文件, 读取 [ ppExt: 读取 Resource 必须 ]
+     * -
+     * 1. ClassPathResource, 需要打包/编译后才能访问到. 识别不是架包内内容
+     * 2. 路径若 WebConfiguration 配置, 可使用配置别名. [详见 WebConfiguration]
+     */
+    @SneakyThrows
+    public static InputStream readPackageResource(String path) {
+        ClassPathResource classPathResource = new ClassPathResource(path);
+        InputStream inputStream = classPathResource.getInputStream();
+        return inputStream;
+    }
+
+    /**
+     * 项目包JSON, 读取 [ ppExt: 读取 Resource 必须 ]
+     */
+    public static Object readJsonObjectFromResource(String path) {
+        return _readJsonObjectFromStream(readPackageResource(path));
+    }
+
+    /// ppExt: 文件流, 转JSONObject
+    /// 1. 使用 JSONUtil.class.getClassLoader().getResource(path).getPath(); 本地访问路径正常, 部署服务器路径访问访问, 需要读取 Resource
+    /// 2. 若是读取本地json, 文件不能使用 .josn 后缀, 会被转义导致解析异常 / 但若是 Resource 资源则不受影响, [访问路径详见 WebConfiguration]
+    @SneakyThrows
+    private static Object _readJsonObjectFromStream(InputStream inputStream) {
+        Reader reader = new InputStreamReader(inputStream, "utf-8");
+        int ch = 0;
+        StringBuffer sb = new StringBuffer();
+        while ((ch = reader.read()) != -1) {
+            sb.append((char) ch);
+        }
+        reader.close();
+        return JSON.parse(sb.toString());
+    }
+
+    /**
+     * 文件绝对路径转json
+     */
+    @SneakyThrows
+    public static Object readJsonObjectFromFile(String absolutePath) {
+        File jsonFile = new File(absolutePath);
+        FileReader fileReader = new FileReader(jsonFile);
+        // 通过文件, 读取流转json对象
+        Object jsonObject = _readJsonObjectFromStream(new FileInputStream(jsonFile));
+        fileReader.close();
+        return jsonObject;
+    }
+}

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

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

+ 58 - 0
src/test/java/com/muzhi/tianhe/TbTest.java

@@ -0,0 +1,58 @@
+package com.muzhi.tianhe;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.muzhi.tianhe.service.AccessTokenService;
+import com.muzhi.tianhe.service.TbService;
+import com.muzhi.tianhe.service.impl.TbApiService;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.joda.time.DateTimeUtils;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+@Slf4j
+@SpringBootTest(classes = TianHeApplication.class)
+public class TbTest {
+
+    @Autowired
+    private TbService tbService;
+    @Autowired
+    private TbApiService tbApiService;
+
+    @Test
+    public void test(){
+        try {
+//            tbService.removeTianhe();
+//            tbService.getXiangmu();
+//            List users=tbService.projectMember("65eefe214f2eb1fe4e88b557");
+//            log.info("u:{}",users);
+            tbService.test();
+//            tbService.getDingUserId("");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void tbTest(){
+        String projectId="65b9e2525a93ac024005cf10";
+        String taskType="";
+        JSONArray array=tbApiService.getProjectList();
+        System.out.println(array.getJSONObject(0));
+    }
+
+
+
+}