一,首先区分 钉钉 专有钉钉 浙政钉 是不一样的,我接入的是普通钉钉的OA审批
二,开发文档地址 https://open.dingtalk.com/

1,引入依赖

<dependency><groupId>com.aliyun</groupId><artifactId>dingtalk</artifactId><version>1.2.15</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.16</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.aliyun</groupId><artifactId>alibaba-dingtalk-service-sdk</artifactId><version>2.0.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency>

2,在配置文件中 添加配置

OAdd:appKey: ***************appSecret: ***************dingTalk: https://oapi.dingtalk.com/processCode: ****************originatorUserId: *****************callToken: *********************callAesKey: *******************

3,编写OA工具类

3.1,事物回调所需类

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.Arrays;
import java.util.HashMap;
import  java.util.Map ;
import java.util.Random;
import  java.security.Security ;
import java.lang.reflect.Field;import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;import com.alibaba.fastjson.JSON;import org.apache.commons.codec.binary.Base64;/*** DingTalk open platform encryption and decryption method* Download JCE Unrestricted Permission Policy File from ORACLE Official Website* JDK6 download address: http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html* JDK7 download address: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html* JDK8 download address https://www.oracle.com/java/technologies/javase-jce8-downloads.html*/public class DingCallbackCrypto {private static final Charset CHARSET = Charset.forName("utf-8");private static final Base64 base64 = new Base64();private byte[] aesKey;private  String  token ;private String corpId;/*** ask getPaddingBytes key fixed length**/private static final Integer AES_ENCODE_KEY_LENGTH = 43;/*** Encrypted random string byte length**/private static final Integer RANDOM_LENGTH = 16;/*** Constructor** @param token On the DingTalk open platform, the token set by the developer* @param encodingAesKey The EncodingAESKey set by the developer on the DingTalk open platform* @param corpId Enterprise self-built application-event subscription, use appKey* Enterprise self-built application - register callback address, use corpId* Third-party enterprise applications, use suiteKey** @throws DingTalkEncryptException failed to execute, please check the exception's error code and specific error message*/public DingCallbackCrypto(String token, String encodingAesKey, String corpId) throws DingTalkEncryptException {if (null == encodingAesKey || encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) {throw new DingTalkEncryptException(DingTalkEncryptException.AES_KEY_ILLEGAL);}this.token = token;this.corpId = corpId;aesKey = Base64.decodeBase64(encodingAesKey + "=");}public Map<String, String> getEncryptedMap(String plaintext) throws DingTalkEncryptException {return getEncryptedMap(plaintext, System.currentTimeMillis(), Utils.getRandomStr(16));}/*** Encrypt the message body synchronized with the DingTalk open platform and return the encrypted Map** @param plaintext The message body plaintext passed* @param timeStamp timestamp* @param nonce random string* @return* @throws DingTalkEncryptException*/public Map<String, String> getEncryptedMap(String plaintext, Long timeStamp, String nonce)throws DingTalkEncryptException {if (null == plaintext) {throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);}if (null == timeStamp) {throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL);}if (null == nonce) {throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_NONCE_ILLEGAL);}// encryptString encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);String signature = getSignature(token, String.valueOf(timeStamp), nonce, encrypt);Map<String, String> resultMap = new HashMap<String, String>();resultMap.put("msg_signature", signature);resultMap.put("encrypt", encrypt);resultMap.put("timeStamp", String.valueOf(timeStamp));resultMap.put("nonce", nonce);return resultMap;}/*** ciphertext decryption** @param msgSignature signature string* @param timeStamp timestamp* @param nonce random string* @param encryptMsg ciphertext* @return decrypted original text* @throws DingTalkEncryptException*/public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)throws DingTalkEncryptException {//check signatureString signature = getSignature(token, timeStamp, nonce, encryptMsg);if (!signature.equals(msgSignature)) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);}// decryptString result = decrypt(encryptMsg);return result;}/** Encrypt plaintext.* @param text The plaintext to be encrypted* @return encrypted base64 encoded string*/private String encrypt(String random, String plaintext) throws DingTalkEncryptException {try {byte[] randomBytes = random.getBytes(CHARSET);byte[] plainTextBytes = plaintext.getBytes(CHARSET);byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);byte[] corpidBytes = corpId.getBytes(CHARSET);ByteArrayOutputStream byteStream = new ByteArrayOutputStream();byteStream.write(randomBytes);byteStream.write(lengthByte);byteStream.write(plainTextBytes);byteStream.write(corpidBytes);byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());byteStream.write(padBytes);byte[] unencrypted = byteStream.toByteArray();byteStream.close();Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);byte[] encrypted = cipher.doFinal(unencrypted);String result = base64.encodeToString(encrypted);return result;} catch ( Exception  e ) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);}}/** Decrypt the ciphertext.* @param text The ciphertext to decrypt* @return decrypted plaintext*/private String decrypt(String text) throws DingTalkEncryptException {byte [] originalArr ;try {// Set the decryption mode to CBC mode of AESCipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);// Decode the ciphertext using BASE64byte[] encrypted = Base64.decodeBase64(text);// decryptoriginalArr = cipher.doFinal(encrypted);} catch ( Exception  e ) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);}String plainText;String fromCorpid;try {// remove the complement characterbyte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);// Separate 16-bit random string, network byte order and corpIdbyte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);int plainTextLegth = Utils.bytes2int(networkOrder);plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);fromCorpid = new String(Arrays.copyOfRange(bytes, 20 + plainTextLegth, bytes.length), CHARSET);} catch ( Exception  e ) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);}// When the corpids are not the sameif (!fromCorpid.equals(corpId)) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_CORPID_ERROR);}return plainText;}/*** digital signature** @param token     isv token* @param timestamp timestamp* @param nonce random string* @param encrypt encrypted text* @return* @throws DingTalkEncryptException*/public String getSignature(String token, String timestamp, String nonce, String encrypt)throws DingTalkEncryptException {try {String[] array = new String[] {token, timestamp, nonce, encrypt};Arrays.sort(array);
//            System.out.println(JSON.toJSONString(array));StringBuffer sb = new StringBuffer();for (int i = 0; i < 4; i++) {sb.append(array[i]);}String str = sb.toString();
//            System.out.println(str);MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(str.getBytes());byte[] digest = md.digest();StringBuffer hexstr = new StringBuffer();String shaHex = "";for (int i = 0; i < digest.length; i++) {shaHex = Integer.toHexString(digest[i] & 0xFF);if (shaHex.length() < 2) {hexstr.append(0);}hexstr.append(shaHex);}return hexstr.toString();} catch ( Exception  e ) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);}}public static class Utils {public  Utils () {}public static String getRandomStr(int count) {String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < count; ++i) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}public static byte[] int2Bytes(int count) {byte[] byteArr = new byte[] {(byte)(count >> 24 & 255), (byte)(count >> 16 & 255), (byte)(count >> 8 & 255),(byte)(count & 255)};return byteArr;}public static int bytes2int(byte[] byteArr) {int count = 0;for (int i = 0; i < 4; ++i) {count <<= 8;count |= byteArr[i] & 255;}return count;}}public static class PKCS7Padding {private static final Charset CHARSET = Charset.forName("utf-8");private static final int BLOCK_SIZE = 32;public PKCS7Padding() {}public static byte[] getPaddingBytes(int count) {int amountToPad = 32 - count % 32;if (amountToPad == 0) {amountToPad = 32;}char padChr = chr(amountToPad);String tmp = new String();for (int index = 0; index < amountToPad; ++index) {tmp = tmp + padChr;}return tmp.getBytes(CHARSET);}public static byte[] removePaddingBytes(byte[] decrypted) {int pad = decrypted[decrypted.length - 1];if (pad < 1 || pad > 32) {pad = 0;}return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);}private  static  char  chr ( int  a ) {byte target = (byte)(a & 255);return (char)target;}}public static class DingTalkEncryptException extends Exception {public static final int SUCCESS = 0;public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;public static final int ENCRYPTION_NONCE_ILLEGAL = 900003;public static final int AES_KEY_ILLEGAL = 900004;public static final int SIGNATURE_NOT_MATCH = 900005;public static final int COMPUTE_SIGNATURE_ERROR = 900006;public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 900007;public static final int COMPUTE_DECRYPT_TEXT_ERROR = 900008;public static final int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009;public static final int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010;private static Map<Integer, String> msgMap = new HashMap();private  Integer  code ;static {msgMap .put ( 0 , " success" ) ;msgMap.put ( 900001 , " Illegal encrypted plaintext" ) ;msgMap.put ( 900002 , " Illegal encryption timestamp parameter" ) ;msgMap.put ( 900003 , " The encrypted random string parameter is illegal" );msgMap.put ( 900005 , "Signature mismatch " ) ;msgMap.put ( 900006 , " Signature calculation failed" ) ;msgMap .put ( 900004 , " Illegal aes key" );msgMap.put ( 900007 , " Error computing encrypted text" ) ;msgMap.put ( 900008 , " Error computing decrypted text" ) ;msgMap.put ( 900009 , " Calculation of decrypted text length does not match" );msgMap.put ( 900010 , "Compute decrypted literal corpid does not match" );}public  Integer  getCode () {return this.code;}public DingTalkEncryptException(Integer exceptionCode) {super((String)msgMap.get(exceptionCode));this.code = exceptionCode;}}static {try {Security.setProperty("crypto.policy", "limited");RemoveCryptographyRestrictions();} catch (Exception var1) {}}private static void RemoveCryptographyRestrictions() throws Exception {Class<?> jceSecurity = getClazz("javax.crypto.JceSecurity");Class<?> cryptoPermissions = getClazz("javax.crypto.CryptoPermissions");Class<?> cryptoAllPermission = getClazz("javax.crypto.CryptoAllPermission");if (jceSecurity != null) {setFinalStaticValue(jceSecurity, "isRestricted", false);PermissionCollection defaultPolicy = (PermissionCollection)getFieldValue(jceSecurity, "defaultPolicy", (Object)null, PermissionCollection.class);if (cryptoPermissions != null) {Map<?, ?> map = (Map)getFieldValue(cryptoPermissions, "perms", defaultPolicy, Map.class);map.clear();}if (cryptoAllPermission != null) {Permission permission = (Permission)getFieldValue(cryptoAllPermission, "INSTANCE", (Object)null, Permission.class);defaultPolicy.add(permission);}}}private static Class<?> getClazz(String className) {Class clazz = null;try {clazz = Class.forName(className);} catch (Exception var3) {}return  clazz ;}private static void setFinalStaticValue(Class<?> srcClazz, String fieldName, Object newValue) throws Exception {Field field = srcClazz.getDeclaredField(fieldName);field.setAccessible(true);Field modifiersField = Field.class.getDeclaredField("modifiers");modifiersField.setAccessible(true);modifiersField.setInt(field, field.getModifiers() & -17);field.set((Object)null, newValue);}private static <T> T getFieldValue(Class<?> srcClazz, String fieldName, Object owner, Class<T> dstClazz) throws Exception {Field field = srcClazz.getDeclaredField(fieldName);field.setAccessible(true);return dstClazz.cast(field.get(owner));}
}

