Index
引言
钉钉在企业移动办公领域有着很高的占有率,但是可能大家都会觉得,他在企业定制化,数据分析等领域有着很大的短板。
而我们的kintone作为paas平台,可以补足这个短板。很多开发者想知道我们的kintone和钉钉还有阿里云这些服务,如何来做一个整合开发呢?
那下面我们就结合钉钉和kintone两者的API,来完成他们之间数据的整合吧。
课题
这次我们的课题是获取钉钉的打卡结果、签到、审批数据,并且同步到kintone。
钉钉篇
创建一个小程序
在钉钉开放平台,在“企业内部开发”下建立一个小程序:
记下他的应用信息:
打开应用权限
在“接口权限”下根据自己的需求打开权限:
钉钉代码片段
API简介
钉钉开放了丰富的服务端接口,借助这些接口我们可以很轻松的抓取数据到kintone端来进行处理,分析和记录。
调用钉钉接口时,需使用HTTPS协议、JSON数据格式、UTF8编码,访问域名为https://oapi.dingtalk.com。POST请求请在HTTP Header中设置 Content-Type:application/json。
调用钉钉的API前需要先获取access_token令牌,通过access_token才能调用钉钉的其他业务的API。
获取access_token
请求方式:GET
请求地址:https://oapi.dingtalk.com/gettoken?appkey=key&appsecret=secret
例:利用access_token获取打卡详情
以下是示例代码:
package d2; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import com.dingtalk.api.DefaultDingTalkClient; import com.dingtalk.api.DingTalkClient; import com.dingtalk.api.request.OapiAttendanceListRequest; import com.dingtalk.api.response.OapiAttendanceListResponse; import com.dingtalk.api.response.OapiUserListbypageResponse.Userlist; import com.dingtalk.api.response.OapiAttendanceListResponse.Recordresult; import cn.cybozu.api.KintoneRecord; public class D2TimeCardResultConnector extends D2Connector { private static final int LIMIT_USER_SIZE = 7; private static final long LIMIT_TIMECARD_SIZE = 50; private Map<String, Userlist> users; public D2TimeCardResultConnector(String appKey, String appSecret, String domain, long appId, long spaceId, String apiToken) { super(appKey, appSecret, domain, appId, spaceId, apiToken); } public void execute(Date startDate, Date endDate) throws Exception { users = getAllUsers(); int count = users.size(); deleteTimeCardResult(startDate, endDate); List<String> userIds = new ArrayList<>(); for(String id: users.keySet()) { userIds.add(id); if(userIds.size() == count) { getTimeCardResult(userIds, startDate, endDate); userIds.clear(); } } if(userIds.size() != 0) { getTimeCardResult(userIds, startDate, endDate); } } /** * 结果的删除 * @throws Exception */ private void deleteTimeCardResult(Date startDate, Date endDate) throws Exception { String query = "workDate >= \"" + formatDate(startDate) + "\" and workDate <= \"" + formatDate(endDate) + "\""; List<KintoneRecord> recs = getRecordsFromKintone(query, new String[] {"$id"}); if(recs.size() == 0) { return; } deleteRecordsToKintone(recs); } private void getTimeCardResult(List<String> userIds, Date startDate, Date endDate) throws Exception { DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/attendance/list"); OapiAttendanceListRequest request = new OapiAttendanceListRequest(); request.setUserIdList(userIds); request.setLimit(LIMIT_TIMECARD_SIZE); Date tmpDate = (Date)startDate.clone(); while(tmpDate.compareTo(endDate) <= 0) { request.setWorkDateFrom(formatDate(tmpDate) + " 00:00:00"); request.setWorkDateTo(formatDate(tmpDate) + " 00:00:00"); long offset = 0; for(;;) { request.setOffset(offset); OapiAttendanceListResponse response = client.execute(request, getAccessToken()); if(response.getErrcode() != 0) { throw new Exception(response.getErrmsg()); } List<Recordresult> list = response.getRecordresult(); if(list.size() == 0) { break; } offset += LIMIT_TIMECARD_SIZE; copyRecords(list); if(!response.getHasMore()) { break; } } tmpDate = new Date(tmpDate.getTime() + TimeUnit.DAYS.toMillis(1)); } } private void copyRecords(List<Recordresult> results) throws Exception { List<KintoneRecord> postRecs = new ArrayList<>(); for(Recordresult result : results) { KintoneRecord rec = new KintoneRecord(); String id = "" + result.getId(); rec.setString("id", id); rec.setString("groupId", result.getGroupId() == null ? null : "" + result.getGroupId()); rec.setString("planId", result.getPlanId() == null ? null : "" + result.getPlanId()); rec.setString("recordId", result.getRecordId() == null ? null : "" + result.getRecordId()); rec.setString("workDate", formatDate(result.getWorkDate())); rec.setString("userId", result.getUserId()); rec.setString("checkType", result.getCheckType()); rec.setString("timeResult", result.getTimeResult()); rec.setString("locationResult", result.getLocationResult()); rec.setString("sourceType", result.getSourceType()); rec.setString("approveId", result.getApproveId() == null ? null : "" + result.getApproveId()); rec.setString("procInstId", result.getProcInstId() == null ? null : "" + result.getProcInstId()); rec.setString("baseCheckTime", formatDateTime(result.getBaseCheckTime())); rec.setString("userCheckTime", formatDateTime(result.getUserCheckTime())); Userlist user = users.get(result.getUserId()); if(user != null) { rec.setString("userName", user.getName()); rec.setString("userNo", user.getJobnumber()); } else { rec.setString("userName", null); rec.setString("userNo", null); } postRecs.add(rec); } postRecordsToKintone(postRecs); } /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { String appKey = "xxxxxxxxxxxxxxxxx"; String appSecret = "xxxxxxxxxxxxxxxxx"; String domain = "xxxxxx.cybozu.cn"; long appId = 116; long spaceId = 0; String apiToken = "xxxxxxxxxxxxxxxxx"; //------------------------------------------------------------------------------------------- if(args.length < 2) { System.err.println("args[0]: start date(yyyy-mm-dd). args[1]: end date(yyyy-mm-dd)."); System.exit(-1); } //modify D2TimeCardResultConnector qyd2 = new D2TimeCardResultConnector(appKey, appSecret, domain, appId, spaceId, apiToken); qyd2.execute(D2Connector.parseDate(args[0]), D2Connector.parseDate(args[1])); System.out.println("success"); } }
其中appKey,appSecret请按照实际注册的钉钉小程序里的内容来填写。
domain,appId,spaceId,apiToken 则是你kintone端的实际信息。
所有API文档请参见钉钉官网:https://open-doc.dingtalk.com/microapp/serverapi2
demo代码里提供了获取打卡结果,签到,审批这3个功能的sample。具体更多的功能,大家可以参照钉钉和kintone的开发文档来自己完成。
文件结构
以下是cybozush和d2-kintone-sample所加载的外部jars包
导出成jar包
kintone篇
导入应用
通过导入模板文件创建 => 选择D2 sample.zip。
导入之后,会自动创建三个应用。
分别是D2审批,D2打卡结果,D2签到。
应用设置
在导入后的每个应用的后台设置里添加API令牌。
环境部署篇
云环境部署
这里我们将他部署在阿里的云服务器 ECS实例上,当然我们也可以部署在亚马逊云,腾讯云等等。
我们只需要配置好java的环境:jre: 1.8即可。
后面,我还会写几篇serverless的部署方式,大家有兴趣也可以看看。
运行方式
例:java -cp test.jar d2.D2TimeCardResultConnector 2019-07-01 2019-07-30
触发器
我们可以配置每日执行来定期同步钉钉的数据到kintone
例:
crontab 0 0 1 * * java -cp test.jar d2.D2TimeCardResultConnector 2019-07-01 2019-07-30
验证
好了,我们已经完成了所有设置,在钉钉里添加一条打卡记录,签到记录,还有审批记录,看看是不是同步到了kintone呢?
源码文件下载地址
下载地址:
https://gitee.com/cybozudeveloper/dingtalk2kintone
1. 使用的libraries
cybozush
-- kintone REST APItaobao-sdk-java-auto_1479188381469-20190425.jar
-- 钉钉SDKcommons-logging-1.2.jar
-- 钉钉SDK使用的外部jar
2. 整合程序的class说明
D2Connector
-- 连接kintone和钉钉的基类D2CheckinConnector
-- 签到sample programD2TimeCardResultConnector
-- 打卡结果sample programD2WfConnector
-- 审批sample program
注意事项
本示例代码不保证其运行。
我们不为本示例代码提供技术支持。