前后折腾了大半个月。这篇文章记录它从无到有的全过程——包括那些文档里永远不会写的坑、翻车、和深夜排查的「啊哈」时刻。

首页

一、缘起:组会上听不懂的那些词

故事要从研究生开学第一次组会说起。

导师在白板上写下「先去查一下这本刊在中科院几区」,我点头如捣蒜,回头就在搜索框里打了一行字:什么是中科院分区。查完了,又冒出来 JCR 分区、SCI 收录、Q1 Q2、一作通讯、OA、DOI……每一个词我都认识,连在一起就完全不知所云。

后来我发现,几乎每个科研新人都要把这些坑一个一个踩一遍:文献去哪查、期刊怎么挑、投稿信怎么写、审稿意见怎么回。这些知识不深,但极其琐碎,而且散落在各处的经验帖里——质量参差不齐,还有不少是营销号钓鱼。

于是就有了一个朴素的念头:能不能做一个网站,把这些东西一次性讲清楚? 像一位耐心的学长,把「文献检索、期刊分区、投稿全流程」这些事,掰开揉碎讲给你听。

二、先采访,再动手

有意思的是,这个项目动手之前的第一步,不是写代码,而是被 AI 采访。

我一开始把想法丢给它,它没有直接开写,而是问了我一串问题:网站给谁用?要不要登录?工作台只有你自己用还是访客也能用?数据存哪里?界面喜欢什么风格?

词典页:按拼音首字母分组的高频名词解释

这一问把我问清醒了。「随便」「都行」的背后,其实藏着无数个必须做的决定。比如「工作台只有我用」这个答案,直接决定了第一版可以做成纯静态、零成本、不用写后端;而「界面要现代 SaaS 风」这个答案,让整个项目的视觉基调从一开始就定了下来。

采访结束,它整理出一份完整的开发提示词。后来的一切,都是从那份文档长出来的。

这段经历给我的启发是:把模糊的想法变成产品,最贵的一步不是实现,而是把需求想清楚。

三、第一版:纯静态,上线只要一个下午

第一版架构简单到令人发指:React + Vite 构建出一堆静态文件,扔进 nginx 指定目录,完事。没有后端,没有数据库,知识内容全部打包在代码里,访客的自定义数据存在他们自己的浏览器 localStorage 里。

这种架构有个天大的好处:它不会坏。没有服务要守护,没有数据要备份,半夜三点它也不会给我打电话。

内容才是大头。查分区、作者排序、五大检索系统、投稿六阶段……我写成了一篇篇文章,每个概念都配大白话类比。写着写着发现,最难的不是写,而是忍住不写废话——「注重学术规范」这种句子谁都会写,但「基金号要逐字核对,结题审计时会查」这种话才是真的有用。我给自己的标准是:每一篇都要有能让读者「哦——原来如此」的具体信息,没有就删掉。

文章页:返修攻略,带阅读进度条和目录高亮

后来又添了模板文库(Cover Letter、审稿回复、催稿信的全文模板,一键复制)、投稿检查清单(选刊、投稿前、返修、校稿四个阶段逐项打钩)、学术句型库。网址导航收录了五十多个真实好用的科研网站,从知网 PubMed 到 Retraction Watch。

网址导航:按场景分类的科研网站大全

四、部署惊魂:服务器上居然有两个 nginx

网站做完,部署到自己服务器上,域名 yan.heylai.cyou。结果第一步就撞了墙。

我以为服务器上就一个干干净净的 nginx,配置加进去就完事。结果一查进程,发现有两个 nginx 在同时活着:一个是宝塔面板管的,占着 80 和 443,上面跑着游戏站、信箱站、影集站等一票兄弟站点;另一个是系统 apt 装的,systemd 状态还是 failed,属于历史遗留。

我第一版配置想当然地加给了系统 nginx,结果端口根本不归它管。发现之后赶紧回退,照着兄弟站点的配置格式,把 vhost 挂到宝塔的目录里,HTTPS 证书用 certbot webroot 方式签发。

上线那天,我把兄弟站点挨个 curl 了一遍:game 200,letter 200,love 200,photo 200。全绿。

那个瞬间的踏实感很难形容——「应该没问题」和「验证过没问题」,是两种完全不同的人生。

五、长出后端:密码、服务器存储和一面「访客也能用」的门

纯静态版很快乐,但有个现实问题:工作台的数据存在浏览器里,换个电脑就没了。于是网站迎来了一次大改造:加后端、加密码、加后台管理。

工作台的门长得像这样:

工作台密码门:输入密码进管理员模式,或免密码以访客身份进入

设计上有三条原则,现在回头看都觉得对:

  1. 不开放注册。这是我自己的工具,顺便开放给同样需要的人试用,不需要账号体系。
  2. 访客模式。没有密码的人点一下就能进来用,数据留在他们自己的浏览器里,不上传。门槛低到没有。
  3. 后端零依赖。我写了一个单文件 Node 服务,不装任何 npm 包,只监听本机回环地址,由 nginx 反代。防火墙一个新端口都不用开。

清单视图:打钩、笔记、挂链接,进度一目了然

每新建一个投稿任务,系统自动生成十个环节的流程线:选刊、格式准备、投稿、Under Review、返修、录用、缴版面费、校稿、Online First、见刊。每个环节可以打钩、写笔记、挂上这个环节要用的网址。

