我用 AI,一天就把一个桌面提醒插件撸完了

我用 AI,一天就把一个桌面提醒插件撸完了

这个插件是基于utools平台的, 标题虽然有点夸张,但在 AI 的加持下,这个插件的 MVP 版本,确实是我和 ChatGPT 搭档一天撸出来的, 感兴趣的可以看看哦


一、先说说 uTools:一个很适合"折腾"的效率神器

如果你还没用过 uTools,可以简单理解为:

一个跨平台的效率启动器 / 快速搜索工具,

再加上一整套插件生态 + 开发者支持。

具体可以看看官网:uTools 是一种高效工作方式,AI 时代的轻工具平台

它有几个特点特别适合做这种"小而精"的工具:

  • 跨平台:Windows / macOS / Linux 都能跑;
  • 插件机制:很多能力(窗口、托盘、系统通知等)都可以通过插件调用;
  • 唤起方便:全局快捷键一呼即出,非常适合"查一下、记一下、记个提醒"这种场景;
  • 开发门槛低:前端就是一套网页技术栈(我用的是 Vue),后端你爱怎么写怎么写。

对我这种常年写 Java 的后端来说,用 uTools 做一个贴合自己使用习惯的"小插件",成了一个很自然的选择。


二、为什么要做一个桌面提醒插件?

市面上提醒 / 待办类应用已经很多了,但一直有几个痛点让我很难坚持用下去:

  1. 提醒不在"当前设备"上

    我工作时间基本都盯着电脑,手机的闹钟提醒太重了,也容易影响到同事,而插件提醒就是一个弹窗,随手一点就能关闭,很轻量,不扰人。

  2. 重复规则太简单

    很多应用只支持:每天 / 每周几 / 每月几号。

    但我经常需要一些更"奇怪"的规则,比如:

    • 每月 1、15、28 号各提醒一次;
    • 每天 09:00、14:00、21:30 都要提醒;
    • 工作日 09:00--18:00,每隔 45 分钟提醒喝水。
  3. 多渠道通知不好用

    有些事情我是真的不能错过,希望:

    桌面弹窗 + 系统通知 + 企业微信 / 钉钉

    能一起推送一遍,有了站外推送, 即便不在电脑前,或者插件退出了,也能收到提醒。


三、插件长什么样?先上效果 & 功能预览

  • 1:主界面 + 提醒列表
  • 2:新增 / 编辑提醒表单

    • 标题、内容
    • 时间设置(首个时间 + 多个额外时间点)
    • 重复规则(每天 / 每周 / 每月 / 每年 / 自定义)
    • 时间段循环提醒:起止时间 + 间隔分钟数
  • 3:通知设置 + 多渠道配置
  • 4:提醒效果

弹窗提醒+系统提醒

当前插件已经支持的功能

简单列一下目前已经实现的功能:

  • 各种重复类型

    • 不重复 / 每天 / 每周(任意组合周几)
    • 每月 / 每年(支持任意日期)
    • 自定义模式(用药提醒、纪念日等)
  • 多时间点提醒

    • 每天可以设置多个时间点:
      比如 09:00 / 14:00 / 21:30 各提醒一次
  • 时间段循环提醒

    • 设置「时间段 + 间隔」,例如:

      • 每天 09:00--18:00,每隔 45 分钟提醒一次
  • Cron 表达式

    • 给自己留了一个"高级玩法入口"
  • AI 一句话创建提醒

    • 比如:"明天下午三点提醒我交电费",后端解析出结构化提醒数据
  • 多渠道通知

    • 桌面弹窗(即使插件窗口隐藏)
    • 系统通知
    • Bark 推送
    • 企业微信机器人
    • 钉钉机器人
  • 后端统一存储

    • 提醒记录保存在后端,天然支持多平台同步(同一账号,多个设备一起用)

四、整体实现

主要分三块:

  1. uTools 插件前端(Vue)

    • 负责配置 / 展示 / 弹窗
    • 通过 HTTP + WebSocket 和后端交互
  2. Spring Boot 后端

    • 负责存储提醒、计算下一次执行时间、触发提醒
  3. 通知适配层

    • 把"提醒事件"统一分发到不同渠道:Bark、企微、钉钉、桌面弹窗、系统通知......

