十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Java新闻管理系统:基于Spring Boot的内容生命周期与权限状态机实现

Java新闻管理系统:基于Spring Boot的内容生命周期与权限状态机实现 简介本资源是一套完整的Java Web毕业设计实战项目面向计算机专业本科生及Java初学者聚焦新闻发布与管理业务场景提供从需求分析到系统部署的全流程学习材料。压缩包共45个文件含40张系统功能界面截图PNG、1份数据库脚本SQL、1份README说明TXT、1份项目报告DOC、1份答辩PPTPPT及1个源代码压缩包ZIP总大小35.49MB截图覆盖登录、新闻列表、编辑、权限配置等核心操作界面SQL文件支持快速建库建表文档与PPT清晰呈现设计思路与技术选型依据。已有91人学习下载读者可直接复现基于ServletJSPJDBC或SpringMyBatis的双技术栈实现方案掌握MVC分层开发、用户角色权限控制、前后端交互及数据库ER建模等关键能力是Java Web全栈开发入门与课程设计落地的高实用性参考范例。1. 这不是又一个“学生管理系统”模板基于 Java 的新闻发布及管理系统解决的是真实内容生产流中的权限断层与发布闭环问题很多计算机专业同学拿到“新闻发布及管理系统”毕业设计选题时第一反应是套用 SSM 或 Spring Boot 模板改个表名、换个登录页——结果答辩被问“新闻审核流程怎么落地”“编辑和管理员权限边界在哪”“数据库里如何体现草稿→待审→已发布状态迁移”当场卡壳。这个项目标题里的“新闻发布及管理系统”核心不在“有页面能增删改查”而在于构建一条可追溯、可管控、可回滚的内容生命周期链路从记者提交初稿、编辑加标签/配图、主编终审、定时发布到下架归档、历史版本比对。它用 Java 做后端主干不是因为“Java 适合写毕业设计”而是因为其成熟的事务控制Transactional、细粒度权限框架Spring Security RBAC、以及与 MySQL 事务日志、Redis 缓存穿透防护的天然协同能力。适合正在做毕业设计、需要展示工程思维而非仅 CRUD 能力的同学也适合想快速复用一套内容型后台骨架的校内信息化项目组——它不追求 Vue3 的炫酷动效但每张表字段设计都对应真实业务动作每个 Controller 方法签名都暴露了状态机流转意图。2. 用 Spring Boot MyBatis-Plus 搭建新闻领域模型从 ER 图到实体类的精准映射2.1 为什么不用纯 JPAMyBatis-Plus 在毕业设计场景下的三重优势在毕业设计中选择 MyBatis-Plus 而非 JPA不是技术倒退而是对可控性、调试透明度和 SQL 可观测性的主动选择。JPA 的二级缓存抽象层在小型项目中反而增加理解成本而 MyBatis-Plus 提供的LambdaQueryWrapper既能类型安全地构造条件又允许在.last(LIMIT 10)中直接嵌入分页逻辑避免 Hibernate 的 N1 查询陷阱。更重要的是其TableField(fill FieldFill.INSERT)注解可自动填充create_time和update_time无需在 Service 层手动 set配合MetaObjectHandler连create_by操作人ID也能在插入时自动注入——这对答辩时演示“谁在什么时候发布了什么新闻”至关重要。此外IService接口自带的saveBatch()和updateBatchById()方法让批量导入新闻、批量下架操作一行代码即可完成远比手写 foreach update 更贴近真实运维场景。提示不要在application.yml中配置mybatis-plus.configuration.map-underscore-to-camel-case: true后就以为万事大吉。MySQL 表字段若为is_top置顶标志Java 实体类必须声明为private Boolean isTop;而非private Boolean is_top;否则 MP 无法自动映射。这是答辩高频扣分点。2.2 新闻核心实体类用枚举约束状态流转拒绝 magic number新闻状态不能靠int status 1这种硬编码维护。本系统定义NewsStatusEnum枚举明确限定合法值public enum NewsStatusEnum { DRAFT(0, 草稿), PENDING_REVIEW(1, 待审核), REJECTED(2, 已驳回), PUBLISHED(3, 已发布), ARCHIVED(4, 已归档); private final int code; private final String desc; NewsStatusEnum(int code, String desc) { this.code code; this.desc desc; } // 提供根据 code 查找枚举实例的静态方法用于 Controller 参数解析 public static NewsStatusEnum fromCode(int code) { for (NewsStatusEnum e : values()) { if (e.code code) return e; } throw new IllegalArgumentException(Invalid news status code: code); } }对应News实体类中status字段类型为NewsStatusEnum而非IntegerTableField(status) private NewsStatusEnum status; // MyBatis-Plus 自动调用 valueOf() 或 fromCode()这样做的好处是数据库status字段仍为TINYINT节省空间但 Java 层获得编译期类型安全switch(status)语句可直接 case 枚举常量避免case 1:这种易错写法返回给前端的 JSON 自动序列化为status: PUBLISHED语义清晰无需额外字典表查询。2.3 关键表结构与外键约束ER 图落地时必须检查的三个细节毕业设计答辩常被追问“你的数据库设计怎么保证数据一致性”。答案就藏在三处外键与索引设计中表名字段约束类型作用说明newscategory_id→news_category.idON DELETE RESTRICT分类删除前必须清空该分类下所有新闻防止孤儿数据newsauthor_id→sys_user.idON DELETE SET NULL记者离职后其发布的新闻仍保留作者字段置为 NULL符合 GDPR 隐私要求news_audit_log(news_id, audit_step)UNIQUE INDEX同一新闻在“初审”“终审”环节只能有一条记录避免重复审批特别注意news_audit_log表的设计它不存储审核人姓名只存auditor_id关联sys_user.id并在Select(SELECT u.real_name FROM sys_user u WHERE u.id #{auditorId})中动态联查——这样当用户姓名修改时历史审核记录仍显示当时的真实姓名而非当前姓名满足审计溯源要求。3. 实现新闻发布全流程从草稿提交到定时上线的四步状态机3.1 Controller 层用Valid和RequestBody封装业务意图而非裸露字段新闻提交接口不接受MapString, Object或泛型JSONObject而是定义强类型 DTOPostMapping(/draft) public Result? saveAsDraft(Valid RequestBody NewsDraftDTO dto) { // dto 包含 title, content, categoryId, coverImage, tags字符串数组 // Valid 触发 NotBlank、Size(max50) 等校验失败时自动返回 400 newsService.saveAsDraft(dto); return Result.success(); }NewsDraftDTO中tags字段声明为String[] tags而非String tags。这样前端传[Java, 毕业设计]后端直接接收为数组省去split(,)的字符串解析错误风险。同时在TableField(exist false)标记该字段不映射数据库由 Service 层拆解后存入news_tag_rel关联表——这体现了“DTO 负责传输Entity 负责持久化”的分层原则也是答辩时展示架构意识的关键点。3.2 Service 层用Transactional划定状态变更原子边界新闻从“待审核”变为“已发布”绝非简单update status3。它必须包含更新新闻状态、记录审核日志、推送至 Redis 缓存、触发 Elasticsearch 同步若集成。这些操作要么全成功要么全回滚Transactional(rollbackFor Exception.class) public void publish(Long newsId, Long auditorId) { // 1. 校验当前状态是否为 PENDING_REVIEW News news newsMapper.selectById(newsId); if (!NewsStatusEnum.PENDING_REVIEW.equals(news.getStatus())) { throw new BusinessException(仅待审核状态可发布); } // 2. 更新状态并设置发布时间 news.setStatus(NewsStatusEnum.PUBLISHED); news.setPublishTime(LocalDateTime.now()); news.setAuditorId(auditorId); newsMapper.updateById(news); // 3. 记录审核日志独立 insert非级联 AuditLog log new AuditLog(newsId, auditorId, 发布, 稿件已上线); auditLogMapper.insert(log); // 4. 清除旧缓存key: news:detail:{id} redisTemplate.delete(news:detail: newsId); // 5. 异步通知 ES此处用线程池非 Async避免事务未提交时 ES 读到脏数据 esUpdateExecutor.submit(() - esService.syncNews(newsId)); }注意Async方法不能在同一个 Service 类内调用否则事务上下文丢失。此处用ThreadPoolTaskExecutor显式提交确保 ES 同步发生在事务 commit 之后。3.3 定时发布功能用Scheduledcron表达式实现零侵入调度毕业设计常忽略“定时发布”需求但企业级系统必备。本方案不引入 Quartz而是用 Spring Boot 原生ScheduledComponent public class ScheduledNewsPublisher { Autowired private NewsService newsService; // 每分钟扫描一次检查 publish_time now 且 status DRAFT 的新闻 Scheduled(cron 0 */1 * * * ?) public void checkAndPublishScheduledNews() { LocalDateTime now LocalDateTime.now(); ListNews scheduledNews newsMapper.selectList( new QueryWrapperNews() .eq(status, NewsStatusEnum.DRAFT.getCode()) .le(publish_time, now) ); for (News news : scheduledNews) { try { // 复用已验证的 publish 逻辑保证事务一致性 newsService.publish(news.getId(), -1L); // -1L 表示系统自动发布 } catch (Exception e) { // 记录错误日志但不抛出避免阻塞后续扫描 log.error(Auto-publish failed for news {}, news.getId(), e); } } } }关键参数说明cron 0 */1 * * * ?表示“秒0分每1分钟时任意日任意月任意周任意年任意”即每分钟执行一次le(publish_time, now)使用 MyBatis-Plus 的leless than or equal方法生成publish_time ?SQL避免手写字符串拼接publish_time字段类型为LocalDateTimeMySQL 对应datetime确保时区无关不使用timestamp避免夏令时偏移。4. 权限控制与角色分离用 Spring Security 实现编辑、审核、发布的三级操作隔离4.1 RBAC 模型落地三张表搞定权限体系不依赖 Shiro 复杂配置毕业设计不必堆砌权限框架用最简 RBAC 即可体现设计深度表名主要字段说明sys_roleid,role_name,code如EDITOR,CHIEF_EDITOR,ADMIN角色编码用于PreAuthorize(hasRole(EDITOR))sys_user_roleuser_id,role_id用户-角色多对多关联sys_role_permissionrole_id,permission_code如news:draft:add,news:review:pass角色-权限细粒度绑定permission_code设计规则资源:操作:范围例如news:draft:add—— 编辑可新建草稿news:review:pass—— 主编可批准发布news:archive:batch—— 管理员可批量归档。这样设计的好处是答辩时可清晰说明“为什么编辑不能看到‘终审’按钮因为其角色未分配news:review:pass权限”而非模糊回答“权限没开”。4.2 方法级鉴权PreAuthorize与 SpEL 表达式的实战写法Controller 方法上直接标注权限比拦截器更直观RestController RequestMapping(/api/news) public class NewsController { PostMapping(/submit) PreAuthorize(hasAuthority(news:draft:add)) public Result? submitDraft(RequestBody NewsDraftDTO dto) { ... } PostMapping(/{id}/approve) PreAuthorize(newsSecurityService.canApprove(authentication, #id)) public Result? approve(PathVariable Long id) { ... } }第二行PreAuthorize调用自定义服务newsSecurityService.canApprove()实现动态鉴权检查当前用户角色是否为CHIEF_EDITOR查询该新闻的category_id判断用户是否被授权管理该栏目通过sys_user_category关联表防止主编越权审批其他栏目的稿件。Service public class NewsSecurityService { public boolean canApprove(Authentication auth, Long newsId) { // 1. 获取当前用户 ID Long userId ((UserDetailsImpl) auth.getPrincipal()).getId(); // 2. 查询新闻所属栏目 Long categoryId newsMapper.selectCategoryIdById(newsId); // 3. 检查用户是否对该栏目有审核权限 return userCategoryMapper.exists(userId, categoryId); } }提示Authentication对象需在SecurityConfig中配置EnableGlobalMethodSecurity(prePostEnabled true)才能注入到 SpEL 表达式中。此配置常被遗漏导致PreAuthorize不生效。4.3 登录态管理JWT Token 有效期与刷新机制的轻量实现毕业设计不需复杂 OAuth2用 JWT 即可。Token 有效期设为 2 小时过期后前端调用/api/auth/refresh刷新PostMapping(/refresh) public ResultJwtResponse refresh(RequestHeader(Authorization) String token) { String jwt token.replace(Bearer , ); if (!jwtUtil.validateToken(jwt)) { return Result.fail(Token 已失效); } Long userId jwtUtil.getUserIdFromToken(jwt); // 生成新 Token有效期延长 2 小时 String newToken jwtUtil.generateToken(userId); return Result.success(new JwtResponse(newToken)); }JwtUtil.generateToken()中setExpiration(new Date(System.currentTimeMillis() 2 * 60 * 60 * 1000))显式设置过期时间而非依赖spring.security.jwt.expiration配置——因为答辩时可能被问“如果配置文件写错Token 会不会永不过期”显式编码可证明你理解原理。5. 毕业设计交付物实操指南从源码编译到答辩 PPT 的关键检查点5.1 源代码包结构自查清单答辩前必做交付的.zip包内目录结构必须严格遵循 Maven 标准且包含以下不可缺失项路径必须存在检查要点/src/main/java/com/example/news/是包名需体现项目特征如com.example.news禁用com.test或com.demo/src/main/resources/application-dev.yml是必须有dev环境配置包含spring.datasource.url、redis.host等且密码字段用******占位非明文/src/main/resources/mapper/是XML 文件名必须与MapperScan扫描路径一致如NewsMapper.xml对应NewsMapper.java/docs/是存放数据库ER图.png、系统架构图.png、核心流程图.png图片需高清可放大箭头标注清晰/pom.xml是parent必须指向spring-boot-starter-parent且version与所用 Spring Boot 版本一致如2.7.18特别注意pom.xml中mysql-connector-java依赖版本必须与 MySQL 服务器版本匹配。例如 MySQL 8.0.x 需用8.0.33若误用5.1.47运行时会报Public Key Retrieval is not allowed错误——这是答辩现场最高频环境问题。5.2 答辩 PPT 的三页黄金结构技术深度 界面美观PPT 不要堆砌截图聚焦三个技术决策页第一页ER 图与关键约束说明展示news、news_category、news_tag_rel三张表关系用红色箭头标出ON DELETE RESTRICT和ON DELETE SET NULL外键行为文字说明“news_tag_rel采用联合主键(news_id, tag_id)避免重复打标”。第二页状态机流转图与异常处理绘制DRAFT → PENDING_REVIEW → [REJECTED \| PUBLISHED] → ARCHIVED状态图在PUBLISHED → ARCHIVED箭头上标注“管理员操作不可逆”文字说明“驳回时自动将reject_reason写入news表前端展示给编辑员”。第三页权限控制代码片段截取NewsSecurityService.canApprove()方法核心代码在userCategoryMapper.exists()调用旁加注释“查询sys_user_category表实现栏目级权限隔离”文字说明“相比角色级权限栏目级权限更贴近媒体组织实际管理结构”。注意PPT 中所有代码截图必须来自你实际运行的源码禁止从网上复制。答辩老师会随机打开你 U 盘里的代码文件验证。5.3 数据库初始化脚本用schema.sqldata.sql分离结构与数据交付包中必须提供src/main/resources/schema.sql建表语句和data.sql初始数据-- schema.sql CREATE TABLE news_category ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 分类名称, sort_order TINYINT DEFAULT 0 COMMENT 排序序号 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- data.sql INSERT INTO news_category (name, sort_order) VALUES (校园新闻, 1), (学术动态, 2), (通知公告, 3);data.sql中的INSERT语句必须用VALUES (...)格式而非INSERT ... SELECT——因为后者在不同 MySQL 版本下兼容性差答辩现场演示时可能失败。同时schema.sql中所有COMMENT字段必须存在体现你对数据库文档规范的理解。本文还有配套的精品资源点击获取
返回列表