Browse Source

森语钉钉移动端调整

fenghaifu 9 months ago
parent
commit
5f19080c11
17 changed files with 724 additions and 0 deletions
  1. 18 0
      jeecg-boot/jeecg-boot-base-common/pom.xml
  2. 5 0
      jeecg-boot/jeecg-boot-base-common/src/main/java/org/jeecg/common/system/vo/LoginUser.java
  3. 146 0
      jeecg-boot/jeecg-boot-base-common/src/main/java/org/jeecg/common/util/DDMsgUtil.java
  4. 14 0
      jeecg-boot/jeecg-boot-base-common/src/main/java/org/jeecg/common/util/DateUtils.java
  5. 58 0
      jeecg-boot/jeecg-boot-base-common/src/main/java/org/jeecg/common/util/security/EncryptUtil.java
  6. BIN
      jeecg-boot/jeecg-boot-base-common/src/main/resources/lib/dingtalk-2.0.18.jar
  7. BIN
      jeecg-boot/jeecg-boot-base-common/src/main/resources/lib/taobao-sdk-java-auto.jar
  8. 1 0
      jeecg-boot/jeecg-boot-module-activiti/src/main/java/org/jeecg/modules/activiti/mapper/ActNodeMapper.java
  9. 2 0
      jeecg-boot/jeecg-boot-module-activiti/src/main/java/org/jeecg/modules/activiti/service/IActNodeService.java
  10. 1 0
      jeecg-boot/jeecg-boot-module-activiti/src/main/java/org/jeecg/modules/activiti/service/Impl/ActNodeServiceImpl.java
  11. 59 0
      jeecg-boot/jeecg-boot-module-activiti/src/main/java/org/jeecg/modules/activiti/service/Impl/ActZprocessServiceImpl.java
  12. 145 0
      jeecg-boot/jeecg-boot-module-activiti/src/main/java/org/jeecg/modules/activiti/web/ActTaskController.java
  13. 19 0
      jeecg-boot/jeecg-boot-module-system/pom.xml
  14. 1 0
      jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/config/ShiroConfig.java
  15. 39 0
      jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/LoginController.java
  16. 4 0
      jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/entity/SysUser.java
  17. 212 0
      jeecg-boot/jeecg-boot-module-system/src/main/resources/application-senyu.yml

+ 18 - 0
jeecg-boot/jeecg-boot-base-common/pom.xml

@@ -29,5 +29,23 @@
 			</snapshots>
 		</repository>
 	</repositories>
+	<dependencies>
+		<!--钉钉对接jar包-->
+		<dependency>
+			<groupId>com.dingtalk.api</groupId>
+			<artifactId>taobao-sdk</artifactId>
+			<version>0.0.1-SNAPSHOT</version>
+			<scope>system</scope>
+			<systemPath>${project.basedir}/src/main/resources/lib/taobao-sdk-java-auto.jar</systemPath>
+		</dependency>
+		<!-- 钉钉最新sdk -->
+		<dependency>
+			<groupId>com.aliyun</groupId>
+			<artifactId>dingtalk</artifactId>
+			<version>jar</version>
+			<scope>system</scope>
+			<systemPath>${project.basedir}/src/main/resources/lib/dingtalk-2.0.18.jar</systemPath>
+		</dependency>
+	</dependencies>
 	
 </project>

+ 5 - 0
jeecg-boot/jeecg-boot-base-common/src/main/java/org/jeecg/common/system/vo/LoginUser.java

@@ -107,4 +107,9 @@ public class LoginUser {
 	 */
 	private String postName;
 
+	/**
+	 * 钉钉用户id
+	 */
+	private String dingUser;
+
 }

+ 146 - 0
jeecg-boot/jeecg-boot-base-common/src/main/java/org/jeecg/common/util/DDMsgUtil.java

