钉钉审批回调流程,曾经踩过的坑,如果是内网需要配置内网穿透才能回调到本地

首先说明一点:之前碰到钉钉审批回调接口的时候发现钉钉会瞬间调用2次具体什么原因不清楚。

我的解决办法会在下面说:

直接上代码:

     首先如果是内网,需要内网穿透一下把钉钉所有审批全部设置到本地,不然没办法本地DEBUG调试,(酌情考虑什么时候测试,不然同时进来的审批不是你自己的,是别人请假的或者是其他的审批流程。

【具体对不对我是这样理解的。。。如有误导请谅解】)

    内网穿透 链接:https://pan.baidu.com/s/174Xe1UAZYx-K5yhxTitzHg 
    提取码:2hc4 
    复制这段内容后打开百度网盘手机App,操作更方便哦

开启之后,切地址到本地

package com.jeecg.sapoading.controller;

import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiCallBackUpdateCallBackRequest;
import com.dingtalk.api.response.OapiCallBackUpdateCallBackResponse;
import com.jeecg.sapoading.entity.TDingOaSsqEntity;
import org.jeecgframework.core.annotation.JAuth;
import org.jeecgframework.core.common.exception.BusinessException;
import org.jeecgframework.core.common.model.json.AjaxJson;
import org.jeecgframework.core.constant.Globals;
import org.jeecgframework.core.enums.Permission;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

@Controller
@RequestMapping("/tDi***UpdateCallBack")
@JAuth(auth= Permission.SKIP_AUTH)
public class TDingOAUpdateCallBack {
    @RequestMapping(params = "updateCallBack")
    @ResponseBody
    public AjaxJson updateCallBack(HttpServletRequest request) {
        String message = null;
        AjaxJson j = new AjaxJson();
        message = "OA更新回调成功";
        try{
            //需要开内网穿透
                DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/call_back/update_call_back");
                OapiCallBackUpdateCallBackRequest requestOa = new OapiCallBackUpdateCallBackRequest();
                requestOa.setUrl("http://**.**.**.**:8090/项目名/***BfController.do?insertBfFromDingding");
                
             //本地穿透地址requestOa.setUrl("http://cfb.vaiwan.com/tAPersonassetBfController.do?insertBfFromDingding");
                requestOa.setAesKey("WyGtmhbKapm*****j86boeQctWrDP7YqyTTCUf");
    //key这个是一个随机字符串
                requestOa.setToken("123456");
   //这个好像就是123456
                requestOa.setCallBackTag(Arrays.asList("bpms_instance_change", "bpms_task_change"));
                //   OapiCallBackUpdateCallBackResponse response = client.execute(request,"7bc30097ee5d**********205dd216ee");
                OapiCallBackUpdateCallBackResponse response = client.execute(requestOa,"7bc30097ee******05dd216ee");
                response.getErrcode();
                 System.out.println("code:"+response.getErrmsg());

        }catch(Exception e){
            e.printStackTrace();
            message = "OA更新回调失败";
            throw new BusinessException(e.getMessage());
        }
        j.setMsg(message);
        return j;
    }

}

  直接上切换代码

