当前位置: 首页 > Web前端 > JavaScript

让签到小工具“更聪明”:添加签到过滤器、令牌自动刷新

时间:2023-03-26 22:31:45 JavaScript

大家好,我是杨成功。之前写过一篇文章,介绍了如何使用Node.js+DingTalkAPI实现一个考勤签到持续提醒的小工具。有同学留言说为什么不直接调用钉钉API自动登录(这个我也想过)。可惜翻遍了钉钉的文档,没有找到这个API。再说了,怎么会有这样的API?你在想什么?有同学板着脸指出问题:“我请假你一直提醒我?再过几个小时token就过期了!”。针对这两个问题,我们在之前代码实现的基础上对代码进行了优化,增加了两个逻辑:在获取未签到人员时,过滤已请假人员;token过期时,自动刷新token如果没有看过上一篇,请先阅读登录小部件第一篇文章,源码在GitHub上。接下来,让我们一起来实现新的需求,优化签到功能。过滤请假人员,使用钉钉API获取部分人员签到状态。我们目前的做法是将需要查看签到状态的人(我们整个团队)的userid维护在一个列表中,然后获取这些人的签到数据,过滤掉没有签到的人。特殊情况下,如果今天我们组有成员请假,会以非签到人的身份不断提醒。事实上,我们需要排除请假的人。第一步是让今天请假的人。获取请假状态API如下:API地址:${baseURL}/topapi/attendance/getleavestatus请求方式:POST文档地址:这里,该API的请求体是一个对象,该对象的属性为如下:userid_list:查询休假状态的userid列表start_time:查询开始时间(当天工作时间)end_time:查询结束时间(当天下班时间)size:返回项数,最大20offset:分页,从0开始把请假状态写成一个单独的方法,代码如下:constdayjs=require('dayjs');constaccess_token=newDingToken().get();//获取休假状态constgetLeaveStatus=async(userid_list)=>{letparams={access_token,};letbody={start_time:dayjs().startOf('day').valueOf(),end_time:dayjs().endOf('day').valueOf(),userid_list:userid_list.join(),//用户id列表偏移量:0,大小:20,};letres=awaitaxios.post(`${baseURL}/topapi/attendance/getleavestatus`,body,{params});如果(res.errcode!=0){返回res;}else{返回资源。result.leave_status.map((row)=>row.userid);}};执行上面的方法后,就可以得到当天请假的用户。然后在所有需要查看签到状态的用户列表中,过滤掉已经请假的用户://需要查看签到的userid数组letalluids=['xxx','xxxx'];//获取离开状态控制台日志(alluids);//过滤后的userid数组是这样的已经请假的用户不会被提醒。DingTalktoken自动刷新获取钉钉API时,首先要获取接口调用凭证(即access_token),每次API调用都必须携带该凭证。但是这个凭证是有时间限制的,一旦超过有效期,API调用就会被禁止。所以这里一个很重要的优化点就是自动刷新access_token。怎么做?其实和前端项目中的实现是一样的。在axios的拦截器中,判断access_token是否过期。如果过期了,会重新获取,然后继续执行请求。首先将获取凭证写成单独的方法,如下:constfetchToken=async()=>{try{letparams={appkey:'xxx',appsecret:'xxx',};leturl='https://oapi.dingtalk.com/gettoken';让result=awaitaxios.get(url,{params});if(result.data.errcode!=0){抛出结果.data;}else{lettoken_str=JSON.stringify({token:result.data.access_token,expire:Date.now()+result.data.expires_in*1000,});newDingToken().set(token_str);返回token_str;}}catch(error){console.log(error);}};该方法主要是调用获取凭证的接口,调用成功后返回access_token和有效时间。这里需要设置一个过期时间,即当前时间+有效时间,并生成过期时间的时间戳:Date.now()+result.data.expires_in*1000,这里还有一个钉钉类用于获取并存储access_token,代码如下:varfs=require('fs');varcatch_dir=path.resolve(__dirname,'../','catch');classDingToken{get(){letres=fs.readFileSync(`${catch_dir}/ding_token.json`);返回res.toString()||无效的;}set(token){fs.writeFileSync(`${catch_dir}/ding_token.json`,token);}}设置access_token和过期时间由一个json字符串组成,保存在一个文件中,然后在axios的请求拦截器中可以获取到json数据,然后判断当前时间是否大于过期时间。如果是,则重新调用fetchToken()方法生成新的token,继续执行请求。拦截器代码如下:constaxios=require('axios');constinstance=axios.create({baseURL:'https://oapi.dingtalk.com',timeout:5000,});constdingToken=newDingToken();//请求拦截器instance.interceptors.request.use(async(config)=>{if(!config.params.access_token){letcatoken={};if(dingToken.get()){catoken=JSON.parse(dingToken.get());//判断是否过期if(Date.now()-catoken.expire>=0){console.log('DingTokenexpired');awaitfetchToken();catoken=JSON.parse(dingToken.get());}}else{//第一次获取tokenawaitfetchToken();catoken=JSON.parse(dingToken.get());}//携带token到请求头config.params.access_token=catoken.token;}returnconfig;});通过上面写在拦截器中的逻辑,我们就不需要关心access_token过期了。而我们会在token过期后重新请求,所以不会触发调用频率限制。小结本文介绍了钉钉登录小部件两个方面的优化,同时我也精简了配置部分的代码,让我可以更快的访问我的钉钉应用。这次代码已经开源了,地址在GitHub上。欢迎大家尝试加星。