基于Spring Boot与Vue的艺术展览网站设计与实现:毕设项目实战指南

发布时间:2026/9/10 17:28:32
基于Spring Boot与Vue的艺术展览网站设计与实现:毕设项目实战指南 每年到了毕设季总有一批计算机专业的同学对着选题列表发愁尤其在web方向和Java方向之间反复横跳。今天我想认真聊聊一个非常适合练手、又容易出效果的题目基于web的艺术展览网站设计与实现。这类题目在很多学校的计算机毕设选题清单里都出现过编号17261这个版本是比较典型的规格——面向B端管理员和C端普通用户的双端系统核心业务包括展览信息展示、作品管理、用户注册登录、在线预约或购票、后台数据维护等。选它最大的好处是业务逻辑清楚、技术栈常规、可视化效果好不管你是准备走前后端分离还是传统JavaWeb单页应用都能塞进去。关键是你做完之后不管用来答辩还是写进简历里都能讲出完整故事。这篇文章不会跟你扯虚的我直接以这个艺术展览网站为题把整个毕设从需求分析、技术选型、数据库设计、前后端实现到打包部署、答辩准备完整撸一遍。文章里所有设计思路、表结构、代码片段都是实践后整理出来的你可以直接拿过去改改就用也能帮你避掉大部分新人常踩的坑。1. 为什么选这个题目选题价值与定位分析1.1 艺术展览网站的行业场景先搞清楚一个根本问题艺术展览网站到底是个什么东西它和普通的电商网站、企业官网、博客系统有什么本质区别艺术展览网站的核心业务是展而不是卖。它服务的对象是美术馆、画廊、艺术策展方主要职能是让观众在线上就能了解到正在展出的艺术展览、欣赏展品图片、查看展品介绍、了解艺术家背景甚至完成在线预约和票务预订。这和电商那种加购物车-下单-支付-发货的强交易流程不一样它更侧重于信息展示的视觉表现、内容的分类组织、用户浏览体验的沉浸感。这个场景放在毕设里是非常合适的。第一需求足够明确业务边界清楚答辩时你说得清自己做了什么。第二它比图书管理系统这类老掉牙题目更有视觉表现力前端能做的东西多用Vue或原生CSS都能做出漂亮页面评委看着舒服。第三它天然带在线预约这类交互动作能引出用户表、订单表、状态流转、时间冲突检测等全套逻辑技术上不会显得太单薄。1.2 这个题目对毕设的适配度很多同学选题目喜欢选那种看起来很厉害的人脸识别、推荐算法、智能客服。但说实话毕设的评分逻辑不是技术越新分越高而是功能和完整性匹配度。我见过太多人写推荐系统结果推荐逻辑就是个简单的按点击量排序答辩时被老师一追问就露馅了。艺术展览网站的优势恰恰在于对味它难度适中往上可以做复杂的权限控制、数据可视化大屏、支付流程对接往下可以做成课程设计级别的CRUD伸缩空间极大。你完全可以根据自己的能力定目标——拿个中等成绩做基础版就能过关想冲优秀再加几个亮点模块也不难。从就业角度说也一样现在Java后端、Web前端、全栈开发仍是市场需求量最大的岗位这类题做完你简历上能写的东西是实实在在的不像某些偏科研的题目做完都不知道怎么向面试官表达。1.3 技术难度评估与目标设定如果你基本功一般建议不要一上来就搞微服务、分布式那是给自己挖坑。把目标定在前后端分离 单机部署 3个核心业务闭环就完全够用了。我给你拆个节奏参考前端Vue 2或Vue 3 Element UI Axios ECharts可选后端Spring Boot 2.7.x MyBatis-Plus MySQL 8.0部署本地开发测试 云服务器部署阿里云/腾讯云学生机即可业务闭环1用户注册登录 个人信息管理业务闭环2展览列表浏览 作品详情 在线预约业务闭环3管理员登录 展览/作品/预约/用户管理这么一套下来代码量不大但五脏俱全你从需求分析到最终答辩其实是有一条非常清晰的叙事线的。2. 技术选型与整体架构设计2.1 各技术栈怎么选才不踩坑先说你最常见的纠结用原生Servlet JSP还是用Spring Boot 模板引擎还是前后端分离我的建议非常明确用Spring Boot做后端API前端选Vue做单页应用。理由不是追求新潮而是这套方案在2024年的今天学习资料最多、出错率最低、就业价值最高。如果你用Servlet JSP短期内写起来可能简单但后期维护很痛苦而且答辩时老师大概率会问为什么不用更现代的框架你不好答。也用不着担心Spring Boot难它已经把大量配置都封装好了说白了就是约定大于配置你只需要关注业务代码本身。MyBatis-Plus更是帮你把单表CRUD都省了直接继承BaseMapper就能拥有基础方法这对赶毕设简直是救命级福利。前端选Vue的原因也很实在它对新手友好模板语法直观文档中文版本成熟社区里随便一搜就是成型的例子。Element UI或新版Element Plus组件库里的轮播、卡片、分页、表格、表单组件正好覆盖这个项目的界面需求不用你自己从零雕CSS细节。2.2 前后端分离的目录结构规划这里贴一个我实际用下来非常顺手的项目结构你可以直接照着建art-exhibition-backend 后端Spring Boot项目 ├── src/main/java/com/example/artexhibition │ ├── controller # 控制层接收前端请求 │ │ ├── AdminController │ │ ├── ExhibitionController │ │ ├── ArtworkController │ │ ├── OrderController │ │ └── UserController │ ├── service # 业务层核心逻辑 │ ├── mapper # MyBatis-Plus Mapper接口 │ ├── entity # 实体类 │ ├── config # 配置类跨域、拦截器 │ ├── common # 统一返回结果、异常处理 │ └── utils # JWT、文件上传等工具 ├── src/main/resources │ ├── mapper # XML文件复杂SQL时可放这里 │ └── application.yml └── pom.xml art-exhibition-frontend 前端Vue项目 ├── public/ ├── src/ │ ├── api/ # Axios接口封装 │ ├── assets/ # 静态资源 │ ├── components/ # 通用组件 │ ├── router/ # 路由配置 │ ├── store/ # Pinia/Vuex状态管理 │ ├── views/ # 页面组件 │ │ ├── admin/ # 后台管理页面 │ │ └── user/ # 前台用户浏览页面 │ ├── App.vue │ └── main.js └── package.json这个结构把前后端完全拆开开发时可以并行推进部署时前端Build出来放到Nginx里后端打成Jar包单独跑互不干扰。2.3 为什么建议用JWT而不是Session老项目里做登录基本都是Session模式服务端存Session浏览器带Cookie。但在前后端分离的场景下CSRF问题、跨域Cookie携带问题都够你喝一壶的。建议直接用JWTJSON Web Token。用户在登录接口输入账号密码后端验证通过后签发一个Token前端拿到存到localStorage里后续每次请求在请求头里带上Authorization: Bearer t。后端用一个拦截器统一校验Token是否有效有效就放行无效就返回401。这样做的好处有三个无状态不占服务端内存天然支持跨域前后端分离架构下非常自然。网上JWT的封装代码一大把你花半天时间理解一下核心原理就能用起来。3. 数据库设计与数据模型3.1 核心数据表的设计思路艺术展览网站的数据库设计是整个项目的基石。我见过太多同学上来就建表结果做到后面发现少字段、关系混乱返工改SQL改到自闭。我建议你按用户-内容-交易三线来梳理用户线sys_user用户表、role字段区分管理员和普通用户。内容线exhibition展览表、artwork展品表、artist艺术家表。注意这里的关联关系一个展览可以有多件展品一件展品通常属于一个展览也可以做多对多但毕设建议用一对多简化展览和艺术家也是多对一。交易线appointment/order预约订单表、comment评论表、collection收藏表。预约单是系统的核心动作要记录预约用户、对应展览、预约日期、状态待审核/已确认/已取消/已完成。3.2 关键字段说明和建表SQL下面这份SQL是我精简过的核心表结构你可以直接用注意官方推荐的命名习惯表名小写加下划线字段没有前缀。-- 用户表 CREATE TABLE sys_user ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码BCrypt加密后存储, nickname varchar(50) DEFAULT NULL COMMENT 昵称, avatar varchar(255) DEFAULT NULL COMMENT 头像URL, phone varchar(20) DEFAULT NULL, email varchar(50) DEFAULT NULL, role varchar(20) DEFAULT USER COMMENT USER/ADMIN, status tinyint(1) DEFAULT 1 COMMENT 0禁用 1启用, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 展览表 CREATE TABLE exhibition ( id bigint(20) NOT NULL AUTO_INCREMENT, title varchar(100) NOT NULL COMMENT 展览标题, cover_image varchar(255) DEFAULT NULL COMMENT 封面图, description text COMMENT 展览简介, content longtext COMMENT 展览详情富文本, start_date date NOT NULL COMMENT 开始日期, end_date date NOT NULL COMMENT 结束日期, location varchar(100) DEFAULT NULL COMMENT 展馆位置, ticket_price decimal(10,2) DEFAULT 0.00 COMMENT 票价, status tinyint(1) DEFAULT 1 COMMENT 1上架 0下架, view_count int(11) DEFAULT 0 COMMENT 浏览量, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 展品表 CREATE TABLE artwork ( id bigint(20) NOT NULL AUTO_INCREMENT, exhibition_id bigint(20) NOT NULL COMMENT 所属展览ID, title varchar(100) NOT NULL, artist varchar(50) DEFAULT NULL COMMENT 作者, image varchar(255) DEFAULT NULL COMMENT 作品图片, description text COMMENT 作品介绍, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 预约/订单表 CREATE TABLE appointment ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 订单编号, user_id bigint(20) NOT NULL COMMENT 预约用户, exhibition_id bigint(20) NOT NULL COMMENT 预约展览, appointment_date date NOT NULL COMMENT 预约参观日期, quantity int(11) DEFAULT 1 COMMENT 预约人数, total_price decimal(10,2) DEFAULT 0.00, status varchar(20) DEFAULT PENDING COMMENT PENDING/CONFIRMED/CANCELLED/COMPLETED, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;另外建议再加两张表news展览资讯公告、banner首页轮播图配置。这两块虽然不是核心但加上它们前台内容会更丰富后台管理也更多了一层可操作性答辩时也有的讲。3.3 表关系梳理与冗余字段的技巧在表设计上有一个很实用的经验不要过度追求范式化该冗余的字段直接冗余。举个例子appointment表里我建议直接存一个exhibition_title快照字段。为什么因为如果展览下架了或者被管理员改名了历史订单仍然应该显示出用户当时预约的展览名称。你要是用联表去查遇到删除场景就会显示空数据。虽然从第三范式角度看这是冗余但对这个业务来说是合理的设计。再比如artwork表中有exhibition_id你在展示展品详情时需要显示展览名称直接通过联表查询没问题但如果你做了展品归档、展览删除的软删除功能也是冗余一个展览名称字段更保险。记住一句话毕设项目的数据量级根本不需要考虑存储成本多放语义明确的字段比精打细算少存几个字节要实用得多。4. 核心功能模块拆解与实现4.1 用户注册登录JWT鉴权与密码加密用户模块是几乎所有系统的入口。我在毕设里见过太多次明文密码这是一个非常不好的习惯答辩老师一看密码字段存的是明文印象分直接砍半。直接用Spring Security的BCryptPasswordEncoder加密用法非常简单// 注册时加密 String encodedPassword new BCryptPasswordEncoder().encode(rawPassword); user.setPassword(encodedPassword); // 登录时校验 boolean matches new BCryptPasswordEncoder().matches(rawPassword, user.getPassword());这个加密方式的特性是同一密码每次加密结果不同所以你不能用普通的等值比较必须用matches方法。登录成功后生成JWTpayload里放userId、username、role设置过期时间。这里有个细节Token的过期时间建议24小时不要设得过长否则安全性差也不要太短否则用户频繁掉线。生成逻辑网上有现成工具类用io.jsonwebtoken的jjwt依赖抽出JwtUtil后在拦截器里统一校验。你还需要处理一个常见问题跨域时前端要能在响应头里读到Authorization字段所以需要在CORS配置里显式暴露这个请求头。前端拿到Token存localStorage路由守卫里判断有没有Token没有就跳登录页。这个流程是一个标准得不能再标准的套路前后端各半小时就能搞定。4.2 展览与作品展示列表分页和详情前台的展览列表是整个网站的门面承载了用户的第一印象。这里有两个关键点封面图质量和分页性能。封面图这块前端展示时建议统一压缩为适合列表展示的尺寸。你可以在上传图片时后端就做一次压缩处理用Thumbnailator或者直接让前端上传前用canvas压缩也可以只做展示层的缩放但要注意大图太多时加载会很慢。图片渲染上如果是用Vue最好配合懒加载指令Element UI自带的v-lazy滚动到可视区域再加载体验会明显好很多。分页查询用MyBatis-Plus的selectPage就能解决PageExhibition page new Page(current, size); LambdaQueryWrapperExhibition wrapper new LambdaQueryWrapper(); wrapper.eq(Exhibition::getStatus, 1) .orderByDesc(Exhibition::getCreateTime); exhibitionMapper.selectPage(page, wrapper);在展览详情页除了基础的标题、时间、地点、票价一定要展示三个东西策展前言/简介、展品列表、预约入口。展品列表用横向滚动卡片或者瀑布流都行前端好看不难但注意数据量一旦大起来图片懒加载是必须的。4.3 在线预约与订单状态流转预约是整个系统业务闭环的核心动作。用户选择展览、选日期、填人数提交后生成一条预约订单。这里要处理好三个问题订单号生成不能简单的用自增ID建议用时间戳加随机数例如yyyyMMddHHmmss 6位随机数字保证唯一。日期校验预约日期必须落在展览的start_date和end_date之间这个校验后端一定要做前端校验可以绕过。重复预约一个展览同一用户能不能重复预约如果你允许那要放开如果不允许需要在数据库加唯一约束或者在查询时校验。建议设计成可重复预约但状态互不影响开发起来简单答辩也好解释。状态机设计上PENDING待审核-CONFIRMED已确认-COMPLETED已完成中间可以CANCELLED已取消。管理员在后台可以完成确认操作用户在前台可以完成取消操作系统定时任务可以把参观日期已过的订单自动置为COMPLETED。这一步如果愿意做可以顺手加一个Spring的Scheduled定时任务也是加分项。4.4 后台管理模块管理员的一站式控制台后台是给策展方/管理员用的功能上围绕内容运营展开。我建议拆成五个页面数据概览展示展览总数、展品总数、用户总数、今日预约数用卡片陈列选上ECharts画个一个预约趋势折线图效果拉满。展览管理CRUD 上下架操作 图片上传。展品管理按展览筛选CRUD。预约管理列表 状态变更确认/取消 按日期筛选。用户管理列表 启用/禁用。后台权限控制这块可以用一个简单方案管理员登录后多存一个role在JWT里前端根据role决定路由是否可见后端接口加拦截器校验只有ADMIN角色能访问/admin/**路径。不需要上Spring Security的注解权限否则学习成本会变大。还有一个细节文件上传。图片上传是后台模块绕不开的功能。前端用饿了么组件库的Upload组件后端实现一个通用的文件上传接口把文件保存到服务器某个目录返回可访问的URL。要注意三点限制文件大小比如单个不超过5MB、限制图片类型jpg/png/webp、给文件重命名用UUID避免中文名乱码和文件名冲突。5. 前端展示设计与交互体验5.1 艺术类网站的前端设计原则艺术展览网站的前端和其他系统的最大不同在于它强调氛围感。你不能把一个美术馆网站做得像后台管理系统一样全是表格和表单。具体操作上有几个方向成本低效果好第一主色调走极简路线。大面积留白、黑白灰打底、用一个点缀色比如赭石色、藏蓝色。避免用大红大绿艺术类用户群体偏好克制的高级感。第二字体讲究一点。标题用衬线字体比如宋体、思源宋体正文用无衬线思源黑体、系统默认就行。不用额外引入太多字体文件用font-family栈指定就行。第三图片是主角。让封面图有足够大的展示尺寸不要用半屏幕的banner直接把展品图放大居中配合少量文字说明这个页面就有美术馆里看展的味道了。5.2 首页结构从引导到转化的布局逻辑首页一般结构我用过很多版本最优的是这个顺序首屏轮播大图展示近期最重要的展览→ 正在热展卡片式展览列表→ 展品精选瀑布流→ 展馆资讯图文列表→ 底部关于/联系方式。首屏轮播是颜值担当建议放3张宽幅海报配合大标题和立即预约按钮。热展列表展示4到6个展览卡片每个卡片放封面图加展览时间地点。展品精选要打破网格感用瀑布流会更有艺术感。整个首页的目标是引导用户点击进入详情页完成预约所以每个卡片都可以加一个查看详情的悬停效果。前端布局用Element的el-row和el-col栅格系统响应式适配PC和移动端。这里有个容易踩得坑el-col的span值总和必须等于24否则布局错乱。做移动端适配时注意用xs、sm、md等断点属性来区分不同屏幕尺寸下的列宽。5.3 图片性能优化压缩、懒加载与CDN艺术展览网站是图片密集型站点不处理图片性能页面加载速度和用户体验都会翻车。实操建议三条上传时压缩。后端用Thumbnailator库一行代码就能生成缩略图和原图两种尺寸Thumbnails.of(inputStream).size(800, 600).toFile(...)。展示时懒加载。Vue项目配合Element的lazy属性或者在自定义图片组件里用IntersectionObserver实现滚动懒加载。部署时上CDN可选。如果你有云服务器图片走阿里云OSS CDN是最好的但没有的话用Nginx直接托管图片也有不错的效果。关键点是设置缓存头Cache-Control让浏览器对图片做强缓存。这些优化点随便挑一个写进论文或答辩PPT里都能体现你真的在考虑系统的实际问题而不是只写了增删改查。6. 完整开发流程与实操记录6.1 从零开始的第一步先搭后端还是先搭前端我的经验是先搭后端再搭前端最后联调。因为前端页面需要数据才能跑起来如果完全没有后端接口你只能写死Mock数据浪费时间。后端搭建流程大概是这样用Spring Initializr创建项目勾选Web、MySQL、MyBatis依赖。配置application.yml配好数据源和MyBatis-Plus的日志输出。编写统一的返回结果类ResultTcode、message、data定义成功失败方法。实现异常处理器RestControllerAdvice让所有异常都能返回统一格式避免一堆堆栈抛给前端。集成MyBatis-Plus写User、Exhibition等实体类继承BaseMapper。写Controller和Service一个接口一个接口测用Postman验证通过后再继续下一个。最后加JWT拦截器和跨域配置。这个顺序能保证你每一步都有可验证的结果而不是咔咔写完一堆代码一启动报500都不知道错在哪。6.2 前端核心代码实现接口封装与登录流程前端接口封装强烈建议做一层Axios拦截器统一处理Token携带和错误提示// request.js import axios from axios; import { ElMessage } from element-ui; import router from /router; const service axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || http://localhost:8080/api, timeout: 10000 }); // 请求拦截器自动携带Token service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] Bearer token; } return config; }); // 响应拦截器统一处理错误 service.interceptors.response.use( response { const res response.data; if (res.code ! 200) { ElMessage.error(res.message || 请求失败); return Promise.reject(new Error(res.message)); } return res; }, error { if (error.response error.response.status 401) { localStorage.removeItem(token); router.push(/login); ElMessage.warning(登录已过期请重新登录); } else { ElMessage.error(网络异常请稍后重试); } return Promise.reject(error); } ); export default service;登录Page里调用登录接口成功后把Token和用户信息存到localStorage和Vuex/Pinia里然后跳转到首页。这个地方有个小细节调用获取用户信息的接口时如果表单已经部分填写直接刷新页面会导致状态丢失建议用Pinia的持久化插件或手动同步到sessionStorage。6.3 本地联调的注意事项前后端分离开发时最大的敌人是跨域和端口冲突。后端配置里前端开发服务器是localhost:5173Vite默认端口后端是localhost:8080两者不同源所以必须在后端加跨域配置。最省事的办法是用CrossOrigin注解加在Controller类上或者在配置类里实现WebMvcConfigurer的addCorsMappings方法统一配置允许的来源、方法、请求头。还有一个高频问题前端请求带了Token但后端拦截器在preHandle方法里校验Token时报token为空。这个多半是跨域预检请求OPTIONS请求导致的——浏览器在发起真正的请求前会先发一个OPTIONS请求这个请求不会带上业务Header所以拦截器必须要放行OPTIONS请求否则前端会报CORS错误。Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行OPTIONS预检请求 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } // ... 校验Token逻辑 }6.4 部署上线从开发环境到云服务器的完整过程本地开发差不多了建议直接把项目部署到云服务器上。很多同学毕设答辩时还打开着localhost演示一旦现场网络或本机环境出问题场面非常尴尬。花钱租一台最便宜的云服务器学生机一年也就几十块把系统跑在公网上答辩时用手机热点访问都行这本身就是加分项。部署流程我列一下在服务器上安装JDK 8、MySQL 8.0、Nginx。把本地数据库导出SQL在服务器上导入。后端项目执行mvn clean package -DskipTests打包成Jar用nohup java -jar后台运行。前端项目执行npm run build生成dist目录配置Nginx静态站点指向dist。Nginx配置反向代理把所有/api开头的请求转发到后端的8080端口。Nginx配置示例server { listen 80; server_name yourdomain.com; root /opt/art-exhibition/dist; index index.html; location / { 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; } }这一步做完整你的毕设就有可交付的完整度了。注意部署后一定要用公网访问一遍所有核心流程浏览器、手机各测一遍别等到答辩前夜才发现图片路径写死了localhost。7. 常见问题与排查技巧实录7.1 典型报错与解决办法速查表整个开发过程中我敢说下面这些报错你至少有三分之一会碰到提前知道答案能省很多时间。现象原因解决办法前端请求后端接口报CORS错误后端没有配置跨域或拦截器拦截了OPTIONS全局CORS配置 拦截器放行OPTIONS登录后访问资源返回401Token过期或未携带检查请求拦截器Header拼接检查JWT过期时间中文乱码保存到数据库变成数据库连接URL没加characterEncodingutf8JDBC URL加?useUnicodetruecharacterEncodingutf8图片上传成功但访问404上传目录没有配置静态资源映射Spring Boot里继承WebMvcConfigurer配置addResourceHandlersElement Plus组件按需加载报样式缺失只引入了组件但没引入样式全量引入或配置unplugin-vue-components打包后刷新页面404Vue Router history模式需要服务端配合修改路由为hash模式或Nginx配置try_files后端启动报数据库连不上数据库没创建、账号权限不对、密码错误检查application.yml确认库名、密码、端口睡个觉回来打开项目发现页面空白开发服务器被系统或代理干扰先看浏览器Console报错Vite需要重启时别犹豫7.2 几个非常值得注意的隐蔽问题这里分享几个我实操中踩过、但网上很少提到的坑。第一个是时间格式化问题。后端返回给前端的时间字段默认是UTC格式比如2024-05-01T04:00:00.00000:00前端直接展示会跟本地时间差8小时。解决办法是在application.yml里统一配置时间格式spring.jackson.date-format: yyyy-MM-dd HH:mm:ss和spring.jackson.time-zone: GMT8。或者你就用前端dayjs库格式化也避免时区歧义。第二个是富文本编辑器的XSS问题。展览详情我建议用富文本编辑器wangEditor或Tinymce管理员可以填图文混排内容。但是富文本里可以插入script标签存在XSS跨站脚本注入风险。虽然毕设系统不面向真实生产但答辩时如果被问到安全问题你答不上来就尴尬了。建议在后端接收入参时做一下过滤用Jsoup库的Jsoup.clean(html, Safelist.basic())把危险的标签和属性洗掉。一行代码说出去却有模有样。第三个是图片上传的目录权限。如果你把上传目录放在项目的src/main/resources下打包成Jar后这个目录是只读的写不进去。正确做法是把图片上传到一个独立的绝对路径下比如/opt/art-exhibition/upload再把它通过静态资源映射暴露出去。这个坑几乎每个做Spring Boot上传功能的新手都会踩一次。7.3 论文与答辩准备的实战建议最后说点跟代码无关但跟毕业直接相关的。艺术展览网站这类题目论文里需要你重点写清楚的是需求分析、数据库设计、系统设计、系统实现、系统测试这几个章节。答辩时老师最常问的几个问题提前准备好答案为什么选这个技术栈答考虑毕设的可维护性 就业市场主流技术数据库为什么这样设计答围绕核心业务闭环兼顾查询效率和扩展性Token的安全性问题答设置过期时间、BCrypt加密存储、动态刷新如果用户量大了怎么优化答分库分表、Redis缓存、CDN加速、消息队列——说思路即可不用真做线上预约冲突怎么处理答日期范围校验 数据库唯一约束 定时任务清理过期订单这些问题都不难但你要自己能圆上别一问就卡壳。一个很实用的答辩技巧准备一张系统功能结构图和一张数据库ER图这是很多评委老师第一眼就会找的东西。做图用ProcessOn或者draw.io就行别用Visio导出的默认样式太丑印象分大打折扣。最后分享一点个人体会我每年都会接触不少做毕设的同学发现一个普遍现象真正把毕设做完的人无论题目简单还是复杂收获都远超预期而那些总想着混过去的人连代码都不是自己写的答辩的时候紧张得连项目里有什么模块都说不清。艺术展览网站做下来你会把从前端到后端、从数据库设计到服务器部署这条路完整走一遍——这比看二十遍教学视频都管用。你可以把这个项目当成一个起点答辩完再往里面加一个虚拟展厅用Three.js做3D空间或者接一个真实支付微信/支付宝沙箱环境整个项目的技术含量和可讲的故事就完全不一样了。做毕设这件事没有捷径但你认真做完一个项目后面求职面试时你会感谢当时那个熬了几夜也要把Bug调通的自己。