一个Spring Boot旅游网站能从零搭到上线,其实核心就那几件事:框架选型、数据库设计、文件存储、部署调试。我用一个实际做过的“Springboot旅游网站o2j3b”项目来讲讲完整链路,从开发环境搭建到最终部署上线,再说说那些文档里不会写、但实测特别坑的细节。这篇文章既适合刚入手Spring Boot的学生朋友做课程设计参考,也适合工作中被安排做类似业务系统的开发者抄作业。
1. 内容整体设计与思路拆解
1.1 这个旅游网站到底在做什么
先把这个项目拆开看。标题里带“o2j3b”,这是典型的自动生成项目代号,不代表任何业务含义,不用纠结。真正的核心是:一个基于Spring Boot的旅游信息管理平台。通常这类系统包含前台展示和后台管理两大块,前台面向普通游客,提供景点列表、旅游攻略、线路推荐、特价活动展示这些内容;后台面向管理员,负责景点信息维护、用户订单管理、评论审核、公告发布等操作。
我拿到这类项目需求时,第一件事不是急着写代码,而是把业务实体理清楚。旅游网站最少涉及这么几张核心表:用户表(普通游客和管理员)、景点分类表、景点信息表、旅游线路表、订单表、评论表、公告表。如果再扩展一点,还可以加收藏表、酒店表、导游表。表设计得好不好,直接决定后面增删改查的复杂度。
为什么选Spring Boot?因为它极大地降低了Spring配置的繁琐程度。以前用Spring写一个HTTP接口,要配一堆XML文件,现在Spring Boot用自动配置加约定优于配置,一个@SpringBootApplication注解就能跑起来一个Web服务。尤其是做课程设计或者小团队内部项目时,Spring Boot自带的Tomcat内嵌容器,不需要单独装Tomcat,打包成Jar直接java -jar就能运行,这点太省事了。
数据库方面,我建议用MySQL 5.7或8.0。免费、社区活跃、官方文档全,而且市面上绝大多数教程资料都基于MySQL,出问题容易搜到答案。如果你只是本地测试不想装MySQL,也可以用H2或SQLite先顶着,但最终交付我建议还是回归MySQL,因为导出数据、备份恢复、团队协作都正规得多。
1.2 技术栈选型的几个避坑理由
很多人写旅游网站项目喜欢堆技术,今天加个Redis,明天加个Elasticsearch,后天再上个消息队列。我不能说这些技术没用,但对于标题这个量级的项目来说,过度设计等于给自己挖坑。我建议的核心技术栈是:Spring Boot 2.7.x、MyBatis-Plus、MySQL 8.0、Thymeleaf(或Vue前后端分离,看需求)、Bootstrap、Maven。
Spring Boot版本这里特别强调一下。Spring Boot 3.x已经出了,但如果你用的JDK是8,千万不要选3.x,因为Spring Boot 3强制要求JDK 17以上,否则启动直接报UnsupportedClassVersionError。很多同学从网上下了一个新项目,结果本地JDK版本不对,跑来问我怎么配置,其实十有八九是版本匹配问题。我的经验是:课程设计和企业小项目,老老实实用Spring Boot 2.7.18,这是2.x系列的最后一个版本,稳定且资料多。
数据库访问层我首选MyBatis-Plus而不是原生MyBatis,也不是JPA。原因很简单:MyBatis-Plus内置了常用的单表CRUD方法,你不需要写一堆重复的XML映射文件,一张实体类加一个Mapper接口,就能直接调用selectById、selectList、insert这些方法。对于业务逻辑不复杂的旅游网站来说,这就够了。等以后业务复杂了,它也能让你照样写自定义SQL,灵活度不丢。
文件存储这里要专门说,因为标题相关热搜词里出现了“minio加入到springboot”。旅游网站难免要上传景点图片、轮播图、攻略封面,如果直接把文件存到服务器的某个目录下面,部署时文件和代码混在一起,迁移和备份都很痛苦。专业的做法是把文件放到对象存储服务中。
MinIO就是一个开源的对象存储服务,完全兼容亚马逊S3 API,可以部署在自己服务器上。Spring Boot集成MinIO的方式很简单:引入minio依赖,配置endpoint、accessKey、secretKey,然后用MinioClient来做文件上传和下载。
我用MinIO做过几次图片存储后,最大的感受是:部署到云服务器后,图片域名和业务域名可以分离,减少主服务器带宽压力;而且MinIO自带Web管理界面,文件列表一目了然。当然,如果学校或者公司没有额外服务器部署MinIO,也可以退一步用本地磁盘存储加Nginx映射静态资源,但那样每次迁移都要手动拷图片目录,体验很差。
为什么强调这些选型决策?因为很多人做项目失败,不是输在编码,而是输在一开始的技术选型。选型错了,后面越写越别扭,改来改去,时间全浪费了。
2. 核心细节解析与实操要点
2.1 数据库表设计的关键细节
数据库是一个网站系统的地基。旅游网站的表设计,我给出一个通用参考,实际项目可以根据需求增删字段。
用户表user:主键id、用户名、密码(加密存储)、昵称、手机号、邮箱、头像、角色(普通用户/管理员)、注册时间、状态。密码绝对不能明文存,用Spring Security自带的BCrypt加密,或者用MD5加盐也行,我建议前者。
景点分类表category:主键id、分类名称、排序、状态、创建时间。这个表不要设计得太复杂,就做一级分类就行,旅游景点的二级分类通常意义不大。
景点信息表scenic:主键id、所属分类id、景点名称、景点简介、详细介绍(富文本)、封面图URL、详情图列表(JSON字符串存储)、地址、开放时间、门票价格、评分、浏览量、状态、创建时间。
订单表orders:主键id、订单编号、用户id、景点id、数量、总金额、下单时间、支付状态、订单状态(待支付/已支付/已取消/已完成)。
评论表comment:主键id、用户id、景点id、评论内容、评分、回复上级评论id、评论时间、状态。
公告表notice:主键id、标题、内容、创建时间、状态。
这个设计里有一个细节值得展开:详情图列表为什么用JSON字符串存?因为一张景点详情可能需要多张图片,如果单独建一张子表,每次查询都要多一次关联;如果直接用一个字段存JSON数组,查询时把字符串解析一下就行。数据量不大、结构又简单的场景下,这种“偷懒”方案反而好用。
另一个细节是订单编号。不要用数据库自增id当订单号,因为用户下单后会把订单号发给平台客服,自增id容易暴露订单量,而且不美观。我习惯用时间戳加随机数生成订单编号,比如20250307093015421,格式是年月日时分秒加四位随机数,简易且够用。
2.2 Spring Boot项目搭建和配置要点
项目骨架用Spring Initializr生成最省事。IDEA里面新建项目时直接选Spring Initializr,或者到start.spring.io网站打包下载,再导入IDEA。需要注意的一点是Spring Initializr默认会选择最新稳定版Spring Boot,如果你要用JDK 8,记得手工改成2.7.x。
生成项目后,先在pom.xml里补充依赖。我每次必加的东西包括:spring-boot-starter-web、spring-boot-starter-thymeleaf、mybatis-plus-boot-starter、mysql-connector-java(新版是com.mysql:mysql-connector-j)、lombok、spring-boot-starter-validation。如果你的系统需要登录鉴权,可以加Spring Security;如果只是简单用Interceptor做登录拦截,也可以不引入Spring Security,避免配置复杂度上升。
配置文件我习惯用application.yml而不是application.properties,因为YAML层级清晰,写多数据源或者自定义配置时更容易阅读。一个最小可用的配置大致是:
server: port: 8080 servlet: context-path: / spring: datasource: url: jdbc:mysql://localhost:3306/travel_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false mybatis-plus: mapper-locations: classpath:/mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这里有一个特别容易踩的坑:数据库连接的serverTimezone参数不设置时,老版本MySQL驱动会报时区异常。另外如果从网上下载的项目是5.x驱动,要注意driver-class-name是com.mysql.jdbc.Driver,而8.x驱动是com.mysql.cj.jdbc.Driver,别搞混。
配置过程中我还遇到过JDK和Maven不匹配导致构建失败的情况。Maven从3.6升级到3.8后,对JDK的默认支持也有变化。如果发现编译报错,先检查Maven编译插件版本,把maven-compiler-plugin的source和target设成和JDK版本一致。这个破问题困扰了我一下午,最后发现是pom.xml里没显式声明编译版本,导致默认编译级别对不上。解决办法是在pom.xml里加上:
<properties> <java.version>1.8</java.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties>2.3 前后端数据交互的常见坑
旅游网站如果采用前后端分离模式,Vue打包后放进Spring Boot的src/main/resources/static目录下,这是一个热搜里出现的操作。做法是:Vue项目执行npm run build,生成dist目录,把里面的内容复制到Spring Boot的static目录,然后重启后端服务,访问http://localhost:8080/就能看到页面。
这里有几个注意点。第一,Vue的router如果使用history模式,刷新页面会出现404,因为Spring Boot默认没有把前端路由转发到index.html。解决办法是写一个转发Controller,把非API路径统统转发:
@Controller public class PageForwardController { @RequestMapping(value = {"/", "/index", "/scenic/**", "/orders/**", "/user/**"}) public String forward() { return "forward:/index.html"; } }第二,Vue打包后,JavaScript文件路径默认是绝对路径/js/...,如果你的项目部署在子路径,要做publicPath配置。第三,API请求的跨域问题,在Spring Boot里加一个WebMvcConfigurer实现类,重写addCorsMappings方法即可。
当然,如果不用前后端分离,直接用Thymeleaf渲染页面,就不存在跨域问题,开发和部署都更简单。对于以单人开发为主的项目,我一直建议先用服务端渲染,把核心业务跑通后再考虑拆分前端。
3. 实操过程与核心环节实现
3.1 从零开始搭建开发环境
先说本地开发环境的准备。JDK 8、Maven 3.6+、MySQL 5.7+、IDEA 2020+,这套组合是我反复验证过的稳定组合。Windows用户下载JDK时记得选对应架构的安装包,装好后在环境变量里配JAVA_HOME和Path。配好后打开命令行输入java -version验证,能输出版本信息就说明没问题。
Maven安装后要换镜像源,不然下载依赖能慢到怀疑人生。打开conf/settings.xml,找到mirrors标签,加入阿里云镜像:
<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>aliyun maven mirror</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>MySQL安装时建议把字符集设置成utf8mb4,因为utf8mb4能存表情符号,而UTF8字符集在MySQL里是3字节,存不了emoji。虽然网站本身不一定需要emoji,但保险起见还是用utf8mb4。
数据库工具我推荐DbVisualizer或者Navicat,热搜里提到的“dbx数据库工具”我不太熟,但只要是支持图形化建表、执行SQL的工具都行。我自己平时就用Navicat导数据、看ER图,方便得很。
开发环境建好后,创建一个数据库travel_db,然后把SQL脚本导入。SQL脚本从哪里来?如果项目自带源码数据库文件,直接用导入功能即可。如果是自己从零开发,就按前面说的表结构,在Navicat里可视化建表,然后让Navicat帮你生成SQL脚本保存起来,方便后续交接。标题里说的“源码+数据库+调试部署”指的就是这套交付物:后端源码工程、数据库初始化脚本、部署文档。
3.2 核心业务功能怎么落地
拿旅游网站最核心的功能举例,写一个景点分页列表的完整实现。
第一步,在Scenic实体类上加上MyBatis-Plus注解:
@Data @TableName("scenic") public class Scenic { @TableId(type = IdType.AUTO) private Long id; private Long categoryId; private String name; private String intro; private String detail; private String cover; private String images; private String address; private String openTime; private BigDecimal price; private Double score; private Long viewCount; private Integer status; private LocalDateTime createTime; }第二步,Mapper接口继承BaseMapper<Scenic>,什么方法都不用写。
第三步,Service层继承ServiceImpl<ScenicMapper, Scenic>,然后写一个自定义查询:
@Override public Page<Scenic> pageScenic(Page<Scenic> page, String keyword, Long categoryId) { LambdaQueryWrapper<Scenic> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.hasText(keyword), Scenic::getName, keyword) .eq(categoryId != null, Scenic::getCategoryId, categoryId) .eq(Scenic::getStatus, 1) .orderByDesc(Scenic::getViewCount); return baseMapper.selectPage(page, wrapper); }第四步,Controller接收参数,返回数据。
这个流程里我重点解释一下LambdaQueryWrapper的好处:它是类型安全的查询条件构造器,字段名用方法引用表示,不会有手写字符串写错的风险。比如你不小心把view_count写成viewcount,编译期根本看不出来,运行期才报错,而LambdaQueryWrapper直接把字段和实体属性绑定,编译期间就能发现属性名错误。
分页参数怎么从前端传过来?我没有用传统pageNum和pageSize,而是让前端传current和size,配合MyBatis-Plus的Page对象,直接从请求参数里绑定,代码少写不少。如果前端要的是pageNum风格,你自己在Controller里做一层参数转换即可。
订单模块还有一个必写的逻辑:下订单时先查库存或状态。旅游景点票务虽然没有实物库存那么复杂,但是要防止用户重复下单。我的做法是用户提交订单时,先用用户id和景点id查一下有没有未支付订单,如果存在,提示“您还有待支付的订单”;如果不存在,再新增订单。这个判断虽然简单,但能挡住绝大多数误操作。
3.3 图片上传与MinIO集成
图片上传功能是旅游网站的高频功能,我把MinIO集成过程完整说一遍。先到项目官网下载MinIO服务器端,Windows直接下载exe文件,Linux下载rpm或二进制文件。我常用的是docker部署:
docker run -p 9000:9000 -p 9001:9001 \ --name minio \ -v /data/minio/data:/data \ -e "MINIO_ROOT_USER=admin" \ -e "MINIO_ROOT_PASSWORD=admin123456" \ quay.io/minio/minio server /data --console-address ":9001"启动后,9000端口是S3 API端口,9001是Web管理界面。第一次登录后新建一个bucket,比如叫travel-images,访问权限设为public或者private看需求。我通常设为public,因为景点图片就是要给游客看,没必要做鉴权。
Spring Boot的pom.xml加入依赖:
<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.5.7</version> </dependency>配置类:
minio: endpoint: http://localhost:9000 access-key: admin secret-key: admin123456 bucket: travel-images然后写一个工具类:
@Component public class MinioUtils { @Autowired private MinioProperties minioProperties; private MinioClient client; @PostConstruct public void init() { client = MinioClient.builder() .endpoint(minioProperties.getEndpoint()) .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey()) .build(); } public String upload(MultipartFile file) throws Exception { String originalFilename = file.getOriginalFilename(); String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); String objectName = UUID.randomUUID().toString().replace("-", "") + suffix; client.putObject(PutObjectArgs.builder() .bucket(minioProperties.getBucket()) .object(objectName) .stream(file.getInputStream(), file.getSize(), -1) .contentType(file.getContentType()) .build()); return minioProperties.getEndpoint() + "/" + minioProperties.getBucket() + "/" + objectName; } }这段代码有两个坑要提一下。第一,file.getInputStream()方法调用后,流只能被读取一次。如果你既想在本地保存,又想上传MinIO,一定要先复制一个流。第二,上传后的URL拼写要注意,MinIO的endpoint如果带了路径,拼接时不要重复。实际部署时,我建议再加一个Nginx反代,把/minio/请求转发到MinIO服务,这样外网访问图片时走的是80端口而不是9000端口,安全性和美观性都好很多。
MinIO集成实际做下来,比传统磁盘存储舒服得多。以前磁盘存储的删除逻辑,要计算文件物理路径再删文件;现在MinIO直接把文件对象删除就行,和管理数据库记录一样自然。
3.4 调试部署的完整流程
本地调试时,IDEA直接点击Run按钮启动Application类,访问http://localhost:8080测试。如果你改了前端页面且用了Thymeleaf,记得把spring.thymeleaf.cache设为false,不然页面改动不生效,还得重启服务,很耽误事。
部署到服务器时,我推荐两种方式。第一种,打Jar包部署,适合中小项目。
先在pom.xml引入Spring Boot打包插件:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>然后在项目根目录执行:
mvn clean package -DskipTests看到BUILD SUCCESS后,在target目录下会生成一个xxx.jar。把这个jar上传到服务器,执行:
java -jar travel-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod如果要让服务在后台常驻,用nohup:
nohup java -jar travel.jar > app.log 2>&1 &第二种,Docker部署,适合有Docker环境的服务器。
我不建议新手一上来就用Docker,因为你得同时搞定Dockerfile、镜像构建、容器网络,任何一个环节出问题都很难查。先把Jar方式跑通,再上Docker不迟。如果确实要用Docker,一个精简的Dockerfile如下:
FROM openjdk:8-jre-alpine WORKDIR /app COPY travel.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]注意基础镜像要选8-jre-alpine而不是8u102-jdk,后者体积大好几倍,部署传输慢。我一开始为了图省事直接拉大镜像,结果上传服务器时等了十分钟,后来换了Alpine版本,体积小、启动也更快。
数据库放服务器上也别裸奔。先用命令行创建数据库并导入SQL:
mysql -u root -p -e "CREATE DATABASE travel_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -u root -p travel_db < travel_db.sql如果项目和数据库在同一台机器,application.yml里的数据库地址就填localhost;分开了就填数据库服务器私有IP。环境配置尽量用Profile区分:
spring: profiles: active: dev --- spring: config: activate: on-profile: dev datasource: url: jdbc:mysql://localhost:3306/travel_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai --- spring: config: activate: on-profile: prod datasource: url: jdbc:mysql://你的数据库IP:3306/travel_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai有人问为什么要用Profile,直接改配置不行吗?行,但多环境Profile的好处是上线时不用手动改任何东西,启动命令里带--spring.profiles.active=prod就完事,不容易出错。
4. 常见问题与排查技巧实录
4.1 启动报错的典型场景
场景一:端口被占用
启动Spring Boot时提示Port 8080 was already in use。这个非常常见,尤其是你开着一堆乱七八糟的服务。解决办法就是找到占用进程并停掉。
Windows下:
netstat -ano | findstr 8080 taskkill /PID 对应PID /FLinux下:
lsof -i:8080 kill -9 对应PID或者干脆改项目端口,把server.port改成8081,也可以。
场景二:无法加载主类或JDK版本不匹配
启动时报UnsupportedClassVersionError,百分百是编译版本和运行版本不一致。压缩包里的源码如果用JDK 17编译,你本地JDK 8运行必然会报这个错误。解决办法是重新编译,或者干脆把JDK升到和源码一致。我建议优先看项目pom.xml里的java.version是多少,然后本地装对应JDK。多版本JDK共存时,用IDEA的Project Structure里把Project SDK切对,问题就能解决。
场景三:数据库连不上
报Access denied for user 'root'@'localhost',先确认密码对不对,再确认用户名是不是root。MySQL 8默认认证插件是caching_sha2_password,老版本MySQL驱动不支持,所以如果你用MySQL 8,驱动要选8.x。把mysql-connector-java换成com.mysql:mysql-connector-j版本即可。
还有一种情况是连接超时,Communications link failure。先ping一下数据库服务器通不通,然后检查数据库端口能不能访问。我遇到过的最离奇问题,是服务器防火墙开着但云服务商安全组没放行3306端口,本地能连,服务器上连不了,折腾半天才知道,安全组规则挡住了一切。
场景四:Thymeleaf模板报错
Error resolving template [index], template might not exist,多半是templates目录下没有index.html,或者文件名写错。还有可能是application.yml里没有配置视图解析器前缀后缀,默认前缀是classpath:/templates/,后缀是.html,如果你的页面放在其他目录就要手动配。
4.2 前端常见问题
Vue项目打包后接口404,通常是接口路径和前缀不匹配。我习惯在后端Controller里统一加/api前缀,比如@RequestMapping("/api/scenic"),这样前后端分离时,逆向代理规则很清晰。还有就是在application.yml里配置:
spring: mvc: servlet: load-on-startup: 1这个参数解决的是后端服务刚启动时,第一次请求偶发慢的问题,相当于让DispatcherServlet提前初始化。实际效果还行,但不是必需。
前后端联调跨域报错时,不是只有后端配CORS这一种方案。你可以让前端用Vite的Proxy配置转发到后端,这样浏览器看到的还是同源请求,Cross-Origin问题直接消失。Vite配置:
server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }这个方法比我配CORS简单得多,而且开发环境下屡试不爽。
4.3 数据库增删改查的日常坑
用MyBatis-Plus做增删改查时,有一个字段填充问题值得说。很多表都有create_time和update_time字段,如果每次插入都手动setCreateTime,代码零散且容易漏。MyBatis-Plus提供了自动填充功能:实体类字段上加@TableField(fill = FieldFill.INSERT),然后在Handler里统一填充:
@Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } }这样控制器里插入记录时完全不用管时间字段,省不少事。
还有一个容易被忽视的坑:MyBatis-Plus的逻辑删除配置。如果你在实体类字段上加了@TableLogic注解,删除操作会自动变成update set deleted=1 where id=?,查询时自动追加deleted=0条件。这个功能很好用,但有一个副作用:加了@TableLogic后,如果用selectById查询,它也会自动带删除条件,这是正常的。如果哪天发现明明有数据却查不到,先想想是不是逻辑删除字段被误加或者值不对。
4.4 部署上线后的监控与日志
上线不是终点,能稳定运行才是目的。我每次部署完都会做三件事:
第一,看启动日志。nohup java -jar travel.jar > app.log 2>&1 &启动后,用tail -f app.log观察日志,看到Started Application in X seconds才算真正启动成功。我见过有人说服务起不来,结果发现是启动上一秒日志还是正常的,下一秒进程就消失了,查下来是服务器内存不足,JVM启动申请不到内存被系统杀掉了。
第二,配置日志文件保存。默认控制台日志不落盘会很麻烦。在application.yml里加:
logging: file: name: logs/travel.log这样日志记录到本地文件,排查问题时直接grep关键字,比翻控制台强一百倍。
第三,用ps -ef | grep java确认进程在跑,再用curl http://localhost:8080/api/scenic/list验证接口通不通。如果接口返回正常JSON,基本就稳了。
5. 论文文档写作的实操经验
标题里明确提到“带论文文档1万字以上”,这里我也分享一点写论文文档的经验。很多同学写技术论文总是把字凑在“绪论”和“国内外研究现状”里,其实评委真正看的是你的系统设计、数据库设计和实现细节。我的写作框架建议如下:
第一章绪论:项目背景、研究意义、国内外研究现状。这里不要空谈,要结合旅游行业信息化趋势去写,引用一些公开统计数据,字数自然就上来了。
第二章需求分析:功能性需求和非功能性需求。功能性需求就写普通用户能浏览景点、搜索、下订单、评论,管理员能维护景点信息、处理订单等。非功能性需求写系统性能、安全性、易用性。
第三章系统设计:系统总体架构图、模块划分、数据库设计。数据库设计可以给出ER图、每张表的字段说明,这一节是字数的大头,一定要写细。
第四章系统实现:每个核心功能模块的实现思路、关键代码、界面截图。代码不要贴一大段,要挑核心方法,配上文字解释。
第五章系统测试:测试用例、测试过程、测试结果。给出登录模块、景点查询模块、下单模块的测试用例表。
这套框架写下来,加上代码和截图,1万字很容易凑够,而且质量高,不水。
写论文时还有一个技巧,界面截图不能少。先把系统跑起来,把前台页面、后台页面、数据库表截图,插入论文中,每一张图配一段文字说明,视觉上充实,逻辑也完整。很多同学的论文被导师打回都是因为图和表太少,论文干巴巴全是字。
6. 实用工具扩展与效率提升
最后说两个实际能提升开发效率的小工具。
第一个是数据库连接池。虽然标题里没直接提,但这属于必备案。MyBatis-Plus默认用的是HikariCP,Spring Boot 2.x默认集成了Hikari,它非常轻量且性能好。如果默认连接池配置不够,可以自定义:
spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000这个配置的重点是maximum-pool-size不要设太大,数据库并发量没有达到几百上千,10到20就够了,设太大会消耗数据库连接资源。
第二个是数据库设计阶段的ER建模工具。我用过ERMaster、Navicat的模型功能,也用过在线工具如draw.io,但最顺手的还是直接在Navicat里设计表,然后让工具生成SQL脚本。如果你要跟同学协作开发,可以用数据库版本管理工具Flyway,把SQL脚本纳入版本控制。不过这个项目规模不需要上Flyway,你们直接把SQL导出成文件,放在db目录下统一定义即可。
热搜里还有一个“数据库同步软件”,这里提一句。如果你在本地开发、服务器部署,需要把本地数据库结构和数据同步到线上,最省事的办法是在Navicat里用“数据传输”功能,选好源和目标的连接,一键同步。尽量不要用命令行手工导出导入,编码问题容易让你头大。
这个旅游网站项目做完之后,我最大的体会是:一个合格的业务系统,代码只是一部分,环境配置、数据设计、部署运维、文档整理,每一环都很关键。我见过太多代码写得很好,但因为没有合理的表设计导致后期改需求改到崩溃的项目;也见过功能并不复杂,却因为Spring Boot版本和JDK不匹配,卡在启动阶段一整天的案例。把基础打牢,把选型做对,后面的路会顺很多。
如果你正打算照着这个项目去复刻或者改造,我的建议是:不要想着一步到位加太多花哨功能,先把用户、景点、订单、评论、公告这五个基础模块做扎实,界面做得干净一些,论文写得规范一些,整个项目就已经足够完整了。等你把这些基础模块全跑通了,再考虑加收藏、加搜索、加支付,都是水到渠成的事。