danci 1:为什么一个背单词应用还需要后台?从单词书管理开始设计整个系统

40 阅读8分钟

一个背单词应用,看起来只需要一个手机页面:展示单词、点击认识或不认识、记录学习进度。

但只要继续往下做,很快就会发现:用户负责背单词,那单词、单词书和内容到底由谁来维护?

这就是 danci 项目真正的起点。


一、先从一个最简单的背单词页面开始

假设我们准备做一个单词学习应用。

用户打开页面以后:

选择单词书
↓
开始学习
↓
apple
↓
认识 / 不认识
↓
下一个单词

第一版看起来并不复杂。

但继续往下想,很快就会遇到几个问题:

“四级词汇”这本单词书是谁创建的?

一本单词书里有哪些单词?

某个单词释义写错了怎么办?

以后想增加“考研词汇”怎么办?

如果所有内容都直接写死在代码里,那么每改一个单词,都需要程序员修改代码、重新发布。

显然不合理。

所以第一个需求自然出现了:

需要一个后台管理系统,让内容维护人员自己管理单词和单词书。

于是 danci 不再只是一个 H5 页面,而是开始分成两个部分:

danci

├── 后台管理系统
│   └── 给管理员维护内容
│
└── H5 单词学习应用
    └── 给普通用户背单词

这也是很多真实产品都会出现的结构:

运营 / 管理员
↓
后台系统
↓
数据库
↑
用户应用
↑
普通用户

后台负责维护数据,用户端负责使用数据。


二、后台第一步做什么?先做单词书管理

既然用户需要:

四级词汇
六级词汇
考研词汇

那么后台最先需要管理的就是:

books
单词书

管理员进入后台以后,需要完成:

创建单词书
查询单词书
修改单词书
删除单词书

这其实就是最经典的 CRUD:

Create   创建
Read     查询
Update   更新
Delete   删除

例如 /books 页面可能展示:

单词书单词数量操作
四级核心词汇2600编辑 / 删除
六级核心词汇3200编辑 / 删除
考研核心词汇5500编辑 / 删除

这个页面看起来很普通,但它已经说明了一件很重要的事:

后台系统的核心不是“页面多漂亮”,而是让业务人员能够维护系统里的数据。


三、谁都能进入后台吗?

单词书可以创建和删除。

那显然不能让所有普通用户都访问:

/books

否则任何人都能修改甚至删除系统数据。

所以第二个需求又自然出现了:

后台需要管理员身份。

可以先设计两种角色:

超级管理员
普通管理员

普通管理员负责内容维护,例如:

维护单词书
维护单词
审核数据

超级管理员除了拥有这些能力,还可以:

添加管理员
删除管理员
管理管理员权限

于是后台又增加了一个模块:

/admin-users

到这里,后台最基础的结构已经出来了:

后台管理系统

├── /books
│   └── 单词书管理
│
└── /admin-users
    └── 管理员管理

四、但是第一个管理员是谁创建的?

现在出现一个很有意思的问题:

只有管理员
↓
才能创建管理员

那么系统刚上线的时候:

第一个管理员是谁创建的?

总不能出现:

需要管理员创建管理员
↓
但是现在没有管理员
↓
所以永远无法创建第一个管理员

因此需要一个“系统初始化”流程。

例如:

系统第一次启动
↓
检查有没有超级管理员
↓
没有
↓
进入超级管理员注册页
↓
创建唯一的初始超级管理员

于是 / 的逻辑可能是:

访问 /
↓
检查系统状态

第一次:

没有超级管理员
↓
/signup
↓
注册超级管理员
↓
/signin
↓
登录
↓
/books

以后:

已经存在超级管理员
↓
/signin
↓
登录成功
↓
/books

这件事看起来只是几个页面跳转,但它背后其实是在做:

业务流程设计。

页面只是业务规则最终表现出来的形式。


五、后台页面越来越多,UI 难道都自己写?

现在后台至少会出现:

登录表单
注册表单
侧边栏
表格
按钮
输入框
确认删除弹窗
下拉菜单
分页

这些东西在各种后台系统里高度重复。

例如一个“删除单词书”的功能,通常就是:

删除按钮
↓
确认弹窗
↓
取消 / 确认

如果每一个项目都从零实现:

Button
Input
Dialog
Table
Sidebar
Select
Dropdown

大量时间都会花在重复造轮子上。

所以这里自然会引出:

UI 组件库。


六、为什么这个项目使用 shadcn/ui