@@ -0,0 +1,146 @@
+package org.jeecg.common.util;
+
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.OapiGettokenRequest;
+import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
+import com.dingtalk.api.response.OapiGettokenResponse;
+import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.security.EncryptUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Component;
+import sun.net.www.http.HttpClient;
+
+import javax.annotation.PostConstruct;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.*;
+
+/**
+ * 钉钉接口类
+ * @author fhf
+ * @date 20240620
+ */
+@Slf4j
+@Component
+public class    DDMsgUtil {
+
+    public static Long agentId;
+
+    private static String appKey;
+
+    private static String appSecret;
+
+    private static String frontBaseUrl;
+
+    @Autowired
+    private Environment env;
+
+    @PostConstruct
+    public void readConfig() {
+        appKey = env.getProperty("dingtalk.appKey");
+        appSecret = env.getProperty("dingtalk.appSecret");
+        agentId = Long.parseLong(env.getProperty("dingtalk.agentId"));
+        frontBaseUrl = env.getProperty("front.baseurl");
+    }
+
+    /**
+     *
+     * @param userIdList : 钉钉用户id列表,多个逗号分隔
+     * @param title
+     * @param body
+     * @param taskId
+     * @return
+     */
+
+    public static Result<?> sendOaMessage(String userIdList, String title, String body,
+                                   String taskId){
+
+        Result<?> tokenResult = getAccessToken();
+        if (!tokenResult.isSuccess()){
+            return tokenResult;
+        }
+        String token = tokenResult.getMessage();
+        if (title.length()>20){
+            title = title.substring(0, 19);
+        }
+
+        try {
+            LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+            // 参数为用户名_taskid des加密
+            String key = URLEncoder.encode(EncryptUtil.desEncrypt(loginUser.getUsername()+"_"+taskId),"UTF-8");
+            String url = frontBaseUrl+"/activiti/check/"+ key;
+
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2");
+            OapiMessageCorpconversationAsyncsendV2Request req = new OapiMessageCorpconversationAsyncsendV2Request();
+            req.setAgentId(agentId);
+            req.setUseridList(userIdList);
+            req.setToAllUser(false);
+            OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
+            msg.setMsgtype("action_card");
+            OapiMessageCorpconversationAsyncsendV2Request.ActionCard cardBody = new OapiMessageCorpconversationAsyncsendV2Request.ActionCard();
+            List<OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList> btnList = new ArrayList<OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList>();
+            OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList btn = new OapiMessageCorpconversationAsyncsendV2Request.BtnJsonList();
+            btnList.add(btn);
+            btn.setActionUrl(url);
+            btn.setTitle("点击审批");
+            cardBody.setBtnJsonList(btnList);
+            cardBody.setBtnOrientation("0");
+            //cardBody.setSingleUrl(url);
+            //cardBody.setSingleTitle(subtitle);
+            cardBody.setMarkdown(title+":"+body);
+            cardBody.setTitle(title);
+            msg.setActionCard(cardBody);
+            req.setMsg(msg);
+            OapiMessageCorpconversationAsyncsendV2Response rsp = client.execute(req, token);
+            if (rsp.isSuccess()){
+                return Result.ok();
+            }else{
+                return Result.error(rsp.getErrmsg());
+            }
+        }catch (Exception ex){
+            ex.printStackTrace();
+            return Result.error(ex.getMessage());
+        }
+    }
+
+    private static Map<String,String> accessTokenCache = new HashMap();
+
+    private static Result<?> getAccessToken(){
+
+        try {
+            if (accessTokenCache.containsKey("accessToken")){
+                String accessToken = accessTokenCache.get("accessToken");
+                Date expireTime =DateUtils.str2Date(accessTokenCache.get("expireTime"), DateUtils.datetimeFormat.get());
+                if (DateUtils.getDate().compareTo(expireTime)<0){
+                    return Result.ok(accessToken);
+                }
+            }
+
+            Date nowDate = DateUtils.getDate();
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
+            OapiGettokenRequest req = new OapiGettokenRequest();
+            req.setAppkey(appKey);
+            req.setAppsecret(appSecret);
+            req.setHttpMethod("GET");
+            OapiGettokenResponse rsp = client.execute(req);
+            if (rsp.isSuccess()){
+                accessTokenCache.put("accessToken", rsp.getAccessToken());
+                String expireTime = DateUtils.date2Str(DateUtils.dateAdd(nowDate, Calendar.SECOND, rsp.getExpiresIn().intValue()), DateUtils.datetimeFormat.get());
+                accessTokenCache.put("expireTime", expireTime);
+                return Result.ok(rsp.getAccessToken());
+            }else{
+                return Result.error(rsp.getErrmsg());
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return Result.error("其他错误");
+    }
+}

+ 14 - 0
jeecg-boot/jeecg-boot-base-common/src/main/java/org/jeecg/common/util/DateUtils.java

@@ -70,6 +70,13 @@ public class DateUtils extends PropertyEditorSupport {
         }
     };
 
+    public static ThreadLocal<SimpleDateFormat> yyyymmddhhmm = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("yyyy-MM-dd HH:mm");
+        }
+    };
+
     // 以毫秒表示的时间
     private static final long DAY_IN_MILLIS = 24 * 3600 * 1000;
     private static final long HOUR_IN_MILLIS = 3600 * 1000;
@@ -667,4 +674,11 @@ public class DateUtils extends PropertyEditorSupport {
     public static void main(String[] args) {
         System.out.println(date_ym());
     }
+
+    public static Date dateAdd(Date srcDate, int part, int diff){
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(srcDate);
+        calendar.add(part, diff);
+        return calendar.getTime();
+    }
 }

+ 58 - 0
jeecg-boot/jeecg-boot-base-common/src/main/java/org/jeecg/common/util/security/EncryptUtil.java