@RequestMapping(params = "insertBfFromDingding")
	@ResponseBody
	public Map<String, String> insertBfFromDingding(@RequestParam(value = "signature", required = false) String signature,
													@RequestParam(value = "timestamp", required = false) String timestamp,
													@RequestParam(value = "nonce", required = false) String nonce,
													@RequestBody(required = false) JSONObject json,OapiCallBackRegisterCallBackRequest req ) throws DingTalkEncryptException {
 		DingTalkEncryptor dingTalkEncryptor = null;
 		List<TABfDataFromDingding> dataList = new ArrayList<>();
		Map<String, String> headerMap = req.getHeaderMap();
		try {
			dingTalkEncryptor = new DingTalkEncryptor("123456", "WyGtmhbKapmTYja******rDP7YqyTTCUf",
					"dinge3******");

			//从post请求的body中获取回调信息的加密数据进行解密处理
			String encryptMsg = json.getString("encrypt");
			String plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp, nonce, encryptMsg);
			JSONObject obj = JSON.parseObject(plainText);

			//根据回调数据类型做不同的业务处理
			String eventType = obj.getString("EventType");

			String result = obj.getString("result");
			String type = obj.getString("type");
			if(result!=null && 审批PROCESS_CODE找钉钉配置审批模板的人要.equals(obj.getString("processCode"))){ //大致就是PROC-4361D700-D650---******
				TDingOaInfoEntity oaInfoEntity = new TDingOaInfoEntity();

				SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
				if("finish".equals(type) && "agree".equals(result)&& "bpms_instance_change".equals(eventType)){ //审批通过
					System.out.println("回调eventType:=="+eventType);
					//获取审批任务返回详情,并进行解析 z组装数据
					System.out.println("-------------OA审批通过------开始------------");
					DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/processinstance/get");
					OapiProcessinstanceGetRequest request = new OapiProcessinstanceGetRequest();
					request.setProcessInstanceId(obj.getString("processInstanceId"));
					OapiProcessinstanceGetResponse response = client.execute(request,OaCustomerAccessTokenThread.accessToken);
					String body = response.getBody();
					JSONObject jsonBody = JSONObject.parseObject(body);
					System.out.println("jsonBody:::::"+jsonBody);
					//第一层解析
					String process_instance = jsonBody.getString("process_instance");
					System.out.println("process_instance::::"+process_instance);
					//第二层解析
					JSONObject jsonProcessInstance = JSONObject.parseObject(process_instance);
					String businessId = jsonProcessInstance.getString("business_id");
					System.out.println("审批编号-businessId ="+businessId);
					String originator_dept_id = jsonProcessInstance.getString("originator_dept_id");
					System.out.println("部门ID ="+originator_dept_id);
					String create_time = jsonProcessInstance.getString("create_time");
					System.out.println("创建时间 ="+create_time);
					oaInfoEntity.setCreateDate(new Date());
					String finish_time = jsonProcessInstance.getString("finish_time");
					System.out.println("审批完成时间 ="+finish_time);
					oaInfoEntity.setUpdateDate(new Date());
					String title = jsonProcessInstance.getString("title");
					System.out.println("标题 ="+title);
					oaInfoEntity.setTitle(title);
					String originator_dept_name = jsonProcessInstance.getString("originator_dept_name");
					System.out.println("部门名称 ="+originator_dept_name);
					oaInfoEntity.setApplicantDepartment(originator_dept_name);
					String originator_userid = jsonProcessInstance.getString("originator_userid");
					System.out.println("userID ="+originator_userid);
					oaInfoEntity.setApplyUserId(originator_userid);
					if("COMPLETED".equals(jsonProcessInstance.getString("status"))){
						String formValue = jsonProcessInstance.getString("form_component_values");
						//第三次解析
						JSONArray OaFormValueArry = JSONArray.parseArray(formValue);
//						for(int i=0;i<OaFormValueArry.size();i++){
//							JSONObject ra = (JSONObject) OaFormValueArry.get(i);
//							String name = ra.getString("name");
//							String value = ra.getString("value");
//							System.out.println("name="+name +"   value="+value);
//						}
						 //审批同意处理:保存数据-----拆分json
						 
						
						 //oaInfoEntity.set(JSONObject.parseObject(OaFormValueArry.getString(12)).getString("value"));//待用字段
						 System.out.println("--------------------------------------获取钉钉回调信息成功------------------------------------------\n");
						 //------------------------开始调用sap 插入数据接口 拿到回执编号--------------
						 System.out.println("------------------------------:开始调用sap 插入数据接口 拿到回执编号----------------------------------\n");
						 
						JCoDestination destination=GetSapConn.getJcoConnection();
						//两着调用试着用
						JCoDestination destinationsss = JCoDestinationManager
								 .getDestination(ABAP_AS);//所属异构系统OA或者SAP提供
						

						JCoFunction function = destination.getRepository().getFunction(
								 "ZSD_FM_CREATE_CUSTOMER");//从对象仓库中获取 RFM 函数
						 if (function == null)
							 throw new RuntimeException("RFC_SYSTEM_INFO not found in SAP.");
						 try {
							 function.execute(destination);
						 } catch (AbapException e) {
							 System.out.println(e.toString());
						 }
						 JCoParameterList is_data_sum = function.getImportParameterList();
						 is_data_sum.setValue("IV_TYPE", "1");//--1 创建所有数据 2--仅创建通用数据 3 -- 创建公司代码数据
						 is_data_sum.setValue("IV_IS_COMMIT", "X");//布尔变量(X= 真,-= 假,空格 = 未知)
						 //IS_DATA 是SAP 结构名称 类是与JAVA Map当参数传入Funtion的参数封装
						 JCoStructure is_data = function.getImportParameterList().getStructure("IS_DATA");
						//OaFormValueArry.getString(1)) getString 下标是从钉钉返回的jsaon 抓取的
						
						 is_data.setValue("BAHNE", JSONObject.parseObject(OaFormValueArry.getString(1)).getString("value"));//提交人-申请人

						 is_data.setValue("KATR1", JSONObject.parseObject(OaFormValueArry.getString(4)).getString("value").equals("null") ? "":JSONObject.parseObject(OaFormValueArry.getString(4)).getString("value"));//客户种类
						 System.out.println("KATR1  客户种类 "+JSONObject.parseObject(OaFormValueArry.getString(4)).getString("value"));

						 is_data.setValue("KATR2", JSONObject.parseObject(OaFormValueArry.getString(5)).getString("value").equals("null") ? "":JSONObject.parseObject(OaFormValueArry.getString(5)).getString("value"));//客户类别

						 
						 //固定参数
						 is_data.setValue("KTOKD", "***");//业务伙伴分组
						
						 is_data_sum.setValue("IS_DATA", is_data);
						System.out.println("---------正在调用sap请等待30秒。。。。。。。。。。。。。。");
						function.execute(destination);//执行调用函数

						 JCoTable table2 = function.getTableParameterList().getTable("MESSTAB");//[MESSTAB 是SAP返回 数据的“表名字”]得到sap返回的参数,你就把他当作c语言的结构体理解就可以了
						 //有时候sap那边只是返回一个输出参数,sap比方说你这边输入一个物料号,想得到sap那边的物料描述,这是sap方是不会返回一个内表给你的
						 //而是只是返回一个输出参数给你这时你就要用到下面的方法来得到输出参数
						 //获取sap回执编号
						 JCoParameterList exportParameterList = function.getExportParameterList();
						 Object ev_kunnr = exportParameterList.getValue("EV_KUNNR");
						 oaInfoEntity.setClientNumber(ev_kunnr.toString()); //客户编号
						 oaInfoEntity.setDingApprovalNumber(businessId);//审批编号
						 System.out.println("----------------------获取SAP回传客户编号: " + ev_kunnr + "---------------------------------\n");
						 System.out.println("---------------------------------开始更新本地客户编号并保存数据----------------------------\n");
						String  MessageAll ="";
						 for (int i = 0; i < table2.getNumRows(); i++) {
							 table2.setRow(i);
							 //这里获取sap函数传出内表结构的字段 【传入的字符串为调用函数需要传入的参数名,必须为大写】
							 String TYPE = table2.getString("TYPE");//[消息类型:S 成功 E 错误 W警告 I信息 A 中断]记住这里参数一定是大写的,不然得不到值【如果报错说明你参数传的不对】
							 String MESSAGE = table2.getString("MESSAGE");//记住这里一定是大写的,不然得不到值
							 MessageAll= MessageAll+MESSAGE;
							 System.out.println("TYPE:" + TYPE + "MESSAGE:" + MESSAGE);
						 }
						 
					//调用sap结束
					}

				}else if("refuse".equals(result)){  //审批未通过

				}
			}else if ("其他审批请继续添加") {

			} else {
				// 其他类型事件处理
			}
			// 返回success的加密信息表示回调处理成功
			return dingTalkEncryptor.getEncryptedMap("success", System.currentTimeMillis(), Utils.getRandomStr(8));
		} catch (Exception e) {
			//失败的情况,应用的开发者应该通过告警感知,并干预修复
			//   mainLogger.error("process callback failed!"+params,e);
			e.printStackTrace();
			return null;
		}
	}

