序言
🐒:
起因是我有很多想法、任务、视频等事情要弄,但磨磨唧唧就忘记干啥了,记了备忘录也磨磨唧唧的,然后不了了之了,有的好不容易弄完了。也不记得什么时候开始的,总感觉用了很长时间,总之,乱七八糟的。
1. 项目愿景与设计哲学
1.1 项目愿景
打造一个个人时间感知工具,帮助用户量化任务拖延程度、复盘完成效率,让 “时间去哪了” 变得可视化、可管理。系统不仅记录任务的开始与结束,更通过情绪化标签和多元化视图,唤醒用户对时间流逝的觉知。
1.2 设计哲学(贯穿全文的三大原则)
| 原则 | 含义 | 本项目体现 |
|---|---|---|
| 反脆弱 | 系统能容忍下游故障,出问题可自愈 | 软删除恢复、定期备份、导出快照、操作解耦 |
| 依赖隔离 | 核心业务不依赖外部框架,升级无忧 | 分层架构(Handler → Service → Repository),Service 层无任何框架导入 |
| 设计前置 | 在动手前想清楚边界和异常 | 明确字段流转、统一时间标准、预留扩展字段 |
2. 核心功能需求
2.1 任务生命周期管理
| 功能 | 说明 |
|---|---|
| 新增任务 | 录入名称、开始时间(默认当前)、任务类型(短期/长期) |
| 完成任务 | 记录完成时间(精确到秒),自动计算总耗时(finish_time - start_time) |
| 放弃任务 | 不记录完成时间,需选择放弃原因(如 “三分钟热度”、“计划变更” ) |
| 重新激活 | 将 “已完成” 或 “已放弃” 的任务重置为 “进行中”(清空完成时间) |
| 软删除 | 删除任务进入回收站,可恢复或彻底清空 |
| 编辑任务 | 仅允许修改 名称 |
2.2 任务类型细分
| 类型 | 标识 | 特性 | 周期 |
|---|---|---|---|
| 短期任务 | short | 默认类型。若创建后超过 24 * 7 小时未完成且未放弃,前端展示 “🔥 热度冷却” 提示。 拖延标签沿用通用规则。 | 1 ~ 3 月 |
| 长期任务 | long | 支持关联子任务(里程碑)。主任务进度条由子任务完成比例自动计算。 最终总耗时仍从主任务 start_time 到 finish_time 计算。 | 一年以上 |
2.3 拖延标签系统(通用规则)
针对所有进行中的任务,根据开始时间距当前时间的自然日数动态生成标签(阈值可配置):
| 天数范围 | 标签 |
|---|---|
| < 7 天 | (无) |
| 7 ~ 2 * 7 天 | 你是在偷懒么? |
| 2 * 7 ~ 4 * 7 天 | 别拖延了!行动起来! |
| 4 * 7 ~ 8 * 7 天 | 拖延症犯了吧!! |
| 8 * 7 ~ 12 * 7 天 | 你是不是在口嗨?! |
| 12 * 7 ~ 16 * 7 天 | 再这样下去这任务你就烂尾了! |
| 16 * 7 ~ 24 * 7 天 | 你又画了一个饼... |
| ≥ 24 * 7 天 | 你果然在放屁!!! |
2.4 子任务管理(仅长期任务)
- 支持增删改子任务,标记完成/未完成,标记完成时自动记录完成时间。
- 主任务进度 = (已完成子任务数 / 总子任务数) * 100%。
- 当所有子任务完成时,系统提醒用户完成主任务(不强制自动完成)。
2.5 四种前端视图
| 视图 | 适用场景 | 操作支持 |
|---|---|---|
| 表格/卡片 | 默认视图,适合快速编辑、筛选、批量操作 | 全功能(增删改、完成、放弃) |
| 甘特图 | 直观查看任务时间跨度,暴露 “长期拖延” | 点击条查看详情,不支持拖拽改期 |
| 看板(泳道) | 按状态(进行中/已完成/已放弃)拖拽管理 | 支持拖拽卡片改变状态,拖至 “完成/放弃” 列触发对应接口 |
| 垂直时间轴 | 按开始时间倒序展示,类 “编年体” 叙事 | 展示标签与耗时,侧重阅读,操作按钮折叠至悬停显示 |
2.6 统计看板
- 统计近三个月(从当前月往前推 3 个月)的任务数据。
- 展示指标:总任务数、完成数/率、放弃数/率、进行中数/率。
- 按月分组展示完成率趋势(柱状图/折线图),按任务类型(短期/长期)分别统计完成率。
3. 数据库设计
3.1 主任务表 tasks
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY | 自增主键 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE | 更新时间 |
| deleted_at | DATETIME | NULL | 软删除时间 (GORM 软删除支持) |
| name | VARCHAR(255) | NOT NULL | 任务名称 |
| type | VARCHAR(20) | NOT NULL DEFAULT 'short' | short / long |
| start_time | DATETIME | NOT NULL | 开始时间 |
| finish_time | DATETIME | NULL | 完成时间 (仅 status = completed 时有值) |
| status | VARCHAR(20) | NOT NULL DEFAULT 'active' | active / completed / abandoned |
| progress | INT | DEFAULT 0 | 0 ~1 00,仅长期任务有意义,由子任务自动计算 |
| abandon_reason | VARCHAR(50) | NULL | 放弃原因 (如 “三分钟热度” 、“计划变更”) |
索引:
status,start_time,deleted_at
3.2 子任务表 subtasks
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY | 自增主键 |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE | 更新时间 |
| task_id | INTEGER | NOT NULL, FOREIGN KEY | 关联 tasks(id),级联删除 |
| name | VARCHAR(255) | NOT NULL | 子任务名称 |
| done | BOOLEAN | DEFAULT FALSE | 是否完成 |
| done_time | DATETIME | NULL | 完成时间 (标记完成时自动写入) |
| order | INT | DEFAULT 0 | 排序序号 |
索引:
task_id,order
4. 后端架构设计(Go + Gin + GORM)
4.1 分层架构与依赖隔离
┌─────────────────────────────────────────────────────────┐
│ 外部依赖(Gin / GORM / 第三方库) │
└─────────────────────────────────────────────────────────┘
⬇
┌─────────────────────────────────────────────────────────┐
│ Handler 层(internal/handler) │
│ - 唯一导入 Gin 的地方 │
│ - 解析请求参数,调用 Service │
│ - 绝不将 gin.Context 传到下层 │
└─────────────────────────────────────────────────────────┘
⬇(传递普通结构体)
┌─────────────────────────────────────────────────────────┐
│ Service 层(internal/service) │
│ - 纯 Go 业务逻辑,无任何框架导入 │
│ - 计算耗时、生成拖延标签、进度重算 │
│ - 依赖 Repository 接口(面向接口编程) │
└─────────────────────────────────────────────────────────┘
⬇(调用接口)
┌─────────────────────────────────────────────────────────┐
│ Repository 接口(定义在 service 包) │
│ - 如:TaskRepository 接口 │
└─────────────────────────────────────────────────────────┘
⬇
┌─────────────────────────────────────────────────────────┐
│ Repository 实现(internal/repository) │
│ - 唯一导入 GORM 的地方 │
│ - 实现 service 层定义的接口 │
└─────────────────────────────────────────────────────────┘
隔离收益:当 Go 版本升级、Gin 或 GORM 升级时,受影响的范围被限制在最外层,核心业务逻辑(
service层)无需修改。
4.2 目录结构
cmd/server/main.go # 入口(含 defer 自动备份)
internal/
├── config/ # 配置加载(Viper)
├── handler/ # 控制器层
│ ├── task.go # 任务 CRUD + 完成/放弃/激活
│ ├── subtask.go # 子任务管理
│ ├── stats.go # 统计接口
│ └── export.go # 导出接口(JSON / CSV / HTML)
├── service/ # 业务逻辑层(纯 Go,无框架依赖)
│ ├── task.go
│ ├── subtask.go
│ ├── stats.go
│ └── repository.go # Repository 接口定义在这里
├── repository/ # 数据访问层(GORM 实现)
│ └── task_repo.go
├── model/ # 数据模型(GORM 标签)
│ ├── task.go
│ └── subtask.go
└── pkg/ # 独立工具包
├── timeutil/ # 时间格式化、解析(隔离 time 包变化)
├── delaytag/ # 拖延标签生成(阈值可配置)
└── backup/ # 数据库备份/恢复工具
migrations/ # 迁移脚本
config.yaml # 配置文件
4.3 API 接口清单
任务基础接口
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/tasks | 任务列表 Query: status, type, all = true(忽略分页),page, pageSize |
| POST | /api/v1/tasks | 创建任务 (Body 含 name, start_time, type, expected_years可选) |
| PUT | /api/v1/tasks/:id | 编辑任务 (仅允许修改 name start_time) |
| PATCH | /api/v1/tasks/:id/complete | 完成任务 (Body 可含 finish_time,默认当前时间) |
| PATCH | /api/v1/tasks/:id/abandon | 放弃任务 (Body 可含 abandon_reason) |
| PATCH | /api/v1/tasks/:id/reactivate | 重新激活任务 (statu s→ active, finish_time → null) |
| DELETE | /api/v1/tasks/:id | 软删除任务 (移入回收站) |
| DELETE | /api/v1/tasks/:id/permanent | 彻底删除 (物理删除) |
| GET | /api/v1/tasks/trash | 获取回收站列表 (deleted_at IS NOT NULL) |
| PATCH | /api/v1/tasks/:id/restore | 从回收站恢复任务 (deleted_at → null) |
子任务接口
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/tasks/:id/subtasks | 获取子任务列表 |
| POST | /api/v1/tasks/:id/subtasks | 新增子任务 |
| PUT | /api/v1/subtasks/:sub_id | 修改子任务 |
| PATCH | /api/v1/subtasks/:sub_id/done | 标记完成/未完成 (Body : {"done": true}) |
| DELETE | /api/v1/subtasks/:sub_id | 删除子任务 |
导出接口(数据安全 + 分享)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/export?format=json | 导出完整 JSON(含子任务嵌套), 用于迁移/恢复 |
| GET | /api/v1/export?format=csv | 导出 ZIP 包(内含主任务表和子任务表两个 CSV) |
| GET | /api/v1/export?format=report | 导出 HTML 可视化报告(含图表) |
统计接口
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/stats/completion | 近三月完成率,按月分组,含类型细分 |
5. 关键业务逻辑实现要点
5.1 软删除与恢复
// 软删除(移入回收站)
db.Delete(&task) // GORM 自动设置 deleted_at
// 恢复(从回收站恢复)
db.Model(&task).Update("deleted_at", nil)
// 彻底删除
db.Unscoped().Delete(&task)
5.2 进度自动计算(长期任务)
在子任务的
Create/Update/Delete及done状态变更时,事务内触发主任务进度重算:progress = (done_count / total_count) * 100若
done_count == total_count && total_count > 0,系统发送提醒(不自动完成)。
5.3 定时备份(反脆弱设计)
- 启动时:利用
defer在程序优雅退出时自动备份。- 每日凌晨 3 点:自动将
tasks.db复制到backup/目录,文件名带时间戳。- 保留最近 30 个备份,自动清理过期文件。
5.4 导出格式设计
| 格式 | 结构 | 用途 |
|---|---|---|
| JSON | 嵌套结构 (主任务 → 子任务数组) | 数据迁移、完整恢复 |
| CSV | 两个表 (主任务表 + 子任务表),打包为 ZIP | Excel 分析、分享给非技术人员 |
| HTML | 自包含可视化报告 ( CSS + ECharts 渲染为图片或纯 CSS ) | 分享给只看结论的人 |
5.5 依赖隔离检查清单
-
service层无import "gorm.io/gorm" -
service层无import "github.com/gin-gonic/gin" -
所有
time.Now()调用统一通过pkg/timeutil包调用 -
Repository接口定义在service包,实现在repository包
6. 前端设计方案(Vue 3 + Element Plus)
6.1 页面布局
┌──────────────────────────────────────────────────────────┐
│ 🔹 任务周期记录 [+ 添加任务] 📊 统计看板 │
│ 总任务: 45 完成率: 33% 🔥 拖延中: 12 │
├──────────────────────────────────────────────────────────┤
│ [📋表格] [📊甘特图] [📌看板] [⏳时间轴] 筛选: [状态▼] │
├──────────────────────────────────────────────────────────┤
│ │
│ 当前视图内容区域 │
│ │
└──────────────────────────────────────────────────────────┘
6.2 四个视图的具体实现
| 视图 | 技术方案 | 核心交互 |
|---|---|---|
| 表格 | el-table | 行操作按钮,长期任务显示进度条,短期任务显示拖延标签 |
| 甘特图 | ECharts 自定义条形图 | X 轴为时间,Y 轴为任务名,进行中的任务条右端延伸到当前时间 |
| 看板 | vue-draggable 三列容器 | 拖拽到 “完成/放弃” 列时调用对应接口,支持从已完成拖回进行中 |
| 垂直时间轴 | el-timeline | 按开始时间倒序排列,操作按钮折叠进 el-dropdown |
6.3 状态管理(Pinia)
store = {
taskList: [],
currentView: 'table', // table | gantt | board | timeline
filters: { status: '', type: '', keyword: '' },
stats: { total, completed_rate, monthly: [] }
}
7. 数据安全与备份策略
7.1 三层防御体系
| 层级 | 方案 | 恢复手段 |
|---|---|---|
| L1 误删防御 | GORM 软删除 ( deleted_at) | 前端回收站一键恢复 |
| L2 损坏防御 | 定时自动备份 (每日凌晨 + 程序退出时) | 替换数据库文件重启 |
| L3 终极防御 | 手动导出 JSON / CSV ( /export 接口) | 导入重建所有数据 |
7.2 备份文件管理
- 备份目录:
./backups/ - 命名规则:
tasks_backup_20260727_030000.db - 保留策略:仅保留最近 30 个备份,自动清理
8. 配置文件示例(config.yaml)
server:
port: 8080
database:
driver: sqlite
dsn: "./data/tasks.db"
backup:
enabled: true
interval_hours: 24 # 备份间隔
retain_days: 30 # 保留天数
path: "./backups/"
delay_tags:
- days: 7
tag: ""
- days: 14
tag: "你是在偷懒么?"
- days: 28
tag: "别拖延了!行动起来!"
- days: 56
tag: "拖延症犯了吧!!"
- days: 84
tag: "你是不是在口嗨?!"
- days: 112
tag: "再这样下去这任务你就烂尾了!"
- days: 168
tag: "你又画了一个饼..."
- days: 999
tag: "你果然在放屁!!!"
export:
max_tasks: 10000 # 导出最大任务数限制
9. 开发排期(总计约 24 个工作日)
| 阶段 | 内容 | 工时 |
|---|---|---|
| 1 | 需求梳理、数据库设计、配置规范 | 1 天 |
| 2 | 后端基础 CRUD + 完成/放弃/激活接口 | 2 天 |
| 3 | 子任务模块 + 进度自动计算逻辑 | 2 天 |
| 4 | 拖延标签 + 统计接口 | 1 天 |
| 5 | 软删除 + 回收站接口 | 1 天 |
| 6 | 备份模块 + 导出接口( JSON / CSV / HTML ) | 2 天 |
| 7 | 前端脚手架、路由、Pinia 状态 | 2 天 |
| 8 | 表格视图 + 增删改查弹窗 | 2 天 |
| 9 | 甘特图视图(ECharts) | 2 天 |
| 10 | 看板视图(拖拽交互) | 2 天 |
| 11 | 时间轴视图 + 统计看板图表 | 2 天 |
| 12 | 回收站界面 + 导出功能联调 | 2 天 |
| 13 | 整体联调、测试、修复 Bug | 3 天 |
| 合计 | 约 24 天 |
10. 扩展性与未来演进
| 方向 | 改动范围 | 难度 |
|---|---|---|
| 多用户支持 | 增加 users 表 + JWT,tasks 加 user_id | 中 |
| 云端同步 | 增加 sync 模块,对接 WebDAV 或云存储 API | 中 |
| 移动端适配 | 前端响应式设计 + PWA 支持 | 中 |
| 邮件/通知提醒 | 增加 cron 任务,检查进行中任务并发送提醒 | 低 |
| 数据导入 | 反向实现 /import 接口,支持 JSON / CSV 导入 | 低 |
11. 附录:设计决策记录
| 决策点 | 选择 | 原因 |
|---|---|---|
| 数据库 | SQLite | 个人项目,无需独立服务,备份方便 |
| ORM | GORM | 支持软删除、迁移方便,社区活跃 |
| Web 框架 | Gin | 轻量,性能好,学习曲线平缓 |
| 前端框架 | Vue 3 | 组合式 API 灵活,Element Plus 组件丰富 |
| 时间存储 | UTC | 避免时区混乱,展示时转换 |
| 备份机制 | 文件复制 | 简单可靠,SQLite 无需复杂备份方案 |
| 导出格式 | JSON / CSV / HTML | 覆盖数据迁移、分析、展示三种场景 |
最后一句:这份计划书不是一份 “完美” 的方案,而是一份 “经得起推敲” 的方案。它最大的价值不在于用了什么新技术,而在于在动手之前,把 “可能出什么问题” 和 “出了问题怎么兜底” 这两件事想清楚了。
正如那篇文章所说:好的架构方案,往往不需要多复杂的技术手段。它需要的是你在动手之前,多花半天时间想想。