天天p图无法打开照片(我开发了一款小工具)

首页教程更新时间:2023-05-14 19:10:49
今天小编为大家带来的是社区作者杨成功的文章,看看他开发的这款有意思的打卡小工具。

大家好,我是杨成功。

我司使用钉钉考勤打卡,人事要求的比较严格,两次未打卡记缺勤一天。但我们组醉心于工作,老是上下班忘记打卡,每月的工资被扣到肉疼。

开始的时候我们都设置了一个打卡闹铃,下班后准时提醒,但有的时候加班,加完班回家又忘记打卡了。还有的时候迷之自信的以为自己打卡了,第二天看考勤记录发现没打卡。

为了彻底解决这个问题,守住我们的钱袋子,我开发了一款打卡提醒工具,让全组连续三个月全勤!

下面介绍一下,这个小工具是如何实现的。

小工具实现思路

首先思考一下:闹铃提醒为什么不能百分之百有用?

  1. 机械的提醒

闹铃提醒很机械,每天一个点固定提醒,时间久了人就会免疫。就像起床闹铃用久了,慢慢的那个声音对你不起作用了,此时不得不换个铃声才行。

  1. 不能重复提醒

闹铃只会在固定时间提醒一次,没有办法判断是否打卡,更不会智能地发现你没有打卡,再提醒一次。

既然闹铃做不到,那我们就用程序来实现吧。按照上述两个原因,我们要实现的提醒工具必须包含两个功能:

  1. 检测用户是否打卡,未打卡则提醒,已打卡不提醒。
  2. 对未打卡用户循环检测,重复提醒,直到打卡为止。

如果能实现这两个功能,那么忘记打卡的问题多半也就解决了。

打卡数据需要从钉钉获取,并且钉钉有推送功能。因此我们的方案是:利用 Node.js 钉钉 API 来实现打卡状态检测和精准的提醒推送。

认识钉钉 API

钉钉是企业版的即时通讯软件。与微信最大的区别是,它提供了开放能力,可以用 API 来实现创建群组,发送消息等功能,这意味使着用钉钉可以实现高度定制的通讯能力。

我们这里用到的钉钉 API 主要有以下几个:

在使用钉钉 API 之前,首先要确认有公司级别的钉钉账号(使用过钉钉打卡功能一般就有公司账号),后面的步骤都是在这个账号下实现。

申请开放平台应用

钉钉开发第一步,先去钉钉开放平台申请一个应用,拿到 appKey 和 appSecret。

钉钉开放平台地址:https://open.dingtalk.com/developer

进入平台后,点击“开发者后台”,如下图:

天天p图无法打开照片,我开发了一款小工具(1)

开发者后台就是管理自己开发的钉钉应用的地方,进入后选择“应用开发->企业内部开发”,如下图:

天天p图无法打开照片,我开发了一款小工具(2)

进入这个页面可能提示暂无权限,这是因为开发企业钉钉应用需要开发者权限,这个权限需要管理员在后台添加。

管理员加开发者权限方式:
进入 OA 管理后台,选择设置-权限管理-管理组-添加开发者权限下的对应权限。

进入之后,选择【创建应用 -> H5 微应用】,根据提示创建应用。创建之后在【应用信息】中可以看到两个关键字段:

天天p图无法打开照片,我开发了一款小工具(3)

这两个字段非常重要,获取接口调用凭证时需要将它们作为参数传递。AppKey 是企业内部应用的唯一身份标识,AppSecret 是对应的调用密钥。

搭建服务端应用

钉钉 API 需要在服务端调用。也就是说,我们需要搭建一个服务端应用来请求钉钉 API。

切记不可以在客户端直接调用钉钉 API,因为 AppKey 和 AppSecret 都是保密的,绝不可以直接暴露在客户端。

我们使用 Node.js 的 Express 框架来搭建一个简单的服务端应用,在这个应用上与钉钉 API 交互。搭建好的 Express 目录结构如下:

|-- app.js // 入口文件
|-- catch // 缓存目录
|-- Router // 路由目录
| |-- ding.js // 钉钉路由
|-- utils // 工具目录
| |-- token.js // token相关

app.js 是入口文件,也是应用核心逻辑,代码简单书写如下:

const express = require('express'); const app = express(); const bodyParser = require('body-parser'); const cors = require('cors'); app.use(bodyParser.json()); app.use(cors()); // 路由配置 app.use('/ding', require('./router/ding')); // 捕获404 app.use((req, res, next) => { res.status(404).send('Not Found'); }); // 捕获异常 app.use((err, req, res, next) => { console.error(err); res.status(err.status || 500).send(err.inner || err.stack); }); app.listen(8080, () => { console.log(`listen to http://localhost:8080`); });