五、后端实现

5.1 表结构设计:为"复杂重复规则"留好余地

提醒系统的本质是一个"带规则的定时任务系统"。

我给每条提醒设计了大致这样的结构(简化版):

sql 复制代码
CREATE TABLE t_user_reminder (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL COMMENT '用户ID',
    template_id BIGINT DEFAULT NULL COMMENT '引用的模板ID,可为空',

    title VARCHAR(128) NOT NULL COMMENT '提醒标题',
    content TEXT COMMENT '提醒内容',

    remind_time DATETIME NOT NULL COMMENT '下一次提醒时间',

    repeat_rule VARCHAR(64) DEFAULT NULL COMMENT 'NONE, DAILY, WEEKLY, MONTHLY, YEARLY, WORKDAY, CUSTOM',
    custom_mode VARCHAR(64) DEFAULT NULL COMMENT 'WEEKLY, YEARLY, MEDICINE, ANNIVERSARY 等',

    repeat_interval INT NOT NULL DEFAULT 1 COMMENT '间隔数,如每2天/每2周',
    repeat_weekdays SET('1','2','3','4','5','6','7') NULL COMMENT '每周的星期几',
    repeat_month_days VARCHAR(256) NULL COMMENT '每月的哪几天,逗号分隔',

    status TINYINT NOT NULL DEFAULT 1 COMMENT '1=启用 0=关闭 2=已结束',

    create_time DATETIME NOT NULL,
    update_time DATETIME NOT NULL
);

几个关键点:

  • repeat_rule + custom_mode 用来描述规则类型;
  • repeat_weekdays / repeat_month_days 支持"一条记录多个日期";
  • remind_time 始终存"下一次执行时间",触发一次就往后推一次。

调度时的伪逻辑:

  1. 定时任务定期扫描 remind_time <= now() 且启用的记录;
  2. 推送通知;
  3. 根据规则计算下一次 remind_time,更新回去;
  4. 如果已经没有下一次了(比如单次提醒已过期),就把 status 改为已结束。

5.2 Cron 表达式 & next 计算

为了给自己留个"高级玩法",我加了 Cron 表达式的支持。

现实问题:

用户可能写 5 段,也可能写 6 段(带秒),还可能是带 ? 的那种。

处理方式:

  1. 后端接收时先做规范化
    缺秒就补秒,缺 ? 就按默认规则填;
  2. 统一交给一个 cron.next(baseTime) 来算下一次执行时间。

baseTime 的选择上,我最后简化成这样:

ini 复制代码
LocalDateTime base = LocalDateTime.now();

if (!isForSchedule 
    && remind.getCronStartTime() != null 
    && base.isBefore(remind.getCronStartTime())) {
    base = remind.getCronStartTime();
}

LocalDateTime next = cron.next(base);
  • 用于"预览"下次执行时间时,可能从 cronStartTime 算;
  • 用于"调度"时,就是简单的 now()

这一块我其实是和 AI 来回沟通了几轮,让它帮我重构逻辑、校验边界条件,省了不少脑细胞。

5.3 AI 一句话创建提醒 & Redis 限流

我给自己加了一个很爽的小功能:

在输入框里直接敲一句话:

"明天下午三点提醒我交电费",

后端调用 AI 解析出时间、标题、备注,直接生成提醒。

为了防止被恶意刷接口,用 Redis 做了一个非常简单的按天限流

ini 复制代码
public boolean checkAiLimit(Long userId) {
    String key = "ai_time_" + userId;

    Long count = redisTemplate.opsForValue().increment(key);
    if (count != null && count == 1L) {
        long seconds = calcSecondsUntilEndOfDay();
        redisTemplate.expire(key, Duration.ofSeconds(seconds));
    }

    return count != null && count <= MAX_TIMES_PER_DAY;
}
  • key 不带日期,过期时间设为当天剩余秒数;
  • 第一次调用时设置失效时间,之后全部简单 increment
  • 超过阈值直接拒绝,给出友好提示。

