这次我们来看一个典型的全栈实战项目:国之动力科普网站。技术栈锁定在 Java + SpringBoot3 + Vue.js3 + MySQL,前后端分离,核心是一套“前台科普内容展示 + 后台内容管理”的网站系统。这个项目最有价值的地方不是某个炫酷的 AI 功能,而是它把 SpringBoot3 的后端接口、Vue3 的组件化页面、MySQL8 的数据建模完整串在了一起,适合正在做课程设计、毕业设计,或者想用 Java 全栈做第二个练手项目的读者。
从技术门槛来看,这套项目不需要特殊硬件,一台 8GB 内存的普通电脑就能跑。本地需要准备 JDK17 及以上、Node.js 18 及以上、MySQL8 和常用的 Java IDE。本文不会去打包下载资源,而是从零拆解这个科普网站的架构设计、数据库设计、后端接口、前端页面、联调方式和常见坑点。你也可以把“国之动力”这个主题替换成航天科普、能源科普、中医药科普等任意内容主题,网站骨架基本不变。
阅读完这篇文章,你能收获几条明确的执行路径:如何设计科普网站的表结构,如何使用 SpringBoot3 写文章/分类/登录相关接口,如何用 Vue3 做文章列表和详情页,如何把前后端跑通,以及部署前后遇到 MySQL 连接、跨域、内存不足、端口冲突等问题时怎么排查。内容偏工程实操,建议先收藏再按章节验证。
1. 核心能力速览与技术选型
先给一套完整的能力画像,方便你在动手前判断这个项目是否符合自己的需求。
| 能力项 | 说明 |
|---|---|
| 项目定位 | 前后端分离的科普内容展示与内容管理网站 |
| 后端框架 | SpringBoot3 |
| 前端框架 | Vue.js3 + Vite + Vue Router |
| 数据库 | MySQL8 |
| 推荐 JDK 版本 | JDK17 及以上 |
| 核心功能 | 科普文章浏览、分类检索、轮播图、用户登录、后台文章发布、分类维护、基础统计 |
| 接口能力 | RESTful API,可配合 Swagger/Knife4j 调试 |
| 批量能力 | 可通过脚本批量导入科普文章和分类数据 |
| 部署方式 | 前端构建静态资源 + 后端 jar 包 + Nginx 反向代理 |
| 适用人群 | 毕设、课设、Java 全栈学习者 |
选型上有几个点值得展开说。
第一,SpringBoot3 和 SpringBoot2 的最大区别是基准版本从 JDK8 提升到了 JDK17,同时原来的javax.*包全部迁移到jakarta.*。如果你以前看的是 SpringBoot2 老教程,现在直接抄代码很可能出现包名导入失败,这一点在这个项目里会反复遇到。
第二,Vue3 项目建议直接使用 Vite 脚手架而不是 Vue CLI。Vite 启动速度快、配置简单,配合@vitejs/plugin-vue就能完成单文件组件开发。状态管理用 Pinia,HTTP 请求用 Axios,UI 组件库推荐 Element Plus,后台管理页面开发效率会高很多。
第三,MySQL 选用 8.x。相比 5.7,MySQL8 默认字符集就是 utf8mb4,对中文科普内容更友好,同时支持窗口函数、CTE 等新特性。如果以后网站要加统计排行,SQL 写起来会轻松很多。
这个项目在技术上不算难,但功能链路比较完整,是典型的“麻雀虽小、五脏俱全”的全栈练习。
2. 适用场景与合规注意
在动手写代码前,先搞清楚这套系统能解决什么问题,不能解决什么问题。
它适合这几种场景:
- 课程设计或毕业设计需要一个前后端分离内容网站,需要展示数据库设计和 REST API。
- 想系统学习 SpringBoot3 + Vue3 的开发流程,而不是只写 CRUD Demo。
- 需要做一个“作品集”项目,展示自己在用户鉴权、分页查询、分类管理、文件上传上的工程能力。
- 想给某个科普主题快速搭一个内容官网雏形,后续接团队或官方内容源。
它不太适合的场景:
- 高并发的门户网站。没有 Redis 缓存、消息队列、分库分表时,单机 MySQL 扛不住大流量。
- 需要复杂富文本协作编辑、多人在线编审的 CMS。这类需求要用更专业的文档中台。
- 内容生产已经非常成熟、只差一个静态托管的情况。此时用 VuePress、Hugo 这类静态站点生成器更省成本。
合规边界需要单独强调。项目名称里有“国之动力”,实际开发时可以理解为一个面向公众的科学动力/大国工程主题科普站。开发这类项目时,要注意以下几点:
- 内容尽量来自权威公开资料,使用科普解读、图片、视频素材前要确认来源和版权,不要直接搬运无版权标注的网络图片。
- 后台管理系统要设置内容发布审核流程,最好有“草稿、待审核、已发布”的状态流转,避免未核实的内容直接上线。
- 涉及人物、机构、历史工程的内容,表述要以官方口径为准,不做主观评价,不渲染争议话题。
- 用户评论和昵称要做敏感词过滤和人工管理入口,保证社区内容合法合规。
技术本身是中性的,但科普内容面向公众,发布链路必须有分寸意识。
3. 系统架构与数据库设计
推荐采用经典前后端分离架构:
Vue3 科普前台(文章列表/详情/搜索) Vue3 后台管理(登录/文章维护/分类维护/轮播图) | HTTP / JSON(RESTful API) | SpringBoot3 控制层 / 业务层 / 数据访问层 | MySQL8(文章/用户/分类等)实际项目里不一定要拆成“前台 + 后台”两个 Vue 工程。如果开发时间有限,可以只建一个 Vue3 工程,用路由做用户端和管理端页面,用角色字段区分是否可进入后台。示例中的角色可以先分ADMIN和USER两种。
数据库设计是科普网站的地基。从零开发时,建议至少设计这几张表:用户表、分类表、文章表、轮播图表、评论表。下面给出一份参考建表 SQL,表名前缀加上sys_或t_都可以,关键是要能表达业务关系。
CREATE DATABASE IF NOT EXISTS guozhi_kepu DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; USE guozhi_kepu; CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT '登录用户名', password VARCHAR(100) NOT NULL COMMENT 'BCrypt 哈希后的密码', nickname VARCHAR(50) COMMENT '昵称', role VARCHAR(20) DEFAULT 'USER' COMMENT 'ADMIN 或 USER', status TINYINT DEFAULT 1 COMMENT '1 正常 0 禁用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_role (role) ) ENGINE=InnoDB COMMENT '用户表'; CREATE TABLE category ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT '分类名,如航天、能源、交通', sort INT DEFAULT 0 COMMENT '排序值', status TINYINT DEFAULT 1 COMMENT '1 启用 0 停用' ) ENGINE=InnoDB COMMENT '科普文章分类表'; CREATE TABLE article ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, category_id BIGINT NOT NULL, summary VARCHAR(500), content LONGTEXT, cover VARCHAR(500) COMMENT '封面图URL', author VARCHAR(100), source_name VARCHAR(200) COMMENT '来源,如某公开科普平台', source_url VARCHAR(500) COMMENT '原文链接,用于版权溯源', is_top TINYINT DEFAULT 0, status TINYINT DEFAULT 0 COMMENT '0 草稿 1 已发布', view_count INT DEFAULT 0, like_count INT DEFAULT 0, publish_time DATETIME, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_category (category_id), KEY idx_status_publish (status, publish_time) ) ENGINE=InnoDB COMMENT '科普文章表';建表时有两个容易被忽略的细节。
第一,文章表建议增加source_name和source_url字段。科普内容经常需要参考公开资料,有来源字段后,后台编辑可以在线复核,避免内容失真。这一点对“国之动力”这类主题尤其重要。
第二,不要把文章正文直接放在列表查询结果里。后端查询文章列表时只返回id、title、summary、cover、publish_time等核心字段,查看文章详情时才加载content大字段。这样列表接口才不会被LONGTEXT拖慢。
如果还要记录用户点赞和评论,可以继续增加评论表和点赞表。点赞表建议使用user_id + article_id做联合唯一索引,避免同一个人重复点赞。
4. SpringBoot3 后端环境准备与项目初始化
后端环境准备建议按以下顺序确认:
- JDK 版本。SpringBoot3 最低要求 JDK17。安装后可用
java -version检查。 - Maven 或 Gradle。推荐 Maven 3.8 以上,用它管理依赖和打包。
- MySQL8。本地安装完成后,确认 MySQL 服务已启动,并准备一个可登录的账号。
- IDE。推荐 IntelliJ IDEA。
创建 SpringBoot3 项目的方式有两种:使用 Spring Initializr 网页生成,或直接在 IDEA 中新建 Spring Initializr 项目。依赖方面,最小组可以勾选:
Spring Web Spring Validation MySQL Driver Lombok如果数据库访问层使用 MyBatis-Plus,要注意 SpringBoot3 需要选择mybatis-plus-spring-boot3-starter这类兼容版本,不能用面向 SpringBoot2 的旧 starter。这里给一个参考pom.xml片段:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> </dependencies>MySQL 连接配置是新手最容易出问题的地方。推荐在application.yml中显式写入时区、编码和连接参数:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/guozhi_kepu?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai username: root password: 你的数据库密码 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl关于allowPublicKeyRetrieval=true,很多读者会困惑。这是 MySQL8 默认认证插件caching_sha2_password引起的,如果客户端连接时没有成功获取公钥就会报错。开发环境下加上这个参数比较省事,生产环境则建议使用更安全的 SSL 连接方案。
后端项目创建完成后,先不要急着写业务,先写一个HealthController,能够返回 JSON 即可,确认 SpringBoot3 能正常启动,再去接数据库。
5. 后端核心模块实现思路
这个科普网站的后端不需要过度设计,但要保证模块边界清晰。推荐包结构如下:
com.example.guozhi ├── config // 跨域、MyBatis-Plus 分页、安全配置 ├── controller // 登录、文章、分类、轮播图接口 ├── service // 业务逻辑层接口和实现 ├── mapper // MyBatis 数据访问接口 ├── entity // 数据库实体类 ├── dto // 接收前端参数的类 ├── vo // 返回给前端的数据类 └── common // Result 统一返回体、异常处理写后端时先定义一个统一的返回体,比如Result<T>,包含code、message、data三个字段。接口签名里尽量不直接返回Map,而是返回Result<ArticleVO>。这样前端拿到数据结构一致,错误处理也简单。
接口路径建议统一带/api前缀:
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/auth/login | 登录,返回 token 和用户信息 |
| GET | /api/articles | 分页查询已发布文章 |
| GET | /api/articles/{id} | 查看文章详情,浏览量加一 |
| GET | /api/categories | 查询启用分类 |
| POST | /api/admin/articles | 新增文章,需要 ADMIN 权限 |
| PUT | /api/admin/articles/{id} | 更新文章 |
| DELETE | /api/admin/articles/{id} | 删除文章 |
| POST | /api/upload | 上传封面图 |
文章查询是一个很典型的后端实现场景。核心业务逻辑是:接收page、pageSize、categoryId、keyword参数,只查询status=1的文章,按is_top和publish_time排序,最后返回分页结果。可以用 MyBatis-Plus 的LambdaQueryWrapper实现:
public Page<ArticleVO> listPublishedArticles(int page, int pageSize, Long categoryId, String keyword) { LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Article::getStatus, 1); if (categoryId != null) { wrapper.eq(Article::getCategoryId, categoryId); } if (StringUtils.hasText(keyword)) { wrapper.like(Article::getTitle, keyword); } wrapper.orderByDesc(Article::getIsTop); wrapper.orderByDesc(Article::getPublishTime); Page<Article> pageResult = articleMapper.selectPage( new Page<>(page, pageSize), wrapper ); // 转成 VO,不返回 content 大字段 return convertToVO(pageResult); }登录鉴权方面,课程设计级项目不必把 Spring Security + JWT 的整套机制做得太复杂,但至少要覆盖两个目标:密码不能明文存数据库;后台接口必须校验登录状态和角色。密码用BCryptPasswordEncoder加密,登录成功后签发 token,管理端请求通过拦截器校验 token。要注意 SpringBoot3 下 Spring Security6 的配置写法与 5.x 差异很大,如果引用网络教程,优先找 SpringBoot3 版本的讲解。
6. Vue3 + Vite 前端环境准备与页面结构
前端建议直接用 Vite 创建 Vue3 项目。先检查 Node 版本:
node -v npm -vNode 18 及以上版本基本都能直接跑 Vite5。创建项目:
npm create vite@latest guozhi-kepu-web -- --template vue cd guozhi-kepu-web npm install npm install axios vue-router@4 pinia element-plus @element-plus/icons-vue项目启动后,开发服务器默认端口是 5173。页面结构可以按以下方式组织:
src ├── api // axios 接口方法 ├── assets ├── components // 公共组件,如文章卡片、分页条 ├── router // 路由配置 ├── store // Pinia 状态,如用户状态 ├── views │ ├── home // 首页,展示轮播和分类文章 │ ├── article // 文章列表和详情 │ └── admin // 后台管理页面 ├── App.vue └── main.js科普网站前台首页通常包含顶部导航、轮播图、科普分类入口、最新文章列表、精选推荐等模块。Vue3 的组件化写法和 Vue2 最明显的差别就是setup语法糖。写一个文章卡片组件时,可以直接用defineProps接收文章对象,模板里渲染标题、摘要和发布时间,减少大量this.xxx逻辑。
Axios 封装建议统一放在src/api/request.js中,方便统一处理 token 和错误码:
import axios from 'axios' const request = axios.create({ baseURL: '/api', timeout: 10000 }) // 请求拦截器:自动带上 token request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) // 响应拦截器:登录失效时跳转 request.interceptors.response.use( response => response.data, error => { if (error.response && error.response.status === 401) { window.location.href = '/login' } return Promise.reject(error) } ) export default request前端开发中最容易犯的错误是“接口联调时开启跨域”。其实解决办法很简单:在项目根目录vite.config.js中配置开发代理,把前端请求转发到后端 8080 端口:
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })这样前端的/api/articles会被 Vite 转发到http://localhost:8080/api/articles,不会出现恼火的跨域报错。后端也可以再配置一个 CORS 过滤器作为兜底。
7. 前后端接口联调与批量任务示例
前后端都启动后,不要直接打开页面点点点,先做一次最小 API 验证。后端启动后,访问http://localhost:8080/api/articles?page=1&pageSize=5,如果能看到 JSON 数据,说明数据库连接正常、文章查询接口正常。
接口自测可以用 curl,也可以用 Apifox 或 Postman。这里给出一组 curl 调用示例。
登录接口:
curl -X POST http://localhost:8080/api/auth/login \ -H "Content-Type: application/json" \ -d '{ "username": "admin", "password": "admin123" }'登录成功后,把返回的 token 保存下来,然后请求文章列表:
curl "http://localhost:8080/api/articles?page=1&pageSize=6&categoryId=1" \ -H "Authorization: Bearer 替换为你的token"如果希望更直观地调试接口,可以在后端加入 Knife4j 或 springdoc-openapi 的依赖,启动后访问/doc.html或/swagger-ui.html,直接在线请求接口。值得注意的一点是,SpringBoot3 对应的 springdoc 版本与 SpringBoot2 不同,如果加不上依赖,优先检查版本兼容性。
批量能力是这个项目容易被忽视的卖点。科普网站的文章数量往往比较多,如果只靠后台手动一条一条发布,效率太低。比较合理的做法是:准备 CSV 或 JSON 文件,字段包括标题、分类、摘要、封面、正文等,然后写一个脚本调用管理端批量接口导入。
下面给出一个 Python 批量导入脚本的伪代码模板:
import csv import requests BASE_URL = "http://localhost:8080/api" TOKEN = "从登录接口获取的token" HEADERS = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json" } def get_category_id(name): # 简化逻辑:调用分类接口,按名称找到分类ID resp = requests.get(f"{BASE_URL}/categories", headers=HEADERS, timeout=10) for item in resp.json().get("data", []): if item["name"] == name: return item["id"] return None def import_articles(csv_path): success_count = 0 fail_count = 0 with open(csv_path, "r", encoding="utf-8-sig") as f: reader = csv.DictReader(f) for row in reader: category_id = get_category_id(row["category"]) payload = { "title": row["title"], "categoryId": category_id, "summary": row.get("summary", ""), "content": row.get("content", ""), "cover": row.get("cover", ""), "status": 1, "publishTime": row.get("publishTime", "2025-01-01 10:00:00") } try: resp = requests.post( f"{BASE_URL}/admin/articles", json=payload, headers=HEADERS, timeout=20 ) if resp.status_code == 200: success_count += 1 else: fail_count += 1 print(f"导入失败: {row['title']}, {resp.text}") except Exception as exc: fail_count += 1 print(f"请求异常: {row['title']}, {exc}") print(f"批量导入完成,成功 {success_count} 条,失败 {fail_count} 条") if __name__ == "__main__": import_articles("articles.csv")这个脚本很适合用来验证后台接口是否稳定。实际执行时建议先拿 5 条测试数据跑通,再全量导入,避免一次导入 500 条后因某条数据异常而无法定位问题。
如果批量导入量比较大,比如一次几千篇,就不建议在一个 HTTP 请求里循环了。更稳妥的做法是把文章数据写入数据库临时表,通过后台定时任务或消息队列消费;也可以在 SpringBoot 里加一个异步批量导入接口,由后端统一处理日志和失败重试。项目早期阶段,先跑通脚本导入已经足够。
8. 资源占用、性能观察与部署建议
这类网站跑在本地时,资源占用大头来自三个地方:后端 JVM、前端开发服务器、MySQL 服务。
本机内存如果只有 8GB,建议启动后端时把 JVM 堆内存限制一下。比如:
java -Xms256m -Xmx512m -jar guozhi-kepu-backend.jar这样后端进程一般能控制在 500MB 左右,防止多个服务一起启动时电脑卡顿。前端开发模式启动npm run dev后,Node 进程占用不大。真正吃内存的通常是后台同时开着 IDEA、Chrome、MySQL,所以在本地写代码时要留意任务管理器里的 Java 进程是否残留。多次重启后端后容易出现旧进程还占着 8080 端口,此时用命令查看并结束进程即可。
显存之类的 GPU 概念在这个项目里不适用,CPU 和数据库索引才是性能关键。观察性能时优先看这几点:
- 文章列表接口响应时间。
- MySQL slow query log 中是否有全表扫描。
- 前端页面加载时是否一次性请求过多接口。
从设计角度,给文章表加上分类索引和状态索引,列表查询基本不会慢。如果article.content很大,查询详情时不要select *,而是只查需要的字段。访问量上来以后,可以引入 Redis 缓存首页热门文章和分类列表,减少后端对 MySQL 的重复查询。
部署到服务器时,推荐顺序是:
- 后端打 jar 包:
mvn clean package -DskipTests。 - 前端构建:
npm run build,产物在dist目录。 - 在服务器安装 Nginx,将
dist目录映射为静态站点。 - 将 Nginx 中
/api请求反向代理到http://127.0.0.1:8080。
Nginx 关键配置片段如下:
server { listen 80; server_name your-domain.com; location / { root /var/www/guozhi-kepu/dist; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这里要注意try_files $uri $uri/ /index.html;是 Vue Router history 模式必需的路由回退配置。如果不加,刷新/article/1这类页面时 Nginx 会返回 404。
9. 常见问题与排查方法
这个项目最常见的报错基本集中在 MySQL 连接、SpringBoot 启动、Vue 请求失败三类。整理成排查表,按表操作可以少走很多弯路。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 启动 Java 报 java.lang.OutOfMemoryError: Insufficient memory | JVM 堆内存不足或系统可用内存不够 | 查看系统内存和 Java 进程 | 调小 -Xmx;关闭多余 Java 进程 |
| MySQL 报 ERROR 2002 (HY000): Can't connect to local MySQL server through socket | MySQL 服务未启动,或客户端默认走 socket 连接 | 检查 MySQL 服务状态 | 重启 MySQL;连接时显式使用 TCP:mysql -h127.0.0.1 -P3306 -uroot -p |
| 连接 MySQL8 报 Public Key Retrieval is not allowed | 认证插件为 caching_sha2_password,客户端不能自动获取公钥 | 检查 JDBC URL | URL 增加allowPublicKeyRetrieval=true&useSSL=false |
报找不到com.mysql.cj.jdbc.Driver | 使用了过旧驱动,或没有引入mysql-connector-j | 检查 pom.xml | SpringBoot3 使用com.mysql:mysql-connector-j,版本跟随 SpringBoot 管理 |
| 数据库中文乱码 | 数据库表字符集或连接编码不一致 | 查询库表字符集 | 库表统一使用 utf8mb4,URL 加characterEncoding=utf8 |
| 前端页面打不开,提示端口被占用 | 5173 或 8080 被占 | `netstat -ano | findstr 端口` |
| 前端请求 /api 返回 404 | 后端接口路径不匹配,或未加统一前缀 | 查看后端日志和浏览器网络面板 | 统一接口前缀,检查 Nginx/Vite 代理路径 |
| 前端请求后端提示 CORS error | 开发环境未配置代理,或后端未配置跨域 | 查看浏览器 Network 和 Request URL | 优先使用 Vite proxy;后端可再配置 CORS 过滤器 |
| 后台接口返回 401 | token 缺失、过期或角色不对 | 查看请求头 Authorization | 重新登录;确认接口需要 ADMIN 角色 |
| 刷新 Vue 页面出现 404 | 部署环境没有配置 history 路由回退 | 直接访问首页正常,刷新子路由失败 | Nginx 增加try_files $uri $uri/ /index.html; |
这里要单独提一下 MySQL 连接问题。很多新手在安装 MySQL8 后,直接执行mysql命令,系统会默认走 socket 方式连接,如果服务没启动就会看到/tmp/mysql.sock之类的报错。实际开发项目时,JDBC 驱动走的都是 TCP 连接,因此只要 MySQL 服务启动、账号密码正确、端口没被防火墙拦截,一般都能连上。如果用 Navicat 这类图形工具连接时报Access denied,优先检查用户名、密码和主机权限。
10. 最佳实践与后续扩展建议
最后给几条工程化建议,这些经验不只在“国之动力科普网站”里有用,任何 SpringBoot3 + Vue3 + MySQL 内容系统都适用。
第一条,先跑通最小闭环再扩展功能。很多读者一上来就设计评论、点赞、收藏、问卷、数据大屏一堆功能,结果代码写了一周还在调试登录。建议按这个顺序推进:数据库建表 -> 登录接口 -> 文章接口 -> 前端文章列表和详情 -> 后台发布文章 -> 批量导入手稿。最小闭环跑通后,再慢慢加统计、审核、搜索等能力。
第二条,把上传文件、数据库密码、外部 API Key 等配置和业务代码分离。本地开发可以用application-dev.yml,生产环境用application-prod.yml,通过启动参数--spring.profiles.active=prod切换。不要把数据库密码直接写在 Vue 的前端代码里,前端只能通过后端接口拿数据。
第三条,批量任务必须考虑失败重试和日志。无论用 Python 脚本导入文章,还是用 Spring 定时任务抓取数据,至少要记录“成功多少、失败多少、失败原因是什么”。最忌讳的是脚本执行完没有任何日志,你不知道数据库里有没有重复数据,也不知道哪条内容导入失败。
第四条,上传素材和内容引用要留痕。科普类网站涉及图片、视频、数据图表较多。建议后台为每个文章维护“封面图 URL、来源名称、来源链接”三个字段,一方面方便溯源,另一方面也能避免无授权的素材直接上线。如果后续有商业化计划,这个问题会更加重要。
后续扩展可以从这几个方向考虑:
- 增加 Redis 缓存首页接口和热门文章,降低数据库压力。
- 增加全文搜索引擎,例如 Elasticsearch,替代 MySQL LIKE 模糊查询。
- 增加文章发布定时任务,支持批量排期发布。
- 增加导出功能,把科普文章内容按 PDF 或 Markdown 导出,方便线下分享。
- 增加数据可视化大屏,展示各分类文章数、浏览趋势、地域分布等数据。
从项目整体来看,SpringBoot3 负责稳定提供数据服务,Vue3 负责把数据变成可读性强的科普页面,MySQL 负责把内容资产沉淀下来,这三者的组合足够支撑一个内容型网站从开发到上线的全流程。建议你拿到这套技术思路后,先把你熟悉的科普主题替换进去,然后把用户注册、文章列表、后台发布、Nginx 部署这四个环节完整跑一遍,踩过的坑基本就能涵盖全栈开发的大部分常见问题。