3.2 OA工具类

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiProcessinstanceCreateRequest;
import com.dingtalk.api.request.OapiProcessinstanceGetRequest;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiProcessinstanceCreateResponse;
import com.dingtalk.api.response.OapiProcessinstanceGetResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taobao.api.ApiException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.*;/*** 钉钉OA审批工具类* @author HZHP*/
@Component
public class OaUtils {@Value("${OAdd.appKey}")private String appKey;@Value("${OAdd.appSecret}")private String appSecret;@Value("${OAdd.dingTalk}")private String dingTalk;@Value("${OAdd.processCode}")private String processCode;@Value("${OAdd.originatorUserId}")private String originatorUserId;private ObjectMapper om=new ObjectMapper();/*** 获取accessToken  有效期 2小时  频繁访问将限流 建议缓存* @return* @throws JsonProcessingException*/public String getAeecssToken() throws JsonProcessingException {String str="";try {DingTalkClient client = new DefaultDingTalkClient(dingTalk+"gettoken");OapiGettokenRequest req = new OapiGettokenRequest();req.setHttpMethod("GET");req.setAppkey(appKey);req.setAppsecret(appSecret);OapiGettokenResponse rsp = client.execute(req);str = rsp.getBody();} catch (ApiException e) {e.printStackTrace();}HashMap hashMap = om.readValue(str, HashMap.class);return hashMap.get("access_token").toString();}/*** 获取审批详情* @return* @throws ApiException*/public Map<String,String> getProcessInsDetail(String insTanceId, String acceToken) throws Exception {Map<String,String> resultMap=new HashMap<>(2);DingTalkClient client = new DefaultDingTalkClient(dingTalk+"topapi/processinstance/get");OapiProcessinstanceGetRequest req = new OapiProcessinstanceGetRequest();req.setProcessInstanceId(insTanceId);OapiProcessinstanceGetResponse rsp = client.execute(req, acceToken);String body = rsp.getBody();HashMap map = om.readValue(body, HashMap.class);HashMap pie =(HashMap) map.get("process_instance");resultMap.put("status",pie.get("status").toString());resultMap.put("result",pie.get("result").toString());return resultMap;}/*** 发起审批* @param acceToken* @return* @throws ApiException*/public Map<String,String> createApproval(String acceToken) throws ApiException {Map<String,String> result=new HashMap<>(2);DingTalkClient client = new DefaultDingTalkClient(dingTalk+ "topapi/processinstance/create");OapiProcessinstanceCreateRequest req = new OapiProcessinstanceCreateRequest();req.setProcessCode(processCode);req.setOriginatorUserId(originatorUserId);// 若发起人属于跟部门 传 -1req.setDeptId(-1L);//单行输入框List<OapiProcessinstanceCreateRequest.FormComponentValueVo> formComponentValueVoList = new ArrayList<OapiProcessinstanceCreateRequest.FormComponentValueVo>();OapiProcessinstanceCreateRequest.FormComponentValueVo formComponentValueVo = new OapiProcessinstanceCreateRequest.FormComponentValueVo();formComponentValueVoList.add(formComponentValueVo);formComponentValueVo.setName("备注");formComponentValueVo.setValue("代码创建审批测试");req.setFormComponentValues(formComponentValueVoList);OapiProcessinstanceCreateResponse rsp = client.execute(req, acceToken);String str = JSON.toJSONString(rsp);JSONObject eventJson = JSON.parseObject(str);result.put("errcode",eventJson.get("errcode").toString());result.put("processInstanceId",eventJson.get("processInstanceId").toString());return result;}
}

3.3 事件回调类

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.zjhcsoft.bi.jzbf.utils.OaUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import com.alibaba.fastjson.JSON;import java.util.Map;@RestController
@RequestMapping("approval")
public class ApprovalResult {@Value("${OAdd.appKey}")private String appKey;@Value("${OAdd.callToken}")private String callToken;@Value("${OAdd.callAesKey}")private String callAesKey;@Autowiredprivate OaUtils oaUtils;@PostMappingpublic Map<String, String> approval(@RequestParam(value = "msg_signature", required = false) String msg_signature,@RequestParam(value = "timestamp", required = false) String timeStamp,@RequestParam(value = "nonce", required = false) String nonce,@RequestBody(required = false) JSONObject json){try {// 1. 从http请求中获取加解密参数// 2. 使用加解密类型// Constant.OWNER_KEY 说明:// 1、开发者后台配置的订阅事件为应用级事件推送,此时OWNER_KEY为应用的APP_KEY。// 2、调用订阅事件接口订阅的事件为企业级事件推送,//      此时OWNER_KEY为:企业的appkey(企业内部应用)或SUITE_KEY(三方应用)DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(callToken, callAesKey, appKey);String encryptMsg = json.getString("encrypt");String decryptMsg = callbackCrypto.getDecryptMsg(msg_signature, timeStamp, nonce, encryptMsg);// 3. 反序列化回调事件json数据JSONObject eventJson = JSON.parseObject(decryptMsg);String eventType = eventJson.getString("EventType");// 4. 根据EventType分类处理if ("check_url".equals(eventType)) {// 测试回调url的正确性System.out.println("测试回调url的正确性");} else if ("bpms_instance_change".equals(eventType)) {// 处理审批事件 TODOString processInstanceId = eventJson.get("processInstanceId").toString();String aeecssToken = oaUtils.getAeecssToken();Map<String, String> processInsDetail = oaUtils.getProcessInsDetail(processInstanceId, aeecssToken);System.out.println("审批事件----------------->响应结果为----------------->");System.out.println(aeecssToken);System.out.println(processInsDetail);System.out.println("审批事件结束----------------------------------------》");} else {// 添加其他已注册的System.out.println("发生了:" + eventType + "事件");}// 5. 返回success的加密数据Map<String, String> successMap = callbackCrypto.getEncryptedMap("success");return successMap;} catch (DingCallbackCrypto.DingTalkEncryptException | JsonProcessingException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}return null;}
}

接入 钉钉 OA 审批相关推荐

