文档章节

WinForm调用钉钉获取考勤结果

o
 osc_z1hvg4cu
发布于 2018/04/24 13:33
字数 1274
阅读 45
收藏 0

精选30+云产品,助力企业轻松上云!>>>

关注点

  • 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();
        }

 

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 
        }

 

 

 

 

效果展示

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。

暂无文章

【软件工具篇02】使用Anki克服遗忘曲线

使用Anki克服遗忘曲线 艾宾浩斯遗忘曲线 百度百科:遗忘曲线由德国心理学家艾宾浩斯研究发现,描述了人类大脑对新事物遗忘的规律。人体大脑对新事物遗忘的循序渐进的直观描述,人们可以从遗...

osc_wobxrheh
5分钟前
0
0
面向对象的理解

面向对象的三大特性 封装 继承 多态 面向对象的七大原则 单一职责原则:每一个类应该专注于做一件事情。即高内聚,低耦合。类的功能要单一,体积不要过于庞大。 开闭原则:一个对象对扩展开发...

osc_2wq8ft8d
6分钟前
0
0
Laravel Redis分布式锁实现源码分析

首先是锁的抽象类,定义了继承的类必须实现加锁、释放锁、返回锁拥有者的方法。 namespace Illuminate\Cache;abstract class Lock implements LockContract{ use InteractsWithTime;...

osc_2jegjdnw
8分钟前
0
0
【HDFS篇03】HDFS客户端操作 --- 开发环境准备

存储越困难,提取越容易 HDFS客户端操作---开发环境准备 步骤一:编译对应HadoopJar包,配置Hadoop变量 步骤二:创建Maven工程,导入pom依赖 <dependencies><dependency><groupId>ju...

osc_ds5ni1ur
9分钟前
0
0
老板,来瓶辣椒酱

最近网剧《隐秘的角落》非常的火爆,结局反转让人难以预料,但前两天发生了一场堪比史诗级大片的纠纷,纠纷的结局反转让人大跌眼镜,估计是神编剧都写不出来那样的剧本...而引发这场纠纷最核...

osc_1loi8uc4
11分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部