第一步:注册钉钉企业账号
打开链接:https://oa.dingtalk.com/#/login,注册账号即可
第二步:创建应用
以创建e应用为例:
还需要授权一个开发人员,并获取CorpSecret,需要把corpId和CorpSecret作为参数请求api接口获取AccessToken,后面的所有接口都需要AccessToken
第三步:接入接口
一、获取token
1 const corpid = 'dingd***587da6ee21d35c2f4657eb63***'; 2 const corpsecret = '*********'; 3 const requestPromise = require("request-promise"); 4 5 const getAccessToken = async (corpid, corpsecret) => { 6 // https://oapi.dingtalk.com/gettoken?corpid={corpid}&corpsecret={corpSecret或开发授权码} 7 const result = await requestPromise({ uri: 'https://oapi.dingtalk.com/gettoken', qs: { corpid, corpsecret } }); 8 console.log(result); 9 }; 10 getAccessToken(corpid, corpsecret);
二、promise请求接口封装
function request(url, method, params, headers = {}) {
const options = {
url,
method,
// timeout: 3000,
headers: Object.assign(headers, {
'content-type': 'application/json',
}),
rejectUnauthorized: false, // https
json: true,
};
switch (method) {
case 'POST':
case 'PUT':
options.body = params;
break;
case 'GET':
case 'DELETE':
options.qs = params;
break;
default:
break;
}
return new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (!error) {
resolve(body);
} else {
reject(error);
}
});
});
.catch (error => ({
msg: error.message,
}));
}
三、接口见代码(后端使用koa.js)
const host = 'https://oapi.dingtalk.com/';
/*
*发送工作通知消息
*/
router.post('/api/dingtalkserve/asyncsend_v2', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['userid_list'] && !body['dept_id_list'] && !body['to_all_user']) {
return response.fail({
'msg': "userid_list,dept_id_list, to_all_user必须有一个不能为空"
});
}
if (!body['msg']) {
return response.fail({
'msg': "msg不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
agent_id: parseInt(agentId4EP),
msg: {
"msgtype": "text",
"text": {
"content": body['msg']
}
}
};
body['to_all_user'] ? params['to_all_user'] = true : false;
body['dept_id_list'] ? params['dept_id_list'] = body['dept_id_list'] : "";
body['userid_list'] ? params['userid_list'] = body['userid_list'] : "";
let messageRes = await request(`${host}topapi/message/corpconversation/asyncsend_v2?access_token=${accessToken}`, 'POST', params);
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取工作通知消息的发送进度
*/
router.post('/api/dingtalkserve/getsendprogress', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['task_id']) {
return response.fail({
'msg': "task_id不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
agent_id: parseInt(agentId4EP),
task_id: body['task_id']
};
let messageRes = await request(`${host}topapi/message/corpconversation/getsendprogress?access_token=${accessToken}`, 'POST', params);
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取工作通知消息的发送结果
*/
router.post('/api/dingtalkserve/getsendresult', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['task_id']) {
return response.fail({
'msg': "task_id不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
agent_id: parseInt(agentId4EP),
task_id: body['task_id']
};
let messageRes = await request(`${host}topapi/message/corpconversation/getsendresult?access_token=${accessToken}`, 'POST', params);
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取子部门ID列表
*/
router.post('/api/dingtalkserve/list_ids', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['id']) {
return response.fail({
'msg': "父部门id不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
id: body['id']
};
let messageRes = await request(`${host}department/list_ids`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取部门列表
*/
router.post('/api/dingtalkserve/list', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['id']) {
return response.fail({
'msg': "父部门id不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
id: body['id']
};
body['lang'] ? params['lang'] = body['lang'] : "";
body['fetch_child'] ? params['fetch_child'] = true : false;
let messageRes = await request(`${host}department/list`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取部门详情
*/
router.post('/api/dingtalkserve/departmentget', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['id']) {
return response.fail({
'msg': "部门id不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
id: body['id']
};
body['lang'] ? params['lang'] = body['lang'] : "";
let messageRes = await request(`${host}department/get`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 查询部门的所有上级父部门路径
*/
router.post('/api/dingtalkserve/list_parent_depts_by_dept', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['id']) {
return response.fail({
'msg': "部门id不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
id: body['id']
};
let messageRes = await request(`${host}department/list_parent_depts_by_dept`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 查询指定用户的所有上级父部门路径
*/
router.post('/api/dingtalkserve/list_parent_depts', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['userId']) {
return response.fail({
'msg': "用户id不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
userId: body['userId']
};
let messageRes = await request(`${host}department/list_parent_depts`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取企业员工人数
*/
router.post('/api/dingtalkserve/get_org_user_count', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['onlyActive']) {
return response.fail({
'msg': "激活钉钉状态不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
onlyActive: body['onlyActive']
};
let messageRes = await request(`${host}user/get_org_user_count`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取用户详情
*/
router.post('/api/dingtalkserve/userinfo', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['userid']) {
return response.fail({
'msg': "userid不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
userid: body['userid']
};
body['lang'] ? params['lang'] = body['lang'] : "";
let messageRes = await request(`${host}user/get`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取部门用户userid列表
*/
router.post('/api/dingtalkserve/getDeptMember', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['deptId']) {
return response.fail({
'msg': "部门id不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
deptId: body['deptId']
};
let messageRes = await request(`${host}user/getDeptMember`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取部门用户(详情)
*/
router.post('/api/dingtalkserve/listbypage', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['department_id']) {
return response.fail({
'msg': "部门id不能为空"
});
}
if (!body['offset']) {
return response.fail({
'msg': "偏移量不能为空"
});
}
if (!body['size']) {
return response.fail({
'msg': "每页数量不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
department_id: body['department_id'],
offset: parseInt(body['offset']),
size: parseInt(body['size'])
};
let messageRes = await request(`${host}user/listbypage`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 根据unionid获取userid
*/
router.post('/api/dingtalkserve/getUseridByUnionid', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['unionid']) {
return response.fail({
'msg': "unionid不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
unionid: body['unionid']
};
let messageRes = await request(`${host}user/getUseridByUnionid`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取通讯录权限范围
*/
router.post('/api/dingtalkserve/authScopes', async ({ request, response, session }) => {
try {
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken
};
let messageRes = await request(`${host}/auth/scopes`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 创建群
*/
router.post('/api/dingtalkserve/createChat', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['name']) {
return response.fail({
'msg': "群名称不能为空"
});
}
if (!body['owner']) {
return response.fail({
'msg': "群主userid不能为空"
});
}
if (!body['useridlist']) {
return response.fail({
'msg': "群成员不能为空"
});
}
if (body['useridlist'].indexOf(body['owner']) < 0) {
return response.fail({
'msg': "群主必须为群成员"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
name: body['name'],
owner: body['owner'],
useridlist: body['useridlist'].split(",")
};
let messageRes = await request(`${host}chat/create?access_token=${accessToken}`, 'POST', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 获取群聊会话信息
*/
router.post('/api/dingtalkserve/chatInfo', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['chatid']) {
return response.fail({
'msg': "群id不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
chatid: body['chatid']
};
let messageRes = await request(`${host}chat/get`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 发送群消息
*/
router.post('/api/dingtalkserve/chatSend', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['chatid']) {
return response.fail({
'msg': "群id不能为空"
});
}
if (!body['msg']) {
return response.fail({
'msg': "群消息不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
chatid: body['chatid'],
msg: {
"msgtype": "text",
"text": {
"content": body['msg']
}
}
};
let messageRes = await request(`${host}chat/send?access_token=${accessToken}`, 'POST', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
/*
* 查询群消息已读人员列表
*/
router.post('/api/dingtalkserve/getReadList', async ({ request, response, session }) => {
try {
let body = request.fields;
if (!body['messageId']) {
return response.fail({
'msg': "messageId不能为空"
});
}
if (!body['cursor']) {
return response.fail({
'msg': "cursor不能为空"
});
}
if (!body['size']) {
return response.fail({
'msg': "每页数量不能为空"
});
}
// 获取TOKEN
let accessToken = await getAccessToken();
let params = {
access_token: accessToken,
messageId: body['messageId'],
cursor: body['cursor'],
size: parseInt(body['size']),
};
let messageRes = await request(`${host}chat/getReadList`, 'GET', params);
console.log("messageRes", messageRes)
return response.success({ 'data': messageRes });
} catch (e) {
console.log(e);
return response.fail({
'msg': e
});
}
});
以上针对的是钉钉企业内部应用
如果是ISV开发者应用,则需要通过接口获取企业的基本信息
nodejs签名实现
/*
* 把timestamp + "\n" + suiteTicket当做签名字符串, suiteSecret做为签名秘钥,
* 使用HmacSHA256算法计算签名, 然后进行Base64 encode获取最后结果。
* 然后把签名参数再进行urlconde, 加到请求url后面。
*/
const crypto = require('crypto');
const accessKey = 'suiteqh0ljtdheuee****'; // suiteKey
const suiteSecret = '******';
const suiteTicket = 'TestSuiteTicket';
const timestamp = Date.now();
const stringToSign = timestamp + "\n" + suiteTicket;
const hash = crypto.createHmac('sha256', suiteSecret)
.update(stringToSign, 'utf8')
.digest().toString('base64');
console.log(hash);
var request = require("request");
var urlencode = require('urlencode');
var options = {
method: 'POST',
url: 'https://oapi.dingtalk.com/service/get_auth_info',
qs: {
signature: hash,
timestamp: timestamp,
suiteTicket: suiteTicket,
accessKey: accessKey
},
headers: {
'Cache-Control': 'no-cache',
'Content-Type': 'application/json'
},
body: { auth_corpid: 'dingd142a587da6ee21d35c2f4657eb6378f' },
json: true
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
钉钉文档开发者链接 :https://open-doc.dingtalk.com/microapp/serverapi2/isu6nk