365天地人网课视频,我用Go语言写了一套时间循环学习系统
- 能源
- 2026-06-30 15:30:19
- 278
从“买课如山倒,上课如抽丝”说起
你有没有那种感觉——买了一套365天的网课,头三天热血沸腾,第七天就开始“明天再说”,…就没有然后了,我去年年初囤了一套“地人网课”,讲的是《周易》里的天地人三才之道,配上现代商业思维,听着很玄乎,但内容确实扎实,可问题是,365节课,一天一节,我硬是拖到年底才看了三分之一。
元旦那天我盯着文件夹里剩下的两百多个视频文件,突然冒出一个念头:要是用Go语言写个小系统,帮我自动编排每天该看哪个视频,顺便记录学习进度,会不会好点?
于是就有了这个项目,它不是什么大厂级架构,就是一个程序员用Go硬啃365天学习任务的个人尝试,今天我把这个思路拆开讲,不是为了推销代码,而是想聊聊——用技术手段对抗“买课综合征”到底靠不靠谱。
底层逻辑:把“地人网课”拆成可执行单元
为什么是Go,而不是Python或Node?
选Go没有太多高大上的理由,首先我熟悉它,其次Go的并发模型很适合处理“定时任务+状态持久化”这种场景,你想想,365天每天更新一次学习状态,如果我用Python写个脚本跑在服务器上,还得操心虚拟环境、依赖冲突,Go编译出来就是一个二进制文件,丢到Linux服务器上,nohup一挂,稳得很。
实际写的时候,我用了三个核心包:
cron(robfig/cron/v3)——做每日定时提醒sqlite(mattn/go-sqlite3)——存学习记录,轻量,不用单独装数据库gin(gin-gonic/gin)——搭个简单的web界面,方便手机上查看进度
代码结构大概是这样的:
├── main.go // 入口,初始化cron和web服务
├── models/ // 数据库操作
│ └── video.go // 视频表结构:id, title, day_index, watched, note
├── handlers/ // web路由处理
├── scheduler/ // 每天定时任务
└── static/ // 前端页面
你说这是不是最好的架构?肯定不是,但够用,而且跑了一年没崩过。
数据模型:把“天地人”装进SQLite
“地人网课”的课程设计很特别——它把一年分成三个阶段:
| 阶段 | 天数 | 核心主题 | 我的理解 |
|---|---|---|---|
| 天时 | 1-120 | 趋势判断、时代红利 | 看懂大环境 |
| 地利 | 121-240 | 资源整合、落地执行 | 搞定具体事 |
| 人和 | 241-365 | 团队管理、自我修炼 | 处理好关系 |
每个视频对应一个day_index,从1到365,数据库里就一张表,简单到有点粗暴:
CREATE TABLE videos (
id INTEGER PRIMARY KEY,
day_index INTEGER UNIQUE,TEXT,
watched BOOLEAN DEFAULT 0,
watched_at DATETIME,
note TEXT
);
一开始我还想过要不要加category字段,后来觉得没必要,因为“天时地利人和”本身就是按天数划分的,day_index在1-120就是天时,121-240就是地利,241-365就是人和,硬编码虽然不优雅,但对于这种固定结构的课程,反而更清晰。
定时任务:用Go的cron实现“每天一课”
核心逻辑在scheduler/job.go里:
func DailyJob() {
today := time.Now().YearDay()
// 计算今天是今年的第几天(1-365)
// 如果今天对应的视频还没看,推送提醒
// 如果看了,记录时长,更新状态
}
说实话,一开始我天真地以为这就能解决拖延症,结果第一个月就发现——技术只能帮你“,不能帮你“行动”,系统每天早上8点提醒我“今天该看第X课了”,我依然会划掉通知,继续刷短视频。
这事让我反思了一个问题:工具的价值到底在哪?
迭代:从“打卡工具”变成“学习伴侣”
加了“弹性机制”:允许补课,但不鼓励
第二个月,我改了一下逻辑,如果今天没看,不会直接标记为“未完成”,而是进入一个“待补”队列,周末的时候,系统会统计本周未看视频数,然后问我:“要不要今天补两节?”
这个设计参考了《原子习惯》里说的——不要打破链条,但允许链条有弹性,在Go代码里,就是一个简单的队列加上每日重试:
type RetryQueue struct {
mu sync.Mutex
pending []int // 未完成的day_index
}
func (q *RetryQueue) Add(day int) {
q.mu.Lock()
defer q.mu.Unlock()
for _, d := range q.pending {
if d == day { return } // 避免重复添加
}
q.pending = append(q.pending, day)
}
这种实现很初级,但够用,真正改变习惯的不是这个队列,而是当你打开系统看到“还有12个视频在等你”时,那种微微的不适感。好的工具,就是让你对“未完成”保持适度的焦虑。
引入“天地人”进度可视化
觉得纯文字太枯燥,我又用Go的html/template做了三个进度条:
- 天时进度:🌞 ████████░░░░ 68%(看了82/120)
- 地利进度:🌍 ██████░░░░░░ 52%(看了63/120)
- 人和进度:👥 ██░░░░░░░░░░ 15%(看了18/125)
UI丑是真的丑,但有效,每次打开看到“人和”那一截短得可怜,心里就咯噔一下。视觉化的差距,比任何提醒都更能刺激行动。
真实翻车现场:Go语言写脚本的“坑”
说点搞笑的,用Go写这种个人工具,其实有点“杀鸡用牛刀”的意思,但既然用了,就得面对几个蛋疼的问题:
时区问题
我的服务器在新加坡,但人在国内,cron默认用UTC时间,导致提醒比预期晚了8小时,查了一天文档才发现,要在初始化cron的时候指定时区:
c := cron.New(cron.WithLocation(time.FixedZone("CST", 8*3600)))
这种低级错误,差点让我弃坑。写代码有时候不是输在算法,而是输在常识。
数据库锁竞争
SQLite是单写多读,有一次我同时开了两个浏览器标签页操作同一张表,直接报database is locked,解决办法很粗暴:改用GORM的AutoMigrate,加上写操作的串行化,实际上就是加了个sync.Mutex,Go的goroutine虽然能并发,但SQLite不行。
最尴尬的设计失误
一开始我把视频标题写死在代码里:
"天地人01:何为天道?",
"天地人02:地道与商道",
// ... 365个
}
写到第50个的时候,我崩溃了,后来改成从CSV文件读取,才救回一条命。永远不要把数据硬编码在Go代码里,这是我用血泪换来的教训。
效果:一年之后,我真看完了365节课
2024年12月31日,系统弹出一条消息:
🎉 恭喜!您已完成全部365节“天地人网课”!
总学习时长:约182小时
笔记条数:47条
重看次数最多的视频:第128课《地利:选址的底层逻辑》 (重看3次)
说实话,看到这条消息的时候我愣住了,不是因为觉得自己多厉害,而是意识到——这一年里,我真正坚持下来的事,竟然是通过一个Go脚本“逼”自己完成的。
如果你要问我,这套系统到底比普通人用备忘录记进度强在哪?我觉得有三点:
- 自动调度:不用每天想“今天该看哪个”,系统替你决定
- 进度可视化:三条进度条让你清楚自己在哪个阶段落后了
- 弹性补课:允许偷懒,但不允许彻底断开
但更重要的是,写这个系统的过程,本身就是一种学习,为了让它跑得稳,我重温了Go的并发模型、学了SQLite的最佳实践、还搞懂了cron表达式怎么写,这些技能,比那365节网课本身还值钱。
给想动手的人一点建议
如果你也想用Go干类似的事,别一开始就追求完美,先用最简单的方式跑起来:
- 不用数据库?先写在TXT文件里
- 不用web界面?先看控制台输出
- 不用cron?先手动执行
技术是为目标服务的,不是用来炫技的。 我那套系统刚跑起来的时候,数据库是JSON文件,界面是黑底绿字的命令行,后来慢慢迭代才变成现在这样,关键是——先跑起来。
至于“天地人网课”本身值不值得学?我只能说,“天时”教你判断风口,“地利”教你落地执行,“人和”教你处理关系。 如果你正好处于职业迷茫期,或者想转型做点自己的事,这套课确实能给你一些框架性的启发,但别指望看完就能怎样,任何课程的价值,都取决于你课后做了什么。
附上我那个简陋系统的首页截图(文字版):
┌─────────────────────────────────────┐
│ 365天地人网课 · 学习进度 │
│ │
│ 天时 ████████░░ 68% (82/120) │
│ 地利 ██████░░░░ 52% (63/120) │
│ 人和 ██░░░░░░░░ 15% (18/125) │
│ │
│ 今日推荐:第256课《人和:沟通》 │
│ [开始学习] [推迟到明天] │
│ │
│ 待补视频:12节 │
└─────────────────────────────────────┘
就是这么个东西,陪我走完了365天。
代码现在还在我的GitHub上躺着,偶尔会收到陌生人的Star,有人留言说“能分享下怎么用Go写学习系统吗”,也有人问“你这个能用到其他课程上吗”,其实改改CSV文件就行,本质就是个通用的“每日任务管理器”。
但说真的,有没有这个工具,可能没那么重要,重要的是——当年你买下那套课的时候,心里确实是想学的。
我写Go,不是为了证明“技术能解决一切问题”,而是因为我知道自己懒,所以提前用代码给自己挖了一条不得不走的轨道。主动给自己设限,反而能获得自由。
你看,现在我正坐在电脑前,打下这些字,窗外下了点雨,桌上的日历已经翻到新年的第一天,系统里的“365天地人网课”状态全部打勾了,但我没有卸载它——我打算用它来记录今年的读书计划。
毕竟,习惯了一个工具陪你成长,就很难回去了。
