WinForm调用钉钉获取考勤结果
关注点:
- 1、钉钉AccessToken的获取和防止过期
- 2、使用TPL并行编程调用钉钉接口
需求详解
公司前台有个大屏,领导想显示全部员工的考勤结果统计情况和车间的实时监控视频,还有车间的看板。简单说就是把大屏分割成几个区域。现在遇到的难题是钉钉获取考勤结果的api是只有明细记录,比如你公司1000人,那么可能回给你2000条考勤结果。分别是上班考勤和下班考勤的。没有整个公司的,我就需要这么一条数据就行了。但人家没有这样的接口提供。卷起袖子,干!
趟坑过程
考勤打卡数据开放
业务场景
该接口仅限企业接入使用,用于返回企业内员工的实际打卡结果。比如,企业给一个员工设定的排班是上午9点和下午6点各打一次卡,即使员工在这期间打了多次,该接口也只会返回两条记录,包括上午的打卡结果和下午的打卡结果
考勤打卡数据开放
请求说明(ISV无调用权限)
如果你是ISV(应用服务商,将开发的应用上架到钉钉应用市场,提供给钉钉其他企业用户使用),则无调用权限
如果你是企业内部开发者(将自己公司的HR、OA、客户管理、业务管理等系统接入钉钉),有权限调用
2017-10-16更新:新增用户userId列表参数(userIdList)和分页参数(offset,limit),提升接口稳定性。
Https请求方式: POST
https://oapi.dingtalk.com/attendance/list?access_token=ACCESS_TOKEN
请求包结构体
1
2
3
4
5
6
7
|
{ "workDateFrom" : "yyyy-MM-dd hh:mm:ss" , "workDateTo" : "yyyy-MM-dd hh:mm:ss" , "userIdList" :[ "员工UserId列表" ], // 必填,与offset和limit配合使用,不传表示分页获取全员的数据 "offset" : 0 , // 必填,第一次传0,如果还有多余数据,下次传之前的offset加上limit的值 "limit" : 1 , // 必填,表示数据条数,最大不能超过50条 } |
参数说明
参数 | 参数类型 | 必须 | 说明 |
---|---|---|---|
access_token | String | 是 | 调用接口凭证 |
workDateFrom | String | 是 | 查询考勤打卡记录的起始工作日 |
workDateTo | String | 是 | 查询考勤打卡记录的结束工作日 |
userIdList | List | 是 | 员工在企业内的UserID列表,企业用来唯一标识用户的字段 |
offset | Long | 是 | 表示获取考勤数据的起始点,第一次传0,如果还有多余数据,下次获取传的offset值为之前的offset+limit |
limit | Long | 是 | 表示获取考勤数据的条数,最大不能超过50条 |
1)获取AccessToken
钉钉的服务器很牛X可以并行访问的。但是获取回来的数据集有点乱,其中有个关于分页的参数需要顺序读取,就是“hasMore”还有没有下一页。所以最终没有使用TPL。
/// <summary>
/// 获取AccessToken
/// </summary>
/// <returns></returns>
private AccessToken GetAccessToken()
{
string URL_GetToken = "https://oapi.dingtalk.com/gettoken?corpid={0}&corpsecret={1}";
string fileName = AppDomain.CurrentDomain.BaseDirectory + "AccessToken.xml";
if (!System.IO.File.Exists(fileName))
{
string respon = Program.HttpGetRequest(string.Format(URL_GetToken, Program.CorpId, Program.CorpSecret));
DingTalkRespond obj = JsonConvert.DeserializeObject<DingTalkRespond>(respon);
if (obj != null && obj.errcode == )
{
var result = new AccessToken
{
access_token = obj.access_token,
expires_in = GetCurrentTimeStamp()
};
SerializerHelper.SerializerToXML(fileName, result);
return result;
}
else
return new AccessToken
{
access_token = obj.access_token,
expires_in = GetCurrentTimeStamp()
};
}
else
{
var fileResult = SerializerHelper.LoadFromXML<AccessToken>(fileName);
long ts = (GetCurrentTimeStamp() - fileResult.expires_in) / ;
if (ts >= )
{
string respon = Program.HttpGetRequest(string.Format(URL_GetToken, Program.CorpId, Program.CorpSecret));
DingTalkRespond obj = JsonConvert.DeserializeObject<DingTalkRespond>(respon);
if (obj != null && obj.errcode == )
{
fileResult.access_token = obj.access_token;
fileResult.expires_in = GetCurrentTimeStamp();
SerializerHelper.SerializerToXML(fileName, fileResult);
}
}
return fileResult;
}
}
2)获取企业总人数
/// <summary>
/// 获取公司总人数
/// </summary>
/// <returns></returns>
private int GetFactoryEmployeeCount(AccessToken token)
{
int result = ;
string url = string.Format("https://oapi.dingtalk.com/user/get_org_user_count?access_token={0}&onlyActive=0", token.access_token);
string jsonObj = Program.HttpGetRequest(url);
var objResult = JsonConvert.DeserializeObject<DingTalkRespond>(jsonObj);
if (objResult != null && objResult.errcode == )
result = objResult.count;
return result;
}
3)获取全体员工的考勤结果
/// <summary>
/// 获取指定页的考勤结果
/// </summary>
/// <param name="offset"></param>
/// <returns></returns>
private void GetAttendance(AccessToken token)
{
EmployeeAttendanceList.Clear();
string url = string.Format("https://oapi.dingtalk.com/attendance/list?access_token={0}", token.access_token);
int offset = ;
bool bHasMore = true;
while (bHasMore)
{
var request = new AttendanceRequest
{
workDateFrom = DateTime.Now.ToString("yyyy-MM-dd") + " 00:00:00",
workDateTo = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
limit = ,
userIdList = null,
offset = offset *
};
string jsonRequest = JsonConvert.SerializeObject(request).Replace("null", "[]");
string jsonResult = Program.PostJsonData(url, jsonRequest);
var objResult = JsonConvert.DeserializeObject<DingTalkRespond>(jsonResult);
if (objResult != null && objResult.errcode == )
{
foreach (var item in objResult.recordresult)
{
if (!EmployeeAttendanceList.Any(a => a.id == item.id))
EmployeeAttendanceList.Add(item);
}
offset++;
bHasMore = objResult.hasMore;
}
else
break;
}
EmployeeAttendanceList = EmployeeAttendanceList.Where(a => a.checkType == "OnDuty").OrderBy(e => e.id).ToList();
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
AccessToken token = GetAccessToken();
GetAttendance(token);
var report = from q in EmployeeAttendanceList
group q by q.timeResult into g
select new AttendanceReportItemDTO
{
TimeResult = g.Key,
PersonNumber = g.Count()
};
BulletinBoardInit();
#region 考勤统计
attendanceReport.Total = GetFactoryEmployeeCount(token);
var normalObj = report.Where(p => p.TimeResult == "正常").FirstOrDefault();
attendanceReport.Normal = normalObj == null ? 0 : normalObj.PersonNumber;
var lateObj = report.Where(p => p.TimeResult == "迟到").FirstOrDefault();
attendanceReport.Late = lateObj == null ? 0 : lateObj.PersonNumber;
var earlyObj = report.Where(p => p.TimeResult == "早退").FirstOrDefault();
attendanceReport.Early = earlyObj == null ? 0 : earlyObj.PersonNumber;
var seriousLateObj = report.Where(p => p.TimeResult == "严重迟到").FirstOrDefault();
attendanceReport.SeriousLate = seriousLateObj == null ? 0 : seriousLateObj.PersonNumber;
var absenteeismObj = report.Where(p => p.TimeResult == "旷工").FirstOrDefault();
attendanceReport.Absenteeism = absenteeismObj == null ? 0 : absenteeismObj.PersonNumber;
int total_temp = report.Sum(p => p.PersonNumber);
var notSignedObj = report.Where(p => p.TimeResult == "未打卡").FirstOrDefault();
attendanceReport.NotSigned = notSignedObj == null ? 0 : notSignedObj.PersonNumber;
attendanceReport.NotSigned = attendanceReport.NotSigned + (attendanceReport.Total - total_temp);
#endregion
#region 判断有没有公告信息
if (BulletinImages != null && BulletinImages.Length > 0)
{
if (BulletinImages.Length == 1)
{
pbxNotity.Image = Image.FromFile(BulletinImages[0]);
}
if (BulletinImages.Length > 1)
{
if (notifyShowIndex == BulletinImages.Length - 1)
{
notifyShowIndex = 0;
}
pbxNotity.Image = Image.FromFile(BulletinImages[notifyShowIndex]);
notifyShowIndex++;
}
}
else
{
Action task = () =>
{
BulletinBoardInit();
};
task.BeginInvoke(null, null);
string dir = AppDomain.CurrentDomain.BaseDirectory + "公告栏图片\\";
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
string[] bgFiles = Directory.GetFiles(dir);
if (bgFiles != null && bgFiles.Length > 0)
{
int imgIndex = rd.Next(bgFiles.Length);
pbxNotity.BackgroundImage = Image.FromFile(bgFiles[imgIndex]);
}
}
#endregion
}
效果展示
WinForm调用钉钉获取考勤结果的更多相关文章
- npm钉钉脚手架,支持考勤信息获取
钉钉官方并未提供nodejs包,第一次调用接口的时候非常费事,而且尝试去寻找相关的钉钉考勤数据模块的时候只找到了一些消息啊,只能办公啊,免登啊之类的模块,有关考勤数据的似乎没有 关于dd的npm包中一 ...
- 钉钉开发中post异步调用问题
最近项目上在做钉钉开发中,经常会遇到使用post方式调用钉钉内部的方法(微信也有一样),这里涉及到跨域的post调用,但跨域一般都是用jsonp格式,而这个格式只支持get方式.尝试了挺多方法都没有返 ...
- 钉钉相关功能介入开发系列一:获取access_token
获取access_token的基本代码,与微信不同的是钉钉的token正常情况下有效期为7200秒,有效期内重复获取返回相同结果,并自动续期,比微信方便多了 //基本信息 string appkey ...
- 钉钉开发入门,微应用识别用户身份,获取用户免登授权码code,获取用户userid,获取用户详细信息
最近有个需求,在钉钉内,点击微应用,获取用户身份,根据获取到的用户身份去企业内部的用户中心做校验,校验通过,相关子系统直接登陆; 就是在获取这个用户身份的时候,网上的资料七零八落的,找的人烦躁的很,所 ...
- 使用python对mysql主从进行监控,并调用钉钉发送报警信息
1.编写python的监控脚本 A.通过获取mysql库中的状态值来判断这个mysql主从状态是否正常 B.进行两个状态值的判断 C.进行调取钉钉机器人,发送消息 2.设置定时任务进行脚本运行 cro ...
- .NET平台下,钉钉微应用开发之:获取userid
工作需求,开发钉钉微应用和小程序,之前有接触过支付宝小程序和生活号的开发,流程没有很大的差别,这里记录下我用ASP.NET MVC实现钉钉微应用的开发,并实现获取用户的userid.小弟我技术有限,本 ...
- 钉钉开发第三方H5微应用入门详细教程[ISV][免登流程][授权码][HTTP回调推送][识别用户身份][获取用户信息]
转载请注明原文地址:https://www.cnblogs.com/applerosa/p/11509512.html (by lnexin@aliyun.com 世间草木) 此教程注意点: 适用于第 ...
- 141_Power Query之获取钉钉审批流自动刷新Power BI报告
博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 钉钉办公给很多企业带来了很多方便,比如审批流线上化,通用化.线上化填写后,数据自动获取又是一个硬伤了,虽然数据可 ...
- 131_Power Query之获取钉钉日志自动刷新Power BI报告
博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 最近在玩钉钉日志,企业填写简单数据后方便汇总到一起比较实用的工具,但数据填写以后还是需要下载日志报表,比较麻烦. ...
随机推荐
- org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project.MavenProject, org.apache.maven.archiver.MavenArchiveConfiguration)
[错误原因:]maven的配置文件不是最新的 [解决办法:]依次点击help ->Install New Software -> add ->https://otto.takari. ...
- ROS笔记3 理解nodes
http://wiki.ros.org/ROS/Tutorials/UnderstandingNodes 介绍几个命令行工具用法 roscore rosnode rosrun A node reall ...
- 第53章 结束会话端点(End Session Endpoint) - Identity Server 4 中文文档(v1.0.0)
结束会话端点可用于触发单点注销(请参阅规范). 要使用结束会话端点,客户端应用程序会将用户的浏览器重定向到结束会话URL.用户在会话期间通过浏览器登录的所有应用程序都可以参与注销. 注意 终端会话端点 ...
- 第20章 定义客户端 - Identity Server 4 中文文档(v1.0.0)
客户端表示可以从您的身份服务器请求令牌的应用程序. 详细信息各不相同,但您通常会为客户端定义以下常用设置: 唯一的客户ID 如果需要的秘密 允许与令牌服务的交互(称为授权类型) 身份和/或访问令牌发送 ...
- win10下rdlc报表在vs(visual studio)中中文显示小方块的批量处理解决方法
在网上找vs中rdlc报表显示中文时显示小方块的解决方案,无外就是修改文本框的字体属性.但是对于维护已有的rdlc报表时,有中文的地方(此时都显示了小方块)会很多,再一个一个设置实在太麻烦.所以自己花 ...
- Could not load file or assembly 'System.ValueTuple'
项目目标框架:.Net Framework 4.6.2 报错:Could not load file or assembly 'System.ValueTuple' 在4.6.2项目中,想要使用C#7 ...
- redis分布式锁工具类
目录 (1)需要导入的包 (2)JedisUtil类 (3)jedisPool配置 (4)使用举例 (1)需要导入的包 <dependency> <groupId>redis. ...
- Java_设计模式之享元模式
1.关于享元模式 享元模式有点类似于单例模式,都是只生成一个对象被共享使用.享元模式主要目的就是让多个对象实现共享,减少不会要额内存消耗,将多个对同一对象的访问集中起来,不必为每个访问者创建一个单独的 ...
- Android入门第一课之Java基础
通知:由于本周六场地申请没通过,所以本周的培训临时取消. 今天给大家带来的是Android入门的第一课,由于教室申请的不确定性,因此,每次培训的内容都会在博客先提前释放出来.首先Android的APP ...
- [外包]!采用asp.net core 快速构建小型创业公司后台管理系统(六.结语)
到这里就结束了,真的结束了,源码会在文末分享! 另外录了两个视频,对这个系统进行了演示! 做有意义的事情,原此生无悔! 视频地址:使用asp.net core 快速构建权限管理模块1 使用asp.ne ...