  1. 钉钉产品介绍_钉钉正式推出智能OA:免费开放、一站解决“人财物事”管理难题...

    9月2日消息,阿里钉钉今日正式上线智能OA产品"OA审批",通过开放工作流.审批流引擎,向中小企业提供场景全面.定制简易.操作门槛低的OA协同服务,实现企业管理流程.业务流程的全链 ...

  2. 钉钉小程序编译时提示 ‘dd is not defined‘

    今天在使用本地环境接入钉钉OA审批(钉钉官方OA审批接入)时,利用小程序开发工具导入从官网下载的前端项目后,编译时控制台一直报'dd is not defined' 的错 折腾了半天,重新去看看官网的 ...

  3. 钉钉OA流程审批,Jenkins自动授权通知用户密码

    目录 一.目的: 二.钉钉表单设计: 三.Jenkins设置(插件Role-based ): 四.服务器部署: 五.Python3定时检测脚本: 一.目的: 公司目前的软件版本发布是通过Jenkins ...

  4. Python3 获取钉钉OA审批数据

    根据钉钉最新的开发文档,使用python实现的获取钉钉OA审批数据内容的连接器. 使用条件: 1.创建一个钉钉应用,复制app key与app secret(在应用信息->应用凭证中). 2.赋 ...

  5. 钉钉OA审批可以设计项目施工管理和工程项目管理吗?

