基于Spring Boot+Vue的新闻发布管理系统设计与实战

发布时间:2026/10/13 13:14:59
基于Spring Boot+Vue的新闻发布管理系统设计与实战 1. 项目背景与需求拆解新闻发布管理系统是个很典型的业务系统我最早接触它是因为一个模拟项目X的需求某个单位需要把内部的公告、行业动态、活动信息集中管理起来既要有人负责录入和发布又要控制谁可以改、谁可以删同时还得保证页面展示够快、够清爽。很多新手容易把这种系统想复杂觉得是不是要做什么推荐算法、千人千面其实不是。新闻发布管理系统的核心就三件事内容管理、权限控制、前端展示。把这三条主线梳理清楚整个系统的骨架就立住了。市面上那些成熟的内容管理系统本质上也是在这三件事上做得更精细而已。在动手做这个项目之前我花了不少时间做需求梳理最后整理出来的核心角色只有三个管理员负责账号管理、分类维护、内容审核和数据统计相当于整个系统的总控台。编辑负责新闻稿件的录入、修改、提交发布是内容产出的主力。访客通过前台页面浏览新闻、搜索内容、查看文章详情只读不写。为什么要把角色拆得这么清楚因为权限控制是这类系统的命门。如果所有人都能删新闻、改分类用不了几天数据就会乱成一锅粥。我之前就见过一个项目因为初期没做好权限设计运营人员误删了一整批已发布的公告恢复数据折腾了两天。这个教训让我在后来的每一个管理系统里都把权限模型放在第一位。确定好角色之后功能需求就自然而然地浮出来了。新闻的增删改查是基础分类管理负责把内容分门别类搜索功能让用户能快速定位文章浏览统计告诉管理员哪些内容更受关注。另外还有一个容易忽略但很重要的需求——发布审核。编辑提交的内容不能直接上线需要管理员确认没问题才能推出去。具体流程可以概括为编辑录入草稿 → 提交审核 → 管理员审核通过 → 正式发布 → 下线归档。这个流程看似繁琐但能有效防止错误内容直接暴露给用户。这个项目适合刚学完 Spring Boot 和 Vue 基础、想用一个完整案例把前后端串联起来的开发者。它不涉及复杂的算法和分布式架构但涵盖了 Web 开发的核心知识面接口设计、数据库建模、权限认证、前端组件通信、打包部署。做完这个项目你对如何从零搭建一个可用的业务系统会有非常完整的认知。2. 技术选型与整体架构设计2.1 为什么是 Spring Boot Vue 这套组合技术选型这个环节我在做模拟项目X的时候纠结过一阵子。当时摆在面前的选择不少比如传统的 JSP Servlet、PHP 系的 ThinkPHP还有 Node.js 系的 Express。但最终我还是选了 Spring Boot Vue 这个组合理由很简单开发效率高、生态成熟、招人好招、后续好维护。Spring Boot 最大的价值在于它把 Spring 全家桶的配置简化到了一个令人舒适的程度。以前用 Spring MVC 写一个能跑起来的项目光是配置 XML 就要写好几页数据源、事务、扫描路径、视图解析器每一块都可能出问题。Spring Boot 用自动配置把这些东西全包了我只需要在配置文件里写几行参数项目就飞起来了。内置的 Tomcat 更是省事打包成一个 Jar 直接运行连独立部署的服务器都不用装。前端选 Vue 也是一个务实的选择。Vue 的模板语法直观、组件化开发符合直觉、生态系统里现成的 UI 库非常多。相对于 React 那种函数式编程风格Vue 对于从传统开发转过来的团队更友好。像 Element UI 这种组件库表格、表单、分页、弹窗全都现成的做后台管理系统简直像搭积木。其实这套组合还隐含了一个更重要的架构决策——前后端完全分离。后端只提供 RESTful 接口前端通过 Ajax 调用数据两者之间只靠 JSON 通信。这样做的好处是前端页面发布到 Nginx 上就能跑后端服务独立部署互不干扰。团队协作也更灵活写前端的人不需要懂 Java写后端的人也不用碰 CSS只要接口文档约定清楚就行。2.2 整体架构与核心依赖清单我搭的这个系统是典型的三层结构前端展示层Vue 2 Vue Router Vuex Element UI Axios负责页面渲染和用户交互。后端服务层Spring Boot 2.x包含 Controller、Service、Mapper 三层负责处理业务逻辑和数据访问。数据存储层MySQL 8.x 存放业务数据Redis 管理登录令牌和缓存热点新闻。后端的核心依赖大致是这样的依赖组件用途说明Spring WebRESTful 接口开发处理 HTTP 请求、参数绑定、异常处理MyBatis Plus数据持久化内置通用 CRUD省去大量 Mapper XML 编写MySQL Driver数据库连接8.x 版本的驱动注意配置时区参数Spring Security认证与授权结合 JWT 实现无状态登录JWT 工具库令牌生成与校验选用 jjwtAPI 简洁好用Redis 客户端缓存与令牌存储使用 Spring Data Redis 封装Lombok简化实体类代码自动生成 Getter/Setter/Builder 等这些依赖里我特别想强调 MyBatis Plus。很多教学项目还在用原生 MyBatis写一个简单的查询都要配 resultMap 和 XML效率很低。MyBatis Plus 提供了非常方便的 CRUD 方法selectById、selectPage、insert这些方法开箱即用复杂的多表查询再额外写 XML 就行。对于新闻发布管理系统这种以单表操作为主的项目简直是利器。架构上的关键决策是统一返回结构 全局异常处理。我定义了一个 Result 类所有接口的返回值都包成{ code, message, data }的格式前端拿到响应后先判断 code 再做后续处理。配合 Spring 的RestControllerAdvice做全局异常拦截业务异常、参数校验异常、兜底异常各有一套处理逻辑。这样做的好处非常明显前端不用每次都判断后端到底返回了什么格式但凡是后端返回的响应结构一定是一致的排查问题方便太多。3. 核心功能模块设计与数据库建模3.1 功能模块划分我把整个系统划分成 6 大模块。这 6 个模块覆盖了系统全部的功能点模块边界清晰开发时也可以按模块分配给不同的人并行推进用户认证模块注册、登录、退出、获取当前登录用户信息。用户管理模块管理员对用户账号的增删改查、状态启停、角色分配。新闻管理模块新闻的增删改查、状态流转草稿/待审核/已发布/已下线、置顶操作。分类管理模块新闻分类的增删改查分类下的新闻数量统计。评论管理模块用户对新闻的评论发表、列表查看、管理员删除违规评论。数据统计模块新闻浏览量、分类分布、用户活跃度等统计分析。模块划分这件事看起来简单但实际做的时候容易踩坑。我见过有人把评论逻辑放进新闻 Service 里结果新闻模块越来越臃肿评论相关的 Bug 也要去新闻代码里找。我的原则是一个 Service 只负责一个业务实体跨实体的操作通过调用对方 Service 的方法来完成。比如删除分类时需要判断该分类下是否还有新闻那就在分类的 Service 里调用新闻的 Service 来查询而不是把新闻查询逻辑直接写在分类 Service 里。3.2 数据库表设计详解数据库设计是很多新手的滑铁卢重点是理解每一个字段存在的理由。我在表设计里踩了不少坑这里把设计的思路完整复盘一遍。先看用户表sys_user字段名类型说明idbigint主键自增usernamevarchar(50)登录名唯一索引passwordvarchar(120)BCrypt 加密后的密码nicknamevarchar(50)显示昵称rolevarchar(20)角色编码admin / editor / userstatustinyint状态0 禁用 1 正常create_timedatetime创建时间update_timedatetime更新时间注意密码字段用的是 BCrypt 加密。很多人图省事直接 MD5 加密这在大厂面试或者真实项目里是很减分的做法。MD5 撞库太容易了连加盐都救不了多少。Spring Security 内置了 BCrypt 支持加解密都不需要自己写直接用就完事。新闻表biz_news是核心表字段设计的关键是状态和标记字段名类型说明idbigint主键category_idbigint所属分类 ID外键关联分类表titlevarchar(120)新闻标题summaryvarchar(255)摘要列表页展示contentlongtext正文内容富文本存储cover_imagevarchar(255)封面图 URLauthor_idbigint作者 ID关联用户表statustinyint状态0 草稿 1 待审核 2 已发布 3 已下线is_toptinyint置顶标记0 否 1 是view_countint浏览量publish_timedatetime发布时间create_timedatetime创建时间update_timedatetime更新时间标题和摘要的长度限制是有讲究的。列表页通常一屏展示 8~10 条新闻摘要太长会导致排版难看得要命。我一开始没限制 summary 的长度结果前端列表页面参差不齐后来才改成 255 字符以内显示的时候再按 80 个字符截断体验就好多了。分类表biz_category和评论表biz_comment相对简单。分类表主要是id、name、sort_order、status几个字段。评论表需要关注的是news_id和user_id两个外键以及content字段的长度限制。3.3 自定义 SQL 该怎么写虽然 MyBatis Plus 把单表操作包圆了但联表查询还是得自己写 SQL。我举两个项目里实际用到的例子。新闻列表需要同时展示作者昵称和分类名称这个时候单查biz_news表是不够的要关联用户表和分类表SELECT n.id, n.title, n.summary, n.cover_image, n.view_count, n.is_top, n.publish_time, n.status, c.name AS category_name, u.nickname AS author_name FROM biz_news n LEFT JOIN biz_category c ON n.category_id c.id LEFT JOIN sys_user u ON n.author_id u.id WHERE n.status 2 ORDER BY n.is_top DESC, n.publish_time DESC这个 SQL 有点经验的地方在于用了LEFT JOIN而不是INNER JOIN。因为新闻的调整、历史数据的变更可能出现关联不到分类或作者的情况LEFT JOIN能保证主表的数据不丢失缺失的信息显示为 NULL 也不会阻断列表渲染。分类统计的 SQL 也很有代表性SELECT c.name, COUNT(n.id) AS news_count FROM biz_category c LEFT JOIN biz_news n ON n.category_id c.id AND n.status 2 GROUP BY c.id ORDER BY news_count DESC重点说一下这个 SQL 里的AND n.status 2为什么写在 JOIN 条件里而不是 WHERE 里。如果把它放在 WHERE 里LEFT JOIN之后已经没有未发布新闻的分类会被整体过滤掉分类就显示不全了。放在 JOIN 条件里查询结果是全部分类统计只统计已发布的新闻。这个细节很多人第一次写都会踩坑。4. 后端核心接口与关键实现4.1 JWT 认证流程与权限拦截新闻发布管理系统用的是 Spring Security JWT 的认证方案核心思路是服务器不存储登录状态客户端每次请求都带上一个令牌服务器验证令牌合法就放行。好处是天然支持前后端分离服务端做水平扩展也不用考虑 Session 同步的问题。JWT 令牌我选用的是 jjwt 0.9.1 这个版本用法简洁稳定。登录成功后生成令牌的逻辑大致是这样的private String generateToken(Long userId, String username, String role) { Date now new Date(); Date expiryDate new Date(now.getTime() 7 * 24 * 60 * 60 * 1000); return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(username, username) .claim(role, role) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }令牌有效期设了 7 天太长不安全用户改密码之后旧令牌还能用太短影响体验用户每天登录一次会烦死。7 天是个折中值配合前端的记住登录功能体验和安全的平衡点刚好合适。权限拦截是认证环节里最核心的部分。我实现了一个 JWT 过滤器继承 OncePerRequestFilter在请求进入 Controller 之前先做校验Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String header request.getHeader(Authorization); if (header ! null header.startsWith(Bearer )) { String token header.substring(7); try { Claims claims Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token) .getBody(); // 从令牌中取出角色放入 Spring Security 的上下文 // 后续的 PreAuthorize 注解就能正常工作了 } catch (ExpiredJwtException e) { // 令牌过期返回 401 错误码 } catch (JwtException e) { // 令牌非法返回 401 错误码 } } chain.doFilter(request, response); }权限控制方面用方法级别的PreAuthorize注解是最省事的。比如新闻发布这个操作只允许管理员和编辑身份执行PreAuthorize(hasAnyRole(admin,editor)) PostMapping(/news) public ResultLong createNews(RequestBody NewsDTO dto) { return Result.success(newsService.createNews(dto)); }这样做的好处是权限逻辑和业务逻辑分离代码里一眼就能看出哪个接口允许哪些角色访问维护起来比在代码里手写if (user.getRole().equals(admin))清爽得多。4.2 新闻发布流程接口设计新闻发布是系统的核心流程涉及状态流转和权限双重逻辑。我设计了一组接口每个接口对应状态图上的一个节点接口方法说明权限/api/news/draftPOST保存草稿编辑/api/news/submit/{id}PUT提交审核编辑/api/news/approve/{id}PUT审核通过正式发布管理员/api/news/reject/{id}PUT审核驳回管理员/api/news/offline/{id}PUT下架新闻管理员/编辑/api/newsDELETE删除新闻管理员这里有几个细节值得展开。提交审核时后端要做状态校验——只有处于草稿状态的文章才允许提交审核。如果不做校验一条已经发布的新闻可能会被重复提交状态记录就乱了。我封装了一个通用方法来判断状态是否允许流转private boolean canTransit(Integer currentStatus, Integer targetStatus) { // 草稿 - 待审核 - 已发布 - 已下线严格的顺序流转 return transitionMap.getOrDefault(currentStatus, Collections.emptyList()) .contains(targetStatus); }另一个细节是发布操作涉及多个数据变更新闻表的状态更新、发布时间写入、细心的同学还会做浏览量清零重新发布算作新的一轮访问。这些操作必须放在同一个事务里保证要么全部成功要么全部回滚。Spring Boot 里只需要在 Service 方法上加Transactional注解就行。4.3 浏览量统计与缓存设计浏览量统计是新闻系统的一个典型需求但实现方式有不少讲究。最简单的做法是每次用户查看详情时view_count 1但这会导致数据库频繁更新热门新闻被访问几千次就有几千次 UPDATE 操作压力不小。我最终的方案是分两步走写操作走 Redis读操作走 MySQL。用户访问新闻详情时后端先在 Redis 里执行INCR news:view:{newsId}把这个自增操作交给 Redis 完成性能非常高。然后通过一个定时任务每隔 5 分钟把 Redis 里的增量同步到 MySQLScheduled(fixedRate 300000) public void syncViewCount() { // 遍历 Redis 中有增量的新闻 ID // 取出增量值执行 UPDATE biz_news SET view_count view_count ? WHERE id ? // 执行完成后删除 Redis 中的缓存键 }Redis 和 MySQL 之间的数据一致性不是强实时的5 分钟的延迟对新闻浏览量的展示完全无感。这套方案上线之后即使有热点新闻被频繁点击数据库的压力也一直很平稳。5. 前端页面结构与核心实现5.1 后台管理端设计后台管理端我用的是 Element UI布局采用经典的侧边栏 顶栏架构。左侧菜单是路由的映射/admin/news、/admin/category、/admin/user、/admin/comment、/admin/stats每个路由对应一个独立模块。这样划分的好处是模块之间互不干扰新加功能的时候只要新增路由和页面组件就行。登录页是用户进入系统的第一道门槛实现逻辑不算复杂但有几个细节要处理好。表单校验必须即时给出反馈用户名和密码不能为空、密码长度不能小于 6 位。登录请求成功后拿到 token存到 localStorage 里同时用 Vuex 做全局状态管理后续所有的请求拦截器都会自动带上这个 token。新闻管理页是这个系统里工作量最大的页面。列表部分用el-table渲染标题列做了超长文字省略状态列用el-tag显示不同颜色的标签置顶状态单独一列显示点击就可以快速切换置顶操作。搜索区域放在表格上方支持按标题模糊搜索、按分类下拉筛选、按状态筛选三个维度。分页用el-pagination组件配合后端的 MyBatis Plus 分页插件每页 10 条数据展示正好。编辑新闻的表单是另一个重点。标题、分类、摘要、封面、正文这些字段都要做表单校验规则标题不能为空且长度不超过 120 字摘要长度不超过 255 字分类必须选择。正文部分使用了wangeditor富文本编辑器内容以 HTML 格式存储回显的时候直接用v-html渲染即可。这里有一个小坑富文本编辑器产生的 HTML 往往带有大量内联样式在后台展示可能没问题但放到前台页面时要注意样式冲突最好在富文本内容外面包一层独立的 class 并做样式隔离。5.2 前台展示端设计前台页面相比后台要清爽很多目标只有一个让用户最快地找到他想看的内容。首页的布局是顶部导航栏包含站点名称、分类快捷入口、搜索框中间是新闻列表流右侧是热门文章排行榜和最新发布。新闻列表的每一条卡片展示封面图、标题、摘要、分类标签和发布时间点击卡片跳转到详情页。这种布局是门户网站的标准设计用户浏览时不需要动脑子该看什么一目了然。详情页的排版要特别注意阅读体验。正文字体我调到了 16px行高 1.8段落间距 12px这是国内主流内容平台的标准设置。文章头部展示标题、作者、发布时间、浏览量、分类标签底部展示上一条和下一条新闻方便用户连续阅读。评论区域放在正文下面用户登录后可以对文章发表看法未登录用户则显示提示信息引导登录。前端的路由配置里我用了全局路由守卫来做登录状态判断router.beforeEach((to, from, next) { const isLogin localStorage.getItem(token) if (to.meta.requiresAuth !isLogin) { next({ path: /login, query: { redirect: to.fullPath } }) } else { next() } })这样做的好处是用户访问需要登录的页面时会被自动引导到登录页登录成功后带着redirect参数跳回原页面体验非常顺畅。5.3 Axios 封装与拦截器Axios 封装是前端工程化的基础功我见过很多人每个页面都写一遍axios.get、axios.post后端接口一调整就能改到怀疑人生。正确做法是统一封装把公共逻辑集中在拦截器里。我在项目里抽了一个request.js文件里面做了三件事第一请求拦截器统一加 token。只要本地存在 token就自动加到请求头里所有页面的请求不用关心认证这件事。第二响应拦截器统一解析数据。后端返回的 Result 结构是{ code, message, data }拦截器里面判断 code 是否为 200是就返回response.data.data页面可以直接拿数据不用再解一层壳子。如果 code 是 401说明登录过期直接清除本地 token 并跳转登录页。第三统一错误提示。业务异常、网络故障、服务器错误这几类问题各有各的处理方式但在拦截器里都统一调用 Element UI 的Message组件弹出提示。页面代码只用关心成功分支的逻辑异常分支全部交给拦截器处理。这套封装做完之后新增一个业务模块的代码量直接砍掉三分之一而且页面里很难再出现如何判断接口返回成功这种重复代码。6. 数据库优化与安全加固实践6.1 索引设计思路新闻系统的数据量初期不大但索引设计仍然要在开发阶段就想清楚否则等数据涨上来再补索引就要经历锁表的痛苦了。我在biz_news表上建了三个索引ALTER TABLE biz_news ADD INDEX idx_status_publish (status, publish_time); ALTER TABLE biz_news ADD INDEX idx_category_status (category_id, status); ALTER TABLE biz_news ADD INDEX idx_title (title);第一个索引支撑列表页的默认查询WHERE status 2 ORDER BY publish_time DESC可以直接命中索引。第二个索引支撑分类筛选WHERE category_id ? AND status 2场景下效率很高。第三个索引是搜索需求做了普通 B-Tree 索引配合LIKE 关键字%的前缀匹配可以使用到索引。日常数据量在几万条以内的时候索引的效果可能感觉不明显。但到了几十万条数据的时候有没有这些索引响应时间可能就是 50 毫秒和 5000 毫秒的差别。索引不是越多越好每个索引都会增加写入开销把最核心的查询场景覆盖到就够了。6.2 接口安全与数据校验Spring Boot 项目里接口安全是一个容易被忽视的重灾区。有几次内部分享的时候我问过一些开发者很多人都没听说过Validated参数校验注解。实际上后端必须对前端提交的数据做二次校验因为前端的校验只能优化体验后端校验才是安全的底线。我用了 Spring Boot 自带的校验框架在 DTO 上声明校验规则public class NewsDTO { NotBlank(message 标题不能为空) Size(max 120, message 标题长度不能超过120字符) private String title; Size(max 255, message 摘要长度不能超过255字符) private String summary; NotNull(message 分类不能为空) private Long categoryId; NotBlank(message 正文不能为空) private String content; }Controller 层只要在参数前加上Validated注解框架就会自动完成校验不通过就直接抛异常被全局异常处理器接住返回友好的错误提示。这套机制写起来简单但能挡住大量的低级错误——空标题、超长内容、非法分类 ID 之类的问题在入口处就被拦截了。另一个容易忽略的安全点是对外暴露的接口要做越权防护。比如编辑自己只能修改自己创建的新闻如果直接通过接口传 ID 就能改别人的文章那系统的权限模型就毫无意义了。我在修改新闻的 Service 方法里加了一层校验if (!currentUser.isAdmin() !news.getAuthorId().equals(currentUser.getId())) { throw new BusinessException(403, 无权操作该新闻); }这个判断逻辑虽然简单但价值很大。数据越权漏洞在安全测试里是非常常见的高危问题接口设计一开始就把 owner 检查做进去后面就不用再返工修补。6.3 富文本内容的安全处理新闻系统会用到富文本编辑器而富文本编辑器的内容直接入库再回显到页面上天然存在 XSS 攻击的风险。用户如果在正文里插入一段恶意脚本其他用户浏览新闻时脚本就会执行可能窃取 Cookie、篡改页面后果可大可小。我处理富文本安全的策略是存储前做一层白名单过滤回显时再做一层转义。过滤逻辑用的是 jsoup 的 Cleaner 功能只保留常见的安全标签public String sanitizeContent(String content) { Safelist safelist Safelist.relaxed() .addTags(img, h1, h2, h3, blockquote) .addAttributes(img, src, alt, title); return Jsoup.clean(content, safelist); }Safelist.relaxed()是 jsoup 提供的一个宽松白名单保留了 b、i、u、a、p、br 等基础标签在此基础上再允许图片和标题标签。所有不在白名单里的脚本、事件属性都会被清理掉。回显时在前端用v-html渲染之前还要对内容做一次 HTML 转义处理双保险更安心。7. 部署上线与常见问题排查7.1 打包部署流程前后端分离项目的部署核心就两步打包后端 Jar 包、构建前端静态文件然后分别放到相应的服务器位置就行。后端打包用 Mavenmvn clean package -DskipTests打包成功后在target目录下会生成一个news-system.jar文件不大几十 MB 左右。复制到服务器上直接运行java -jar news-system.jar --spring.profiles.activeprod我习惯在application-prod.yml里配置生产环境的数据库连接和 Redis 地址用--spring.profiles.activeprod参数激活这样开发和生产的配置就完全分开了环境切换只需要改启动参数代码不用动。前端构建执行npm run build构建完成后dist目录里就是可以直接被 Nginx 托管的静态文件。把dist下的所有文件复制到 Nginx 的 html 目录然后修改 Nginx 配置把 API 请求反向代理到后端服务server { listen 80; server_name your-domain.com; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files那一行是 SPA 应用部署的关键它保证前端路由在刷新页面时不会返回 404。Nginx 发现请求的文件不存在就回退到 index.html让 Vue Router 接管路由。7.2 上线后高频 Bug 与排查实录部署上线之后实际会遇到不少开发环境里发现不了的问题。我总结了几个高频的坑都是从实际项目里踩出来的。跨域问题CORS是最常见的第一道坎。开发环境下前端跑在 8080 端口后端跑在 8081 端口浏览器会拦截跨源请求。我的解决方式是在后端加一个全局的跨域配置类Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowCredentials(true) .maxAge(3600); } }生产环境因为有 Nginx 做同源反向代理通常不存在跨域问题。但开发环境如果不提前配好前后端联调的第一天就会卡在浏览器控制台那些红色的报错信息上。时间格式化问题。MySQL 里存的datetime类型经 MyBatis 查询出来直接返回给前端时默认格式是2024-05-06T10:30:00.00000:00跟国内用户习惯的2024-05-06 10:30:00差了 8 个小时。最省事的解决方案是配置全局的时间和格式化规则spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai分页查询的 total 不准确。MyBatis Plus 的分页插件如果配置不对total字段永远返回 0 或者错误的数值。排查后发现问题基本出在没有引入分页插件或者配置写法有误。新版 MyBatis Plus 使用PaginationInnerInterceptor作为分页拦截器我用的是 3.5.x 版本Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }配置正确的情况下分页查询返回的 IPage 里会包含总记录数前端分页组件就能正常显示共 N 条共 M 页了。富文本图片上传失败。我在本地测试的时候没发现问题部署到线上之后发现编辑插入的图片全都打不开。刷了一下后端日志发现是服务器的磁盘路径权限问题。开发环境用绝对路径存储图片没问题但生产环境的目录不一样必须把图片存储路径做成可配置的并且确保目标目录有写入权限。我的方案是在配置文件里加一个upload.path参数代码里统一从配置读取部署时按服务器的实际目录调整就行。发布后的页面样式错乱。有次发布新版本之后用户反馈页面样式乱了。排查发现是静态资源没有做版本管理浏览器缓存了旧的 CSS 和 JS 文件。Vue 构建时文件名通常带 hash 后缀新版本的文件名变了就不会命中旧缓存。但有时候 Nginx 配置了强缓存index.html本身也被缓存了导致用户加载的还是旧的入口文件。解决办法是给 Nginx 的location /配置设置Cache-Control: no-cache让 html 文件每次都回源检查静态资源文件则可以放心使用长缓存因为文件名带 hash 天然会更新。7.3 性能优化建议与扩展方向项目上线稳定之后我陆续做了一些性能优化其中有几个方向值得展开说说。Redis 缓存热点新闻。首页的新闻列表和热门排行榜每次用户刷新都要查库几个查询都是高频低变更的。我在 Service 层加了一层缓存逻辑——先查 Redis缓存没有命中再查 MySQL然后把结果写入 Redis 并设置 30 分钟的过期时间。由于查询结果最终以 JSON 字符串的形式保存前端直接拿现成的 JSON性能提升了不止一个层级。QPS 和并发量的真实感受。这套系统在几十人规模的内网里跑毫无压力接口响应都在 50 毫秒以内。如果部署到公网支撑数百人同时访问也是没问题的瓶颈大概率不在应用层而在带宽和数据库连接数上。后面的优化重点应该是增加数据库连接池配置和前端 CDN 加速。扩展方向。这套系统做完之后往以下方向扩展会非常顺手接入 OSS 对象存储做图片和附件的统一管理增加标签系统让新闻支持多标签检索加入操作日志功能记录谁在什么时间改了哪篇文章数据统计维度从浏览量扩展到用户行为分析。这些都是内容类系统常见的进阶需求架构不用改只需要往上叠模块就行。8. 踩坑复盘与个人实战体会做这个新闻发布管理系统前后大概花了两周多的时间。第一版只用了五天就把核心功能跑通了但后续的打磨和踩坑修复花的时间更长回头看看这些时间花得非常值。我最想分享的一条体会是写管理系统不要一上来就写代码先把数据表和状态流转图画清楚。我第一版就是因为表设计里漏了新闻的发布状态字段写到一半发现草稿、待审核、已发布、已下线这些状态没法表达被迫回炉改表。这个教训让我在后来的任何项目里都养成了先画状态图再动手的习惯。第二条体会是前后端分离的项目一定要先约定接口文档再分头开发。我见过太多项目因为接口字段名不统一前后端联调阶段改来改去时间和精力都浪费在对齐字段上了。我的做法是先用 Mock 数据或者写一份简单的接口说明文档把请求路径、请求参数、返回结构和错误码定义清楚前后端按照同一份文档开发联调时的问题能减少八成。第三条体会是关于权限控制务必从第一版就做好。很多初学项目喜欢先把功能写出来权限控制后面再加结果功能写完之后再往里面嵌权限逻辑代码结构已经被破坏了改起来非常痛苦。Spring Security 方法级注解这套方案开发初期多花两小时布置好后面每一个接口的权限控制都只是加一个注解的事。第四条体会关于富文本编辑器的安全。这个问题在新手项目里被提起的频率不算高但一旦出事就是大事。我在做另一个项目的时候遇到过一次富文本内容注入恶意代码的事件从那以后所有涉及富文本的场景都强制走白名单过滤宁可牺牲一些格式上的灵活性也要保证安全底线。最后再分享一个开发效率上的小技巧。Spring Boot 项目用 DevTools 工具做热重启配合前端 Vite 的热更新前后端代码改动都能实时生效不用反复重启服务或者刷新浏览器。加上 MyBatis Plus 的代码生成器直接从数据库表生成实体类和 Mapper 层代码基础 CRUD 代码都不用手写了。这套开发流程用顺了之后做一个标准的管理系统骨架搭建基本上一天就能完成剩下的时间都花在业务逻辑和页面交互的打磨上。如果你也想动手做一个类似的新闻发布管理系统建议从数据库设计开始然后写后端的接口再用 Postman 把接口测试通了最后才动前端页面。按照这个顺序做每完成一个环节都有明确的验证手段不会有人做了半天才发现基础设计有问题的尴尬情况。祝顺利。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询