Browse Source

工作报告发送对接钉钉、优化部分代码

fuyuhchuan 8 months ago
parent
commit
c19fb0ad32

+ 14 - 0
service-iot/service-iot-biz/pom.xml

@@ -94,6 +94,20 @@
             <artifactId>ruoyi-common-core</artifactId>
         </dependency>
 
+        <!--钉钉-->
+        <!--获取企业accessToken(企业内部应用) 新版SDK-->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dingtalk</artifactId>
+            <version>2.1.34</version>
+        </dependency>
+        <!--旧版SDK-->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>alibaba-dingtalk-service-sdk</artifactId>
+            <version>2.0.0</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 54 - 0
service-iot/service-iot-biz/src/main/java/com/usky/iot/constant/dingTalkConstant.java

@@ -0,0 +1,54 @@
+package com.usky.iot.constant;
+
+public class dingTalkConstant {
+
+    // 钉钉组织id CorpId
+    public static final String DING_TALK_CORP_ID = "dingefbc6eb33c7f3d80";
+
+    // App Id(应用id)
+    public static final String DING_TALK_APP_ID = "e9908c06-c9cf-49a9-b8ae-a80c4a298782";
+
+    // 原企业内部应用AgentId
+    public static final String DING_TALK_AGENT_ID = "3130849582";
+
+    // Client ID (原 AppKey 和 SuiteKey)
+    public static final String DING_TALK_CLIENT_ID = "dingbrwslwgx0bykrjbc";
+
+    // Client Secret (原 AppSecret 和 SuiteSecret)
+    public static final String DING_TALK_CLIENT_SECRET = "G77mhy4tOJJxoBctvmRUIFpGphV39h8dv9hlVI9kJHrq3sLIDBzggwi22mTpLjpa";
+
+    // 企业内部 access_token 的获取URL(2个参数:Client ID 和 Client Secret)
+    public static final String DING_TALK_ACCESS_TOKEN_URL = "https://oapi.dingtalk.com/gettoken";
+
+    // 用户 userId 获取URL      根据手机号获取(2个参数:手机号 mobile ,access_token)
+    public static final String DING_TALK_USERID_URL = "https://oapi.dingtalk.com/topapi/v2/user/getbymobile";
+
+    // 日报模板id
+    public static final String DING_TALK_DAILY_REPORT_TEMPLATE_ID = "153cb1ea26eee62842e14fa40fa92b95";
+
+    // 钉钉日报模板名
+    public static final String DING_TALK_DAILY_REPORT_TEMPLATE_NAME = "日报";
+
+    // 日报是否发送聊天消息到抄送人
+    public static final Boolean DING_TALK_DAILY_REPORT_TO_CHAT = false;
+
+    // 日报内容类型
+    public static final String DING_TALK_DAILY_REPORT_CONTENT_TYPE = "markdown";
+
+    //日报内容key 今日完成工作
+    public static final String DING_TALK_DAILY_REPORT_COMPLETED_WORK = "今日完成工作";
+
+    //日报内容key 明日工作计划
+    public static final String DING_TALK_DAILY_REPORT_TOMORROW_PLAN = "明日工作计划";
+
+    //日报内容key 需协调工作
+    public static final String DING_TALK_DAILY_REPORT_COORDINATE_WORK = "需协调工作";
+
+    // 日志发送到的群id
+    public static final String DING_TALK_DAILY_REPORT_TO_CIDS = "";
+
+    // 钉钉日报发送地址
+    public static final String DING_TALK_CREATE_DAILY_REPORT_URL = "https://oapi.dingtalk.com/topapi/report/create";
+
+
+}

+ 5 - 0
service-iot/service-iot-biz/src/main/java/com/usky/iot/domain/MceMbuser.java