甘特视图:按月铺开的时间线,绿=完成,紫=进行中,红=超期

切换到甘特视图,整个投稿周期按月份横向铺开:绿色是已完成的,紫色是进行中的,红色是已经超期的,还有一条「今天」的竖线钉在那里。哪一步拖了、整件事预计什么时候结束,一眼可见。

六、两次让我后背发凉的安全测试

后端上线后,我做了自己扮演攻击者的测试。第一次就出事了。

我写鉴权逻辑时只拦了「写」操作,把「读」漏了——不登录就能读到工作台里的所有任务数据。看到 200 的那一刻,后背真的凉了一下。修复之后,我把整个鉴权矩阵打印出来逐格打钩:未登录读 401、写 403、登录后 200,一格一格验。

后台管理:壁纸和全站内容都在这里维护

第二次是「毒任务」事件。我用 curl 测试接口,随手塞了一条缺字段的任务进服务器——就这一条脏数据,让整个工作台白屏。前端渲染时对它调了 .filter,直接崩了。

修法是双保险:后端存储前清洗字段,前端加载时归一化。顺手把「读取失败时禁止编辑」也做了——因为你不能以空数据为基线,把服务器上的真数据覆盖掉。

这两次事故教会我一件事:你的代码不仅要对付正常输入,还要对付未来的自己随手塞进来的垃圾。

七、内容覆盖机制:后台改一个字,全站跟着变

后台管理页可以让站长(也就是我)在网页上直接改全站内容:壁纸、网址导航、知识库文章、模板、句型,保存即生效。

后台壁纸管理:上传图片,调透明度、模糊、文字遮罩

实现思路挺有意思的:前端所有内容都是结构化数据,后台保存的「覆盖数据」会在页面加载时原地写回这些数据模块——页面代码根本不知道远端数据的存在,照常用。后端不在线时,自动回落到打包在代码里的内置默认。

这个机制运行得很好,直到我在测试 SPA 内的刷新时发现:后台明明保存成功了,跳到导航页看到的还是旧数据。排查了一晚上,先怀疑数据没写入(不是),再怀疑模块双实例(不是),最后加探针一路追到真相:页面里对数据做了 useMemo 缓存,依赖数组里没有数据版本号,React 就一直给我用旧缓存。

那晚最讽刺的是,这个 bug 不是在写功能时发现的,而是在「验证功能确实正常」的测试里现形的。测试的价值从来不在于证明代码能用,而在于抓住那些你发誓自己已经处理好的地方。

八、它现在长这样

移动端也适配了,深色模式也带了:

移动端首页

现在的站点,五大模块:五十多篇知识文章、五十多个精选网址、十四份全文模板、五份检查清单、五十九个学术句型。工作台有密码,数据存服务器;访客免密码进来玩,数据留在自己浏览器。后台能换壁纸、改内容,保存即生效。

全部跑在一台我自己的服务器上,零新增端口,零新增依赖,和一票兄弟站点挤在同一个 nginx 里,相安无事。

九、折腾记录(专栏式碎碎念)

关于双 nginx。 服务器这东西,永远比你想的多一层。文档上写「nginx 配置在 /etc/nginx」的时候,你的服务器很可能有一份配置在 /www/server 下面笑而不语。动手前先 ss -tlnp,看看端口到底归谁管。

关于 "type": "module"。 前端 package.json 里的一行配置,能把后端的 require 全干趴。报错信息里藏着「rename it to .cjs」的小字,而我在 systemctl 的重启循环里困了半小时才去看它。教训:报错信息的第一行永远是给搜索引擎的,最后一行才是给你的。

关于「一票否决」的测试。 每次改完代码,我都会跑一遍完整的回归清单:登录、访客、新建任务、打钩、甘特、搜索、深色模式……二十多项,全绿才睡觉。这套流程救过我不止一次——有好几回,我以为无关紧要的改动,就是被这个清单揪出来的。

关于写作的良心。 网站里的每一条建议,我都问过自己:这条信息能不能让读者省下十分钟、避开一个坑?「基金号逐字核对,结题审计会查」这种话,是从真实的审计规则里来的;「LetPub 低于 3 分慎投」,是社区里无数投稿人的血泪共识。通识内容的价值不在多,而在每一条都值得信。

十、写在最后

回头看,从「组会上听不懂分区」的那个下午,到现在一个真正跑在自己服务器上的网站,最值得的其实不是网站本身,而是这一路养成的习惯:先问清楚再动手,每次改完就测试,看见全绿才睡觉,以及——永远对「应该没问题」保持怀疑。

网站还在慢慢长。也许哪天心血来潮,我会给它加个壁纸合集,或者把某篇文章再改得更好读一点。它活着,就会变化,就像科研这件事本身。

如果它帮到了哪怕一个刚刚进组、正在搜索「什么是中科院分区」的陌生人,这大半个月的折腾就都值了。


网站地址:yan.heylai.cyou(公开免费,工作台密码不公开但访客模式对所有人开放)

如果你也在折腾自己的小站,欢迎交流。祝我们都能在深夜的报错信息里,找到那行真正属于自己答案的小字。

谁在弹黑框?——记一次 22 分钟的 Windows 幽灵窗口围剿战 📸 摄影约拍还在群里捞人?我们做了一个「摄影广场」