WinForm调用钉钉获取考勤结果

原文: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 == 0)
                {
                    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) / 1000;
                if (ts >= 7200)
                {
                    string respon = Program.HttpGetRequest(string.Format(URL_GetToken, Program.CorpId, Program.CorpSecret));
                    DingTalkRespond obj = JsonConvert.DeserializeObject<DingTalkRespond>(respon);
                    if (obj != null && obj.errcode == 0)
                    {
                        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 = 0;
            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 == 0)
                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 = 0;
            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 = 50,
                    userIdList = null,
                    offset = offset * 50
                };
                string jsonRequest = JsonConvert.SerializeObject(request).Replace("null", "[]");
                string jsonResult = Program.PostJsonData(url, jsonRequest);
                var objResult = JsonConvert.DeserializeObject<DingTalkRespond>(jsonResult);
                if (objResult != null && objResult.errcode == 0)
                {
                    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();
        }

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

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

       }

效果展示

原文地址:https://www.cnblogs.com/lonelyxmas/p/12204188.html

时间: 2024-10-08 04:50:08

WinForm调用钉钉获取考勤结果的相关文章

钉钉开发中post异步调用问题

最近项目上在做钉钉开发中,经常会遇到使用post方式调用钉钉内部的方法(微信也有一样),这里涉及到跨域的post调用,但跨域一般都是用jsonp格式,而这个格式只支持get方式.尝试了挺多方法都没有返回 {"errcode":43002,"errmsg":"需要POST请求"} 让人很费解,用js方式不行,只能尝试从后台解决问题,然后写了如下方法: /// <summary> /// /// </summary> ///

Jenkins与钉钉机器人实现手机端获取当前服务日志

马上要过年了,各位运维们除了因为买不到回家的火车票而嚎嚎大哭之外也开始扩容服务器和提前调整监控值,目的就是为了过一个消停的春节.可是这毕竟十天左右不在公司,要是模块真出了什么意外肯定没法第一找到日志分析问题,毕竟这几天都在串门拜年和醉生梦死中度过,走到哪都要再背一个笔记本实在太不方便了. 那么这个时候,我就琢磨使用手机端来启动服务器里脚本,让这个脚本可以去获取当前的日志,然后再把结果返回到手机端.这样就不用到哪里都带那个一看就很扫兴的公司笔记本电脑了. 使用手机端启动服务器里脚本?我又不会开发a

教大家一个钉钉考勤打卡定位更改的好方法可以考勤打卡改位置模拟WiFi模拟水印照片

随着智能办公的普及,钉钉成为了很多公司办公软件.上班族上下班考勤打卡一般可通过"钉钉"定位到公司位置进行打卡,但由于一些特殊原因,定位不准确,或者不能及时定位打卡.虚拟上神教你解决"钉钉"考勤的问题,实现上班族随时随地定位考勤用收藏地址随时打卡考勤.能解决上下班考勤,让你再也不会迟到的! 最近钉钉又更新到了4.3.2版本,面对钉钉如此之快的更新速度,许多上班族是更加头痛了,因为每一次的更新就有可能修复之前的BUG,导致自己的虚拟定位软件用不了,那么面对钉钉的持续更新

.NET平台下,钉钉微应用开发之:获取userid

工作需求,开发钉钉微应用和小程序,之前有接触过支付宝小程序和生活号的开发,流程没有很大的差别,这里记录下我用ASP.NET MVC实现钉钉微应用的开发,并实现获取用户的userid.小弟我技术有限,本文中的一些命名或方法写的不好的,还请指点. 钉钉开发者平台上有各个平台的SDK,我也有下载对应的.NET版本的SDK,但是还没有全搞懂,我先用最土的办法:直接发起http请求,来获取我需要的数据,后面如果有时间我会把.NET的也补上. 1.注册钉钉微应用: 注册完成之后,继续完善下应用配置,如IP白

钉钉调用审批流

原文:钉钉调用审批流 进入钉钉 "钉钉开放平台"  下载SDK   https://ding-doc.dingtalk.com/doc#/faquestions/vzbp02 1 public ActionResult Dingding(string sheetno, long dept, string plant, string Class) { 2 //需填写的主表数据 3 var bill = tApp.GetList().Where(t => t.F_SHEETNO ==

通知神器——java调用钉钉群自定义机器人

创建群自定义机器人 在指定钉钉群(或者随便拉两个人建个群,然后把别人T出去)的群设置里选择 群机器人 -> 自定义,如图: 然后,添加机器人,设置名字,添加成功时如下图: 其中webhook非常重要,下文详述.点击设置说明可以看相关使用文档,文档链接见本文末尾. 使用HTTP POST请求发送消息 直接向上文所述的webhook链接发送POST请求已达到自定义机器人向群内发消息的功能 按照文档中所属的参数格式直接向链接发送请求即可,我使用Postman发送请求如下: 效果如下: 发送其他格式(如

调用钉钉群组机器人在当前群组发送Zabbix报警信息

关于钉钉机器人的创建,基本用法详见我另一篇文章<创建自定义机器人> 本实验测试zabbix版本为3.4 接下来我们将直接通过下面的操作完成zabbix报警往我们的钉钉机器人所在群组发送: 首先在附件中下载对应的程序到我们的zabbix server的AlertScriptsPath目录,可以通过以下命令查看目录所在位置 grep "AlertScriptsPath" zabbix_server.conf 更改文件的权限为运行zabbix的用户,并给执行权限 chmod 75

钉钉自定义机器人配合SVN钩子事件进行消息的推送实践

目前很多公司还是使用SVN(TortoiseSVN)进行版本控制,使用简单,适合管理一般项目.协同办公软件目前钉钉比较成熟,阿里也一直在宣传推广,这两年公司也在使用,主要用于信息的沟通,其它的绩效.考勤.日志.审批等都是挂载的我们公司内部的系统,很方便,之后钉钉推出了钉钉机器人,一开始觉得没用,也就一直没关注,但是最近我看了钉钉自定义机器人,觉得这个可以好好利用起来,看到网上也有这方面的应用说明,看着很简单,但是按照流程操作起来各种坑,而且相关知识过于碎片化,今天我在这里进行一下简单的整合,梳理

js api 实现钉钉免登

js api 实现钉钉免登,用于从钉钉微应用跳转到企业内部的oa,erp等,我刚刚实施完了我公司的这个功能,钉钉用起来还不错. 1 js api 实现钉钉免登,页面配置. <title>利用jsapi实现免登</title>     <script type="text/javascript" src="https://g.alicdn.com/ilw/ding/0.7.5/scripts/dingtalk.js" ></s