@@ -44,6 +44,11 @@ public class MceMbuser implements Serializable {
      */
     private String cids;
 
+    /**
+     * 钉钉用户id
+     */
+    private String dingTalkId;
+
     /**
      * 创建人
      */

+ 6 - 0
service-iot/service-iot-biz/src/main/java/com/usky/iot/domain/PmWorkReport.java

@@ -110,4 +110,10 @@ public class PmWorkReport implements Serializable {
      */
     @TableField(exist = false)
     private Integer readFlag;
+
+    /**
+     * 是否同步钉钉 默认:0,同步;1:不同步
+     */
+    @TableField(exist = false)
+    private Integer dingTalk;
 }

+ 2 - 0
service-iot/service-iot-biz/src/main/java/com/usky/iot/mapper/MceMbuserMapper.java

@@ -2,6 +2,7 @@ package com.usky.iot.mapper;
 
 import com.usky.iot.domain.MceMbuser;
 import com.usky.common.mybatis.core.CrudMapper;
+import org.springframework.stereotype.Repository;
 
 /**
  * <p>
@@ -11,6 +12,7 @@ import com.usky.common.mybatis.core.CrudMapper;
  * @author han
  * @since 2024-05-10
  */
+@Repository
 public interface MceMbuserMapper extends CrudMapper<MceMbuser> {
 
 }

+ 8 - 0
service-iot/service-iot-biz/src/main/java/com/usky/iot/service/PmWorkReportService.java

@@ -2,6 +2,7 @@ package com.usky.iot.service;
 
 import com.usky.iot.domain.PmWorkReport;
 import com.usky.common.mybatis.core.CrudService;
+import org.springframework.scheduling.annotation.Async;
 
 import java.util.List;
 import java.util.Map;
@@ -31,9 +32,16 @@ public interface PmWorkReportService extends CrudService<PmWorkReport> {
      */
     void addReport(PmWorkReport pmWorkReport);
 
+
     /**
      * 工时计算
      * @return
      */
     List<Map<String, Object>> countTime();
+
+    /**
+     * 同步钉钉
+     * @param workReport 工作报告
+     */
+    void sendDingTalkDailyReport(PmWorkReport workReport);
 }

+ 231 - 34
service-iot/service-iot-biz/src/main/java/com/usky/iot/service/impl/PmWorkReportServiceImpl.java

@@ -1,17 +1,26 @@
 package com.usky.iot.service.impl;
 
 import com.alibaba.fastjson.JSONObject;
+import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
+import com.aliyun.tea.TeaException;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.OapiReportCreateRequest;
+import com.dingtalk.api.request.OapiReportSavecontentRequest;
+import com.dingtalk.api.request.OapiV2UserGetbymobileRequest;
+import com.dingtalk.api.response.OapiReportCreateResponse;
+import com.dingtalk.api.response.OapiV2UserGetbymobileResponse;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.taobao.api.ApiException;
 import com.usky.common.core.exception.BusinessException;
 import com.usky.common.security.utils.SecurityUtils;
-import com.usky.iot.domain.PmProject;
-import com.usky.iot.domain.PmReceive;
-import com.usky.iot.domain.PmWorkContent;
-import com.usky.iot.domain.PmWorkReport;
-import com.usky.iot.mapper.PmReceiveMapper;
-import com.usky.iot.mapper.PmWorkContentMapper;
-import com.usky.iot.mapper.PmWorkReportMapper;
+import com.usky.iot.constant.dingTalkConstant;
+import com.usky.iot.domain.*;
+import com.usky.iot.mapper.*;
+import com.usky.iot.service.MceMbuserService;
 import com.usky.iot.service.PmProjectService;
 import com.usky.iot.service.PmWorkContentService;
 import com.usky.iot.service.PmWorkReportService;
@@ -19,7 +28,10 @@ import com.usky.common.mybatis.core.AbstractCrudService;
 import com.usky.iot.service.vo.PmProjectWorkTimeVo;
 import com.usky.system.RemoteMceService;
 import com.usky.system.domain.SysUser;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -42,6 +54,7 @@ import java.util.stream.Collectors;
  * @author fu
  * @since 2024-05-20
  */
+@Slf4j
 @Service
 public class PmWorkReportServiceImpl extends AbstractCrudService<PmWorkReportMapper, PmWorkReport> implements PmWorkReportService {
 
@@ -67,6 +80,12 @@ public class PmWorkReportServiceImpl extends AbstractCrudService<PmWorkReportMap
     @Autowired
     private PmReceiveMapper pmReceiveMapper;
 
+    @Autowired
+    private MceMbuserMapper mceMbuserMapper;
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
     /**
      * 获取时间内工作报告
      *
@@ -248,7 +267,13 @@ public class PmWorkReportServiceImpl extends AbstractCrudService<PmWorkReportMap
             newReport.setDeptId(deptId);
             newReport.setTenantId(tenantId);
             newReport.setTotalHours(totalWorkTime);
-            pmWorkReportMapper.insert(newReport);
+            try {
+                pmWorkReportMapper.insert(newReport);
+
+            } catch (Exception e) {
+                e.printStackTrace();
+                throw new BusinessException("已存在重复数据,一人一天一篇报告");
+            }
 
             //获取报告中所有项目id
             List<Integer> projectIds = new ArrayList<>();
@@ -288,23 +313,12 @@ public class PmWorkReportServiceImpl extends AbstractCrudService<PmWorkReportMap
                         .orElse(Collections.emptyList());
             }
             sendAsyncMessage(newReport, ids);
-            //查username
-            if (ids.size() > 0) {
-                List<SysUser> usersName = pmWorkContentService.nickNames(ids);
-                for (Long id : ids) {
-                    PmReceive pmReceive = new PmReceive();
-                    pmReceive.setReceiverId(id);
-                    pmReceive.setReportId(newReport.getId());
-                    pmReceive.setTenantId(tenantId);
-                    pmReceive.setDeptId(deptId);
-                    pmReceive.setCreateBy(userName);
-                    pmReceive.setCreateTime(dateTime);
-                    pmReceive.setReceiverName(userName);
-                    pmReceive.setReadFlag(0);
-                    pmReceiveMapper.insert(pmReceive);
-                }
+            //存入消息接收
+            receiveMessages(ids, newReport.getId());
+            //是否同步钉钉
+            if (pmWorkReport.getDingTalk() == 0) {
+                sendDingTalkDailyReport(pmWorkReport);
             }
-
         } else if (repeat.size() > 0) {
             PmWorkReport rp = new PmWorkReport();
             rp.setId(pmWorkReport.getId());
@@ -344,7 +358,31 @@ public class PmWorkReportServiceImpl extends AbstractCrudService<PmWorkReportMap
         }
     }
 
-    private void sendAsyncMessage(PmWorkReport newReport, List<Long> userId) {
+    private void receiveMessages(List<Long> ids, Integer reportId) {
+        List<SysUser> users = pmWorkContentService.nickNames(ids);
+        if (ids.size() > 0) {
+            for (Long id : ids) {
+                PmReceive pmReceive = new PmReceive();
+                pmReceive.setReceiverId(id);
+                pmReceive.setReportId(reportId);
+                pmReceive.setTenantId(SecurityUtils.getTenantId());
+                pmReceive.setDeptId(SecurityUtils.getLoginUser().getSysUser().getDeptId());
+                pmReceive.setCreateBy(SecurityUtils.getUsername());
+                pmReceive.setCreateTime(LocalDateTime.now());
+                for (SysUser user : users) {
+                    if (user.getUserId().equals(id)) {
+                        pmReceive.setReceiverName(user.getUserName());
+                    }
+                }
+                pmReceive.setReadFlag(0);
+                pmReceiveMapper.insert(pmReceive);
+            }
+        }
+    }
+
+    @Async("asyncServiceExecutor")
+    public void sendAsyncMessage(PmWorkReport newReport, List<Long> userId) {
+        log.info("start asyncServiceExecutor remoteMceService.addMce--------------------");
         JSONObject jsonObject = new JSONObject();
         jsonObject.put("infoTitle", INFO_TITLE);
         jsonObject.put("infoContent", SecurityUtils.getLoginUser().getSysUser().getNickName() + INFO_CONTENT);
@@ -354,15 +392,12 @@ public class PmWorkReportServiceImpl extends AbstractCrudService<PmWorkReportMap
         if (userId != null && !userId.isEmpty()) {
             jsonObject.put("userIds", userId);
         }
-        // 异步发送消息
-        CompletableFuture.runAsync(() -> {
-            try {
-                // 推送消息中心
-                remoteMceService.addMce(jsonObject.toString());
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        });
+        // 推送消息中心
+        try {
+            remoteMceService.addMce(jsonObject.toString());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
     }
 
     @Override
@@ -448,4 +483,166 @@ public class PmWorkReportServiceImpl extends AbstractCrudService<PmWorkReportMap
         returnList.add(monthMap);
         return returnList;
     }
+
+    /**
+     * 指定请求
+     * @return
+     * @throws Exception
+     */
+    public static com.aliyun.dingtalkoauth2_1_0.Client createClient() throws Exception {
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+        config.protocol = "https";
+        config.regionId = "central";
+        config.method = "POST";
+        return new com.aliyun.dingtalkoauth2_1_0.Client(config);
+    }
+
+    /**
+     * 获取钉钉企业内部AccessToken
+     * @return
+     * @throws Exception
+     */
+    public String getDingTalkToken() throws Exception {
+        GetAccessTokenResponse responseBody = new GetAccessTokenResponse();
+        com.aliyun.dingtalkoauth2_1_0.Client client = createClient();
+        com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest getAccessTokenRequest = new com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest()
+                .setAppKey(dingTalkConstant.DING_TALK_CLIENT_ID)
+                .setAppSecret(dingTalkConstant.DING_TALK_CLIENT_SECRET);
+        try {
+            responseBody = client.getAccessToken(getAccessTokenRequest);
+        } 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 responseBody.getBody().accessToken;
+    }
+
+    /**
+     * 获取钉钉userId
+     * @param userId 平台userid
+     * @return
+     */
+    public String getDingTalkUserId(Long userId) {
+        String dingTalkId = "";
+        LambdaQueryWrapper<MceMbuser> dingTalkQuery = Wrappers.lambdaQuery();
+        dingTalkQuery.select(MceMbuser::getDingTalkId, MceMbuser::getUserId)
+                .eq(MceMbuser::getUserId, userId);
+        MceMbuser mceMbuser = mceMbuserMapper.selectOne(dingTalkQuery);
+        if (mceMbuser != null) {
+            String dingId = mceMbuser.getDingTalkId();
+            if (dingId != null && dingId != "") {
+                return mceMbuser.getDingTalkId();
+            }
+        }
+            LambdaQueryWrapper<SysUser> phoneQuery = Wrappers.lambdaQuery();
+            phoneQuery.select(SysUser::getPhonenumber, SysUser::getUserName)
+                    .eq(SysUser::getUserId, userId);
+            SysUser user = sysUserMapper.selectOne(phoneQuery);
+            String mobile = user.getPhonenumber();
+            try {
+                String accessToken = getDingTalkToken();
+                DingTalkClient client = new DefaultDingTalkClient(dingTalkConstant.DING_TALK_USERID_URL);
+                OapiV2UserGetbymobileRequest req = new OapiV2UserGetbymobileRequest();
+                req.setMobile(mobile);
+                OapiV2UserGetbymobileResponse rsp = client.execute(req, accessToken);
+                ObjectMapper objectMapper = new ObjectMapper();
+                JsonNode rootNode = objectMapper.readTree(rsp.getBody());
+                JsonNode resultNode = rootNode.get("result");
+                dingTalkId = resultNode.get("userid").asText();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            if (mceMbuser != null) {
+                mceMbuser.setDingTalkId(dingTalkId);
+                mceMbuser.setUpdateBy(SecurityUtils.getUsername());
+                mceMbuser.setUpdateTime(LocalDateTime.now());
+                mceMbuserMapper.update(mceMbuser, dingTalkQuery);
+            } else {
+                MceMbuser mbuser = new MceMbuser();
+                mbuser.setDingTalkId(dingTalkId);
+                mbuser.setPhone(mobile);
+                mbuser.setUserId(userId);
+                mbuser.setCreateBy(user.getUserName());
+                mbuser.setCreateTime(LocalDateTime.now());
+                mceMbuserMapper.insert(mbuser);
+            }
+        return dingTalkId;
+    }
+
+    @Async("asyncServiceExecutor")//异步发送
+    @Override
+    public void sendDingTalkDailyReport(PmWorkReport workReport) {
+        String ccToStr = workReport.getCcTo();
+        String[] items = ccToStr.split(",");
+        List<Long> userIds = new ArrayList<>();
+        for (String item : items) {
+            long number = Long.parseLong(item.trim());
+            userIds.add(number);
+        }
+        List<String> ccTo = new ArrayList<>();
+        for (Long userId : userIds) {
+            String dingTalkUserId = getDingTalkUserId(userId);
+            ccTo.add(dingTalkUserId);
+        }
+        String coordinateWork1 = workReport.getCoordinateWork();
+        String tomorrowPlan1 = workReport.getTomorrowPlan();
+        List<PmWorkContent> workContents = workReport.getWorkContents();
+        StringBuilder contentBuilder = new StringBuilder();
+        for (PmWorkContent content : workContents) {
+            String projectName = content.getProjectName();
+            BigDecimal workTime = content.getWorkTime();
+            String workContent = content.getWorkContent();
+            StringBuilder markdown = new StringBuilder();
+            markdown.append("# ").append(projectName).append(" ").append(workTime).append("h\n\n");
+            markdown.append(workContent).append("\n");
+            contentBuilder.append(markdown);
+        }
+        String completedWork = contentBuilder.toString();
+        try {
+            DingTalkClient client = new DefaultDingTalkClient(dingTalkConstant.DING_TALK_CREATE_DAILY_REPORT_URL);
+            OapiReportCreateRequest req = new OapiReportCreateRequest();
+            OapiReportCreateRequest.OapiCreateReportParam obj1 = new OapiReportCreateRequest.OapiCreateReportParam();
+            List<OapiReportCreateRequest.OapiReportContentVo> list3 = new ArrayList<>();
+            OapiReportCreateRequest.OapiReportContentVo completedWorkD = new OapiReportCreateRequest.OapiReportContentVo();
+            completedWorkD.setSort(0L);
+            completedWorkD.setType(1L);
+            completedWorkD.setContentType(dingTalkConstant.DING_TALK_DAILY_REPORT_CONTENT_TYPE);
+            completedWorkD.setContent(completedWork);
+            completedWorkD.setKey(dingTalkConstant.DING_TALK_DAILY_REPORT_COMPLETED_WORK);
+            list3.add(completedWorkD);
+            OapiReportCreateRequest.OapiReportContentVo tomorrowPlan = new OapiReportCreateRequest.OapiReportContentVo();
+            tomorrowPlan.setSort(1L);
+            tomorrowPlan.setType(1L);
+            tomorrowPlan.setContentType(dingTalkConstant.DING_TALK_DAILY_REPORT_CONTENT_TYPE);
+            tomorrowPlan.setContent(tomorrowPlan1);
+            tomorrowPlan.setKey(dingTalkConstant.DING_TALK_DAILY_REPORT_TOMORROW_PLAN);
+            list3.add(tomorrowPlan);
+            OapiReportCreateRequest.OapiReportContentVo coordinateWork = new OapiReportCreateRequest.OapiReportContentVo();
+            coordinateWork.setSort(2L);
+            coordinateWork.setType(1L);
+            coordinateWork.setContentType(dingTalkConstant.DING_TALK_DAILY_REPORT_CONTENT_TYPE);
+            coordinateWork.setContent(coordinateWork1);
+            coordinateWork.setKey(dingTalkConstant.DING_TALK_DAILY_REPORT_COORDINATE_WORK);
+            list3.add(coordinateWork);
+            obj1.setContents(list3);
+            obj1.setToUserids(ccTo);
+            obj1.setTemplateId(dingTalkConstant.DING_TALK_DAILY_REPORT_TEMPLATE_ID);
+            obj1.setToChat(dingTalkConstant.DING_TALK_DAILY_REPORT_TO_CHAT);
+            obj1.setDdFrom(dingTalkConstant.DING_TALK_CORP_ID);
+            obj1.setUserid(getDingTalkUserId(SecurityUtils.getUserId()));
+            //obj1.setToCids(dingTalkConstant.DING_TALK_DAILY_REPORT_TO_CIDS);//发送到群,群id
+            req.setCreateReportParam(obj1);
+            OapiReportCreateResponse rsp = client.execute(req, getDingTalkToken());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
 }

+ 14 - 0
service-iot/service-iot-biz/src/main/java/com/usky/iot/service/vo/DingTalkDailyReportVO.java

@@ -0,0 +1,14 @@
+package com.usky.iot.service.vo;
+
+import lombok.Data;
+
+/**
+ * @description:TODO
+ * @author: fu
+ * @create: 2024-07-24 13:15
+ */
+@Data
+public class DingTalkDailyReportVO {
+
+}
+

+ 1 - 0
service-iot/service-iot-biz/src/main/resources/mapper/iot/MceMbuserMapper.xml

@@ -13,6 +13,7 @@
         <result column="create_time" property="createTime" />
         <result column="update_by" property="updateBy" />
         <result column="update_time" property="updateTime" />
+        <result column="ding_talk_id" property="dingTalkId" />
     </resultMap>
 
 </mapper>