@@ -0,0 +1,58 @@
+package org.jeecg.common.util.security;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.SecureRandom;
+import java.util.Base64;
+
+/**
+ * 加解密,主要用到des
+ * @author fhf
+ * @date 2024-06-25
+ */
+@Slf4j
+public class EncryptUtil {
+    private static String Key = "cUiDa239";
+
+    /**
+     * Des加密
+     * @param text
+     * @return
+     * @throws Exception
+     */
+    public static String desEncrypt(String text){
+        try {
+            SecretKey secretKey = new SecretKeySpec(Key.getBytes(), "DES");
+            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
+            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+            byte[] encryptedBytes = cipher.doFinal(text.getBytes());
+            return Base64.getEncoder().encodeToString(encryptedBytes);
+        }catch (Exception ex){
+            ex.printStackTrace();
+        }
+        return "";
+    }
+
+    /**
+     * Des解密
+     * @param text
+     * @return
+     * @throws Exception
+     */
+    public static String desDecrypt(String text) {
+        try {
+            SecretKey secretKey = new SecretKeySpec(Key.getBytes(), "DES");
+            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
+            cipher.init(Cipher.DECRYPT_MODE, secretKey);
+            byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(text));
+            return new String(decryptedBytes);
+        }catch (Exception ex){
+            ex.printStackTrace();
+        }
+        return "";
+    }
+}

BIN
jeecg-boot/jeecg-boot-base-common/src/main/resources/lib/dingtalk-2.0.18.jar


BIN
jeecg-boot/jeecg-boot-base-common/src/main/resources/lib/taobao-sdk-java-auto.jar


+ 1 - 0
jeecg-boot/jeecg-boot-module-activiti/src/main/java/org/jeecg/modules/activiti/mapper/ActNodeMapper.java

@@ -34,4 +34,5 @@ public interface ActNodeMapper extends BaseMapper<ActNode> {
     List<LoginUser> findUserByRoleId(@Param("id") String id);
     @Select("select * from sys_user where id in (select user_id from sys_user_depart where dep_id = #{id}) and identity = 2")
     List<LoginUser> findUserDepartmentId(@Param("id") String id);
+
 }

+ 2 - 0
jeecg-boot/jeecg-boot-module-activiti/src/main/java/org/jeecg/modules/activiti/service/IActNodeService.java

@@ -16,4 +16,6 @@ import java.util.Map;
 public interface IActNodeService extends IService<ActNode> {
     ////查询所有用户可根据条件过滤(公司id)
     List<LoginUser> getSysUserList(Map<String,Object> map);
+
+    List<LoginUser> queryAllUser();
 }

+ 1 - 0
jeecg-boot/jeecg-boot-module-activiti/src/main/java/org/jeecg/modules/activiti/service/Impl/ActNodeServiceImpl.java

@@ -75,6 +75,7 @@ public class ActNodeServiceImpl extends ServiceImpl<ActNodeMapper, ActNode> impl
         return departmentByNodeId.stream().map(Department::getId).distinct().collect(Collectors.toList());
     }
 
+    @Override
     public List<LoginUser> queryAllUser() {
         return this.baseMapper.queryAllUser();
     }

+ 59 - 0
jeecg-boot/jeecg-boot-module-activiti/src/main/java/org/jeecg/modules/activiti/service/Impl/ActZprocessServiceImpl.java

@@ -27,8 +27,10 @@ import org.apache.shiro.SecurityUtils;
 import org.jeecg.common.exception.JeecgBootException;
 import org.jeecg.common.system.api.ISysBaseAPI;
 import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.DDMsgUtil;
 import org.jeecg.common.util.DateUtils;
 import org.jeecg.common.util.SpringContextUtils;
+import org.jeecg.common.util.oConvertUtils;
 import org.jeecg.modules.activiti.entity.*;
 import org.jeecg.modules.activiti.mapper.ActReModelMapper;
 import org.jeecg.modules.activiti.mapper.ActZprocessMapper;
@@ -171,6 +173,7 @@ public class ActZprocessServiceImpl extends ServiceImpl<ActZprocessMapper, ActZp
                 if(users==null||users.size()==0){
                     throw new RuntimeException("任务节点未分配任何候选审批人,发起流程失败");
                 }else{
+                    String ddUserIds = "";
                     // 分配了节点负责人分发给全部
                     for(LoginUser user : users){
                         taskService.addCandidateUser(task.getId(), user.getUsername());
@@ -181,8 +184,32 @@ public class ActZprocessServiceImpl extends ServiceImpl<ActZprocessMapper, ActZp
                         // 异步发消息 森语取消所有审批消息
 //                        sendActMessage(loginUser,user,actBusiness,task.getName(), actBusiness.getSendMessage(),
 //                                actBusiness.getSendSms(), actBusiness.getSendEmail(),map);
+                        if (oConvertUtils.isNotEmpty(user.getDingUser())){
+                            ddUserIds+=user.getDingUser()+",";
+                        }
+                    }
+
+                    // 发送钉钉消息
+                    if (oConvertUtils.isNotEmpty(ddUserIds)){
+                        String title = actBusiness.getTitle()+"需要您审批";
+                        String content = "";
+                        // 质量事故单
+                        if ("incident_ticket".equalsIgnoreCase(tableName)){
+                            content= oConvertUtils.getString(params.get("informant"))+
+                                    ","+oConvertUtils.getString(params.get("department_main"))+
+                                    ","+oConvertUtils.getString(params.get("accident_theme"));
+                        }else if ("preparation_material_v1".equalsIgnoreCase(tableName)) {
+                            content = oConvertUtils.getString(params.get("informant")) +
+                                    ","+oConvertUtils.getString(params.get("cvenabbname")) +
+                                    ","+oConvertUtils.getString(params.get("main_create_by")) +
+                                    ","+oConvertUtils.getString(params.get("imquantity"));
+                        }
+                        if (oConvertUtils.isNotEmpty(content)) {
+                            DDMsgUtil.sendOaMessage(ddUserIds, title, content, task.getId());
+                        }
                     }
                 }