发起审批:

@RequestMapping(params = "createZyProcessinstance")

	@ResponseBody

		public Map<String,Object> createZyProcessinstance(String jsonData,String fromUserId,String toUserId,String deptId,HttpServletRequest request){

		Map<String,Object> paramMap = new HashMap<>();

		try{

				//数据解析

				List<TABfDataFromDingding> zyData = JSONObject.parseArray(jsonData, TABfDataFromDingding.class);

				String accessToken = PersonAccessTokenThread.accessToken;

				//注册转移审批流程回调地址

				DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/processinstance/create");

				OapiProcessinstanceCreateRequest processinstanceRequest = new OapiProcessinstanceCreateRequest();

				Long aLong = Long.valueOf("41605932");

				processinstanceRequest.setProcessCode(ResourceUtil.getConfigByName("PROCESS_CODE_ZY"));



				List<OapiProcessinstanceCreateRequest.FormComponentValueVo> formComponentValues = new ArrayList<>();

				List<List<OapiProcessinstanceCreateRequest.FormComponentValueVo>> detailFormList = new ArrayList<>();

				detailFormList = formComponentValue(zyData,fromUserId,toUserId);

				OapiProcessinstanceCreateRequest.FormComponentValueVo vo4 = new OapiProcessinstanceCreateRequest.FormComponentValueVo();

				vo4.setName("明细");

				vo4.setValue(JSON.toJSONString(detailFormList));



				formComponentValues.add(vo4);



				processinstanceRequest.setFormComponentValues(formComponentValues);

				//获取发起审批人是不是领导

				DingTalkClient getPersonClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/get");

				OapiUserGetRequest personRequest = new OapiUserGetRequest();

				personRequest.setUserid(toUserId);

				personRequest.setHttpMethod("GET");

				OapiUserGetResponse rs = getPersonClient.execute(personRequest, accessToken);

				String isLeaderInDepts = rs.getIsLeaderInDepts();

				Map maps = (Map)JSON.parse(isLeaderInDepts);

				boolean isLeader = (Boolean) maps.get(deptId);

				//获取发起人的父部门

				DingTalkClient parentDeptClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/department/list_parent_depts_by_dept");

				OapiDepartmentListParentDeptsByDeptRequest parentDeptRequest = new OapiDepartmentListParentDeptsByDeptRequest();

				parentDeptRequest.setId(deptId);

				parentDeptRequest.setHttpMethod("GET");

				OapiDepartmentListParentDeptsByDeptResponse parentDeptResponse = parentDeptClient.execute(parentDeptRequest, accessToken);

				List<Long> parentIds = parentDeptResponse.getParentIds();

				OapiDepartmentGetResponse  deptResponse = null;

				OapiDepartmentGetRequest deptRequest = null;

				DingTalkClient deptClient = null;

				List<String> sendList = new ArrayList<>();

				String approversIds = "";

				if(isLeader){  //如果是主管 那么就去主管的上级

					if(!parentIds.isEmpty()){

						deptClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/department/get");

						deptRequest = new OapiDepartmentGetRequest();

						deptRequest.setHttpMethod("GET");

						deptRequest.setId(String.valueOf(parentIds.get(1)));

						deptResponse = deptClient.execute(deptRequest, accessToken);

						String deptManagerUseridList = deptResponse.getDeptManagerUseridList();

						approversIds=deptManagerUseridList;

					}

				}else if(!isLeader){

					deptClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/department/get");

					parentIds.remove(parentIds.size()-1);

					parentIds.remove(parentIds.size()-1);

					for(Long dId:parentIds){

						deptRequest = new OapiDepartmentGetRequest();

						deptRequest.setHttpMethod("GET");

						deptRequest.setId(String.valueOf(dId));

						deptResponse = deptClient.execute(deptRequest, accessToken);

						String sendIds = deptResponse.getDeptManagerUseridList();

						if(StringUtils.isNotBlank(sendIds)){

							sendList.add(sendIds);

						}

					}



					for(String s:sendList){

						approversIds = approversIds+s;

						approversIds = approversIds+",";

					}

				}

					if(approversIds.endsWith(",")){

						approversIds = approversIds.substring(0,approversIds.length()-1);

					}

                    approversIds = toUserId+","+approversIds;

					processinstanceRequest.setApprovers(approversIds);  //审批人钉钉ID集合

					processinstanceRequest.setOriginatorUserId(fromUserId); //发起审批人钉钉ID

				//	processinstanceRequest.setApprovers("21274328231170238");  //审批人钉钉ID集合

				//	processinstanceRequest.setOriginatorUserId("21274328231170238"); //发起审批人钉钉ID

					processinstanceRequest.setDeptId(Long.valueOf(deptId)); //审批人部门ID

				//	processinstanceRequest.setDeptId(Long.valueOf("118496372"));

					//	request.setCcList(userIds);

					//	request.setCcPosition("START_FINISH");

					System.out.println(approversIds);

					if(StringUtils.isNotBlank(rs.getJobnumber())){

						OapiProcessinstanceCreateResponse response = client.execute(processinstanceRequest,accessToken);

						System.out.println(response.getErrmsg());

						paramMap.put("msg","转移审批提交成功");

						paramMap.put("success",true);

					}else{

						paramMap.put("msg","转移审批发起失败,该接受人没有工号信息,请通知人资部门维护该员工信息!");

						paramMap.put("success",false);

					}

			}catch (Exception ex){

				ex.printStackTrace();

				paramMap.put("msg","程序异常,请检查网络连接");

				paramMap.put("success",false);

			}

			return paramMap;

	}

 

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
钉钉内网穿透是一种可以将本地服务映射到公网的工具,可以方便地让外部用户访问您本地的应用程序。根据引用内容,使用钉钉nginx内网穿透需要进行以下步骤: 1. 首先,您需要下载并解压pierced工具,可以通过执行以下命令进行下载: ``` git clone https://github.com/open-dingtalk/pierced.git ``` 然后,您可以运行钉钉内网穿透工具,命令示例如下: ``` ding.exe -config=./ding.cfg -subdomain=changsheng 80 ``` 这个命令将会将本地的80端口映射到一个可访问的域名下,如`changsheng.vaiwan.com`。 2. 您还需要进行nginx的配置。您可以创建一个nginx的配置文件,并将以下配置内容写入该文件中: ``` server { listen 1580 default_server; listen [::]:1580 default_server; location / { root /usr/local/data/project/web/netpatrol; # 放前端页面的路径 try_files $uri /index.html index index.html index.htm; } location ~.*(jpg|mp4|txt)$ { root /usr/local/data/netpatrol; # 放静态文件的路径,如图片、视频 } location /netpatrol/ { proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-PORT $remote_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://127.0.0.1:8000/; index index.html index.htm; } } ``` 这个配置文件将会将来自1580端口的请求代理到本地的8000端口,并提供前端页面和静态文件的访问。 通过以上步骤,您就可以使用钉钉内网穿透工具和nginx配置,实现对本地服务的内网穿透,使其可以在外网访问。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [钉钉免费内网穿透工具使用](https://blog.csdn.net/bj_ameng/article/details/118764567)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [内网穿透之HTTP穿透-钉钉开放平台](https://blog.csdn.net/snow_love_xia/article/details/122692332)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值