拓冰建站拓冰建站
首页 / 资讯中心 / 正文

假期作业三:极简技术栈实现情绪记账、自动备份与实时数据看板

“假期作业三”这个系列我做了好几期不少朋友从第一期追到现在。简单说第三期延续了我之前定下的规矩假期里不搞宏大叙事就憋三个拿得出手的小项目从零到一做完收工。这次的三样东西分别是一个带情绪分析的极简记账本、一个局域网内自动备份关键文件夹的小工具还有一块把自己博客访问数据做成实时可视化的电子看板。没有用到任何重型框架全部是能在一个月内消化完的技术栈。这个系列的核心价值不是秀技术而是让每个项目都能在三天到一周内完整落地陪你度过一段不焦虑的、有产出的假期时光。如果你正在纠结假期该做点什么练手或者想找几个既能写进简历、又能解决实际小问题的项目这期的内容应该能给你一些灵感。照例每个项目我都会把设计思路、核心代码、参数取舍和踩过的坑展开讲清楚方便你照着做或者改成自己的版本。1. 三个项目怎么选出来的1.1 选题逻辑不是越大越好而是要能闭环假期做项目最忌讳的就是开局一张图、后面全靠编。我在定第三期选题时给自己列了几个硬性标准第一每个项目的需求必须来自真实生活场景不能是纯学术的概念演示第二单个项目的开发周期控制在三到七天超过一周就要砍功能第三所有项目必须在本地环境能跑通不依赖花钱的云服务。根据这个标准我淘汰了好几个看似炫酷但落地成本很高的想法比如做一个基于深度学习的图像分类器、做一个多人在线的协同白板。原因是这些项目要么需要大量标注数据要么需要部署服务器、处理实时通信短期冲刺容易烂尾。最后留下来的这三个项目共同点是都能在个人电脑上独立完成闭环记账本解决月初看账单一脸懵的问题备份工具解决重要文件误删后找不到的问题数据看板解决博客访问数据只能看数字、看不出规律的问题。1.2 技术选型思路用自己最顺手的东西这套项目的技术栈整体上是“旧瓶装新酒”核心语言选了Python和JavaScript数据库用了SQLite和浏览器自带的localStorage可视化用了ECharts和轻量的socket服务。没有引入诸如分布式、微服务这类概念原因很简单项目一旦引入和核心需求匹配不上的技术学习成本和排错成本就会同步升高假期时间根本不够用。比如记账本项目为什么要用SQLite而不是MySQL因为SQLite是文件型数据库零配置文件可以随项目一键打包适合这种单机应用。为什么不用Django或Flask的完整脚手架因为一个单页应用加上几个API接口就能满足需求没必要为了一个杯子买一整套茶具。这些取舍让三个项目都保持了很高的完成度也便于读者用较短的代码量看懂每个功能背后的逻辑。1.3 项目一情绪记账本——记账不只是记数字记账的难点从来不是“记录”本身而是“坚持记录”。市面上绝大多数记账App功能过重光是为每笔消费选分类就要点好几次屏幕时间一长就失去了记录的欲望。我做这个记账本的核心思路是让“记一笔”的操作在十秒内完成并且自动给每笔支出打上情绪标签等月底回看时你能清楚地看到哪段时间花钱花得最开心、哪段时间是在报复性消费。技术上前端就是一个单纯的HTML页面所有数据保存在浏览器本地通过localStorage持久化。后端不是重点重点在于数据结构和交互设计每一笔记录必须包含金额、分类、日期、备注和情绪开心、一般、烦躁、焦虑五个字段这五个字段构成了后续所有统计分析的原始素材。1.4 项目二关键文件夹备份工具——向冷备份说再见第二件事源于一次真实事故我有一次误删了一个存满旅行照片的文件夹等到发现时回收站已经被清空了还好之前手动拷贝过一次到移动硬盘不然几年的记忆就全没了。手动备份的问题是你永远不知道下一次备份该在什么时候“经常备份”这个美好的愿望执行几次之后就变成了“偶尔备份”最后变成“再也不备份”。这次我干脆写了一个自动备份工具设定好源文件夹和目标文件夹之后工具启动时会自动对比两边文件的差异把新增和修改过的文件增量拷贝过去同时在目标文件夹里保留最近十个历史版本。这样一来备份动作不再依赖我的记忆只要记得偶尔运行一次程序或者干脆把它加到开机启动项里它就会在后台默默完成所有工作。1.5 项目三博客实时访问看板——用数据看见内容的长尾价值写博客的人大多想知道自己写的内容到底有没有人看、大家是通过什么渠道来的、哪一类内容更受欢迎。第三期收尾项目就是把博客的访问日志做成一个实时刷新的可视化看板让我在写新文章的时候瞄一眼屏幕就能感受到旧内容的持续吸引力。这个项目用到的方法是Nginx访问日志会产生一行行固定格式的文本我用Python脚本定期读取新增的行解析出IP、访问时间、请求路径、User-Agent等字段写入SQLite数据库然后通过一个轻量的WebSocket服务把数据变化推送到浏览器端浏览器端用ECharts渲染出实时曲线、地域分布和热门文章排行。整个过程听起来复杂但真正实现起来核心代码大约只有三百行。2. 每个项目的实现要点和难点2.1 情绪记账本的字段设计为什么很关键很多初学者写记账本时数据库表结构往往只设计金额和时间两个字段觉得够用了。实际上没有分类和备注的记账本基本上是一堆没有意义的数据你能看到哪天花了两百但完全想不起来花在了哪里。所以我的核心表结构一开始就确定下来不给自己留返工的空间。建表语句非常简单但字段的含义需要仔细斟酌。我把“分类”设计成可自定义的文本字段而非预置的下拉选项是因为每个人关心的消费类别不一样有人对餐饮敏感有人对游戏支出敏感硬编码的分类列表会逼着用户在“其他”这个垃圾筐里塞进大量真实信息。情绪字段同样重要它不参与金额计算却能为月底的复盘提供有趣的视角。2.2 localstorage 与 SQLite 的取舍记账本项目的前端存储层用的是localStorage因为它足够简单且浏览器会自动管理数据持久化。不过这个方案有一个明显的边界localStorage的数据容量一般在5MB到10MB之间如果每天记二十笔账每一笔按两百字节算一年也不到2MB所以完全够用。但如果你打算在这个项目上继续扩展数据库比如加多人协同那就必须换到后端数据库了。我在项目里额外做了一层数据导入导出功能可以把localStorage里的所有记录导出成一个JSON文件也支持导入。这个功能听起来不起眼但在换电脑、清缓存的时候能救你一命是防止数据丢失的最后一道保险。2.3 文件夹对比算法与增量备份的实现逻辑备份工具的核心挑战在于如何快速判断哪些文件需要备份而不是每次都把整个文件夹无脑拷贝一遍。最简单粗暴的做法是遍历源文件夹中的所有文件然后逐个判断目标文件夹里是否存在同名且同大小的文件如果不存在就拷贝。这个方案在文件数量较少时运行得很快但当文件夹里有两万张照片时遍历一次可能要几十秒体验很差。我采用的优化思路是引入一个“文件清单快照”文件。这个快照里记录了上一次备份后所有文件的相对路径、大小和修改时间。每次运行备份工具时先读取快照然后对比当前文件系统的状态只在文件列表发生变化的位置执行拷贝操作。对于新增文件直接拷贝对于修改过的文件覆盖旧版本对于已经删除的文件则在快照中同步移除。处理完之后更新快照整个过程记录日志方便回溯。2.4 增量备份的版本管理与轮转策略备份工具除了增量拷贝之外还必须解决“误备份”的问题。比如如果你不小心删除了一个重要文件然后运行了备份工具快照就会把这个文件的删除状态记录下来这会导致旧版本在下次备份时被清理掉。为了避免这种情况我设计了多版本轮转机制每次备份之前先把目标文件夹里的现有内容压缩成一个带时间戳的版本包保留最近十个版本包后自动删除最旧的。这样即使你在当天误删了文件只要版本包里还有昨天甚至前天的记录就能翻出来恢复。版本轮转的参数需要根据磁盘空间和备份频率来调整。我个人的默认策略是备份频率设为每六小时一次配合定时任务版本保留数设为十个。换算下来可以回溯大约两天半以内的任何状态同时磁盘占用一般不会超过源文件夹总大小的两倍。如果你的文件夹里全是视频这类大文件建议把保留数降到3到5个否则版本包的体积会非常可观。2.5 实时看板的数据链路设计看板项目的整个数据链路可以拆成四个环节日志采集、数据存储、消息推送和前端渲染。日志采集这步常规思路是写一个死循环定期读文件的新增行但这样做有一个问题如果Nginx由于某种原因很久不写日志脚本就会一直空转浪费系统资源。我的改进方案是借助生成器的惰性求值特性每当有新行可读时才产生数据没有新行时挂起等待这样脚本的资源占用几乎可以忽略不计。数据存储端我仍然选择了SQLite。它的并发读写能力虽然不如真正的数据库但对于个人博客这种每秒最多几个请求的量级来说绰绰有余。消息推送环节我用WebSocket替代了传统的轮询因为轮询每隔几秒请求一次API不仅浪费带宽还会给日志数据库带来不必要的压力。前端渲染选择了ECharts的折线图、柱状图和地图配合WebSocket推送的数据可以实现秒级刷新的效果。2.6 ECharts 动态数据更新的一个关键细节使用ECharts做动态数据展示时新手最容易踩的坑是不理解setOption的合并机制。如果每次数据更新都调用一次完整的setOption并且传入了全新的series图表实例会重新创建series导致颜色闪烁、动画抖动。正确做法是初始化图表之后把系列数据通过更新对应索引的series.data来替换并且设置notMerge参数为false让ECharts只更新数据而不重建系列这样才能保持视觉上的连贯性。另外一个细节是时间轴的处理。实时数据的时间字段如果精确到秒折线图上的点会非常密集而且由于网络波动偶尔会出现数据点缺失。我建议在前端展示层做一次聚合把最近一小时的数据按分钟聚合最近二十四小时的数据按半小时聚合这样曲线既平滑又不牺牲细节。3. 实际操作全过程记录3.1 记账本项目从页面到逻辑的完整实现这个项目我最终做成了纯前端单页面文件结构非常简单一个HTML文件、一个CSS文件、一个JavaScript文件。所有页面逻辑分成录入、列表、统计三个区域页面底部有一个导出按钮和一个导入按钮。界面的设计原则是清爽主色调用莫兰迪绿字体全部用系统默认字体不额外加载字体文件这样离线打开也能正常使用。录入区的交互流程是这样的用户输入金额后点击分类按钮选择一个分类再点五个情绪图标中的一个最后可选填备注。每次提交时程序会生成一个唯一ID用时间戳加随机数拼接而成然后写入localStorage的records数组中。为了让用户产生“记录一下很快”的正反馈我加入了一个微动效金额数字会快速跳动一下并变绿提示录入成功。统计区的逻辑是重头戏。我用一个combine函数把所有记录按日期聚合生成每日支出数组然后在Canvas上画出一条趋势线。趋势线能直观地显示支出高峰出现在哪天情绪标签则用饼图展示不同情绪下花费金额的占比。比较有趣的是我把“烦躁”这种负面情绪与金额的分布做了关联当烦躁情绪下的消费占比超过总消费的20%时页面会出现一个温和的提示语“这段时间似乎有些情绪化消费可以试着关注一下自己的状态。”3.2 备份工具的命令行参数和配置项备份工具是一个Python命令行程序调用方式很简单在终端里执行python backup.py /path/to/source /path/to/target即可。为了让不同场景的用户能灵活调整行为我提供了几个可选参数--interval用来设置备份间隔按小时计--versions用来设置保留版本数--dry-run则会在不实际拷贝文件的情况下打印出这次备份会执行哪些操作方便先在测试目录里跑一遍验证。核心的增量对比函数大致逻辑是先读取目标目录下的snapshot.json文件把里面的文件列表加载成一个字典再遍历源目录里的所有文件计算每个文件的相对路径、大小和修改时间对比新旧数据后把差异类型分为新增、修改、删除、无变化四类。对于新增和修改文件用shutil.copy2拷贝并保留元信息对于删除文件只在记录中标记而不真正删除目标文件因为目标文件可能存在于某个旧版本包中贸然删除会导致回溯失败。3.3 用定时任务让备份变成“无需思考”的动作有人可能觉得既然工具已经写了每天手动跑一次也挺好。但我个人的体会是凡是需要靠人去记住的运维动作最终一定会被遗忘。所以我建议把备份工具注册到系统的定时任务里。在macOS或Linux上可以用crontab设置一行0 */6 * * *意思是每隔六小时整点运行一次在Windows上则可以用“任务计划程序”新建一个触发器设定触发条件为“每天”重复间隔设为6小时。注册完定时任务后我习惯再做一个验证动作手动运行一次backup.py --dry-run检查输出日志是否正常然后故意改动一个测试文件等下一个周期触发时去看这个修改是否被正确备份。这一步虽然简单但能避免“以为在备份、实际没跑通”的乌龙。我的经验是如果你做完工具后连一次真实的恢复演练都没做过那这个备份工具本质上跟没做差不多。3.4 看板项目日志采集、WebSocket推送和前端渲染看板项目的实现我按数据流动方向拆分成了三个独立模块方便分别调试。第一个模块是log_watcher.py负责监听Nginx日志文件。它用seek定位到文件末尾然后进入循环每次尝试读取新行并解析。解析用的是一个正则表达式把日志行中的IP、时间、请求方法、请求路径、状态码、响应字节数和User-Agent提取成结构化字段再插入SQLite的access_log表。第二个模块是websocket_server.py启动后同时承担两个职责一是持续消费数据库中的最新记录二是把变化推送给订阅了WebSocket连接的前端页面。为了让实时性足够高我没用“读数据库全表”的方式而是在access_log表中维护一个自增ID字段每次推送时只读取ID大于上次推送最大值的记录这样即使日志量很大推送的负载也很小。第三个模块是前端看板页面。页面布局是典型的监控大屏风格顶部是访客总数、今日浏览量、在线人数三个指标卡片中间是实时流量曲线左侧是热门文章Top10右侧是最近来访地域分布。ECharts初始化时只创建一次实例后续接收WebSocket消息时通过myChart.setOption更新对应series保证图表流畅。3.5 部署时遇到的端口冲突与跨域问题看板项目在本地调试时一切正常但部署到服务器上时遇到了两个典型问题。第一个是端口冲突WebSocket服务默认挂在9000端口而服务器上已经有一个旧服务占用了这个端口导致启动直接报错。解决办法很简单把WebSocket端口改成了9001并更新前端的连接地址。这里要特别提醒如果前端页面是HTTPS环境WebSocket连接必须用wss协议否则会被浏览器拦截。第二个问题是跨域。如果看板页面部署在域名A下但是WebSocket服务在域名B的9001端口上浏览器会认为这是跨域请求并阻止连接。解决办法是在WebSocket服务端设置允许跨域CORS头。Python标准库里的websockets库本身不直接提供CORS配置我在服务端加了自定义的响应头把Access-Control-Allow-Origin设为具体的前端域名避免用*这种全放开的方式减少安全风险。4. 常见问题与排查技巧实录4.1 记账本的数据丢失和浏览器缓存策略记账本项目最常见的用户反馈是“记录突然没了”。排查这类问题第一件事不是去翻代码而是确认浏览器是不是开启了“退出时清除浏览数据”的功能。凡是把数据放在localStorage里的前端应用都逃不过这个坑用户正常关闭浏览器再打开数据还在但如果用的是某些手机浏览器的“无痕模式”或“自动清理”选项页面刷新或关闭时数据就会被清理掉。我给出的解决方案是在页面加载时自动从localStorage读取数据同时设置一个导出到文件的功能建议用户每周手动导出一次。如果你希望数据更稳妥可以在现有架构上加一个“同步到WebDAV”的按钮通过WebDAV协议把JSON文件上传到自己的云盘。这个方案能让你在手机和电脑之间共享记账数据同时不需要自己维护服务器。不过同步冲突的问题需要单独处理如果两台设备同时修改记录就会产生两个版本的JSON文件建议在导入时按最后修改时间合并而不是简单地覆盖。4.2 备份工具误报“文件已修改”的原因分析增量备份在使用一段时间后偶尔会出现一种让人困惑的情况明明某个文件没有动过但备份工具却把它判定为“已修改”。我排查下来这种情况通常不是文件内容真的变了而是文件元信息发生了变化。比如你在手机或相机上处理过照片再拷回电脑时文件的修改时间很可能被更新到了拷贝那一刻即使内容没有变化。解决这个问题的办法是调整对比策略不是只比较大小和修改时间而是对文件内容做分块哈希校验。具体做法是在快照中记录每个文件的总大小和前几块内容的哈希指纹。每次对比时先看大小是否一致如果一致再看首块内容哈希。这样既避免了全文件哈希带来的性能开销又能过滤掉大部分仅元信息变化的情况。要注意的是这个方案不能百分百覆盖所有场景比如文件恰好被修改但头部没变的极端情况但实际使用中误报率可以从接近10%降到不足1%。4.3 看板不显示数据时从哪几个方向逐一排查看板页面空白是小概率出现但一定会出现的问题。我的排查顺序一般是这样的先看WebSocket服务端有没有收到日志更新数据如果服务端没有输出说明日志监听模块没有正常工作这时需要检查日志文件路径是否正确、Nginx是否开启了日志写入权限如果服务端有数据推送再看浏览器端的控制台有没有报错常见的报错是连接被拒绝或跨域被阻止。如果两端看起来都正常但图表依然空白那大概率是前端数据解析或渲染的问题。我遇到过一种情况是Nginx日志的日期格式跟解析正则不匹配导致所有记录的时间字段都是空值而ECharts的时间轴不接受空值整个图就静默失败了。解决办法是在解析之后加一层数据校验如果某条记录的时间字段为空就直接丢弃并打出一条警告日志让异常能在开发阶段暴露出来而不是默默藏到线上。4.4 三个项目通用的一条避坑原则小步快跑关于避坑我想分享一条适用于所有项目的通用原则小步快跑先让最核心的链路跑通再逐步叠加周边功能。写记账本的时候我的第一步是让“录入一笔记录并立刻在列表中显示出来”这个动作跑通导出功能、统计图表都是后面一两天才加的。备份工具也一样一开始只写一个最朴素的“整个文件夹拷贝过去”的脚本确认没有问题之后才着手加增量对比、版本轮转和定时任务。看板项目的第一步是“把日志文件某一行读出来并打印到控制台”这一步通了后面的一切才有支撑。这个原则听上去像废话但执行起来很容易跑偏。特别是当你开项目前刷了很多技术文章看到别人用各种高级框架和架构时就会忍不住想一步到位。我吃过这个亏第三个项目一开始想用消息队列来解耦日志采集和WebSocket推送后来冷静想了想个人博客的日志量根本到不了需要消息队列的级别引入它只会给自己增加部署和排查的负担于是果断砍掉保留了最直接的那条路径。5. 假期做项目的一些额外心得5.1 代码能力之外记录习惯同样重要这三期“假期作业”做下来我最大的收获不全是技术上的。以前假期做项目总会有完成一个功能就心满意足、停下整理文档的情况结果到了假期结束回头看代码虽然写了但很多设计决策和调试过程都忘得差不多了。后来我养成一个习惯每天结束前不管项目进展到哪一步都在项目根目录下新建一个日志文件用几句话记录当天做了什么、遇到了什么问题、明天打算做什么。这套记录习惯在收尾写总结时特别有价值。比如这篇“假期作业三”的分享很多细节我并不是靠记忆写出来的而是翻看当时记录的building_log.md文件。这种文件的格式完全不需要讲究甚至可以是一堆零散的短语和命令记录只要自己一个月后能看懂就达到了目的。我建议每个假期做项目的人都能把“记录”这件事当成项目的一部分它的长期回报远大于多写几十行代码。5.2 如何让项目做到“可暂停”而不是“烂尾”假期项目最大的风险不是不会做而是中途可能因为旅行、聚会、临时加班等原因中断三五天。一旦中断再回来时大脑需要重新加载上下文如果加载成本太高人就容易放弃。解决这个问题的方法叫“可暂停性”让项目在任何时刻都能被安全地暂停一段时间然后再无缝恢复。三个项目的设计里我都刻意考虑了这一点。记账本完全依赖浏览器本地数据哪怕半个月不打开再打开时数据还在继续记录就行备份工具由定时任务驱动就算我十天不上手动运行它也会按计划自动执行看板项目更是全自动的数据管道日志采集和推送服务会持续运行新页面打开就能看到最新数据。反过来说如果当时选了那种需要持续互动的在线协同步伐中断几天很可能就真的回不去了。5.3 假期作业三之后还能往哪些方向扩展这三个项目虽然是完整闭环但都保留了扩展空间。记账本可以再加一个“月度预算告警”功能如果当月支出超过预算的80%就给用户提醒这个逻辑可以在统计区直接实现。备份工具可以增加一个跨设备同步的后端比如把版本包上传到对象存储服务这样即使本机硬盘坏了备份数据也不会丢失。看板项目可以把Nginx日志换成自己写的API服务请求日志从而把整个后端服务的运行状态也纳入监控范围变成一个轻量级的可观测性工具。我个人计划在下一期假期作业里把这三个项目整合成一个简单的“个人效率工具箱”一个统一的首页入口三个模块共享一套认证和配置信息这样既能减少日常使用的重复操作也为后续增加第四、第五个小模块打好了基础。如果你也打算做类似的系列项目不妨提前想好不同项目之间的共同点在后期合并时能省下不少重构的时间。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门