这段逻辑的方案设计,其实也是我先把想法和约束丢给 AI,让它帮我写完,再自己做一轮 review。


六、前端实现:复杂表单 + WebSocket + 独立提醒窗口

前端整体基于 Vue,核心页面包括:

  • 提醒列表
  • 新增 / 编辑提醒
  • 即将提醒预览
  • 通道配置弹窗
  • 独立提醒弹窗窗口

6.1 复杂时间 / 重复设置表单

"新增提醒"这个表单是整个插件 UI 里最复杂的一块,要处理:

  • 首次提醒时间(日期 + 时间)
  • 多个额外时间点 extraTimes(仅时间)
  • 重复类型:不重复 / 每天 / 每周 / 每月 / 每年 / 自定义
  • 自定义下的子模式:用药、纪念日等
  • 时间段循环提醒:起止时间 + 间隔分钟

展示多时间点时,我搞了一个简化版的展示字段 displayTimesText

kotlin 复制代码
const displayTimesText = computed(() => {
  const [date, time] = extractDateAndTimeFromFirstDateTime();
  const times = [time, ...form.extraTimes.filter(Boolean)].filter(Boolean);
  if (!times.length) return "未设置时间";
  return times.join("、");
});

开发过程中,AI 在这里帮我做了很多琐碎事情,比如:

  • 把"用户需求描述"翻译成结构化的 form 字段设计;
  • 帮我生成初版的组件模板和样式;
  • 排查 input[type="time"] 在不同浏览器下的一些小兼容问题;
  • 优化滚动布局,解决双滚动条、按钮抖动等细节问题。

6.2 WebSocket 长连接 & 独立提醒窗口

为了保证 "人不在插件窗口,提醒也能到桌面" ,我前端用了 WebSocket + 独立提醒窗口的方案。

基本流程:

  1. 插件启动时,与后端建立 WebSocket 连接;

  2. 后端一旦有提醒触发,就推送一条消息;

  3. 前端收到消息后:

    • 如果主窗口在前台,可以直接弹页面内弹窗;
    • 如果主窗口隐藏,则使用 uTools 能力打开一个独立提醒窗口 remindWindow,显示提醒信息、播放提示音。

伪代码结构类似这样:

ini 复制代码
let ws: WebSocket | null = null;
let heartbeatTimer: number | null = null;
let reconnectTimer: number | null = null;
let reconnectAttempts = 0;
const MAX_RECONNECT_ATTEMPTS = 5;
let remindWindow: any = null;

function initWebSocket() {
  ws = new WebSocket(HTTP_BASE + "/ws/reminder");

  ws.onopen = () => {
    startHeartbeat();
  };

  ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    handleReminder(msg);
  };

  ws.onclose = () => {
    tryReconnect();
  };
}

这里 AI 给我的主要帮助是:

  • 一次性生成了 WebSocket + 心跳 + 重连的模板代码;
  • 帮我梳理清楚"重连最大次数""心跳间隔""关闭连接时清理定时器"等边界情况;
  • 根据我贴出来的报错信息,定位过几次逻辑问题。

6.3 多渠道通知设置 & 提示音试听

在插件设置页里,我提供了几种通知通道配置:

  • Bark URL
  • 企业微信机器人 Webhook
  • 钉钉机器人 Webhook
  • 是否开启桌面弹窗、系统通知
  • 提示音选择 + 一键试听

提示音做法很简单:

  • default.mp3 / soft.mp3 / strong.mp3 放在静态资源目录;
  • 选择不同值时,更新 form.soundName
  • 点击"试听"按钮时,用 <audio> 或 Audio 对象播放当前选择的声音。

这些交互细节基本都是我先丢一句话给 AI:"我有三个提示音文件,如何在选择时可以试听?"

它给我一段完整实现,我再根据项目结构稍微改一下就能用。


七、通知适配层:给未来扩展通道留个口子

后端有一个统一的 NotifyChannel 接口,所有通道都实现它:

java 复制代码
public interface NotifyChannel {

    boolean available(UserSettings settings);

