从功能开发到服务器上线:校园学习协作平台项目复盘
从功能开发到服务器上线:校园学习协作平台项目复盘
关键词:Vue 3、TypeScript、Rust、Axum、MySQL、Redis、Docker、Caddy、HTTPS、全栈项目、部署排障
一、项目背景:为什么做这个平台
这个项目最初来自一个很直接的需求:在学院课程学习和项目实践中,信息经常散落在聊天群、网盘、个人文档和口头经验里。学生想了解一门课的难度、工作量、资料质量,或者想找同学一起做课程项目、比赛项目,往往需要反复询问。信息没有沉淀下来,后续同学也很难复用。
因此我做了一个“软件工程学院学习协作平台”,希望把课程经验、学习资料和项目招募统一放到一个系统中。它不是一个单纯的 CRUD 练习,而是一次从需求拆解、前后端开发、数据库建模、权限控制,到 Docker 部署、域名 HTTPS 配置和线上问题排查的完整工程实践。
从就业展示角度看,这个项目的重点不只是“页面能不能点”,而是我在项目中完整经历了一个 Web 应用从本地开发到公网可访问的过程,并解决了多个真实部署问题。
二、项目功能概览
平台主要分为用户端和管理端。
用户端包含课程列表、课程详情、课程评价、学习资料、项目招募和个人中心。用户可以按课程名称、课程代码、教师搜索课程,也可以对课程进行评分和评价。学习资料支持两种形式:文件资料和外部链接资料。项目招募模块支持用户发布项目、申请加入项目,发布者可以在个人中心处理收到的申请。
管理端包含后台首页、课程管理、内容管理、用户管理和项目管理。管理员可以查看系统统计数据,管理课程,搜索和处理学习资料,禁用或恢复用户,隐藏或关闭违规项目。后台还支持今日活跃人数和每日活跃情况统计,用来观察系统使用情况。
项目中还加入了一些更贴近真实业务的规则,例如:
- 每个用户最多保留 5 个未关闭、未隐藏的招募。
- 用户上传的文件资料总大小限制为 1 GiB。
- 单个文件大小通过
MAX_UPLOAD_MB配置。 - 上传资料后立即展示,后台后续可以隐藏或删除。
- 点赞可以取消,经验值会随点赞和取消点赞同步变化。
- 管理员权限在后端校验,不能只依赖前端隐藏按钮。
这些规则让项目从简单的数据展示,逐步接近真实系统中的状态管理和权限边界问题。
三、技术选型与架构设计
前端使用 Vue 3、Vite、TypeScript、Pinia、Vue Router 和 Element Plus。Vue 3 适合快速构建单页应用,Element Plus 提供了表格、表单、分页、弹窗等常用组件,能快速搭建课程列表、后台管理、个人中心等页面。
后端使用 Rust、Axum、Tokio、SQLx、JWT、Redis 和 MySQL。选择 Rust + Axum 的原因是希望在项目中实践更强的类型约束和更清晰的错误处理。SQLx 用来访问 MySQL,Redis 用于邮箱验证码和发送频率限制,JWT 用于无状态登录认证。
生产环境通过 Docker Compose 编排四个核心服务:
- MySQL:保存用户、课程、评价、资料、项目、申请等数据。
- Redis:保存验证码和缓存类数据。
- Rust 后端:提供 REST API。
- 前端 Nginx:托管静态文件,并将
/api请求转发到后端。
最终线上访问链路是:
1 | 浏览器 |
这个结构的好处是:前端、后端、数据库、缓存都可以容器化管理,对外只暴露 80 和 443,数据库和 Redis 只监听本机端口,安全边界更清晰。
四、从本地开发到线上部署
本地开发时,我使用 Docker 启动 MySQL 和 Redis,后端通过 cargo run --release 启动,前端通过 npm run dev 启动。这样调试速度较快,也方便查看后端日志和数据库状态。
上线时,我将项目推送到 GitHub,然后在阿里云 ECS 上拉取代码,使用 docker-compose.prod.yml 进行生产部署:
1 | docker compose -f docker-compose.prod.yml up -d --build |
部署完成后,通过下面命令检查服务状态:
1 | docker compose -f docker-compose.prod.yml ps |
HTTPS 使用 Caddy。域名解析到服务器公网 IP 后,Caddy 自动申请证书。最终采用 host network 模式运行 Caddy,反向代理到前端服务:
1 | campus.chenh735blog.top { |
最终验证命令:
1 | curl -i https://campus.chenh735blog.top/api/courses?page_size=10 |
当返回 HTTP/2 200 时,说明 HTTPS、Caddy、前端 Nginx、后端 API 这一整条链路已经打通。
五、部署过程中遇到的问题与解决
1. Rust 后端构建版本不兼容
第一次在 Docker 中构建后端时,遇到了 Rust 依赖要求更高编译器版本的问题。错误信息指向部分依赖需要更新版本的 rustc。
这个问题让我意识到:Dockerfile 里的基础镜像版本不是随便选的,后端依赖升级后,基础镜像也要匹配。最终我将构建镜像调整为较新的稳定 Rust 镜像,解决了 MSRV 不匹配的问题。
2. 服务器配置太低导致编译卡死
最开始使用 2 核 2 GiB 的服务器构建 Rust release 包,构建过程非常慢,甚至会导致 SSH 和 VS Code Remote 连接不稳定。
排查后发现主要是内存不足。Rust release 编译本身比较吃内存,Docker 构建、Node 构建和系统进程叠加后,服务器很容易卡住。
解决方式有两个:
- 临时增加 swap,缓解内存不足。
- 将服务器升级到 2 核 4 GiB。
同时我使用 tmux 保持构建任务,即使 SSH 断开,服务器上的构建进程也能继续运行。
3. Docker 和 Cargo 依赖下载慢
服务器上拉取 Docker 镜像和下载 Rust crates 依赖时,经常出现速度慢或超时。
这个问题的解决思路是减少不必要构建,并配置更稳定的镜像源。比如只改前端时,不再重新构建后端,而是使用:
1 | docker compose -f docker-compose.prod.yml build --no-deps frontend |
这让我对 Docker 构建缓存、服务依赖和增量部署有了更直接的理解。
4. SMTP 邮箱验证码发送失败
注册功能依赖邮箱验证码。部署后我遇到过验证码发送失败,日志里出现连接失败或网络不可达。
排查过程中,我分别检查了 SMTP 配置、服务器到 SMTP 服务的连通性、容器 DNS 解析和网络模式。最后发现容器默认网络环境下访问外部 SMTP 服务不够稳定,于是将后端容器改成 host network,让后端直接使用宿主机网络访问 SMTP。
这个问题让我意识到:线上问题不能只看代码,还要看网络、DNS、端口、安全策略和第三方服务。
5. Caddy 反向代理 502
上线 HTTPS 后,前端和后端本地访问都正常,但通过域名访问 API 返回 502。
我的排查顺序是:
1 | curl -i http://127.0.0.1:8080/api/courses?page_size=10 |
前两个返回 200,域名返回 502,说明问题不在业务代码,而在最外层 Caddy 代理。
根因是 Caddy 运行在容器网络中,访问不到宿主机绑定的 127.0.0.1:8081。最终我让 Caddy 使用 host network,并代理到 127.0.0.1:8081,问题解决。
这次经历让我更加理解“容器里的 localhost”和“宿主机 localhost”不是同一个东西。
6. favicon 缓存问题
我给网站添加图标时,发现文件已经更新,但浏览器还是显示旧图标。后来发现 favicon 的缓存非常顽固。
解决方式是给 favicon 地址加版本号,例如:
1 | <link rel="icon" type="image/svg+xml" href="/site-icon.svg?v=1" /> |
再通过无痕窗口或强制刷新验证。这是一个小问题,但也提醒我:前端上线后,缓存策略同样会影响用户看到的效果。
六、我如何定位线上问题
这次部署让我形成了一个比较清晰的排障顺序:
- 先看容器状态:
docker compose ps - 再看后端日志:
docker logs --tail 100 campus-backend - 从内到外测试接口:后端端口、前端代理、HTTPS 域名
- 如果本地端口正常但域名失败,优先查 Caddy 或 Nginx
- 如果服务刚重启,先确认 MySQL 和 Redis 是否 healthy
- 如果构建卡住,检查内存、swap、CPU 和网络下载情况
比起直接猜问题,我更倾向于用 curl、日志和容器状态逐层缩小范围。这个习惯在实际开发和运维中非常重要。
七、测试与验收
部署完成后,我主要验证了以下功能:
- 注册登录、邮箱验证码、管理员登录。
- 课程搜索、分页、分类筛选和详情页。
- 课程评价发布、点赞、取消点赞。
- 文件资料上传、链接资料上传、资料下载。
- 项目招募发布、申请加入、发布者处理申请。
- 后台用户管理、内容管理、课程管理、项目管理。
- 今日活跃人数统计。
- HTTPS 域名访问。
- Docker 重启后数据不丢失。
这些测试覆盖了普通用户、管理员、游客三类角色,也覆盖了前端、后端、数据库、缓存、文件上传和 HTTPS 访问链路。
八、项目收获
这个项目让我对“完成一个能上线的 Web 应用”有了更完整的认识。
在功能开发上,我学习了如何把需求拆成课程、资料、项目、用户、后台管理等模块,并处理不同角色之间的权限边界。
在后端开发上,我实践了 Rust Axum 的接口组织、SQLx 数据库访问、JWT 鉴权、Redis 验证码缓存、文件上传和错误处理。
在前端开发上,我使用 Vue 3 和 Element Plus 搭建了较完整的用户端和管理端页面,并处理分页、搜索、表单、弹窗、状态同步等交互。
在部署运维上,我真正经历了 Docker 构建、服务器资源不足、SMTP 网络问题、域名解析、HTTPS 证书、Caddy 502 等问题。这些经历让我意识到,工程能力不只是在本地把功能写出来,还包括把系统稳定地跑在服务器上,并能在出问题时定位原因。
九、后续优化方向
如果继续迭代,我会优先做这些事情:
- 增加自动化测试,覆盖核心接口和权限边界。
- 接入 CI/CD,让 GitHub 推送后自动构建和部署。
- 将上传文件迁移到对象存储,减轻服务器磁盘压力。
- 增加后台监控和错误告警。
- 优化前端打包体积和首屏加载速度。
- 增加更完整的搜索能力,例如按标签、课程学期、资料类型检索。
- 完善审计日志,记录更多后台关键操作。
总体来说,这个项目是一次从“会写功能”到“能部署、能排障、能复盘”的完整练习。它让我更清楚地认识到,一个就业向项目最有价值的地方,不只是技术栈列表,而是背后解决问题的过程。