+
             }else {
                 // 分配第一个任务用户
                 String assignees = actBusiness.getAssignees();
@@ -196,6 +223,38 @@ public class ActZprocessServiceImpl extends ServiceImpl<ActZprocessMapper, ActZp
 //                    LoginUser user = sysBaseAPI.getUserByName(assignee);
 //                    sendActMessage(loginUser,user,actBusiness,task.getName(), actBusiness.getSendMessage(),
 //                            actBusiness.getSendSms(), actBusiness.getSendEmail(),map);
+
+
+                }
+                // 获取钉钉消息要发送的用户
+                List<LoginUser> allUserList = actNodeService.queryAllUser();
+                List<String> assigneeUserIdList = Arrays.asList(assignees.split(","));
+                StringBuilder ddUserIds = new StringBuilder();
+                allUserList.forEach(u->{
+                    if (oConvertUtils.isNotEmpty(u.getDingUser())){
+                        if (assigneeUserIdList.stream().filter(e->e.equals(u.getUsername())).count()>0){
+                            ddUserIds.append(u.getDingUser()+",");
+                        }
+                    }
+                });
+                // 发送钉钉消息
+                if (ddUserIds.length()>0){
+                    String title = actBusiness.getTitle()+"需要您审批";
+                    String content = "";
+                    // 质量事故单
+                    if ("incident_ticket".equalsIgnoreCase(tableName)){
+                        content= "填报人:"+oConvertUtils.getString(params.get("informant"))+
+                                ";责任部门:"+oConvertUtils.getString(params.get("department_main"))+
+                                ";主题:"+oConvertUtils.getString(params.get("accident_theme"));
+                    }else if ("preparation_material_v1".equalsIgnoreCase(tableName)) {
+                        content = "计划单号:"+oConvertUtils.getString(params.get("informant")) +
+                                ";供应商:"+oConvertUtils.getString(params.get("cvenabbname")) +
+                                ";申请人:"+oConvertUtils.getString(params.get("main_create_by")) +
+                                ";申请数量:"+oConvertUtils.getString(params.get("imquantity"));
+                    }
+                    if (oConvertUtils.isNotEmpty(content)) {
+                        DDMsgUtil.sendOaMessage(ddUserIds.toString(), title, content, task.getId());
+                    }
                 }
             }
             // 设置任务优先级

+ 145 - 0
jeecg-boot/jeecg-boot-module-activiti/src/main/java/org/jeecg/modules/activiti/web/ActTaskController.java

@@ -31,20 +31,25 @@ import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.system.api.ISysBaseAPI;
 import org.jeecg.common.system.vo.ComboModel;
 import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.DDMsgUtil;
 import org.jeecg.common.util.DateUtils;
 import org.jeecg.common.util.HttpUtils;
 import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.common.util.security.EncryptUtil;
 import org.jeecg.modules.activiti.entity.*;
+import org.jeecg.modules.activiti.service.IActNodeService;
 import org.jeecg.modules.activiti.service.IActReModelService;
 import org.jeecg.modules.activiti.service.ITbTableInfoOuterService;
 import org.jeecg.modules.activiti.service.ITbTableInfoPracticeService;
 import org.jeecg.modules.activiti.service.Impl.ActBusinessServiceImpl;
