做毕设这几年,每年总有学生拿“XX文化宣传系统”来问我,今年这个《基于Spring Boot的黄河流域非遗数字化展示与传播平台》算是印象比较深的一个。题目看着长,拆开就是三件事:用Spring Boot做一套黄河文化相关的非遗资源管理与展示网站,前台面向访客做数字化传播,后台给运营人员做资源管理。它解决的核心问题是黄河沿线非遗项目信息散落各处、缺乏集中展示和推广入口的问题。适合参考的人群很明确:正在做文化类、宣传类、资源展示类毕设的本科生,以及想通过一个完整项目系统上手Spring Boot实战的新手。
这类题目在毕业设计里属于“立意好、难度适中”的类型,不需要多高深的算法,但要你完整跑通一个流程——从需求分析、数据库设计、前后端联调到最终部署演示。做完一遍,Spring Boot的核心用法、MyBatis-Plus的增删改查、权限控制的思路基本就全熟了。下面我把这类项目从零到一的整体思路、关键代码、踩坑记录和答辩准备全部梳理一遍,给你一份可以直接对照操作的参考。
1. 项目定位:这个系统到底在做什么
1.1 题目里的三个关键词,决定了系统的边界
“黄河文化”“非遗数字化”“展示与传播”,这三个词已经把系统的功能和业务范围框死了。先说“黄河文化”——黄河流经青海、甘肃、宁夏、陕西、山西、河南、山东等地,沿线非遗资源非常密集:青海的花儿、甘肃的兰州太平鼓、陕西的华阴老腔、山西的晋南威风锣鼓、河南的豫剧和黄河澄泥砚,还有山东的鲁西南鼓吹乐和杨家埠木版年画,都是国家级非遗项目。这些内容各有各的地理分布、传承流派和技艺特点,信息天然就是碎片化的。
“非遗数字化”决定了系统里一定要有一张承载结构化非遗信息的主表,名称、地区、级别、类别、封面图、简介、正文这些字段都需要。而“展示与传播”说明前台要有比较好的浏览体验——轮播推荐、分类导航、热门排行、检索筛选,还得给后台留内容维护的入口。很多学生把这个题目做成了纯后台管理系统,全是列表和表单,前台几乎没有展示能力,这就等于把题目砍掉了一半。一个合格的文化宣传平台,前台体验占的分量不比后台功能轻。
1.2 技术选型:为什么非Spring Boot不可
毕设圈绕不开的一个问题:为什么用Spring Boot,而不是用以前的SSM框架,或者干脆用Python的Flask?我的看法是,Spring Boot并不是什么激进的新技术,它把Spring生态里大量繁琐的配置工程化、约定化了。你只需要写很少的配置文件,就能把一个内嵌Tomcat的Web应用直接跑起来,这种开箱即用的体验对毕设开发节奏非常友好。
Spring Boot在这个项目里的具体价值,可以总结成三点:
- starter依赖帮你统一管理版本号,不用再像SSM那样手动协调Spring、SpringMVC、MyBatis三套框架的版本兼容问题;
- 内嵌Tomcat让部署变为“打一个jar包直接运行”,答辩演示的时候不用在机房配环境配到崩溃;
- 自动配置配合application.yml,把数据源、连接池、上传大小、端口这些基础设施集中到一处管理。
相比之下,SSM光配置文件就要写七八个,花时间还没学到更多东西。Spring Boot把效率省出来放在业务实现上,对毕设来说是最优解。如果答辩老师问“为什么不用Spring Cloud”,你也很容易回答:一个单体项目用微服务架构是过度设计,Spring Boot作为基础框架已经足够,技术栈真正要解决的是业务问题。
1.3 功能模块划分:前台展示与后台管理两条线
这类宣传平台的标准结构,我习惯拆成两个角色、三条功能线来规划。游客和普通用户可以浏览首页、查看非遗列表、按分类或地区筛选、搜索关键词、进入详情页后收藏和评论;管理员负责维护非遗项目的增删改查,管理分类和标签,发布资讯和轮播推荐,并审核用户评论。
| 角色 | 功能模块 | 核心设计要点 |
|---|---|---|
| 游客/普通用户 | 首页轮播、非遗列表、分类浏览、地区筛选、关键词搜索 | 查询条件组合、热门排序 |
| 普通用户 | 详情展示、收藏、评论 | 收藏唯一约束、评论状态控制 |
| 管理员 | 非遗管理、分类管理、资讯管理、轮播管理、评论审核 | 登录鉴权、操作权限控制 |
这种划分基本覆盖了题目里“管理”和“推广”两个动作,答辩时候也能把话讲清楚:前台负责数字化的“展示与传播”,后台负责文化资源的“管理与维护”,两边加起来才是一个完整的平台。
2. 数据库设计:非遗资源的数据建模
2.1 核心业务表一览
数据库设计是我判断一个毕设有没有用心的重要指标。很多人的表结构是照着页面功能硬建的,字段拍脑袋就写,关系之间乱成一锅粥。其实这个系统的核心表就那么几张,但每张表都有讲究。
- user:用户表,存储账号、密码、角色、昵称和头像;
- heritage:非遗项目表,是整个系统的主表;
- heritage_category:非遗分类表;
- tag 和 heritage_tag:标签表以及标签关联表;
- comment:评论表;
- collect:收藏表;
- news:文化资讯表;
- banner:首页轮播图表。
其中heritage表字段设计如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增 |
| name | varchar(100) | 非遗项目名称 |
| category_id | bigint | 关联分类表 |
| province | varchar(50) | 所属省份/地区 |
| level | varchar(20) | 级别:国家级、省级等 |
| cover_image | varchar(255) | 封面图路径 |
| summary | varchar(500) | 项目简介,列表页展示 |
| content | longtext | 详细介绍正文 |
| view_count | int | 浏览量,用于热门排序 |
| collect_count | int | 收藏量,用于推荐排序 |
| status | tinyint | 上下架状态 |
| create_time | datetime | 创建时间 |
| update_time | datetime | 更新时间 |
建表时我特别建议给name加普通索引,给category_id和province加上联合索引。非遗数据虽然体量不大,但列表页会频繁按分类和地区查,没有索引也能跑,有了索引执行计划会明显更规范,这也是答辩时能拿出来讲的性能设计点。
2.2 分类表和标签表为什么要独立
分类和标签都表示资源的属性,但它们的性质不一样。分类是树状的、有限的,比如传统音乐、传统舞蹈、传统戏剧、传统美术、传统技艺、民俗这几大类,适合用单独一张表维护,管理员可以在后台增改分类。而标签是扁平的、灵活的,比如“黄河流域”“非遗进校园”“活态传承”这种主题性标记,一个项目可以挂多个,就跟电商商品的标签一样。
所以我把标签设计成tag和heritage_tag两张表,中间表只存heritage_id和tag_id。一定有同学问:为什么不直接在heritage表里加一个tags字段用逗号分隔存?这种设计在展示的时候确实省事,但一旦要“按标签筛选所有相关项目”,SQL就得写LIKE去模糊匹配,数据量一大就吃力。而且中间表还能方便统计每个标签下有多少项目,将来做推荐或者做数据大屏都有基础。设计上的这点取舍,放在数据库设计说明里就是一个小亮点。
2.3 用户、收藏、评论的关系处理
用户和项目是多对多关系,中间产物就是收藏表和评论表。收藏表最简单,字段只有id、user_id、heritage_id和create_time,但必须给(user_id, heritage_id)加唯一约束,防止同一个用户对同一个项目重复收藏。很多人在代码里用先查后插的方式去重,其实数据库层面的唯一约束才是兜底方案,双写同时并发的时候才不会被穿透。
评论表要预留一个status字段,0表示待审核、1表示已通过、2表示已被驳回。非遗平台虽然不像社交平台那么敏感,但毕竟是面向公众的文化展示平台,评论审核是必要的管理动作。审核功能实现也很简单:后台列表按status筛选,通过就是把status改成1,删除就是把这条记录删除。
用户表里密码字段不建议明文存,用BCrypt加密是很成熟的做法。Spring Security里自带BCryptPasswordEncoder,就算不引入整个Spring Security,单独引入spring-security-crypto这个依赖也能用。密码加密是答辩老师比较喜欢追问的点,提前准备好这一手能加分。
3. 核心功能实现:从工程搭建到关键代码
3.1 快速创建一个Spring Boot项目并做好基础配置
创建项目最快的方式还是Spring Initializr。你在浏览器打开start.spring.io,把工程信息填好,依赖勾上Spring Web、MyBatis框架相关starter和MySQL驱动,生成压缩包解压后导入IDEA就能跑。国内网络环境下载依赖比较慢,可以把Maven中央仓库换成国内镜像,能省很多时间。
依赖方面我建议的完整组合是这样的:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>这里要特别提醒版本选择。如果你的JDK是8,Spring Boot老老实实用2.7.x,MyBatis-Plus用3.5.x就没问题;如果你用JDK 17甚至更高,Spring Boot需要用3.x,这时一定要确认MyBatis-Plus的starter版本在3.5.5以上,因为Spring Boot 3换了javax到jakarta的包名体系,老版本的自动配置类会直接失效。这个坑我后面专门展开说。
application.yml里的基础配置大致如下:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/yellowriver?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0使用MyBatis-Plus时,启动类上别忘了加@MapperScan注解,否则mapper接口不会被扫描注册,运行起来就会报找不到bean。这个注解的位置在Spring Boot入门阶段特别容易漏掉。
3.2 非遗列表的搜索、筛选与分页
非遗列表页是整个前台访问量最大的接口,需要同时支持关键词、分类、地区三个维度的组合查询,还要做分页。MyBatis-Plus的分页和条件构造器能让这段代码写得非常干净。
先定义一个实体类,对应heritage表:
@Data @TableName("heritage") public class Heritage { @TableId(type = IdType.AUTO) private Long id; private String name; private Long categoryId; private String province; private String level; private String coverImage; private String summary; private String content; private Integer viewCount; private Integer collectCount; private Integer status; private LocalDateTime createTime; private LocalDateTime updateTime; }然后在ServiceImpl里写查询逻辑:
public IPage<Heritage> queryPage(int current, int size, String keyword, Long categoryId, String province) { Page<Heritage> page = new Page<>(current, size); LambdaQueryWrapper<Heritage> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Heritage::getStatus, 1) .like(StringUtils.hasText(keyword), Heritage::getName, keyword) .eq(categoryId != null, Heritage::getCategoryId, categoryId) .eq(StringUtils.hasText(province), Heritage::getProvince, province) .orderByDesc(Heritage::getViewCount); return this.page(page, wrapper); }这段代码有三个细节值得说。第一,所有条件都用了条件参数,keyword为空就不会拼接LIKE条件,这样Controller传参的时候不需要写一堆if判断;第二,.eq(条件, 列, 值)这种写法是MyBatis-Plus非常核心的用法,能避免手动拼接SQL时的空格和注入问题;第三,默认排序用viewCount倒序,浏览量高的项目自然排前面,这就是“热门推荐”最简单的实现方式,完全不用额外做推荐系统。
Controller层接收请求参数,返回统一结果对象:
@GetMapping("/heritage/list") public Result list(@RequestParam(defaultValue = "1") int current, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String keyword, @RequestParam(required = false) Long categoryId, @RequestParam(required = false) String province) { return Result.success(heritageService.queryPage(current, size, keyword, categoryId, province)); }分页插件别忘了配置,否则分页会变成查全表。MyBatis-Plus 3.5.x需要在配置类里注册一个PaginationInnerInterceptor。
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }我曾经见过一个学生没配这个拦截器,前端传pageNum=2页面显示的还是第一页的数据,排查了半天发现是分页SQL压根没生效。这个配置项必须写。
3.3 详情页展示与富文本内容处理
详情页展示的是heritage表里的content长文本。这里有一个前后端技术选型的分岔口:如果你用Thymeleaf做服务端渲染,正文直接用th:utext输出HTML就行;如果你做前后端分离,前端拿到返回的HTML字符串再用v-html渲染。两种方案我都试过,毕设场景我更推荐Thymeleaf,少一套跨域和联调的麻烦,答辩演示也稳定。
不管用哪种方案,都有一个必须注意的安全问题:富文本内容输出时要防止XSS跨站脚本攻击。如果是后台管理员自己录入的内容,风险相对可控,但严谨的做法是后端在保存时进行过滤,输出时不直接信任用户输入的内容。在答辩时能主动提到“我对富文本做了XSS过滤”,是比只说“实现了详情功能”高一个档次的表现。
详情页计数器也是一个小亮点。每访问一次就把viewCount加一,实现起来一行SQL:
@Update("UPDATE heritage SET view_count = view_count + 1 WHERE id = #{id}") void increaseViewCount(Long id);这种更新方式比先查出来加一再更新回去好得多,因为它是数据库原子操作,并发访问时不会丢失计数。这个写法可以在答辩时说成“基于数据库原子更新的计数器设计”,听起来就很有经验。
3.4 后台管理:登录鉴权与权限控制
后台管理的第一道门槛是登录。毕设这个规模,我不建议直接上完整的Spring Security,因为它的过滤器链和配置项对新手非常不友好,光是Spring Security 6和老版本配置差异就能耗费大量时间。我通常是建议用JWT加拦截器的轻量方案:
- 用户名密码登录成功后,将userId和role放进JWT Token返回;
- 前端把Token存在localStorage或请求头里;
- 后端写一个拦截器,校验请求头里的Token,解析出用户信息后放入ThreadLocal;
- 对/admin/**路径统一鉴权,非管理员角色直接返回403。
拦截器实现大致是这样:
@Component public class AdminInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("token"); if (StringUtils.hasText(token) && JwtUtil.parseToken(token) != null) { return true; } response.setStatus(401); return false; } }注册拦截器时要指定路径:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(adminInterceptor) .addPathPatterns("/admin/**") .excludePathPatterns("/admin/login"); } }注意一个容易忽略的问题:如果你加了拦截器,但Controller里用request.getSession()去取用户信息,而项目又是前后端分离部署的,那Session基本是拿不到东西的。JWT方案天然规避了Session跨域失效的问题,这也是为什么它在现代Web项目里通用。
3.5 图片上传与静态资源映射
非遗项目的封面图、详情图、资讯图,都涉及文件上传。毕设阶段没有必要买云存储,本地目录存储完全够用。上传接口的核心逻辑:
@PostMapping("/upload") public Result upload(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return Result.error("请选择文件"); } String originalFilename = file.getOriginalFilename(); String ext = StringUtils.getFilenameExtension(originalFilename); String fileName = UUID.randomUUID().toString().replace("-", "") + "." + ext; File dir = new File(System.getProperty("user.dir") + "/upload/"); if (!dir.exists()) { dir.mkdirs(); } file.transferTo(new File(dir, fileName)); return Result.success("/upload/" + fileName); }一个很实用的细节:存储文件名一定不要用用户上传的原始文件名,而要用UUID重命名。原因有两个,一是避免中文文件名在不同编码环境下乱码,二是防止文件名重复覆盖。这个细节特别能体现工程经验,我建议在写设计文档的时候特意标出来。
把文件存到了本地,还要让浏览器能访问到,需要配置静态资源映射:
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:" + System.getProperty("user.dir") + "/upload/"); }上传大小限制也要在yml里显式配置:
spring: servlet: multipart: max-file-size: 10MB max-request-size: 20MB默认只有1MB,很多同学第一次传图片就会报MaxUploadSizeExceededException,改这里就能解决。
4. 实操中踩过的坑与排查实录
4.1 版本匹配:JDK、Spring Boot与MyBatis-Plus的兼容问题
这个项目做下来,我遇到的第一大类问题全是版本兼容。网上搜到的教程鱼龙混杂,有的是Spring Boot 2.x的老写法,有的是Spring Boot 3.x的新写法,混着用就出事了。最典型的就是Spring Boot 3开始使用jakarta命名空间,以前代码里导入的javax.servlet.http.HttpServletRequest需要在Spring Boot 3里改成jakarta.servlet.http.HttpServletRequest,如果你从老的SSM项目里扒代码,编译直接报红。
我的建议是列一个版本对照表,开工前就定好:
| JDK版本 | Spring Boot版本 | MyBatis-Plus版本 | 包名特点 |
|---|---|---|---|
| JDK 8 | 2.7.x | 3.5.3.x | javax.*命名空间 |
| JDK 11 | 2.7.x | 3.5.3.x | javax.*命名空间 |
| JDK 17 | 3.0-3.2 | 3.5.5+ | jakarta.*命名空间 |
| JDK 17/21 | 3.3+ | 3.5.5+ | jakarta.*命名空间 |
毕设我通常建议用JDK 8 + Spring Boot 2.7,因为学校机房、演示电脑环境多数还是老JDK,兼容性最稳。如果你就想用新东西,JDK 17 + Spring Boot 3也完全没问题,但要做好所有依赖版本逐一确认的心理准备。
4.2 数据库中文乱码和连接时区问题
数据表里存中文乱码,这个坑几乎每个做Java Web毕设的人都会遇到。表象是页面上显示中文全是问号,原因可能出在三处:数据库表默认字符集不是utf8mb4、JDBC连接URL没指定characterEncoding、MySQL服务器本身的字符集设置不对。
正确的连接串写法是:
jdbc:mysql://localhost:3306/yellowriver?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=falseuseUnicode和characterEncoding两个参数必须同时出现,少一个都可能乱码。另外MySQL 8.0以上的驱动强制要求指定serverTimezone,不指定就会报The server time zone value提示。建表的时候也记得统一:
CREATE TABLE heritage ( ... ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;用utf8mb4而不是utf8,是因为utf8在MySQL里最多只支持3字节的编码,存不了生僻字和emoji,非遗项目名称里有些生僻字,用utf8mb4才是稳妥的。
4.3 上传文件访问不到:路径与映射的坑
文件明明上传成功了,目录里也能看到文件,但浏览器访问图片地址就是404。这个问题我遇到过太多次,原因基本都是静态资源映射没配或者配错了。addResourceLocations里的路径必须以file:开头,Linux和Windows的路径分隔符不同,最好用System.getProperty("user.dir")动态拼接当前项目所在的绝对目录,不要写死成/root/project/upload这种路径,否则换一台机器部署就失效。
还有一个隐蔽问题:如果你加了拦截器,并且拦截路径写了/**,那么这个拦截器会把上传文件的静态资源请求也拦住。正确做法是把/upload/**加入excludePathPatterns,或者在拦截器里放行静态资源后缀。这个细节不注意,就会出现“图片一会儿能访问一会儿不能访问”的怪现象。
4.4 Bean注入失败:@Autowired、@Resource与构造器注入的选择
“spring boot bean注入控制”是搜索热词,说明很多人在这个环节出过问题。最常见的报错是Field xxx required a bean of type 'xxx' that could not be found。原因有三类:一是mapper接口没加@Mapper或@MapperScan,Spring容器里压根没有这个bean;二是Service实现类没有@Service注解;三是注解用错了,@Autowired是按类型注入,@Resource是按名称注入,两者行为不一样。
我个人的习惯是:同一类组件保持统一风格,不要混用。新代码里推荐使用构造器注入:
@Service public class HeritageServiceImpl implements HeritageService { private final HeritageMapper heritageMapper; public HeritageServiceImpl(HeritageMapper heritageMapper) { this.heritageMapper = heritageMapper; } }用构造器注入的好处是Spring启动时如果依赖缺失会直接报错,而不是等到某个方法执行时才报空指针,问题暴露得更早。这个点同样适合写进答辩的技术亮点里。
4.5 日志排查技巧:把SQL和异常看明白
遇到Bug最忌讳的是一遍遍System.out.println,正确做法是把日志配置好。MyBatis-Plus里开启SQL日志只需要在application.yml加一行:
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这样控制台就能看到每次执行的完整SQL以及参数和结果集条数,排查“查到的数据不对”“分页没生效”“条件没拼上”这类问题非常管用。
系统级日志建议用Spring Boot默认的Logback,在application.yml里设置日志级别:
logging: level: com.example.yellowriver: debug org.springframework.web: info平时写代码注意不要用print输出调试信息,用Logger记录,这个习惯在答辩时老师如果看代码,会留下很好的印象。日志里最有用的是异常堆栈,看到NullPointerException,先不要急着改代码,要循着堆栈找到是哪一行触发,很多问题其实都是调用链上一层的参数没传对。
5. 答辩准备与后续扩展方向
5.1 答辩时最容易被追问的五个问题
根据我旁观多场毕业答辩的经验,这个项目会被老师追问的问题高度集中,提前准备好答案非常划算。
第一个必问:为什么选Spring Boot?答题思路就是框架对比,强调它简化配置、内嵌容器、生态成熟,与项目规模匹配。不要说“因为大家都用”,要有自己的比较维度。
第二个常问:数据库表为什么这么设计?你需要从业务出发,讲清楚heritage主表、category分类表、tag标签表的关系,说明为什么用中间表而不用逗号字段。这体现的是数据建模能力,不是背表结构。
第三个高频追问:用户密码是怎么存储的?回答“加密存储,用了BCrypt哈希算法,每次登录校验时比对哈希值”就够了。如果被追问为什么不直接存明文,就简单解释数据库泄露场景下的危害。
第四个问题涉及性能:如果用户量变大,系统哪里会先出问题?你可以说列表查询是压力集中的地方,优化方案包括给查询字段加索引、用Redis给热门列表做缓存、详情页浏览量用缓存加定时落库。能说出这个思路,比死记硬背并发概念加分得多。
第五个问题比较开放:这个系统还有什么不足?不要说自己没有不足,那是减分的。诚恳地讲三点:一是目前内容是后台人工维护的,后续可以接入公开的文化资源数据;二是缺少用户行为分析,后续可以做个性化推荐;三是图片文件存在本地,生产环境应该换成对象存储并加CDN。这反而是展示你思考深度的机会。
5.2 从毕设到可落地项目的扩展思路
做完一个能跑的版本之后,这个题目的扩展空间非常大。如果你时间充裕,想把它从“毕设水平”提升到“项目水平”,可以从几个方向入手。
第一个方向是缓存优化。把首页轮播、热门非遗列表这类热点数据用Redis缓存起来,设置合理的过期时间,数据库压力会显著下降。如果你不想引入Redis这个重组件,Caffeine本地缓存是轻量替代方案,Spring Boot集成非常顺。这两个方案随便选一个,都能写进“系统优化”章节里。
第二个方向是检索升级。目前是SQL的LIKE模糊查询,毕设够用了。如果非遗条目上千上万条,可以引入全文检索引擎做搜索,这个能聊的内容很多,但要注意控制工作量。
第三个方向是传播功能的深化。题目里有“传播”,那就可以考虑接入直播能力,比如非遗演出、传承人直播展示技艺,WebSocket做互动弹幕;也可以做一个数据可视化大屏,用ECharts展示黄河流域各省非遗项目数量和分布情况,效果非常出彩,答辩现场比PPT还抓眼球。
第四个方向是工程化细节。文件上传改成云对象存储,数据库连接池配置调优,接口统一异常处理,日志采集和监控,这些都是从开发到运维的进阶点。选一到两个做到位,整个项目的完成度会明显拉开跟同学的距离。
最后再分享一个我对这类题目的体会:文化宣传类毕设真正的难点从来不是某个技术点,而是你能不能把一个有文化内涵的业务主题,转化成一套结构清晰的软件系统。表设计是否合理、模块划分是否清楚、有没有考虑到内容安全与审核、能不能讲清楚每一次技术选型的理由,这些才是评审老师真正看重的东西。把上面这些环节踏踏实实走一遍,这个毕设就不仅仅是一个“能跑的作品”,而是一份站得住脚的完整项目实践。