    ​钉钉OA审批可以设计项目施工管理和工程项目管理吗?如何用钉钉OA审批搭建部署工程施工类型项目管理的?钉钉免费的应用可以完成工程项目管理的应用需求吗,如何用最低的成本处理工程施工项目管理的应用? 大家 ...

  6. 钉钉OA审批事件回调遇坑梳理-如何注册多个事件回调

    项目场景: 问题:使用钉钉http调用事件回调注册,遇坑 本项目有两套服务. 当 600 服务,在使用钉钉注册回调的接口后,再次注册800回调服务,800 回调会覆盖掉600服务 测试服务 600 正 ...

  7. 对接钉钉审批_简信CRM分享:钉钉CRM应用

    钉钉办公软件是阿里巴巴集团在2014年推出的一款免费办公软件,它包含了企业常需的请假.考勤.审批等简单协同功能,使用简单,且不需要任何费用,因此,在国内市场迅速铺开,是目前国内首屈一指的免费OA软件. ...

  8. 5分钟接入钉钉工作流之模板配置、钉钉接口流程发起

    一.前言 自从上次 水了一篇 写了一篇5分钟快速接入钉钉实现考勤后过了1个多月的时间,我福乐里又和大家见面了,今天我来聊聊工作流的那些事. 想必各位攻城狮都做过或者接触过OA系统,说到OA系统肯定离不 ...