另一个 router/ding.js 文件是 Express 标准的路由文件,在这里编写钉钉 API 的相关逻辑,代码基础结构如下:

// router/ding.js var express = require('express'); var router = express.Router(); router.get('/', (req, res, next) => { res.send('钉钉API'); }); module.exports = router;

现在将应用运行起来:

$ node app.js

然后访问 http://localhost:8080/ding,浏览器页面显示出 “钉钉 API” 几个字,表示运行成功。

对接钉钉应用

一个简单的服务端应用搭建好之后,就可以准备接入钉钉 API 了。

接入步骤参考开发文档,文档地址:https://open.dingtalk.com/document/org/application-types

1. 获取 API 调用凭证

钉钉 API 需要验证权限才可以调用。验证权限的方式是,根据上一步拿到的 AppKey 和 AppSecret 获取一个 access_token,这个 access_token 就是钉钉 API 的调用凭证。

后续在调用其他 API 时,只要携带 access_token 即可验证权限。

钉钉 API 分为新版和旧版两个版本,为了兼容性我们使用旧版。旧版 API 的 URL 根路径是https://oapi.dingtalk.com,下文用 baseURL 这个变量替代。

根据文档,获取 access_token 的接口是 ${baseURL}/gettoken。在 utils/ding.js 文件中定义一个获取 token 的方法,使用 GET 请求获取 access_token,代码如下:

const fetchToken = async () => { try { let params = { appkey: 'xxx', appsecret: 'xxx', }; let url = `${baseURL}/gettoken`; let result = await axios.get(url, { params }); if (result.data.errcode != 0) { throw result.data; } else { return result.data; } } catch (error) { console.log(error); } };

上述代码写好之后,就可以调用 fetchToken 函数获取 access_token 了。

获取到 access_token 之后需要持久化的存储起来供后续使用。在浏览器端,我们可以保存在 localStorage 中,而在 Node.js 端,最简单的方法是直接保存在文件中。

写一个将 access_token 保存为文件,并且可读取的类,代码如下:

var fs = require('fs'); var path = require('path'); var catch_dir = path.resolve(__dirname, '../', 'catch'); class DingToken { get() { let res = fs.readFileSync(`${catch_dir}/ding_token.json`); return res.toString() || null; } set(token) { fs.writeFileSync(`${catch_dir}/ding_token.json`, token); } }

写好之后,现在我们获取 access_token 并存储:

var res = await fetchToken(); if (res) { new DingToken().set(res.access_token); }

在下面的接口调用时,就可以通过 new DingToken().get() 来获取到 access_token 了。

2. 查找组员 ID

有了 access_token 之后,第一个调用的钉钉 API 是获取员工的 userid。userid 是员工在钉钉中的唯一标识。

有了 userid 之后,我们才可以获取组员对应的打卡状态。最简单的方法是通过手机号获取员工的 userid,手机号可以直接在钉钉上查到。

根据手机号查询用户文档:https://open.dingtalk.com/document/orgapp-server/query-users-by-phone-number

接口调用代码如下:

