当前位置:首页 > 能源 > 正文

365天地人网课视频,我用Go语言写了一套时间循环学习系统

  • 能源
  • 2026-06-30 15:30:19
  • 278
摘要: 从“买课如山倒,上课如抽丝”说起你有没有那种感觉——买了一套365天的网课,头三天热血沸腾,第七天就开始“明天再说”,…就没有然...

从“买课如山倒,上课如抽丝”说起

你有没有那种感觉——买了一套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,解决办法很粗暴:改用GORMAutoMigrate,加上写操作的串行化,实际上就是加了个sync.Mutex,Go的goroutine虽然能并发,但SQLite不行。

最尴尬的设计失误

一开始我把视频标题写死在代码里:

    "天地人01:何为天道?",
    "天地人02:地道与商道",
    // ... 365个
}

写到第50个的时候,我崩溃了,后来改成从CSV文件读取,才救回一条命。永远不要把数据硬编码在Go代码里,这是我用血泪换来的教训。

效果:一年之后,我真看完了365节课

2024年12月31日,系统弹出一条消息:

🎉 恭喜!您已完成全部365节“天地人网课”!
总学习时长:约182小时
笔记条数:47条
重看次数最多的视频:第128课《地利:选址的底层逻辑》 (重看3次)

说实话,看到这条消息的时候我愣住了,不是因为觉得自己多厉害,而是意识到——这一年里,我真正坚持下来的事,竟然是通过一个Go脚本“逼”自己完成的。

如果你要问我,这套系统到底比普通人用备忘录记进度强在哪?我觉得有三点:

  1. 自动调度:不用每天想“今天该看哪个”,系统替你决定
  2. 进度可视化:三条进度条让你清楚自己在哪个阶段落后了
  3. 弹性补课:允许偷懒,但不允许彻底断开

但更重要的是,写这个系统的过程,本身就是一种学习,为了让它跑得稳,我重温了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天地人网课”状态全部打勾了,但我没有卸载它——我打算用它来记录今年的读书计划。

毕竟,习惯了一个工具陪你成长,就很难回去了。

365天地人网课视频,我用Go语言写了一套时间循环学习系统