传统组件库,例如 Ant Design、Element Plus,通常是:

安装组件库
↓
从 npm 包中 import 组件
↓
直接使用

shadcn/ui 的思路有些不同。

例如添加 Button、Dialog、Table 后,项目中会出现类似:

components/
└── ui/
    ├── button.tsx
    ├── dialog.tsx
    ├── input.tsx
    └── table.tsx

也就是说:

组件源码直接进入自己的项目。

所以它有一个很明显的特点:

不是完全接受组件库提供的最终样子
↓
而是拿到一套不错的基础组件
↓
继续按照自己的项目修改

这对于后台系统很适合。

因为后台里 80% 的基础组件本来就高度趋同:

按钮还是按钮
输入框还是输入框
表格还是表格
弹窗还是弹窗

真正需要定制的是:

组合方式
业务字段
交互规则
视觉风格

而不是重新实现一个 <button>


七、Tailwind CSS 在这里负责什么

shadcn/ui 经常和 Tailwind CSS 一起使用。

两者不要混为一谈。

可以理解成:

shadcn/ui
↓
提供可复用 UI 组件

Tailwind CSS
↓
负责组件的样式表达

例如:

<Button>创建单词书</Button>

背后依然需要处理:

padding
颜色
边框
圆角
hover
disabled
响应式

这些属于样式系统负责的事情。

因此:

Next.js
↓
组织应用

shadcn/ui
↓
提供 UI 组件

Tailwind CSS
↓
负责样式

三者职责并不相同。


八、为什么这种组件方式对 Coding Agent 也很友好

假设项目已经有:

components/ui/button.tsx
components/ui/dialog.tsx
components/ui/input.tsx

现在让 Coding Agent 完成:

实现“新增单词书”弹窗

它可以直接读取已有组件,然后组合:

Dialog
+
Input
+
Button

而不是重新生成另一套按钮和弹窗。

这其实体现了一条很重要的 AI Coding 原则:

项目规范越清晰,AI 越不需要猜。

对于开发者也是一样。

如果整个项目的 Button、Dialog、Table 都有统一实现,那么新增页面时就只需要关心:

这个业务到底要什么?

而不是每次重新处理基础 UI。


九、为什么 danci 先做后台 + H5

一个单词产品未来可以运行在很多“端”上:

PC Web
H5
小程序
Android
iOS
桌面端

但这些并不应该一开始全部开发。

PC Web

运行在电脑浏览器中,更适合:

后台管理
办公
内容浏览
需要 SEO 的 Web 页面

H5

在国内开发语境里,H5 通常指:

面向手机浏览器的移动 Web 页面或 Web 应用。

它本质上依然是:

HTML
CSS
JavaScript

只是重点变成:

手机屏幕
触摸操作
移动端布局
响应式适配

Android / iOS

属于移动客户端。

跨平台开发可以考虑:

React Native
Flutter

桌面端

Windows、macOS 等桌面应用可以使用 Electron 等方案,让 Web 技术运行在桌面客户端中。


十、为什么不是第一天就做所有端

如果一开始就同时做:

PC
H5
小程序
Android
iOS
Electron

会产生大量额外成本。

更合理的路线是:

先把核心业务跑通
↓
后台管理系统
+
H5
↓
验证产品
↓
再根据需求扩展其他端

因此“多端”更适合作为:

未来扩展能力

而不是第一版必须全部实现的功能。


十一、到这里,项目架构已经不是“拍脑袋选技术”

把整个过程重新串一次:

想做一个背单词应用
↓
单词内容需要有人维护
↓
需要后台管理系统
↓
后台先做单词书 CRUD
↓
后台不能谁都能进
↓
需要管理员
↓
第一个管理员无法凭空出现
↓
需要超级管理员初始化流程
↓
后台有大量通用 UI
↓
使用 shadcn/ui
↓
整个 Web 应用需要统一组织
↓
使用 Next.js

所以真正重要的不是背:

Next.js + shadcn/ui + Tailwind CSS

而是理解:

为什么项目走到这里以后,这些东西会自然出现。


十二、但是现在还有一个更重要的问题

到这里,页面可以做出来了。

例如管理员在 /books 点击:

创建单词书

填写:

name = 四级词汇

然后点击:

保存

问题来了:

这本“四级词汇”到底保存到哪里?

页面里的一个变量显然不够。

所以接下来就必须进入整个项目最重要的数据层:

PostgreSQL
Supabase
ORM
Drizzle
schema
migration
RLS

下一篇就从这个问题开始。