  9. 对接钉钉审批_钉钉审批对接是什么-和钉钉审批对接相关的问题-阿里云开发者社区...

    关于 钉钉审批对接的搜索结果 问题 企业系统对接钉钉生成审批单,企业系统处理审批后,如何撤销钉钉审批单 公司erp系统与钉钉对接,erp发起审批后同步到钉钉生成审批实例,在erp中用户处理了审批,如何 ...

  10. 蓝凌专属钉钉智能OA 让数千万中小企业成长更高效

    中国软件网 报道 | 公众号:Hapiweb-soft6 8月27日,阿里钉钉未来组织大会盛大开启,在数百位名企CEO.CIO的见证下,蓝凌专属钉钉等新产品隆重发布,聚焦中小企业数字化需求的蓝凌专属钉 ...

最新文章

  1. MySQL 中的共享表空间与独立表空间如何选择
  2. 带括号的计算器 java_【福利】java新手做的复合型计算器!
  3. Scala模式匹配:for循环表达式中的模式匹配
  4. @Transient注解作用
  5. AJAX异步--ajax请求
  6. php查询mysql数据库乱码_PHP怎样处理查询MySQL数据库中文乱码?_后端开发
  7. Pytorch-NLU,一个中文文本分类、序列标注(实体识别、分词、词性标注)的极简工具包,基于pytorch与tramsforers
  8. pandas(一) Series和DataFrame
  9. pycharm报错: with exit code -1073740791 (0xC0000409)
  10. 超高频RFID电子标签技术和应用分析
  11. 使用wget遍历网页文件进行下载
  12. Java的学习(下)
  13. 计算机内存的安装方法,电脑内存条安装教程_电脑内存条安装注意事项
  14. 英文文本导入去停用词
  15. C# 读取电脑硬件信息
  16. vc++ 读书笔记(摘抄)
  17. 首个WICKET HELLOL World 报错Can not determine Markup. Component is not yet connected to a parent.
  18. SQL索引工作原理----摘抄自别人的
  19. java code combat_CodeCombat编程游戏
  20. SpringCloud Alibaba 之Sentinel

热门文章

  1. 20162327WJH程序设计与数据结构第七周总结
  2. php通过函数怎么禁止百度蜘蛛抓取,怎么屏蔽百度蜘蛛(Baiduspider)抓取网站
  3. 了凡四训 第一篇《立命之学》
  4. 《众妙之门——用户体验设计的秘密》一第2章 设计“好脾气”的网页2.1 巴赫和他的十二平均律...
  5. 彼得·林奇的成功投资
  6. 股指期货日内平仓手续费高,锁仓可以解决吗
  7. 述职答辩提问环节一般可以问些什么_2.50 述职报告与评审提问注意事项
  8. php jion用法,ThinkPHP关于JOIN使用方法详细说明
  9. 两台计算机互联方案,如何连接两台电脑
  10. WIN10系统安装金蝶K3 WISE14.0以下客户端版本