    void send(ReminderMsg msg, UserSettings settings);
}

然后分别有:

  • BarkNotifyChannel
  • WeComRobotNotifyChannel
  • DingTalkRobotNotifyChannel
  • ......(未来要扩的都可以继续加)

总的推送服务大概这样:

scss 复制代码
@Service
public class ReminderPushService {

    @Autowired
    private List<NotifyChannel> notifyChannels;

    public void pushReminder(TUserReminder reminder, UserSettings settings) {
        ReminderMsg msg = buildMsg(reminder);
        for (NotifyChannel channel : notifyChannels) {
            if (channel.available(settings)) {
                channel.send(msg, settings);
            }
        }
    }
}

这种设计没什么炫技的地方,就是老老实实地面向接口编程

但对后面扩展通道非常友好


八、为什么说"我用 AI,一天就把这个插件撸完了"?

标题当然是带一点点"互联网文体"的 😂

准确一点说:

  • 如果让我自己从 0 开始设计、踩坑,这个插件可能要拆成好几天甚至一两周的业余时间

  • 这次我把很多事情都丢给 AI 帮忙:

    • 表结构初稿 & 字段命名建议;
    • Cron 表达式兼容逻辑的实现 & 重构;
    • Redis 限流的方案设计 + 代码;
    • WebSocket + 心跳 + 重连的模板;
    • Vue 表单的初版代码、样式优化建议;
    • 各种细碎 bug:乱码、时间类型转换、滚动条问题......

结果就是:在已经很熟悉的 Java & Vue 基础上,
靠 AI 填了大量机械、重复、查文档的部分
,核心 MVP 确实是一整天搞定的。

我更多地是在做:

  • 需求决策:要不要这个功能?交互怎么做才顺手?
  • 代码 Review:AI 给的代码哪里需要调整?
  • 业务规则定义:这个提醒系统应该怎样才"顺人性"?

AI 更适合作为一个高强度不抱怨的结对编程伙伴,帮我把"我已经知道怎么做,但懒得敲"的那部分快速完成。


九、写在最后

到现在,「灵犀提醒」已经稳定地接管了我日常的很多小事:

  • 每天上班打卡、喝水、写日报;
  • 每月房租、信用卡还款、各种账单;
  • 特定日期的生日、纪念日提醒;
  • 工作上的一些关键 follow-up,会同时打到企业微信 / 钉钉。

这篇文章一方面是想分享下这套 "uTools + Spring Boot + AI 协作"的开发实践

如果你:

  • 也常年对着电脑;
  • 也老是忘东忘西;
  • 也想找个机会实战一下"AI 辅助开发";

那不妨也找个周末,试试做一个属于你的"小插件"。

如果你对这个提醒插件感兴趣,或者想看更细的某一块实现(比如 Cron 核心调度逻辑、WebSocket 服务端实现、AI 解析那一块),可以在评论区告诉我,

如果,你也是个utools使用者, 不妨试用下插件哦

最后

如果文章对你有帮助,点个免费的赞鼓励一下吧!关注公众号:加瓦点灯, 每天推送干货知识!

相关推荐
红色石头本尊1 小时前
27-集成swagger接口文档
后端
嘻哈baby1 小时前
Linux系统性能排查实战指南:从定位到解决
后端
开心就好20251 小时前
使用开心上架上架 App Store,一次跨平台团队的完整发布流程调度记录
后端
南囝coding1 小时前
《独立开发者精选工具》第 023 期
前端·后端·开源
文心快码BaiduComate1 小时前
Agent如何重塑跨角色协作的AI提效新范式
前端·后端·程序员
C***u1762 小时前
Spring Boot 实战篇(四):实现用户登录与注册功能
java·spring boot·后端
Java水解2 小时前
# SpringBoot权限认证-Sa-Token的使用与详解
spring boot·后端
头发那是一根不剩了2 小时前
Spring Boot「多数据源并存」的设计思路,它与动态数据源又有什么区别?
java·spring boot·后端
W***53312 小时前
SpringBoot Maven 项目 pom 中的 plugin 插件用法整理
spring boot·后端·maven