+import org.jeecg.modules.activiti.service.Impl.ActNodeServiceImpl;
 import org.jeecg.modules.activiti.service.Impl.ActZprocessServiceImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.net.URLDecoder;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -85,6 +90,8 @@ public class ActTaskController {
     private ITbTableInfoOuterService tbTableInfoOuterService;
     @Autowired
     private ITbTableInfoPracticeService tbTableInfoPracticeService;
+    @Autowired
+    private IActNodeService actNodeService;
 
     public void show(String userId,String name,String categoryId, Integer priority, HttpServletRequest request,List<Task> taskList){
         TaskQuery query = taskService.createTaskQuery().taskCandidateOrAssigned(userId);
@@ -473,6 +480,8 @@ public class ActTaskController {
         List<Task> tasks = taskService.createTaskQuery().processInstanceId(procInstId).list();
 
         ActBusiness actBusiness = actBusinessService.getById(pi.getBusinessKey());
+        // 表单数据
+        Map<String, Object> busiData = actBusinessService.getBaseMapper().getBusiData(actBusiness.getTableId(), actBusiness.getTableName());
         // 判断下一个节点
         if(tasks!=null&&tasks.size()>0){
             //修改公司id
@@ -502,6 +511,7 @@ public class ActTaskController {
                         // 避免重复添加
                         List<String> list = actBusinessService.selectIRunIdentity(t.getId(), "candidate");
                         if(list==null||list.size()==0) {
+                            String ddUserIds = "";
                             // 分配了节点负责人分发给全部
                             for (LoginUser user : users) {
                                 taskService.addCandidateUser(t.getId(), user.getId());
@@ -513,6 +523,26 @@ public class ActTaskController {
 //                                actZprocessService.sendActMessage(loginUser,user,actBusiness,task.getName(),  sendMessage, sendSms, sendEmail,map);
                             }
                             taskService.setPriority(t.getId(), task.getPriority());
+
+                            // 发送钉钉消息
+                            if (oConvertUtils.isNotEmpty(ddUserIds)){
+                                String title = actBusiness.getTitle()+"需要您审批";
+                                String content = "";
+                                // 质量事故单
+                                if ("incident_ticket".equalsIgnoreCase(actBusiness.getTableName())){
+                                    content= "填报人:"+oConvertUtils.getString(busiData.get("informant"))+
+                                            ";责任部门:"+oConvertUtils.getString(busiData.get("department_main"))+
+                                            ";主题:"+oConvertUtils.getString(busiData.get("accident_theme"));
+                                }else if ("preparation_material_v1".equalsIgnoreCase(actBusiness.getTableName())) {
+                                    content = "计划单号:"+oConvertUtils.getString(busiData.get("informant")) +
+                                            ";供应商:"+oConvertUtils.getString(busiData.get("cvenabbname")) +
+                                            ";申请人:"+oConvertUtils.getString(busiData.get("main_create_by")) +
+                                            ";申请数量:"+oConvertUtils.getString(busiData.get("imquantity"));
+                                }
+                                if (oConvertUtils.isNotEmpty(content)) {
+                                    DDMsgUtil.sendOaMessage(ddUserIds, title, content, task.getId());
+                                }
+                            }
                         }
                     }
                 }else{
@@ -532,6 +562,38 @@ public class ActTaskController {
 //                            actZprocessService.sendActMessage(loginUser,user,actBusiness,task.getName(),  sendMessage, sendSms, sendEmail,map);
                             taskService.setPriority(t.getId(), priority);
                         }
+
+                        // 获取钉钉消息要发送的用户
+                        List<LoginUser> allUserList = actNodeService.queryAllUser();
+                        List<String> assigneeUserIdList = Arrays.asList(assignees.split(","));
+                        StringBuilder ddUserIds = new StringBuilder();
+                        allUserList.forEach(u->{
+                            if (oConvertUtils.isNotEmpty(u.getDingUser())){
+                                if (assigneeUserIdList.stream().filter(e->e.equals(u.getUsername())).count()>0){
+                                    ddUserIds.append(u.getDingUser()+",");
+                                }
+                            }
+                        });
+
+                        // 发送钉钉消息
+                        if (ddUserIds.length()>0){
+                            String title = actBusiness.getTitle()+"需要您审批";
+                            String content = "";
+                            // 质量事故单
+                            if ("incident_ticket".equalsIgnoreCase(actBusiness.getTableName())){
+                                content= "填报人:"+oConvertUtils.getString(busiData.get("informant"))+
+                                        ";责任部门:"+oConvertUtils.getString(busiData.get("department_main"))+
+                                        ";主题:"+oConvertUtils.getString(busiData.get("accident_theme"));
+                            }else if ("preparation_material_v1".equalsIgnoreCase(actBusiness.getTableName())) {
+                                content = "计划单号:"+oConvertUtils.getString(busiData.get("informant")) +
+                                        ";供应商:"+oConvertUtils.getString(busiData.get("cvenabbname")) +
+                                        ";申请人:"+oConvertUtils.getString(busiData.get("main_create_by")) +
+                                        ";申请数量:"+oConvertUtils.getString(busiData.get("imquantity"));
+                            }
+                            if (oConvertUtils.isNotEmpty(content)) {
+                                DDMsgUtil.sendOaMessage(ddUserIds.toString(), title, content, task.getId());
+                            }
+                        }
                     }
                 }
             }
@@ -904,4 +966,87 @@ public class ActTaskController {
         result.put("data", data);
         return result;
     }
+
+    /*根据加密的taskid获取任务信息*/
+    @RequestMapping(value = "/getSelfTaskById" )
+    public Result<Object> getSelfTaskById(String key, HttpServletRequest request){
+        try {
+            String decryptKey = EncryptUtil.desDecrypt(key);
+            String[] arr = decryptKey.split("_");// 第一个是username,第二个是task_id
+            String taskId = arr[1];
+
+            List<TaskVo> list = new ArrayList<>();
+            LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+            String userId = sysUser.getUsername();
+
+            List<Task> taskList = new ArrayList<>();
+            Map<String, Object> map = new HashMap<>();
+            map.put("username", sysUser.getUsername());
+            map.put("userId", sysUser.getId());
+            //公司id
+            map.put("pkOrg", sysUser.getOrgCode());
+            taskList = iActReModelService.getListTask(map).stream().filter(e -> e.getId().equals(taskId)).collect(Collectors.toList());
+            // 转换vo
+            for (Task e : taskList) {
+                TaskVo tv = new TaskVo(e);
+                // 关联委托人
+                if (StrUtil.isNotBlank(tv.getOwner())) {
+                    String realname = sysBaseAPI.getUserByName(tv.getOwner()).getRealname();
+                    tv.setOwner(realname);
+                }
+                List<IdentityLink> identityLinks = runtimeService.getIdentityLinksForProcessInstance(tv.getProcInstId());
+                for (IdentityLink ik : identityLinks) {
+                    // 关联发起人
+                    if ("starter".equals(ik.getType()) && StrUtil.isNotBlank(ik.getUserId())) {
+                        tv.setApplyer(sysBaseAPI.getUserByName(ik.getUserId()).getRealname());
+                    }
+                }
+                // 关联流程信息
+                ActZprocess actProcess = actZprocessService.getById(tv.getProcDefId());
+                if (actProcess != null) {
+                    tv.setProcessName(actProcess.getName());
+                    tv.setRouteName(actProcess.getRouteName());
+                }
+                // 关联业务key
+                ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(tv.getProcInstId()).singleResult();
+                tv.setBusinessKey(pi.getBusinessKey());
+                ActBusiness actBusiness = actBusinessService.getById(pi.getBusinessKey());
+                if (actBusiness != null) {
+                    tv.setTableId(actBusiness.getTableId());
+                    tv.setTableName(actBusiness.getTableName());
+                } else {
+                    continue;
+                }
+
+
+                // 关联额外信息
+                TbTableInfoPractice tbTableInfoPractice = tbTableInfoPracticeService.getById(actBusiness.getTableId());
+                if (tbTableInfoPractice != null && oConvertUtils.isNotEmpty(tbTableInfoPractice.getExtraInfo())) {
+                    tv.setDescription(tbTableInfoPractice.getExtraInfo());
+                } else {
+                    try {
+
+                        QueryWrapper<TbTableInfoPractice> practiceQueryWrapper = new QueryWrapper<>();
+                        practiceQueryWrapper.eq("business_table_id", actBusiness.getTableId());
+                        practiceQueryWrapper.and(i -> i.eq("is_initial", "1").or().eq("is_initial", null).or().eq("is_initial", "").or().isNull("is_initial"));
+                        tbTableInfoPractice = tbTableInfoPracticeService.getOne(practiceQueryWrapper);
+                        if (tbTableInfoPractice != null && oConvertUtils.isNotEmpty(tbTableInfoPractice.getExtraInfo())) {
+                            tv.setDescription(tbTableInfoPractice.getExtraInfo());
+                        }
+                    } catch (Exception ee) {
+                        System.out.println("业务信息获取错误" + ee.getMessage());
+                    }
+                }
+                if (tv.getDescription() == null) {
+                    tv.setDescription("");
+                }
+
+                list.add(tv);
+            }
+            return Result.ok(list);
+        }catch (Exception ex){
+            ex.printStackTrace();
+            return Result.error(ex.getMessage());
+        }
+    }
 }

+ 19 - 0
jeecg-boot/jeecg-boot-module-system/pom.xml

@@ -56,10 +56,29 @@
 			<plugin>
 				<groupId>org.springframework.boot</groupId>
 				<artifactId>spring-boot-maven-plugin</artifactId>
+				<!-- 非分离式打包 -->
 				<configuration>
 					<fork>false</fork>
 					<includeSystemScope>true</includeSystemScope>
 				</configuration>
+
+				<!-- 分离式打包
+				<configuration>
+					<layout>ZIP</layout>
+					<includes>
+						<include>
+							<groupId>nothing</groupId>
+							<artifactId>nothing</artifactId>
+						</include>
+					</includes>
+				</configuration>
+				<executions>
+					<execution>
+						<goals>
+							<goal>repackage</goal>
+						</goals>
+					</execution>
+				</executions>-->
 			</plugin>
 		</plugins>
 	</build>

+ 1 - 0
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/config/ShiroConfig.java

@@ -101,6 +101,7 @@ public class ShiroConfig {
 		filterChainDefinitionMap.put("/sys/common/upload/**", "anon");//文件预览不受限制
 		filterChainDefinitionMap.put("/sys/common/pdf/**", "anon");//pdf预览
 		filterChainDefinitionMap.put("/generic/**", "anon");//pdf预览需要文件
+		filterChainDefinitionMap.put("/sys/oaLogin", "anon");//订单打开验证用户信息
 		filterChainDefinitionMap.put("/", "anon");
 		filterChainDefinitionMap.put("/doc.html", "anon");
 		filterChainDefinitionMap.put("/**/*.js", "anon");

+ 39 - 0
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/LoginController.java

@@ -19,6 +19,7 @@ import org.jeecg.common.system.util.JwtUtil;
 import org.jeecg.common.system.vo.LoginUser;
 import org.jeecg.common.util.*;
 import org.jeecg.common.util.encryption.EncryptedString;
+import org.jeecg.common.util.security.EncryptUtil;
 import org.jeecg.modules.shiro.vo.DefContants;
 import org.jeecg.modules.system.entity.SysDepart;
 import org.jeecg.modules.system.entity.SysUser;
@@ -39,6 +40,7 @@ import org.springframework.web.client.ResponseErrorHandler;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
+import java.net.URLDecoder;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -835,5 +837,42 @@ public class LoginController {
 		}
 		return result;
 
+	}
+
+	@ApiOperation("钉钉审批通过des解密登录接口")
+	@RequestMapping(value = "/oaLogin", method = RequestMethod.POST)
+	public Result<JSONObject> oaLogin(@RequestBody Map<String,String> map){
+		Result<JSONObject> result = new Result<JSONObject>();
+		try {
+			String encryptKey = map.get("key");
+			String decryptKey = EncryptUtil.desDecrypt(encryptKey);
+			String[] arr = decryptKey.split("_");// 第一个是username,第二个是task_id
+			String username = arr[0];
+			//1. 校验用户是否有效
+			SysUser sysUser = sysUserService.getUserByName(username);
+			result = sysUserService.checkUserIsEffective(sysUser);
+			if(!result.isSuccess()) {
+				return result;
+			}
+
+			//用户登录信息
+			userInfo(sysUser, result);
+			sysBaseAPI.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null);
+
+			String is_possess = getDeptHead(username,sysUser.getOrgCode());
+			if(is_possess.equals("0")){
+				sysUser.setIsPossess(2);
+			}else{
+				sysUser.setIsPossess(sysUser.getIdentity());
+			}
+			return result;
+
+		}catch (Exception ex){
+			ex.printStackTrace();
+			return result.error500(ex.getMessage());
+		}
+
+
+
 	}
 }