let access_token = new DingToken().get(); let params = { access_token, }; axios .post( `${baseURL}/topapi/v2/user/getbymobile`, { mobile: 'xxx', // 用户手机号 }, { params }, ) .then((res) => { console.log(res); });

通过上面请求方法,逐个获取所有组员的 userid 并保存下来,我们在下一步使用。

3. 获取打卡状态

拿到组员的 userid 列表,我们就可以获取所有组员的打卡状态了。

钉钉获取打卡状态,需要在 H5 应用中申请权限。打开前面创建的应用,点击【权限管理 -> 考勤】,批量添加所有权限:

天天p图无法打开照片,我开发了一款小工具(4)

接着进入【开发管理】,配置一下服务器出口 IP。这个 IP 指的是我们调用钉钉 API 的服务器 IP 地址,开发的时候可以填为 127.0.0.1,部署后更换为真实的 IP 地址。

做好这些准备工作,我们就可以获取打卡状态了。获取打卡状态的 API 如下:

API 地址:${baseURL}/attendance/list
请求方法:POST

这个 API 的请求体是一个对象,对象必须包含的属性如下:

这里的字段解释一下。workDateFrom 和 workDateTo 表示查询考勤的时间范围,因为我们只需要查询当天的数据,因此事件范围就是当天的 0 点到 24 点。

userIdList 就是我们上一步取到的所有组员的 userid 列表。

将获取打卡状态写为一个单独的方法,代码如下:

const dayjs = require('dayjs'); const access_token = new DingToken().get(); // 获取打卡状态 const getAttendStatus = (userIdList) => { let params = { access_token, }; let body = { workDateFrom: dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'), workDateTo: dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss'), userIdList, // userid 列表 offset: 0, limit: 40, }; return axios.post(`${baseURL}/attendance/list`, body, { params }); };

查询考勤状态的返回结果是一个列表,列表项的键字段如下:

其他更多字段的含义请参考:https://open.dingtalk.com/document/orgapp-server/open-attendance-clock-in-data

上面的 4 个字段可以轻松判断出谁应该打卡,打卡是否正常,这样我们就能筛选出没有打卡的用户,对这些未打卡的用户精准提醒。

筛选打卡状态分为两种情况:

上下班打卡要筛选不同的返回数据。假设获取的打卡数据存储在变量 attendList 中,获取方式如下:

// 获取上班打卡记录 const getOnUids = () => attendList .filter((row) => row.checkType == 'OnDuty') .map((row) => row.userId); // 获取下班打卡记录 const getOffUids = () => attendList .filter((row) => row.checkType == 'OffDut') .map((row) => row.userId);

获取到已打卡的用户,接着找到未打卡用户,就可以发送通知提醒了。

4. 发送提醒通知

在钉钉中最常用的消息推送方式是:在群聊中添加一个机器人,向这个机器人的 webhook 地址发送消息,即可实现自定义推送。

还是进入前面创建的 H5 应用,在菜单中找到【应用功能 -> 消息推送 -> 机器人】,根据提示配置好机器人。

天天p图无法打开照片,我开发了一款小工具(5)

创建好机器人后,打开组员所在的钉钉群(已有群或新建群都可)。点击【群设置 -> 智能群助手 -> 添加机器人】,选择刚才创建的机器人,就可以将机器人绑定在群里了。

天天p图无法打开照片,我开发了一款小工具(6)

绑定机器人后,点击机器人设置,会看到一个 Webhook 地址,请求这个地址即可向群聊发送消息。对应的 API 如下:

API 地址:${baseURL}/robot/send?access_token=xxx
请求方法:POST

现在发送一条“我是打卡机器人”,实现代码如下:

const sendNotify = (msg, atuids = []) => { let access_token = 'xxx'; // Webhook 地址上的 access_token // 消息模版配置 let infos = { msgtype: 'text', text: { content: msg, }, at: { atUserIds: atuids, }, }; // API 发送消息 axios.post(`${baseURL}/robot/send`, infos, { params: { access_token }, }); }; sendNotify('我是打卡机器人');

解释一下:代码中的 atUserIds 属性表示要 @ 的用户,它的值是一个 userid 数组,可以 @ 群里的某几个成员,这样消息推送就会更精准。

发送之后会在钉钉群收到消息,效果如下:

天天p图无法打开照片,我开发了一款小工具(7)

综合代码实现

前面几步创建了钉钉应用,获取了打卡状态,并用机器人发送了群通知。现在将这些功能结合起来,写一个检查考勤状态,并对未打卡用户发送提醒的接口。

在路由文件 router/ding.js 中创建一个路由方法实现这个功能:

var dayjs = require('dayjs'); router.post('/attend-send', async (req, res, next) => { try { // 需要检测打卡的 userid 数组 let alluids = ["xxx", "xxxx"]; // 获取打卡状态 let attendList = await getAttendStatus(alluids); // 是否9点前(上班时间) let isOnDuty = dayjs().isBefore(dayjs().hour(9).minute(0)); // 是否18点后(下班时间) let isOffDuty = dayjs().isAfter(dayjs().hour(18).minute(0)); if (isOnDuty) { // 已打卡用户 let uids = getOnUids(attendList); if (alluids.length > uids.length) { // 未打卡用户 let txuids = alluids.filter((r) => !uids.includes(r)); sendNotify("上班没打卡,小心扣钱!", txuids); } } else if (isOffDuty) { // 已打卡用户 let uids = getOffUids(attendList); if (alluids.length > uids.length) { // 未打卡用户 let txuids = alluids.filter((r) => !uids.includes(r)); sendNotify("下班没打卡,小心扣钱!", txuids); } } else { return res.send("不在打卡时间"); } res.send("没有未打卡的同学"); } catch (error) { res.status(error.status || 500).send(error); } });

上述接口写好之后,我们只需要调用一下这个接口,就能实现自动检测上班或下班的打卡情况。如果有未打卡的组员,那么机器人会在群里发通知提醒,并且 @ 未打卡的组员。

# 调用接口
$ curl -X POST http://localhost:8080/ding/attend-send

检查打卡状态并提醒的功能实现了,现在还差一个”循环提醒“功能。

循环提醒的实现思路是,在某个时间段内,每隔几分钟调用一次接口。如果检测到未打卡的状态,就会循环提醒。

假设上下班时间分别是上午 9 点和下午 18 点,那么检测的时间段可以划分为:

上班打卡相对比较紧急,所以时间检测短,频率高。下班打卡相对比较宽松,下班时间也不固定,因此检测时间长,频率低一些。

确定好检测规则之后,我们使用 Linux 的定时任务 crontab 来实现上述功能。

首先将上面写好的 Node.js 代码部署到 Linux 服务器,部署后可在 Linux 内部调用接口。

crontab 配置解析

简单说一下 crontab 定时任务如何配置。它的配置方式是一行一个任务,每行的配置字段如下:

// 分别表示:分钟、小时、天、月、周、要执行的命令
minute hour day month weekday cmd

每个字段用具体的数字表示,如果要全部匹配,则用 * 表示。上班打卡检测的配置如下:

29-59/5 8 * * 1-5 curl -X POST http://localhost:8080/ding/attend-send

上面的 29-59/5 8 表示在 8:29 到 8:59 之间,每 5 分钟执行一次;1-5 表示周一到周五,这样就配置好了。

同样的道理,下班打卡检测的配置如下:

*/10 18-19 * * 1-5 curl -X POST http://localhost:8080/ding/attend-send

在 Linux 中执行 crontab -e 打开编辑页面,写入上面的两个配置并保存,然后查看是否生效:

$ crontab -l
29-59/5 8 * * 1-5 curl -X POST http://localhost:8080/ding/attend-send
*/10 18-19 * * 1-5 curl -X POST http://localhost:8080/ding/attend-send

看到上述输出,表示定时任务创建成功。

现在每天上班前和下班后,小工具会自动检测组员的打卡状态并循环提醒。最终效果如下:

天天p图无法打开照片,我开发了一款小工具(8)

总结

这个小工具是基于钉钉 API Node.js 实现,思路比较有意思,解决了实际问题。并且这个小项目非常适合学习 Node.js,代码精简干净,易于理解和阅读。

,
图文教程
相关文章
热门专题
推荐软件
奇热小说
奇热小说
下载
QQ2019手机版
QQ2019手机版
下载
王者荣耀
王者荣耀
下载
百度浏览器迷你版
百度浏览器迷你版
下载
2345浏览器手机版
2345浏览器手机版
下载
网易邮箱
网易邮箱
下载
爱奇艺
爱奇艺
下载
网易云音乐
网易云音乐
下载
WPSOffice
WPSOffice
下载
优酷
优酷
下载
谷歌浏览器(Chrome)
谷歌浏览器(Chrome)
下载
迅雷看看播放器
迅雷看看播放器
下载
UC浏览器
UC浏览器
下载
QQ音乐
QQ音乐
下载
阿里旺旺买家版v9.12.10C官方版
阿里旺旺买家版v9.12.10C官方版
下载
360安全卫士v12.1官方版
360安全卫士v12.1官方版
下载
猜你喜欢
和平战警
和平战警
下载
全科医学高级职称
全科医学高级职称
下载
MesaExifMac版V1.5.1
MesaExifMac版V1.5.1
下载
速蜂智融
速蜂智融
下载
仁王2女天狗的服装MOD
仁王2女天狗的服装MOD
下载
生化危机0HD重制版瑞贝卡商务短裙mod
生化危机0HD重制版瑞贝卡商务短裙mod
下载
逐鹿天下
逐鹿天下
下载
太空大胡子TV版v1.0
太空大胡子TV版v1.0
下载
青芒果旅行
青芒果旅行
下载
淘优惠
淘优惠
下载
视大大
视大大
下载
甲克虫
甲克虫
下载
FZ9时空飞梭电脑版
FZ9时空飞梭电脑版
下载
时光老照片修复
时光老照片修复
下载
我的世界阿斯兰萨拉皮肤
我的世界阿斯兰萨拉皮肤
下载
iKeyboardMac版V1.0.6
iKeyboardMac版V1.0.6
下载