效率提升-动态提醒工具

77 阅读2分钟

背景

  动态提醒也是来源于我自己的需求,其实现在手机上或者网上类似的产品和工具很多。但是基于我一些特殊场景的需求,所以我也做了工具,支持动态提醒,支持设置复杂Cron表达式。

提醒方式

技术介绍

消息提醒器

  消息提醒是采用了Windows或者Mac自带的Toast提醒,原来也是准备自己重新搞个提醒页面,后面分析了自己的需求,发现没这个必要,所以也就没有自己去弄了。

采用了 node-notifier

"node-notifier": "^10.0.1",
//使用也比较简单,直接封装一下就行了
const notifier = require("node-notifier");
/**
 * 消息提醒
 * @param data
 */
function show(data) {
    const {title, message} = data;
    // noinspection JSUnusedLocalSymbols
    notifier.notify(
        {
            title,
            message,
            icon: 'static/info.png',
        },
        (err) => {
            if (err) {
                console.log("error:", err);
            }
        }
    );
}

export {
    show
}

这里就是打包的时候有一个小坑,需要把引用单独打出来,不然实际运行会有问题

package.json

"asarUnpack": [
    "./node_modules/node-notifier/vendor/**"
],

提醒内容定义(Cron表达式)

提醒内容的定义比较容易(最终数据也都是存在云端的),重点就是对Cron表达式的处理

其中执行一次,每天,每周选择以后,都会根据当前时间(获取手动调整时间以后),自动生成cron表达式

//执行一次自动生成cron表达式
setOnceTimeCron() {
    const value = this.onceTime;
    this.minderInfo.cron = `${value.seconds()} ${value.minutes()} ${value.hour()} ${value.date()} ${value.month() + 1} ? ${value.year()}`
},
//每天自动生成表达式
setPerDayTimeCron() {
    const value = this.perDayTime;
    this.minderInfo.cron = `${value.seconds()} ${value.minutes()} ${value.hour()} * * ? *`
},
//每周自动生成表达式
setPerWeekTimeCron() {
    const timeValue = this.perWeekTime;
    const weekList = this.weekList;
    weekList.sort((a, b) => {
    return a - b;
    })
    let weekValue = "?";
    if (weekList.length > 0) {
    weekValue = weekList.join(",");
    }
    this.minderInfo.cron = `${timeValue.seconds()} ${timeValue.minutes()} ${timeValue.hour()} * * ${weekValue} *`
},

更复杂的场景,就是手动输入Cron表达式,或者接入Cron表达式生成器去生成。

通过选择时分秒,年月日,周,动态组合生成表达式。

代码逻辑不是太复杂,就是整个功能开发比较繁琐,那时候也系统的学习了下Cron表达式,后面手写就更快了。布局样式整体参考互联网上的样式。

执行计划预览

表达式写入成功以后,可以预览一下表达式,看能不能达到效果。

yarn add cron-parser
import CronParser from "cron-parser";
getExecutePlan(){
    // 去掉年份参数
    const e = this.cron
    // console.info('>>>>>>' + e)
    const format = 'yyyy-MM-dd hh:mm:ss'
    const options = {
        currentDate: dateFormat(this.startTime, format)
    }
    const result = []
    try{
        const iter = CronParser.parseExpression(e, options)
        for (let i = 0; i < 50; i++) {
            result.push((i+1) + "、"+dateFormat(new Date(iter.next()), format))
        }
        this.preTimeList = result.length > 0 ? result.join('\n') : '无执行时间'
    }
    catch (e){
        for (let i = 0; i < 50; i++) {
            result.push("无法解析表达式")
        }
        this.preTimeList = result.join('\n')
        console.log("当前表达式无法解析")
    }
}

定时程序处理

  这个工具最重要的环节,就是要把Cron表达式转换成真正的任务计划,因为考虑到工具一打开就要自动开发计算和执行定时任务(这时候页面可能还没打开),所以把定时任务的处理放在主进程里面。

yarn add  node-schedule
const schedule = require('node-schedule')
const notify  = require("./notify")
/**
 * 程序一加载的时候,就会获取所有的定时任务信息,然后循环解析cron表达式,并创建任务执行
 * 界面上如果对定时任务进行了操作,也会触发
 * 界面新增定时任务,则这里同步建立执行计划
 * 界面修改了定时任务,则这里重置执行计划
 * 界面删除了定时任务,则这里也会删除执行计划
 * 界面上还有停止和启用定时任务的功能,这里也会同步创建和删除
 */
function execute(data,log){
    for (let i = 0; i < data.length; i++) {
        const object = data[i];
        const haveSchedule = schedule.scheduledJobs[object.id];
        //先删除定时器,然后重新开始
        if (haveSchedule) {
            if(object.status===1){
                log.info(`${object.id}-${object.title}定时器被重置了!`);
                schedule.rescheduleJob(haveSchedule, object.cron)
            }
            else{
                log.info(`${object.id}-${object.title}定时器被重置(删除)了!`);
                schedule.cancelJob(haveSchedule)
            }
        }
        else{
            try {
                if(object.status===1){
                    schedule.scheduleJob(object.id, object.cron, () => {
                        log.info(`开始任务调度了${object.id}-${object.title}`);
                        notify.show({
                            title: object.title,
                            message: object.content
                        })
                    });
                    log.info(`创建调度任务${object.id}-${object.title}`);
                }
                else{
                    log.info(`当前任务已被注销${object.id}-${object.title}`);
                }

            } catch (e) {
                log.info(`创建调度失败${object.id}-${object.title}`);
                log.error(e)
            }
        }
    }

    for(let job in schedule.scheduledJobs){
        //判断如果 除job
        const tempJob = data.filter(p=>p.id===job);
        if(!tempJob || tempJob.length===0){
            schedule.cancelJob(job);
        }
    }

}

export {
    execute
}

总结

  动态提醒给我帮助还是挺大的,很复杂的定时方式都是支持的,提醒的也比较直接。原来是准备接入短信提醒的(云平台有提供每年5000条的免费短信额度),后来发现也有点多余,就一直没加上去,后续有需要可以再加。