+ 4 - 0
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/entity/SysUser.java

@@ -410,5 +410,9 @@ public class SysUser implements Serializable {
      * 附件
      */
     private java.lang.String fileName;
+    /**
+     * 钉钉用户id
+     */
+    private String dingUser;
 
 }

+ 212 - 0
jeecg-boot/jeecg-boot-module-system/src/main/resources/application-senyu.yml

@@ -0,0 +1,212 @@
+#森语配置
+server:
+  port: 8087
+  tomcat:
+    max-swallow-size: -1
+  servlet:
+    context-path: /jeecg-boot
+  compression:
+    enabled: true
+    min-response-size: 1024
+    mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/*
+
+management:
+ endpoints:
+  web:
+   exposure:
+    include: metrics,httptrace
+
+spring:
+  servlet:
+     multipart:
+        max-file-size: 10MB
+        max-request-size: 10MB
+  mail:
+    host: smtp.qq.com
+    username: 350012167@qq.com
+    password: ??
+    properties:
+      mail:
+        smtp:
+          auth: true
+          starttls:
+            enable: true
+            required: true
+  ## quartz定时任务,采用数据库方式
+  quartz:
+    job-store-type: jdbc
+  #json 时间戳统一转换
+  jackson:
+    date-format:   yyyy-MM-dd HH:mm:ss
+    time-zone:   GMT+8
+  aop:
+    proxy-target-class: true
+  #配置freemarker
+  freemarker:
+    # 设置模板后缀名
+    suffix: .ftl
+    # 设置文档类型
+    content-type: text/html
+    # 设置页面编码格式
+    charset: UTF-8
+    # 设置页面缓存
+    cache: false
+    prefer-file-system-access: false
+    # 设置ftl文件路径
+    template-loader-path:
+      - classpath:/templates
+  # 设置静态文件路径,js,css等
+  mvc:
+    static-path-pattern: /**
+  resource:
+    static-locations: classpath:/static/,classpath:/public/
+  autoconfigure:
+    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
+  datasource:
+    druid:
+      stat-view-servlet:
+        enabled: true
+        loginUsername: admin
+        loginPassword: 123456
+        allow:
+      web-stat-filter:
+        enabled: true
+    dynamic:
+      druid: # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置)
+        # 连接池的配置信息
+        # 初始化大小,最小,最大
+        initial-size: 5
+        min-idle: 5
+        maxActive: 20
+        # 配置获取连接等待超时的时间
+        maxWait: 60000
+        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+        timeBetweenEvictionRunsMillis: 60000
+        # 配置一个连接在池中最小生存的时间,单位是毫秒
+        minEvictableIdleTimeMillis: 300000
+        validationQuery: SELECT 1
+        testWhileIdle: true
+        testOnBorrow: false
+        testOnReturn: false
+        # 打开PSCache,并且指定每个连接上PSCache的大小
+        poolPreparedStatements: true
+        maxPoolPreparedStatementPerConnectionSize: 20
+        # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
+        filters: stat,wall,slf4j
+        # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
+        connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
+      datasource:
+        master:
+          url: jdbc:mysql://192.168.1.158:3306/cd_work_flow_dev?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&useSSL=false
+          username: root
+          password: root
+          driver-class-name: com.mysql.jdbc.Driver
+          # 多数据源配置(森语U8数据库)
+        master1:
+          url: jdbc:sqlserver://192.168.1.253;DatabaseName=UFDATA_101_2021
+#          url: jdbc:sqlserver://192.168.5.104;DatabaseName=UFDATA_101_2021
+          username: sa
+          password: Windows2008R2
+          driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
+        master2:
+          url: jdbc:sqlserver://192.168.1.253;DatabaseName=UFDATA_102_2021
+          username: sa
+          password: Windows2008R2
+          driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
+        master3:
+          url: jdbc:sqlserver://192.168.1.253;DatabaseName=UFDATA_103_2021
+          username: sa
+          password: Windows2008R2
+          driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
+  #redis 配置
+  redis:
+    database: 11
+    host: 127.0.0.1
+    lettuce:
+      pool:
+        max-active: 8   #最大连接数据库连接数,设 0 为没有限制
+        max-idle: 8     #最大等待连接中的数量,设 0 为没有限制
+        max-wait: -1ms  #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
+        min-idle: 0     #最小等待连接中的数量,设 0 为没有限制
+      shutdown-timeout: 100ms
+    password: ''
+    port: 6379
+  main:
+    allow-bean-definition-overriding: true
+  #Activiti property configuration
+  activiti:
+    database-schema-update: true
+    job-executor-activate: true # asyncExecutorEnabled属性设置设置true后将代替那些老的Job executor
+    history-level: full
+    db-history-used: true
+    #async-executor-activate: true
+    check-process-definitions: false # 自动部署验证设置:true-开启(默认)、false-关闭
+#mybatis plus 设置
+mybatis-plus:
+  mapper-locations: classpath*:org/jeecg/modules/**/xml/*Mapper.xml
+  global-config:
+    # 关闭MP3.0自带的banner
+    banner: false
+    db-config:
+      #主键类型  0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)";
+      id-type: 4
+      # 默认数据库表下划线命名
+      table-underline: true
+  configuration:
+    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
+    #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+    # 返回类型为Map,显示null对应的字段
+    call-setters-on-nulls: true
+#jeecg专用配置
+jeecg :
+  # 本地:local\Minio:minio\阿里云:alioss
+  uploadType: local
+  path :
+    #文件上传根目录 设置D:\service\oa
+    upload: D://upload
+    #webapp文件路径
+    webapp: D://upload
+  #短信秘钥
+  sms:
+     accessKeyId: ??
+     accessKeySecret: ??
+  shiro:
+     excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**
+  #阿里云oss存储配置
+  oss:
+    endpoint: oss-cn-beijing.aliyuncs.com
+    accessKey: WegDpuKzOuPK6D3N
+    secretKey: ??
+    bucketName: jeecgos
+    staticDomain: ??
+  # ElasticSearch 设置
+  elasticsearch:
+    cluster-name: docker-cluster
+    cluster-nodes: 127.0.0.1:9200
+  # 表单设计器配置
+  desform:
+    # 主题颜色(仅支持 16进制颜色代码)
+    theme-color: "#1890ff"
+  # 在线预览文件服务器地址配置
+  file-view-domain: http://fileview.jeecg.com
+  # minio文件上传
+  minio:
+    minio_url: http://minio.jeecg.com
+    minio_name: ??
+    minio_pass: ??
+    bucketName: ??
+#钉钉配置
+dingtalk:
+  appKey: dingi6akuh6rsnfl5lz0
+  appSecret: 4Ja6tg9cLpKb9t8vfKwW3BlG1U7I3Vfym3E-XGh-h5Vx4nDcc_1xoBWNyEpmV4TI
+  agentId: 3118815276
+#Mybatis输出sql日志
+logging:
+  level:
+    org.jeecg.modules : debug
+#cas单点登录
+cas:
+  prefixUrl: http://www.myfitt.cn:18005/cas
+#前端
+front:
+  baseurl: http://127.0.0.1:3000