首页 / 资讯中心 / 文章详情

SpringBoot+Vue汽车资讯网站管理系统实战开发全流程

SpringBoot+Vue汽车资讯网站管理系统实战开发全流程 ★ FEATURED ARTICLE
做JavaWeb项目这些年SpringBootVue这对组合算是绕不开的搭配。前段时间刚完成一个汽车资讯网站管理系统从需求梳理、数据库设计到前后端联调最后打包部署整个流程走下来收获不少。这个项目用JavaMySQLMyBatis做后端数据层SpringBoot搭建接口服务Vue负责前端页面渲染覆盖了资讯发布、分类管理、内容检索、用户浏览这些核心场景。对于正在做毕业设计、或者想熟悉前后端分离开发全流程的同学来说是一个很完整的参考样本。我把这个项目的设计思路、核心细节和踩坑过程整理出来里面有完整的源码结构和数据库脚本照着做就能跑起来。1. 项目定位与技术选型为什么是SpringBootVue1.1 汽车资讯网站到底在解决什么问题汽车资讯网站不是一个简单的“文章展示页”。用户端需要按分类浏览新车评测、行业动态、用车技巧要能搜关键词、看详情、点赞评论管理端需要发布文章、管理分类、控制上下架状态、查看数据。这些需求背后其实就两个核心问题内容怎么管、内容怎么展现。传统的做法是JSPServlet页面和逻辑混在一起改一个小功能就要动整个页面尤其是后台管理界面维护成本极高。换成前后端分离之后前端只负责展示和交互后端只提供数据接口两边可以并行开发互不干扰。这个项目选型的时候我直接把技术栈定为SpringBootVueMyBatis一是这套组合在Java生态里最成熟二是网上资料多遇到问题容易找到解决方案。1.2 各层技术各自负责什么先明确一下每个框架在这个项目里的职责边界SpringBoot负责提供RESTful接口处理业务逻辑管理Bean的生命周期。内置Tomcat打包后一个jar直接跑部署非常省事。MyBatis负责数据库的增删改查。SQL由开发者自己控制复杂多表查询、动态条件拼接都比JPA灵活尤其适合资讯这种需要多条件筛选的场景。Vue负责页面渲染和用户交互。组件化开发让页面像搭积木一样组合数据驱动视图不用再手动操作DOM。MySQL存储所有业务数据文章、分类、用户、评论都落在这里。这四样配合起来有一个明显的好处任何一个环节有问题都能快速定位。比如前端页面显示不对打开控制台看接口返回再查后端日志最后看SQL语句问题在哪一层一目了然。2. 数据库设计与后端核心实现2.1 表结构设计从需求到建表数据库设计是整个项目的根基。我设计表的时候坚持一个原则先列需求再建表。所有功能点先写出来再拆成字段最后整合成表。这个项目一共设计了五张核心表管理员表admin_userid、username、password、role、create_time。密码必须加密存储这里用的是BCrypt千万不能明文存这是最基本的红线。用户表userid、nickname、avatar、phone、create_time。用户表的字段按需增减如果要做第三方登录后面可以加openid字段。分类表content_typeid、type_name、sort_order。为什么单独建一张分类表而不是直接写死在代码里因为分类需要后台动态维护今天叫“新能源”明天可能改叫“电动汽车”直接写死会导致每次改动都要重新发版。资讯文章表articleid、title、author、type_id、cover_image、summary、content、view_count、status、create_time、update_time。这是核心表status字段用于控制发布和下线状态type_id关联分类表。content字段类型用longtext或者mediumtext因为资讯正文可能很长varchar根本放不下。评论表commentid、article_id、user_id、content、comment_time。设计外键关系时我建议逻辑关联不要物理外键也就是在实体里维护articleId和userId但不建数据库级的外键约束。这样做的原因是后续分库分表或者数据迁移时物理外键会成为阻碍。2.2 SpringBoot三层架构怎么写后端代码严格按Controller-Service-Mapper三层切分。Controller只负责接收参数和封装返回结果不写任何业务逻辑Service负责业务处理Mapper负责SQL操作。以文章列表查询为例调用链路是这样的Controller层接收前端传过来的分页参数和筛选条件调用Service接口。RestController RequestMapping(/api/article) public class ArticleController { Resource private ArticleService articleService; GetMapping(/list) public Result getArticleList(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, String keyword, Integer typeId) { return Result.success(articleService.pageQuery(pageNum, pageSize, keyword, typeId)); } }Service层组装查询条件调用Mapper同时处理一些附加逻辑比如浏览量自增时先查后更避免并发写覆盖。Mapper层通过MyBatis的接口绑定直接操作数据库。这里我用了PageHelper插件做物理分页拦截器自动生成带LIMIT的SQL非常方便省去了手动拼分页参数的工作。2.3 MyBatis动态SQL和结果映射MyBatis在这个项目里最出彩的地方是动态SQL。资讯列表页的筛选条件经常变化——用户可能只按分类看可能搜标题可能同时带两个条件。用传统的JDBC拼SQL条件一多就得写一堆if判断而且还要小心SQL注入。用MyBatis的where标签加if标签问题迎刃而解。select idpageQuery resultTypecom.example.entity.Article SELECT * FROM article where if testkeyword ! null and keyword ! AND (title LIKE CONCAT(%, #{keyword}, %) OR summary LIKE CONCAT(%, #{keyword}, %)) /if if testtypeId ! null AND type_id #{typeId} /if AND status 1 /where ORDER BY create_time DESC /select注意两点一是#{keyword}预编译变量会自动转义防止SQL注入不能用${}直接拼接二是LIKE查询用CONCAT拼接通配符而不是写成%#{keyword}%后者在MyBatis里不会按预期解析。结果映射方面由于表字段名用下划线风格type_id、create_timeJava实体用驼峰风格typeId、createTime直接在application.yml里开一下驼峰映射开关就能自动转换mybatis: configuration: map-underscore-to-camel-case: true不用写一堆resultMap简洁很多。3. Vue前端与接口联调3.1 前端工程结构和路由设计前端用Vue CLI搭建的项目目录结构按模块拆分views放页面组件components放公共组件router集中管理路由api目录封装所有后端接口请求。路由设计上要注意权限控制。游客只能访问首页、资讯列表、详情页后台管理页面必须登录后才能进入。我用的是路由守卫router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path.startsWith(/admin) !token) { next(/login); } else { next(); } });这个逻辑简单直接后端接口也要做同样的校验不然光靠前端路由拦截等于没锁门。3.2 资讯列表和详情页的实现前端页面开发有一个非常实用的思路先把页面静态结构写出来再替换成真实数据。资讯列表页由三部分组成顶部分类导航栏、中部搜索框、下方文章卡片列表。分类导航的数据从后端/api/type/list接口获取点击分类触发路由跳转带typeId参数再调用文章列表接口重新拉数据。Axios请求封装在这类项目里很有必要。统一设置baseURL统一处理token附加统一拦截错误码const service axios.create({ baseURL: /api, timeout: 10000 }); service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization token; } return config; });这里我把baseURL设成了/api而不是完整地址配合后端统一加/api前缀后续如果换域名或者调整端口只需改一处配置。详情页需要注意一个细节页面加载时先请求详情接口同时调用浏览量自增接口。浏览量自增我放在后端Service层处理当查询详情时在同一个事务里把view_count加一这样前端不用做额外的埋点操作。3.3 后台管理界面从零开始的增删改查后台管理界面是整个项目里工作量最重的部分因为管理功能本质上就是一堆增删改查页面但要做到好用并不简单。我实现了几个关键模块登录页输入用户名密码调登录接口拿到token存localStorage跳转到管理首页。文章管理表格展示所有文章支持按标题搜索、按分类筛选、上下架操作、编辑和删除。这里用到了Element UI的el-table组件配合分页组件体验很接近主流后台管理模板。分类管理增删改分类排序调整删除分类时要注意提醒用户该分类下还有文章需要先转移或者删除文章。编辑文章用的是富文本编辑器。我选了一个开源的轻量级编辑器将编辑器实例绑定到Vue组件的数据模型上。这里有个容易踩的坑富文本编辑器渲染出来的内容包含HTML标签详情页展示时vue的插值表达式{{ }}会转义所有标签导致内容变成一堆源码文本。解决办法是用v-html指令渲染同时后端接口返回时要注意别做HTML转义。4. 完整源码部署与复现4.1 环境准备与版本选型在动手跑项目之前先把环境准备好。我用的版本组合如下大家参考时尽量保持大版本一致减少兼容性问题组件版本说明JDK1.8稳定SpringBoot 2.x完美兼容Maven3.6依赖管理Node.js14前端构建需要MySQL5.7或8.08.0注意认证插件SpringBoot2.5.x不用追求新版稳定优先Vue CLI4.x工程化开发脚手架数据库导入我用的是Navicat执行SQL脚本也可以用命令行source。导入时务必注意字符集选择utf8mb4这个字符集能存emoji表情也兼容所有中文。4.2 配置文件里的几个关键点后端配置文件application.yml是整个项目能否跑通的关键我最关注的几个配置项server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/car_news?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 servlet: multipart: max-file-size: 10MB mybatis: mapper-locations: classpath:/mapper/*.xml configuration: map-underscore-to-camel-case: true数据库连接串里的serverTimezoneAsia/Shanghai必须要加否则MySQL驱动会报时区错误useSSLfalse是为了避免本地环境SSL握手警告useUnicodetruecharacterEncodingutf8保证中文不乱码。前端代理配置在vue.config.js里开发阶段把/api代理到后端地址module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } };这样前端所有/api开头的请求都会转发到后端既解决了跨域问题又不用每次CORS请求都额外处理。4.3 启动步骤与常见打包方式开发环境启动顺序启动MySQL导入数据库脚本。后端在IDEA中打开等Maven下载完依赖运行SpringBoot启动类。前端在项目根目录执行npm install安装依赖然后npm run serve启动开发服务器。浏览器访问http://localhost:8081看到前端页面接口自动代理到8080端口。生产环境部署我一般这样做后端执行mvn clean package打包成jar然后java -jar target/car-news-0.0.1.jar启动前端执行npm run build构建出dist目录丢到Nginx里托管。Nginx配置需要加一个反向代理让/api请求转发到后端jar的端口location /api/ { proxy_pass http://127.0.0.1:8080/; }这里注意proxy_pass后面是否带末尾斜杠带斜杠表示去掉/api/前缀再转发不带斜杠则原样转发两种行为截然不同踩过一次坑才明白。5. 常见问题与排查技巧实录5.1 跨域问题的三种表现和应对前后端分离开发中跨域问题几乎无法回避。我用过三种方案分别适用于不同阶段开发阶段用Vue CLI代理最省事不用动后端代码。流程就是上面提到的vue.config.js配置。后端加全局CORS配置适用于生产环境不由Nginx托管或者前端部署在其他域名的场景Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*); } }Nginx层统一处理最推荐的生产方案。利用add_header Access-Control-Allow-Origin *或者反代方式消除跨域。排查跨域问题最简单的判断方法打开浏览器控制台看到Access to XMLHttpRequest ... CORS policy相关报错基本就是跨域问题按上面三个维度定位。5.2 MySQL连接报错的坑用MySQL 8.0时最经典的报错是Public Key Retrieval is not allowed。原因是MySQL 8默认使用caching_sha2_password认证插件。解决方案有两个一是连接串里加allowPublicKeyRetrievaltrue二是创建用户时指定认证方式为mysql_native_password。我倾向于用第二种因为从规范性和安全性角度数据库的账号配置应该由DBA统筹规划不是每个应用都适合在连接串里放宽公钥检索限制。开发环境图省事可以加参数生产环境还是规规矩矩建专用账号。另外还有一个很诡异的坑MySQL 8.0以上版本时区配置如果缺失会直接拒绝连接提示The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。看到一堆乱码别慌就是serverTimezone没设置或者设错了。5.3 前端白屏和接口数据对不上信息列表页白屏最常见的两种原因一是接口请求报404或500二是数据结构对不上导致渲染异常。我调试时会先看控制台的Network面板确认接口是否真的成功返回。数据对不上这类问题本质上就是前后端字段名不一致。举例来说后端Article实体里字段叫viewCount前端代码却写成views页面上浏览量就一直是空的。这种情况解决方案没有捷径前后端约定接口文档或者让后端直接返回前端要求的字段名。还有一个容易被忽略的问题后端返回的日期格式是2024-05-12T10:30:00这种带T的格式前端直接显示很难看。我一般在后端加一个全局配置统一格式化日期字段。6. 项目扩展与个人经验谈数据库表结构在设计初期就预留了扩展位所以后续加功能比较顺手。如果现在让我在这个项目基础上继续做我会优先考虑加三个模块数据统计模块统计每篇文章的阅读量、评论数按分类聚合展示做成简单的图表面板。后端用GROUP BY查询前端用开源图表库画折线图或者柱状图跟现有架构天然契合。标签系统一篇文章归属多个标签需要拆一张中间表article_tag。资讯网站的SEO优化和推荐算法都需要标签做支撑。用户评论优化目前评论是简单的单体回复。如果评论量上来需要改成父子关系结构用parent_id字段实现楼层式展示再考虑接入敏感词过滤。最后说一点个人体会。这类管理系统被很多人当成“玩具项目”觉得就是CRUD没什么技术含量。但真正做完一个完整的项目从建表到上线你会发现细节远比自己预想的多事务什么时候加、索引建在哪几个字段上、接口怎么设计才够优雅、前端组件怎么拆分才好维护每一个选择都在影响后续的开发效率。这套源码里有些地方的写法不一定是最优解但胜在结构清晰、功能完整跑通一遍再改动就心里有数了。拿它做二次开发的起点比从零开始闷头造轮子靠谱得多。
阅读完成 · 觉得有帮助?
咨询建站