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

    效果展示

  • 相关阅读:
    百度病了,必应挂了,Yandex疯了。
    SpringBoot从零单排 ------ 拦截器的使用
    SpringBoot从零单排 ------初级入门篇
    我为什么放弃MySQL?最终选择了MongoDB
    NSURL组成部分详解
    关于MPMoviePlayerController 缓存播放的一些技术准备
    动画系列收藏
    代码简化
    自动循环滚动ScrollView
    iOS WKWebView 使用笔记
  • 原文地址:https://www.cnblogs.com/datacool/p/datacool20180424.html
Copyright © 2020-2023  润新知