
做毕业设计这些年我帮不少同学看过题目、评过代码也带过不少人从零把一个Spring Boot项目跑起来。说实话很多人在选题这一步就开始卡壳——太简单的怕答辩过不了太复杂的怕自己写不出来。如果你对“图书借阅、数字化资源、公益主题”这类方向有兴趣那基于Spring Boot的乡村共享书屋平台是一个难度适中、功能完整、又有现实价值的Java毕设题目比较适合作为Java后端方向的毕业设计项目。这篇文章我会把这个项目的设计思路、技术选型、数据库结构、核心代码流程、踩坑点、论文写法一次讲清楚给正在选题目或已经开始动手的同学做参考。1. 为什么建议选这个题目选题背景与项目价值1.1 乡村书屋数字化是当下很务实的应用场景毕设选题要“落地”首先要回答一个问题做出来给谁用、解决什么问题。乡村书屋的项目背景非常好理解——在很多乡镇和村级社区书屋或图书室的线下管理还很原始最常见的状态是图书登记靠手写借书还书靠自觉管理员往往是兼职的村干部或志愿者没办法时刻守在书屋门口。这直接导致几个现实问题书被借走之后不知道在谁手里、逾期不还要靠人工催还、新书到了没人通知村民、书屋开放时间不稳定导致很多人白跑一趟。共享书屋平台要解决的就是把这一整套流程数字化。村民在线注册、查书、预约、借书、续借、留言反馈管理员在线管理图书分类、上下架图书、审核借阅申请、统计借阅数据。这个需求不做作、不悬浮是现实生活中真实存在的管理痛点所以无论是写在开题报告里还是答辩时讲给评委听都很容易引起共鸣。答辩老师最不爱听的就是“为了写系统而写系统”而这个题目的业务背景能让你的选题理由站得住脚。1.2 这个题目覆盖的技术点与难易度分析从技术角度看这个项目覆盖了Java后端开发的核心知识面Spring Boot的自动配置与请求处理、MyBatis-Plus的持久层操作、MySQL数据库设计与SQL编写、用户权限控制管理员/普通用户、文件上传图书封面、电子文档、关键字检索、数据统计图表、以及可能的定时任务。这些内容单看每一项都不算特别高深但组合在一起恰好构成一个完整的后端业务系统难度属于中等偏下非常适合本科毕设。对比一下市面上常见的几个Java毕设方向单纯的“图书管理系统”太老套连业务包装都没有很容易被认为是几年前的项目电商系统又太卷前后端分离、支付、分布式一上来工作量爆炸学生容易做不完。乡村共享书屋这个题目正好卡在中间——它有一个不算复杂但“五脏俱全”的业务闭环又有一个明确的场景包装技术上通过努力可以独立完成答辩时也有足够的延伸空间。无论你是刚学完Java基础还是已经能在GitHub上读源码这个题目都有对应的切入深度。2. 技术选型与整体设计为什么用Spring Boot这套组合2.1 后端技术选型Spring Boot MyBatis-Plus MySQL后端技术栈我建议的核心组合是Spring Boot 2.7 MySQL 5.7/8.0 MyBatis-Plus Maven。这套组合是当下Java后端最主流、最稳妥的配置网上资料多、排错容易几乎你踩过的每一个坑都有人替你踩过了。Spring Boot版本上我特别提醒一句不是版本越新越好。Spring Boot 3.x要求JDK 17起步有些学校的机房电脑、实验室环境还在用JDK 8而且3.x版本中部分旧依赖的兼容性问题更多。如果你的环境检查下来是JDK 8老老实实用Spring Boot 2.7.x这是目前兼容性和稳定性最平衡的版本。等到项目写顺了再考虑升级也不迟。持久层框架我推荐MyBatis-Plus而不是原生MyBatis或Spring Data JPA。原因是MyBatis-Plus内置了通用的CRUD方法单表操作几乎不需要写SQL比如selectById、selectPage、deleteById直接用即可它还支持逻辑删除注解TableLogic、自动填充字段、分页插件这些功能恰好是毕设项目里高频使用的。JPA也行但和MySQL配合时很多同学对惰性加载、字段映射不熟一旦报错会非常懵。用MyBatis-Plus相对注意力更集中。数据库方面用MySQL 5.7或8.0都可以。需要注意的一点是如果你用的是8.0驱动名要写com.mysql.cj.jdbc.Driver连接串里一定要带serverTimezoneAsia/Shanghai否则会因为时区问题启动报错。旧教程里写的com.mysql.jdbc.Driver在8.0下已经过时了网上很多报错帖都是从这里开始的。关于权限控制常见方案有Shiro、Spring Security、以及自定义拦截器。我的建议是如果你的精力和基础有限做一个基于Session的自定义拦截器注解就可以解决80%的需求。真正需要引入Shiro/JWT的场景是前后端完全分离且需要Token鉴权的架构但毕设项目多数情况下不需要把复杂度拉满。后面我会给出一套可用的拦截器实现方案你可以直接套用。2.2 前端与项目结构模板引擎还是前后端分离前端这块我见过很多同学纠结用Thymeleaf服务端渲染还是做Vue前后端分离我的判断标准是看你的工作量和目标。如果你希望以最快的速度让系统跑起来后端代码尽量复用答辩演示更流畅那Thymeleaf模板引擎是首选。用Thymeleaf静态资源和HTML页面直接放在src/main/resources/templates和static目录下页面里通过th:each、th:text等属性渲染数据不需要处理跨域不需要单独启动前端服务打包成一个JAR直接就能访问。如果你已经在Vue上比较熟练或者想展示“前后端分离”这种架构能力那可以用Vue 2/3 Element UI做管理端页面后端提供JSON接口。但说实话前后端分离意味着你要多维护一套前端工程部署时也要考虑跨域配置、静态资源打包等问题工作量至少多30%。对于以“完成任务、顺利毕业”为首要目标的同学我建议保守一点。文章后面的代码示例我以Spring Boot Thymeleaf Bootstrap作为默认方案来讲但核心后端逻辑对两种方案完全适用。无论选哪种前端方式后端代码目录结构我建议这样组织清晰且好维护src/main/java/com/example/library/ ├── controller/ // 控制层接收请求调用service ├── service/ // 业务层业务逻辑 │ └── impl/ // 业务实现 ├── mapper/ // 持久层MyBatis-Plus的Mapper接口 ├── entity/ // 实体类与数据库表对应 ├── dto/ // 数据传输对象接收前端参数、包装返回结果 ├── config/ // 配置类拦截器、跨域、静态资源映射 ├── common/ // 通用类统一返回结果、异常处理、常量 └── LibraryApplication.java // 启动类分层别偷懒Controller里不要写大段业务逻辑。答辩时老师追问“你这个项目架构分几层”是必问题清晰的目录结构本身就是最好的回答。2.3 核心功能模块设计一个合格的乡村共享书屋平台至少要包含下面这些业务闭环。用户端功能注册与登录手机号或用户名注册密码加密存储。图书查询与浏览按书名、作者、ISBN关键字搜索按分类筛选查看图书详情。借阅申请选择想借的书提交借阅申请。在线续借在应还日期前申请续借限制续借次数。预约借阅图书被借走时可登记预约归还后通知。读书心得与留言用户对读过的书写短评形成书友之间的互动。个人中心查看当前在借图书、历史借阅记录、预约状态、修改个人信息。管理员端功能图书管理新增图书、编辑信息、上传封面、上下架。分类管理维护图书分类树。借阅管理审核借阅申请、办理归还、处理逾期、查看借阅记录。用户管理查看用户列表、禁用/启用账号。公告管理发布书屋通知例如假期闭馆、新书到馆。数据统计图书总量、借出数量、热门图书排行、每日借阅数量趋势。这些功能合起来就是一个完整的业务闭环用户能完成查书→借书→还书→评价的全流程管理员能完成维护→审核→统计的全流程。要注意的是功能做全很重要但更重要的是把这些功能串成合理的流程。比如借书的状态流转不能是零散的借阅申请提交后要经过“待审核→已通过/已拒绝→已借出→已归还→已逾期”这样一个完整链路答辩时你能画出一条清晰的状态流程图这就是系统设计能力的体现。3. 数据库设计与核心表结构说明3.1 核心表清单数据库设计一定是在写业务代码之前先做的不要反过来。下面是这个项目最基本的几张表建好后基本能满足核心功能。用户表userCREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录用户名, password varchar(100) NOT NULL COMMENT BCrypt加密后的密码, real_name varchar(20) DEFAULT NULL COMMENT 真实姓名, phone varchar(20) DEFAULT NULL COMMENT 手机号, role tinyint(4) NOT NULL DEFAULT 1 COMMENT 角色0管理员 1普通用户, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 状态0禁用 1启用, create_time datetime NOT NULL COMMENT 注册时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;图书表bookCREATE TABLE book ( id bigint(20) NOT NULL AUTO_INCREMENT, isbn varchar(20) DEFAULT NULL COMMENT ISBN编号, title varchar(100) NOT NULL COMMENT 书名, author varchar(50) DEFAULT NULL COMMENT 作者, publisher varchar(100) DEFAULT NULL COMMENT 出版社, category_id bigint(20) DEFAULT NULL COMMENT 分类ID, cover_url varchar(255) DEFAULT NULL COMMENT 封面图片地址, description text COMMENT 简介, total_stock int(11) NOT NULL DEFAULT 1 COMMENT 总库存, available_stock int(11) NOT NULL DEFAULT 1 COMMENT 可借库存, borrow_count int(11) NOT NULL DEFAULT 0 COMMENT 总借阅次数, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 状态0下架 1上架, create_time datetime NOT NULL COMMENT 入库时间, PRIMARY KEY (id), KEY idx_title (title), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT图书表;借阅记录表borrow_recordCREATE TABLE borrow_record ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL COMMENT 用户ID, book_id bigint(20) NOT NULL COMMENT 图书ID, borrow_start_date date DEFAULT NULL COMMENT 借出日期, due_date date DEFAULT NULL COMMENT 应还日期, return_date date DEFAULT NULL COMMENT 实际归还日期, status tinyint(4) NOT NULL COMMENT 状态0待审核 1借出中 2已归还 3已拒绝 4已逾期, create_time datetime NOT NULL COMMENT 申请时间, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_book_id (book_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT借阅记录表;上面这三张表是系统的地基。除此之外你还会需要图书分类表book_category字段就是id、分类名、上级分类ID。预约表reservation记录哪个用户预约了哪本书预约状态等待中/已通知/已取消/已过期。公告表notice标题、内容、发布时间。读书心得表book_review用户ID、图书ID、评分、评论内容、发布时间。阅读活动表activity与活动报名表activity_signup如果想让项目内容更丰富可以加上阅读打卡、线下读书会报名这类公益服务功能这个模块在答辩时很加分因为它是“共享书屋”区别于“普通图书管理系统”的标志性功能。3.2 关键设计细节状态、索引、逻辑删除、并发设计表的时候有四个细节值得注意。状态字段统一用tinyint数字表示不要直接用字符串。例如借阅状态0待审核、1借出中、2已归还、3已拒绝、4已逾期。程序里可以定义常量类或者枚举类代码里永远只出现数字对应的常量名不要散落魔法数字。数字状态在数据库里占空间小、查询快也方便做统计。索引不是越多越好但用户ID、图书ID、状态这种高频查询条件一定要加索引。比如查询“某用户当前在借的书”SQL会走user_id和status两个条件索引能显著加快速度。书名做模糊查询LIKE %关键字%时普通索引其实用不上所以项目里如果数据量不大可以忽略数据量大则需要考虑全文索引或Elasticsearch但毕设不必过度设计。逻辑删除用MyBatis-Plus的TableLogic注解非常方便。比如用户注销、图书被删除时物理删除容易把关联记录搞乱逻辑删除只是在记录上打标记查询时MyBatis-Plus会自动拼接deleted0条件不影响数据完整性。但要注意逻辑删除会导致唯一索引失效如果表中字段需要唯一性比如用户名就不能只靠逻辑删除字段做唯一需要在代码里手动判断。并发场景一定要考虑。共享书屋最常见的并发是两个人同时借同一本书书的可借库存只有1本。如果只用select update可能出现两个请求都查到库存为1然后都执行扣减最后库存变成-1。解决办法是用乐观锁在book表加一个version字段执行更新时带上版本条件int count bookMapper.update( null, new LambdaUpdateWrapperBook() .eq(Book::getId, bookId) .eq(Book::getVersion, book.getVersion()) .gt(Book::getAvailableStock, 0) .setSql(available_stock available_stock - 1) .setSql(borrow_count borrow_count 1) .set(Book::getVersion, book.getVersion() 1) ); if (count 0) { // 库存不足或数据已被修改提示用户 }这里利用update语句的原子性和条件判断在数据库层面保证了“扣库存”和“检查库存”是一个原子操作比“先查再改”安全得多。4. 核心业务实现与代码走读4.1 注册登录与权限校验登录模块是每个系统的门面也是答辩时老师最常上手操作的功能。注册时密码不能明文保存Spring Security自带的BCryptPasswordEncoder可以直接用public class PasswordUtil { private static final BCryptPasswordEncoder ENCODER new BCryptPasswordEncoder(); public static String encode(String rawPassword) { return ENCODER.encode(rawPassword); } public static boolean matches(String rawPassword, String encodedPassword) { return ENCODER.matches(rawPassword, encodedPassword); } }BCrypt的加密结果每次都不同但它内置了盐值校验时用matches方法即可安全性比MD5高得多。MD5加不加盐在现代算力下都很容易被彩虹表破解毕设里如果用了MD5被答辩老师问一句“密码怎么保证安全”就会比较尴尬。登录成功后我建议把用户关键信息放进SessionUser loginUser userService.login(username, password); session.setAttribute(loginUser, loginUser);然后在Spring Boot里注册一个拦截器拦截所有需要登录才能访问的路径public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); User user (User) session.getAttribute(loginUser); if (user null) { response.sendRedirect(/login); return false; } return true; } }管理员接口单独用注解或者路径匹配来限制比如/admin/**前缀的请求要求role0public class AdminInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { User user (User) request.getSession().getAttribute(loginUser); if (user null || user.getRole() ! 0) { response.sendRedirect(/login); return false; } return true; } }这里需要注册进WebMvcConfigurer里并配置拦截路径Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /register, /css/**, /js/**, /images/**, /error); registry.addInterceptor(new AdminInterceptor()) .addPathPatterns(/admin/**); } }这段代码虽短但概念很齐全拦截器、Session、角色区分。答辩时老师问“你的权限控制是怎么实现的”你就可以沿着这条链路讲清楚不用绕到复杂的Spring Security配置里去。4.2 借阅与归还流程的实现借阅流程是项目的核心也是逻辑最容易出Bug的地方。完整流程是这样的第一步用户提交借阅申请。这一步只做一件事插入一条borrow_record记录状态为0待审核图书的库存此时不扣减。为什么要等管理员审核后才扣库存因为如果用户申请了但不来取书库存被提前扣了其他真正想借的人就借不到了。第二步管理员审核通过。管理员在后台看到待审核记录点击“确认借出”此时后端要在一个数据库事务里完成三件事把借阅记录状态从0改为1借出中并设置borrow_start_date为今天、due_date为今天加30天借期规则可以配在系统参数表里扣减图书的available_stock给图书的borrow_count加1。第三步管理员办理归还。把借阅记录状态改为2已归还写入return_date同时把图书的available_stock1。如果系统发现return_date晚于due_date可以在界面上提示“该书已逾期X天”并记录逾期状态。第四步是简化设计如果用户借书超过应还日期仍没归还系统需要自动把状态标记为4已逾期。这里用Spring Boot的定时任务最简单Scheduled(cron 0 0 1 * * ?) // 每天凌晨1点执行 public void markOverdueRecords() { LambdaUpdateWrapperBorrowRecord wrapper new LambdaUpdateWrapper(); wrapper.eq(BorrowRecord::getStatus, 1) .lt(BorrowRecord::getDueDate, LocalDate.now()) .set(BorrowRecord::getStatus, 4); borrowRecordService.update(wrapper); }注意使用Scheduled前要在启动类上加EnableScheduling。这个定时任务虽然代码量不大但能体现你对业务完整性的理解——系统不是只能处理正常流程还能处理异常分支。续借逻辑稍微梳理一下用户点击续借后端校验借阅记录存在、状态是借出中、距离应还日期还有至少1天、续借次数未超过限制。满足条件后把due_date往后延15天同时记录续借次数。很多同学容易漏掉“续借只能操作一次”的限制没有加校验导致一本书被无限续借这也是答辩时一个很好的提问点。4.3 图书检索、推荐与电子资源上传图书检索是最直接的体验点。用户进入首页在搜索框输入关键词希望同时匹配书名、作者和ISBN。用MyBatis-Plus的LambdaQueryWrapper可以写成public PageBook searchBooks(String keyword, Long categoryId, int pageNum, int pageSize) { PageBook page new Page(pageNum, pageSize); LambdaQueryWrapperBook wrapper new LambdaQueryWrapper(); if (StringUtils.hasText(keyword)) { wrapper.and(w - w.like(Book::getTitle, keyword) .or().like(Book::getAuthor, keyword) .or().like(Book::getIsbn, keyword)); } if (categoryId ! null) { wrapper.eq(Book::getCategoryId, categoryId); } wrapper.eq(Book::getStatus, 1) .orderByDesc(Book::getCreateTime); return bookMapper.selectPage(page, wrapper); }分页这里要用MyBatis-Plus的分页插件在配置类里注册Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }别小看这个配置少了它selectPage查出来的数据永远是全量分页不生效这是个非常典型的新手坑。推荐逻辑可以做得朴素但有效首页默认排序按borrow_count降序就是“借阅次数最多”的热门推荐加上create_time倒序就是新书推荐。这种基于简单统计的推荐虽然不如协同过滤那么“高级”但对一个书屋系统来说非常自然而且答辩讲起来也不生硬。电子资源数字化这块是这个题目区别于“传统图书管理”的地方。书屋不光可以借纸质书还可以上传电子资源比如PDF文档、推荐阅读清单、本地名师讲座PPT等。因此我建议在图书表旁边加一张book_resource表记录电子文件的名称、路径、大小、上传时间关联到具体图书ID。用户打开图书详情页时可以看到“在线查看电子资源”的按钮。文件上传用Spring Boot自带的文件处理能力就够了。上传接口接收MultipartFile存储到本地目录再通过静态资源映射对外提供访问# application.yml file: upload-dir: /data/library/uploads/PostMapping(/admin/book/resource/upload) public Result uploadResource(RequestParam(file) MultipartFile file, RequestParam(bookId) Long bookId) { if (file.isEmpty()) { return Result.error(文件不能为空); } String originalFilename file.getOriginalFilename(); String ext originalFilename.substring(originalFilename.lastIndexOf(.)); if (!Arrays.asList(.pdf, .doc, .docx, .txt).contains(ext.toLowerCase())) { return Result.error(不支持的文件类型); } String fileName UUID.randomUUID() ext; File dest new File(uploadDir, fileName); file.transferTo(dest); // 保存数据库记录 return Result.success(); }这里踩过一个坑本地直接写file.transferTo(new File(/data/...))在Windows开发环境跑没问题但部署到Linux服务器时如果目录不存在会直接报FileNotFoundException。所以上传前一定要dest.getParentFile().mkdirs()或者用PostConstruct在启动时创建目录。4.4 数据统计与可视化最后做一个锦上添花的功能数据统计。管理员首页通常要展示几个数字卡片比如“图书总数”“在借数量”“用户总数”“今日新增借阅”加一个折线图展示最近7天/30天的借阅趋势。后端接口用SQL按天聚合即可Select(SELECT DATE(create_time) AS date, COUNT(*) AS cnt FROM borrow_record WHERE create_time #{startDate} GROUP BY DATE(create_time) ORDER BY date) ListMapString, Object countBorrowByDate(String startDate);前端展示折线图推荐直接用ECharts用Thymeleaf渲染时把JSON数据通过th:inlinejavascript放进页面的script标签里即可。这个模块从开发难度上讲并不高但它的亮点在于能配合项目背景讲故事通过统计看出哪个村的借阅率高、哪类图书最受欢迎这说明数字化的价值不只是线上登记而是为阅读推广提供了数据支撑。5. 调试部署与常见问题清单5.1 本地开发环境配置开发环境建议统一为JDK 8或11配合Spring Boot 2.7、Maven 3.6、IDEA、MySQL 5.7。application.yml里最基础也最容易出错的配置是数据源spring: datasource: url: jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 20MB max-request-size: 50MB注意三点第一MySQL 8.0必须用com.mysql.cj.jdbc.Driver第二characterEncodingutf8必须加否则中文乱码第三serverTimezoneAsia/Shanghai必须加否则会报时区错误。这三个问题我在帮人调试时几乎每次都遇到配置好它们能少走一大半弯路。如果使用MyBatis-Plus记得配置逻辑删除和驼峰映射mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0map-underscore-to-camel-case: true是让数据库的create_time自动映射到Java的createTime很多新手忘了配结果查出来的时间字段全是null又排查半天。5.2 打包与部署开发完成后的打包很简单mvn clean package -DskipTests生成的可执行JAR在target目录下。本地运行就是java -jar library-system.jar如果是部署到服务器建议用systemd或者直接后台运行nohup java -jar ... 。需要注意的一点是JAR包里不包含外部上传的文件所以部署时一定要把file.upload-dir指向一个持久化目录数据库数据要单独导入JAR更新后上传目录里的内容不能丢。如果在Windows环境下遇到“端口被占用”netstat -ano | findstr :8080 taskkill /F /PID 进程号这是每个Java开发者都会遇到的日常操作写论文时也可以把这个问题写进测试章节的“问题与解决”里显得有真实调试经验。5.3 高频报错与排查思路借阅系统开发过程中有四个报错几乎是必踩的。第一个MyBatis-Plus分页不生效查出来的记录总是第一页数据。这个问题的根源就是没注册分页插件PaginationInnerInterceptor。检查一下自己的配置类里有没有这个方法没有就补上。第二个前端页面中文乱码。一般发生在两种场景页面本身编码不是UTF-8或者数据库连接串没加字符编码参数。排查办法是从源头到页面一层层确认数据库表字符集是否为utf8mb4、连接串是否包含characterEncodingutf8、HTML的meta charsetUTF-8是否存在。三者缺一不可缺的都是乱码。第三个启动报错Failed to configure a DataSource。这种问题基本是application.yml里的数据源配置没被加载到或者配置文件名字写错了。检查target/classes目录下有没有application.yml检查SpringBootApplication扫描的包路径是否覆盖了配置类。第四个跨域问题。如果用了前后端分离前端请求后端接口会报CORS错误。解决办法是在后端加一个全局CORS配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(http://localhost:8081) .allowedMethods(GET, POST, PUT, DELETE) .allowCredentials(true); } }注意allowedOrigins里写的是前端页面的地址不是后端的地址。我见过有人把地址写反结果还是报跨域排查了一个多小时才发现是配置反了。6. 从代码到论文答辩材料与文档组织6.1 论文大纲与写作节奏代码写完之后论文和答辩材料是紧接着要准备的事情。不要等代码结束再去想论文做项目的同时把需求分析、系统设计部分同步写掉最后补测试和结论部分效率会高很多。论文可以参考下面的章节结构第一章 绪论背景、意义、国内外研究现状、本文主要工作。第二章 相关技术介绍Spring Boot、MyBatis-Plus、MySQL、前端技术。第三章 系统分析可行性分析、需求分析、用例分析、业务流程。第四章 系统设计总体架构、功能模块设计、数据库设计。第五章 系统实现按功能模块展示关键代码和界面截图。第六章 系统测试测试环境、测试用例、测试结果、问题修复。第七章 总结与展望。第三章和第四章是论文的主体也是工作量最难注水的部分因为画图和分析的深度直接决定论文篇幅。需求分析里每个角色至少画一张用例图业务流程里借书流程和还书流程各画一张活动图或流程图数据库设计里至少有一张清晰的ER图。这里有一个良心建议画图不要用网上随手找的模板截图评审老师一眼就能看出来。用Visio、draw.io或PowerDesigner自己画哪怕画得朴素一些只要关系正确可信度就比堆砌模板高很多。6.2 答辩演示与提问准备答辩时最常见的问题无非这几类项目用了哪些技术以及它们之间如何协作数据库为什么这样设计各表之间关系是什么安全性怎么考虑密码加密、权限控制、SQL注入如果没有使用Redis缓存当下数据量大了怎么办。这些问题在写论文时就要提前想好答案。答辩演示的路径建议是先讲3-5页PPT背景、技术选型、数据库设计然后直接进入系统演示演示顺序按业务流走例如登录→管理员新增图书→用户注册/登录→搜索图书→提交借阅申请→管理员审核→用户查看借阅记录→管理员办理归还→查看统计图表。这条路径把系统所有核心功能都覆盖了而且层层递进逻辑非常顺老师跟着你的节奏走提问空间反而变少。系统演示时有一个非常实用的建议提前准备好测试数据不要现场临时注册、新增图书。现场打字很浪费时间而且容易手滑。库里至少放几十本书、几个用户、十几条借阅记录这样演示统计图表时才有数据可看不会出现空白页面的尴尬场面。7. 定制扩展方向与项目展望如果做完基础功能后学有余力还有几个扩展方向值得考虑。一个是增加“阅读积分”体系。用户借书、按时还书、参加阅读活动可以获得积分积分可以兑换小礼品或优先借阅热门图书。这个功能虽然实现不复杂一张积分流水表即可但能提升“共享书屋”平台的互动性答辩时也能体现你的思考不止停留在CRUD层面。另一个是增加“图书漂流”功能。用户在平台上发布自己闲余的图书其他用户申请交换或领取实现“人人可共享”的更大范围共享。这更贴切“共享”这个主题也是乡村书屋平台区别于普通图书馆管理系统的差异化亮点。如果对自己的并发处理有更高要求可以引入Redis缓存热门图书列表、用消息队列异步处理借阅通知。但这些对毕设来说属于加分项不是必选项——先把基础功能写得健壮比堆砌一堆没跑通的高级概念更有价值。我个人的体会是Java毕设项目没有完美的题目但有适合你的题目。乡村共享书屋平台的优势在于它的业务逻辑不烧脑技术栈主流社会意义能讲清楚后期论文也好写。无论你接下来是打算自己从零写还是参考开源项目再二次开发都要记住一句话一定要亲手把每一行核心代码跑通理透。答辩时老师上手操作几次再看看你代码里的注释和结构就知道这个项目到底是不是你自己做的。能把这个项目完整地写明白、讲清楚你就已经把Spring Boot这条路走通了一大半。