ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0电影评论网站开发实战

SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0电影评论网站开发实战 如果你最近在折腾电影评论网站这类 Java Web 前后端分离项目SpringBoot2 Vue3 MyBatis-Plus MySQL8.0 这套组合应该没少听。我完整做过一版带着全套文档从需求分析到数据库设计再到前后端联调和部署上线都走了一遍。今天不打算给你贴一个“跑起来就行”的教程而是把技术选型背后的考虑、核心模块的实现方式、以及真正让我花时间踩平的坑都拿出来聊。这套系统适合正在准备毕业设计、课程设计或者想找一个能写进简历的项目来练手的同学前端能练到 Vue3 组合式 API 和组件化后端能练到 SpringBoot2、MyBatis-Plus 的快速开发套路数据库还能把 MySQL8.0 的索引和连接配置弄明白。先说明我的判断电影评论网站业务本身不复杂难的是把权限、评分、评论、点赞这些看似简单的功能做到逻辑闭环。很多人一上来就写代码结果表结构对不上、字段类型踩坑、联调时前端拿不到想要的数据。这篇文章会按照“整体设计 → 后端 → 前端 → 数据库 → 部署排坑 → 文档使用”的顺序推进你跟着思路走基本可以复刻一版。1. 项目整体设计与技术选型思路1.1 电影评论网站到底要解决什么问题一个典型的电影评论网站用户故事其实很集中游客能浏览电影列表和详情注册登录后可以给电影打分、写评论、回复别人的评论还能删除自己的评论管理员在后台维护电影信息、管理用户状态、审核或删除违规评论。这里有个容易被忽略的点评分和评论虽然都围绕电影展开但它们本质上不是同一个功能。评分是用户对电影的一个主观分数通常一个用户对一个电影只能有一个有效评分评论则可以有主楼和回复形成树状结构。把这两个需求分开建模后续的接口和页面都会清晰很多。业务闭环虽然不深但它覆盖了常见的工程问题用户注册登录、JWT 鉴权、文件上传海报头像、多表关联查询、分页、点赞去重、平均分聚合、后台管理权限。这些点做扎实了项目不会显得“只有一个壳子”。1.2 为什么是 SpringBoot2 Vue3 MyBatis-Plus MySQL8.0这套技术栈放在今天依然很主流。SpringBoot2.7.x 是稳定版本用 JDK8 或 JDK11 都能跑第三方开源组件支持非常全反而 SpringBoot3 强制要求 JDK17并且 Jakarta EE 命名空间变化很多老教程和开源框架会踩兼容性坑。对课程设计、毕业设计而言稳定压倒一切所以我选了 SpringBoot2而不是最新版本。Vue3 的理由也很简单组合式 API 让逻辑复用比 Vue2 的 Options API 舒服太多写一个电影列表页、一个评论递归组件都能明显感觉到思路清晰。配合 Vite 的开发体验远比 Webpack 快热更新几乎无感。Pinia 替代 Vuex 后状态管理样板代码也少了很多。MyBatis-Plus 则是后端效率利器。它本身不改变 MyBatis 的能力只是把单表 CRUD、条件构造器、分页插件、代码生成器这些重复劳动给封装好了。你完全可以用它写简单单表操作复杂多表查询仍然手写 XML灵活度没有损失。MySQL8.0 相比 5.7 的升级体验很明显默认字符集是 utf8mb4emoji 和生僻字不会乱码支持窗口函数和 CTE写复杂统计 SQL 更方便身份认证插件变成了 caching_sha2_password确实带来了一点连接坑但配置好了以后连接性能更好。单机数据量在百万级以内这个组合完全撑得住。1.3 功能模块划分与接口设计我把系统拆成用户、电影、评论、评分、后台管理五个模块前端使用 Vue3 单页应用后端提供 RESTful API。这样划分以后开发任务可以并行页面和接口对应关系也清晰。模块主要接口说明用户POST /api/user/register、POST /api/user/login、GET /api/user/info注册、登录、获取当前用户信息电影GET /api/movie/page、GET /api/movie/{id}、GET /api/movie/search分页列表、详情、条件搜索评论GET /api/comment/list、POST /api/comment、DELETE /api/comment/{id}、POST /api/comment/like评论列表、发表、删除、点赞评分POST /api/movie/rate用户给电影打分后台GET /api/admin/movie/page、POST /api/admin/movie、DELETE /api/admin/comment/{id}电影管理、评论审核接口设计成 RESTful 风格统一返回 JSON 结构。你如果愿意可以用 Apifox 把这套接口录入好前端和后端分开联调会非常舒服。字段定义一定要以接口文档为准前后端最怕“我以为你返回的是这个字段”这种问题我后面会专门聊。2. 后端核心实现SpringBoot2 与 MyBatis-Plus2.1 初始化项目与依赖版本搭配我用 Spring Initializr 创建工程选择 SpringBoot 2.7.xJDK 版本用的 1.8。依赖除了基础的 web、mysql、lombok还需要引入 MyBatis-Plus 和 JWT 相关包。pom.xml 里最关键的几个依赖如下dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency /dependencies这里有一个很容易踩的版本坑MyBatis-Plus 3.5.x 的 starter 依赖 mybatis-spring。如果你把 MyBatis-Plus 升级到 3.5.7 以上可能引入 mybatis-spring 3.x而 SpringBoot2.7 的自动装配并不完全兼容。所以别一味追求“最新版”组件升级前先看 release notes。我用 3.5.3 这个版本实测过配 SpringBoot2.7 很稳。application.yml 里的数据源和 MyBatis-Plus 配置是重点server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/movie_comment?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: 123456 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0allowPublicKeyRetrievaltrue 这个参数我要单独说。MySQL8.0 默认认证插件是 caching_sha2_password第一次连接时需要从服务器获取公钥而 JDBC 驱动出于安全考虑默认不允许自动获取于是报 “Public Key Retrieval is not allowed”。加上这个参数不是安全漏洞只是让驱动在首次连接时按正常流程拿公钥很多教程不会提你自己查 bug 的话很容易卡半天。2.2 统一返回结果与全局异常处理前后端对接最容易乱的就是返回结构。我写了一个 R 类作为统一返回体code 为 200 表示成功其他都是业务错误前端只认这一个结构。Data public class RT { private Integer code; private String message; private T data; public static T RT ok(T data) { RT r new R(); r.setCode(200); r.setMessage(success); r.setData(data); return r; } public static T RT fail(String message) { RT r new R(); r.setCode(500); r.setMessage(message); return r; } }配合 RestControllerAdvice 做全局异常捕获业务里就可以放心地直接抛异常。比如评论内容过长、电影不存在、用户未登录都抛一个自定义 BizException统一返回 R.fail。这样 controller 里不会堆满 try-catch代码可读性好很多。2.3 通用 CRUD 与条件查询的正确姿势MyBatis-Plus 最香的部分是 BaseMapper 和 ServiceImpl。我生成实体类后UserMapper 只要继承 BaseMapper UserService 继承 IService 大部分单表操作就都有了。我建议不要直接用 Controller 继承 ServiceImpl 那种“零写代码”的 CRUD那样接口暴露太粗暴项目看起来就不像认真做的。正确姿势是 Service 层继承 IServiceController 只暴露必要的接口业务校验放在 Service 中。条件查询我习惯用 LambdaQueryWrapper比如电影列表的筛选LambdaQueryWrapperMovie wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(keyword), Movie::getTitle, keyword) .eq(region ! null !region.isEmpty(), Movie::getRegion, region) .eq(type ! null !type.isEmpty(), Movie::getType, type) .orderByAsc(Movie::getReleaseYear); PageMovie page movieMapper.selectPage(new Page(pageNum, pageSize), wrapper);Wrapper 的第一个 boolean 参数是条件是否生效这样前端传不传参都不会拆出错误 SQL。用 Lambda 表达式引用字段而不是写字符串是为了重构字段名时不至于在 SQL 里漏改。如果你要写复杂多表关联比如评论列表联用户表就自定义 XML这时候 MyBatis-Plus 的分页插件依然能拦截分页所以体验是统一的。2.4 评论、点赞与评分背后的逻辑细节评论接口看起来是 insert 一条数据但需要注意的点不少。用户从 JWT 里拿 userId校验电影是否存在再校验评论内容长度然后插入评论表。如果系统需要敏感词审核还得根据 content 判断 status 是 0 还是 1。插入成功后可以选择性更新电影的评论统计数但评论本身对电影列表的影响不大所以我这里没有强行维护数量字段。点赞功能最怕的是重复点赞。我的方案是单独建一张 comment_like 表字段包含 user_id、comment_id并在数据库里加唯一索引。前端点“赞”时后端先查这条点赞记录不存在就插入存在就删除并发情况下两个请求同时插入依赖唯一索引来兜底捕获 DuplicateKeyException 后把已存在记录删除同时更新评论表的 like_count。这个做法的好处是代码简单、数据不会脏。面试如果问到还能顺带讲清楚唯一索引为什么能兜底并发。评分接口我单独做成“一个用户对一个电影只能有一个有效评分”。因为用户可能改了主意所以不是纯插入而是先查该用户对这个电影的评分记录存在就更新 score不存在就插入。movie 表里冗余了 rating_sum、rating_count、rating_avg 三个字段业务方法上加上 Transactional保证评分表和聚合字段的更新同时成功或同时失败。事务在这里很重要否则会出现评分表插入了电影平均分没更新的情况。电影平均分不建议每次从评分表实时计算。数据量一旦上来实时聚合的性能会很差。维护冗余字段在写入时计算是最简单也最可靠的方案。如果怕并发问题可以在事务里先 SELECT ... FOR UPDATE 锁住评分记录但电影评论网站的数据压力远没到必须做悲观锁的程度先查后更新足够。3. 前端核心实现Vue3 组件化与状态管理3.1 Vite 搭建 Vue3 工程与目录规划创建 Vue3 工程我用的 Vite命令很简单npm create vitelatest movie-front -- --template vue cd movie-front npm install npm install vue-router4 pinia axios element-plus element-plus/icons-vue sass目录结构建议在一开始就定好不然后面代码全堆在组件里非常痛苦。我的实践是src ├── api │ ├── http.js # axios 封装 │ ├── movie.js │ ├── comment.js │ └── user.js ├── components │ └── CommentItem.vue # 评论递归组件 ├── router │ └── index.js ├── stores │ └── user.js # Pinia 用户状态 ├── views │ ├── HomeView.vue │ ├── MovieList.vue │ ├── MovieDetail.vue │ └── LoginView.vue └── App.vue由于项目以后要部署到 Nginx开发环境最好走 Vite 代理解决跨域。vite.config.js 里配置 /api 代理import { defineConfig } from vite import vue from vitejs/plugin-vue import { fileURLToPath, URL } from node:url export default defineConfig({ plugins: [vue()], resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)) } }, server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })使用代理之后前端所有请求都写成 /api 前缀切换环境不用改业务代码生产环境由 Nginx 做同样的反向代理。这样跨域问题从根上避开而不是在 SpringBoot 后端开 CORS 放行所有来源。如果你非要后端开 CORS配置时也别用“*”以后部署有安全风险。3.2 Axios 封装与 Pinia 状态管理Axios 封装直接决定联调效率。我的 http.js 里做了请求拦截器带 token、响应拦截器处理业务码和 401 跳登录。import axios from axios import { ElMessage } from element-plus import router from /router import { useUserStore } from /stores/user const http axios.create({ baseURL: /api, timeout: 15000 }) http.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers.Authorization Bearer ${userStore.token} } return config }) http.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?.status 401) { const userStore useUserStore() userStore.logout() router.push(/login) } ElMessage.error(error.message || 网络异常) return Promise.reject(error) } ) export default http这里有个我亲身踩过的坑在模块顶层直接调用 useUserStore()会报 “getActivePinia was called with no active Pinia”。原因很简单axios 模块被引入时 Pinia 实例还没创建。正确的做法是在拦截器函数内部调用 useUserStore()也就是代码里写的那样。按需引入 Element Plus 组件时样式不生效的坑也很常见解决方法是安装 unplugin-vue-components 和 unplugin-auto-import 插件让它自动处理。Pinia 的 user store 里存 token 和用户信息登录成功后写入 localStorage刷新页面时再取出来恢复状态。退出登录时要清空 token 和用户信息并跳转首页。3.3 电影详情页与评论递归组件电影详情页是核心页面包含电影基本信息、平均分、评分组件和评论列表。页面一进来并行请求电影详情和第一页评论评分组件用 Element Plus 的 Rate 组件max 属性设为 10value 绑定当前用户的分数如果还没评分就默认 0。评论列表我用了一个递归组件 CommentItem.vue这样多级回复可以无限展开代码结构也很清晰。Vue3 递归组件允许在模板里使用自身文件名作为组件名只要把组件注册到自己的 name 属性即可。父组件传一个 comment 对象子组件内部渲染评论内容和操作按钮操作时通过 emit 事件通知父组件重新加载列表或更新点赞数。前端处理评论列表要特别注意“列表状态别直接改 props”。评论删除和点赞都涉及数组更新正确的做法是父组件维护 comments 数组子组件 emit 事件后父组件重新请求数据或者本地更新对应字段。不要为了图方便在子组件里直接操作 props那一开始能用后面一定会出现列表不刷新、数据对不上的问题。还有一个体验细节筛选条件保留。用户从电影列表页点了搜索再进详情页返回时如果条件丢了体验很断层。我后来把筛选条件放到 URL query 里比如 /movie?region中国type爱情这样刷新和路由返回都不会丢。Vue3 里用 useRoute().query 和 router.push 配合即可这个细节在项目答辩时也能加分。4. 数据库设计MySQL8.0 表结构与索引优化4.1 四张核心表的设计整站最核心的数据表是 user、movie、comment、rating。我的建表设计思路可以浓缩成下面几个表格。用户表 user字段名类型说明idbigint主键usernamevarchar(50)登录名唯一passwordvarchar(100)BCrypt 加密后的密码nicknamevarchar(50)昵称avatarvarchar(255)头像地址create_timedatetime注册时间deletedtinyint逻辑删除 0/1电影表 movie字段名类型说明idbigint主键titlevarchar(200)电影名postervarchar(255)海报地址summarytext简介release_yearint上映年份regionvarchar(50)制片地区typevarchar(50)类型durationint时长(分钟)rating_sumbigint评分累计和rating_countint评分人数rating_avgdecimal(3,1)平均分statustinyint1 上架 0 下架deletedtinyint逻辑删除评论表 comment字段名类型说明idbigint主键movie_idbigint电影 iduser_idbigint用户 idparent_idbigint父评论 id0 表示顶级评论contentvarchar(1000)评论内容like_countint点赞数statustinyint0 待审核 1 通过create_timedatetime评论时间deletedtinyint逻辑删除评分表 rating字段名类型说明idbigint主键user_idbigint用户 idmovie_idbigint电影 idscoretinyint1-10 分create_timedatetime评分时间deletedtinyint逻辑删除这里我想专门讨论逻辑删除和评分表唯一索引的关系。如果 rating 表用逻辑删除一个用户评分后再取消再评分数据库里会出现重复逻辑删除痕迹。所以我在评分表里不对评分做逻辑删除而是采用“存在即更新、不存在即插入”的业务逻辑。这个细节看起来不大但真实项目里很影响数据完整性文档中我都写成了注意事项。password 字段必须用 BCrypt 加密存储不要用 MD5。MD5 在彩虹表面前基本是裸奔BCrypt 每次生成的盐不同安全性高得多。Spring Security 自带 BCryptPasswordEncoder如果你不想引入整个 Security 框架也可以单独引入 spring-security-crypto 依赖。4.2 索引设计与分页查询优化索引设计我遵循“查询驱动”的原则先看最常用的查询条件再建索引。评论列表最常按 movie_id 过滤并且按时间倒序所以建了 idx_movie_time(movie_id, deleted, create_time desc)。MySQL8.0 支持降序索引这对倒序查询有优化帮助如果你还在用 5.7那 sort 方向不同时只能靠文件排序差别没那么大。评分表唯一索引 uk_user_movie(user_id, movie_id) 是保证一个用户只能有一个有效评分的关键也是并发下防重复的兜底。电影表常用 status 和 release_year 作为筛选我建了一个组合索引 (status, release_year)。用户表的 username 是登录唯一标识需要唯一索引。这些索引建完以后整个系统的常见查询都能走索引数据量在几万评论量级内表现很好。分页如果遇到深偏移比如注释翻到第 5000 页直接 limit 100000, 20 的效率会让人崩溃因为 MySQL 会扫描前十万行再丢弃。我用过的改进方案有两种第一种是“延迟关联”先查出符合条件的主键再回表查完整数据第二种是“游标分页”前端记录上一页最后一条评论的 create_time 和 id下一页查询条件改成小于这个值然后 limit 20。第二种体验最好但会失去快速的页码跳转。这种优化未必是毕设必需但写进项目文档里能说明你不是只会简单 CRUD。自定义 XML 里要特别小心排序字段的注入。MySQL 的 ORDER BY 后面不能用 #{} 普通参数绑定很多人图方便用 ${} 拼接这就给了 SQL 注入可能。我的处理方式是排序字段走白名单校验比如只能传入 create_time、like_count、score 等固定值排序方向只能是 asc 或 desc前后端约定好任何非法输入直接返回默认排序。5. 联调部署与问题排查实录5.1 前后端联调最容易踩的四个坑联调阶段我遇到过并总结下来的问题集中在下面这张表里问题表现解决方案跨域浏览器控制台报 CORS errorPreflight 失败开发环境用 Vite proxy生产用 Nginx 反向代理时间格式后端返回 2024-10-01T12:00:00配置 Jackson 的 LocalDateTime 格式为 yyyy-MM-dd HH:mm:ssLong 精度丢失前端显示的 id 末几位变成 0让 Jackson 将 Long 序列化为字符串401 循环跳转登录页不断刷新页面拦截器放行 /api/user/login、/api/user/register 等公开接口Long 精度丢失这个问题很多人到了项目答辩都没发现。数据库 id 是 bigint超过 JS Number 安全整数范围时前端会把末尾几位变成 0。删除评论、编辑电影这类需要传 id 的操作就会选错数据。解决方式是在全局 Jackson 配置里把 Long 类型统一序列化为字符串或者给实体 id 字段加 JsonSerialize(using ToStringSerializer.class)。我选择前者一个配置类搞定Configuration public class JacksonConfig { Bean public Jackson2ObjectMapperBuilderCustomizer customizer() { return builder - { builder.serializerByType(Long.class, ToStringSerializer.instance); builder.serializerByType(Long.TYPE, ToStringSerializer.instance); }; } }5.2 用 Nginx 部署到服务器的完整流程部署流程不复杂但前端路由模式会藏坑。前端用 history 模式时如果在非首页路径刷新Nginx 默认会返回 404。原因是浏览器请求 /movie/detailNginx 没找到这个文件就报 404 了。必须配置 try_files 回退到 index.html让 Vue Router 接管路径。Nginx 关键配置如下server { listen 80; server_name your-domain.com; root /var/www/movie/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }部署步骤我当时按这个顺序执行前端执行 npm run build生成 dist 目录。将 dist 上传到服务器 /var/www/movie/dist。后端执行 mvn clean package -DskipTests得到 jar 包。启动 jarnohup java -jar movie-server.jar server.log 21 重新加载 Nginxnginx -s reload。用 curl http://localhost:8080/api/health 验证后端再访问前端域名验证页面。整个过程一定要先验证后端再验证前端不然你分不清问题是出在 jar 包没起来还是 Nginx 指向错了。5.3 版本兼容与 MySQL8.0 连接细节版本兼容是我在项目里花时间最多的部分。MySQL8.0 的驱动类名不再是 com.mysql.jdbc.Driver而是 com.mysql.cj.jdbc.Driver连接 URL 需要指定 serverTimezoneAsia/Shanghai否则时间差点会差 8 小时。allowPublicKeyRetrievaltrue 这个参数我在前面也提过是 MySQL8.0 连接报错的高频原因。字符集方面数据库和表都要明确 utf8mb4不然评论里出现 emoji 表情、特殊字符会变成问号。建表的时候我统一写了 DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci实测下来中文和 emoji 都能正常存储。还有一个容易被忽略的坑SpringBoot2.7 自带的 MySQL 驱动版本通常在 8.0.33 附近如果你手动改成更旧或更新的版本会导致认证插件不匹配。我建议直接用 SpringBoot 父依赖管理版本不要手动指定 mysql-connector-j 的版本号这样可以少踩很多驱动兼容性的坑。前端版本也一样。Vite 5 和 Vue3.2 以上要求 Node 版本至少 18。我一开始用 Node16Vite 启动直接报错升级 Node18 后才正常。Element Plus 2.x 也不会和 Vue3.4 冲突但如果你用npm create vuelatest生成工程时选了 TypeScript后面改起来会牵扯很多类型问题。我个人的建议是毕设项目别强行上 TS先保证业务跑通再谈工程化升级。6. 项目文档的价值与后续扩展空间6.1 拿到含文档项目第一步该做什么很多人拿到源码第一反应是双击运行但我建议先花半小时读文档。带文档的项目文档就是地图不读地图直接跑跑不动时你会到处瞎改。我的阅读顺序一般是先看 README确认环境要求和启动步骤再看数据库设计文档弄清楚表关系尤其注意逻辑删除和唯一索引的约定然后导入 SQL 脚本初始化数据库打开 Apifox 或 Postman按接口文档先把后端的注册、登录、电影列表测通最后启动前端页面联调。这样做的优势是出问题时你能准确判断是环境问题、数据问题还是代码问题而不是把所有文件都怀疑一遍。文档里如果写了“默认管理员账号”“测试数据”“接口字段说明”这些都是开发者提前帮你踩平的路照着做能省大量时间。等到你后续想扩展功能文档里已有的接口设计思路也是重要的参考依据。6.2 从毕设项目到真实应用的扩展方向这套系统做完以后我整理了三个我实际觉得值得做的扩展方向一是后台管理。当前系统只做了前台如果要变成一个可用系统必须有电影管理、评论审核、用户管理。后台可以单独开一个 Vue3 Element Plus 的 admin 页面界面参考成熟开源项目里的路由权限思路不要直接复制理解后再自己做权限控制。二是缓存和异步。热门电影列表用 Redis 缓存 10 分钟能明显降低数据库压力。点赞数如果活动期间激增可以考虑先写 Redis 再异步落库。但刚开始别直接把所有接口都套 Redis只需要缓存热点数据即可否则系统复杂度会膨胀。三是简单推荐。根据用户浏览历史和评分偏好按地区、类型统计出用户喜欢的方向推荐同类型电影。不用上复杂的算法SQL GROUP BY 就能做。这个扩展比你去套一个深度学习模型更实用项目文档和答辩都能讲清楚逻辑。如果你还想做搜索数据量小的时候用 MySQL 的全文索引就够数据量大了再上 Elasticsearch。毕设阶段不要为了炫技而上一套沉重的基础设施先解决实际问题。我做完这个项目最大的感受是看似简单的业务真正跑通一遍后里面全是工程细节。很多痛苦都出在接口字段对不上、索引没建好、逻辑删除和唯一索引打架这些文档里不会写的地方。文档的价值就在于把这些坑提前标出来所以不管是自己做还是参考别人做的都要把“设计先行”这个习惯练起来。希望这篇内容能帮你少走几个弯路。
返回列表