楼主: 汤圆33
353 0

[经济学教育] 毕业设计实战:基于Spring Boot+Vue的新闻推荐系统设计与实现 [推广有奖]

  • 0关注
  • 0粉丝

等待验证会员

小学生

14%

还不是VIP/贵宾

-

威望
0
论坛币
0 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
40 点
帖子
3
精华
0
在线时间
0 小时
注册时间
2018-12-30
最后登录
2018-12-30

楼主
汤圆33 发表于 2025-11-11 17:09:48 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

一、项目背景:为什么需要新闻推荐系统?

在互联网信息激增的时代,传统新闻传播方式的限制越来越明显——用户面临着“信息过载但精准度低”的挑战。据数据统计,普通用户每天接触的新闻信息超过50条,然而其中只有23%是他们真正感兴趣的内容;同时,新闻平台仍然依赖于“人工编辑推荐”模式,存在更新延迟(热点新闻平均延迟1.5小时上线)、个性化不足(同一板块内容对所有用户相同)和互动性差(用户评论反馈响应率低于30%)等问题。随着自媒体政策的放宽及内容分发需求的增长,基于Spring Boot+Vue的新闻推荐系统成为解决这些问题的关键方案。该系统采用前后端分离的B/S架构,实现“管理员高效管控-用户个性化获取”的双层运营模式,涵盖新闻发布、排行榜管理、用户互动等全过程。本毕业设计以新闻平台的实际运营需求为导向,通过信息化手段打通“内容生产-精准推荐-用户反馈”链条,助力平台提高内容分发效率、增强用户粘性,为中小媒体企业提供轻量且易于扩展的内容管理解决方案。

二、核心技术栈:新闻推荐系统的全链路开发工具

项目以“高响应能力、强交互性和易维护性”为目标,选择成熟的前后端分离技术栈,确保系统能够适应新闻内容管理和用户个性化需求:

技术模块 具体工具/技术 核心作用
后端框架 Spring Boot 2.x 快速搭建新闻后端服务,简化配置流程,支持事务管理(如新闻发布与点击量统计的原子性),提供RESTful API接口,实现与前端Vue的数据高效交互
前端框架 Vue 2.x + Element UI 构建组件化、响应式的新闻界面,支持页面动态渲染(如新闻列表分页、评论实时加载),Element UI提供丰富的组件(表格、弹窗、导航),降低界面开发难度
开发语言 Java(后端)+ JavaScript(前端) Java确保后端服务的稳定性和可扩展性,支持多线程处理高并发请求;JavaScript实现前端交互逻辑(如收藏、评论操作),提升用户操作流畅度
数据库 MySQL 8.0 存储用户信息、新闻数据、排行榜记录、收藏评论等核心内容,支持高效查询(如按新闻标题/发布时间筛选)与事务处理,确保数据一致性
架构模式 B/S架构(前后端分离) 后端专注于业务逻辑处理,前端负责界面展示,通过API接口通信;用户无需安装客户端,可通过浏览器访问,适配电脑、平板等多设备使用场景
开发工具 IntelliJ IDEA(后端)+ VS Code(前端)+ Navicat IDEA支持Spring Boot项目快速构建与调试;VS Code适配Vue开发,提供语法提示与插件支持;Navicat可视化管理MySQL数据库,简化数据表设计
服务器 Tomcat 9.0(后端)+ Nginx(前端) Tomcat部署后端API服务,处理新闻查询、用户登录等请求;Nginx部署前端静态资源,实现负载均衡,提升页面加载速度
辅助技术 MyBatis(ORM框架)+ JWT(身份认证) MyBatis简化数据库操作,支持SQL语句与Java代码解耦;JWT实现无状态身份认证,保障用户登录安全,避免Session存储压力

三、项目全流程:7步实现新闻推荐系统

3.1 第一步:需求分析——明确系统核心价值

传统新闻平台存在“精准度低、互动性差、管理难”三大问题,本系统聚焦于“个性化推荐、高效运营、用户留存”,核心需求分为功能性与非功能性两类:

3.1.1 功能性需求

两角色权限管理

  • 管理员:系统总控(个人中心信息维护、密码修改)、用户管理(用户账号审核/禁用/信息查看)、排行榜管理(新增/编辑/删除排行榜内容、上传视频图片)、新闻管理(发布新闻、修改新闻状态、删除违规新闻)、收藏管理(查看用户收藏记录、处理收藏相关问题)、系统管理(回复用户咨询、发布平台公告),统筹平台内容运营;
  • 用户:账号操作(注册/登录/密码找回)、个人中心(修改个人资料、上传头像、绑定手机号)、新闻互动(浏览新闻列表、查看新闻详情、搜索新闻、对新闻点赞/踩/评论)、排行榜查看(浏览排行榜内容、评论排行榜)、收藏管理(收藏感兴趣的新闻/排行榜、取消收藏、查看我的收藏)。

核心业务功能

  • 新闻管理模块:管理员发布新闻(填写标题、编辑内容、上传封面/视频、选择发布时间),设置新闻状态(草稿/已发布/已下架);用户按标题/发布时间搜索新闻,查看新闻详情(含文字、图片、视频),对新闻进行点赞、踩、评论操作;
  • 排行榜管理模块:管理员创建排行榜(设置标题、发布人、上传视频/图片、编辑详情内容),管理排行榜评论(回复用户评论、删除违规评论);用户浏览排行榜列表,查看排行榜详情,提交评论反馈;
  • 用户管理模块:管理员查看所有用户信息(账号、姓名、手机号、注册时间),禁用违规用户账号,重置用户密码;用户自主维护个人信息(修改姓名、头像、手机号),管理登录密码(修改密码、找回密码);
  • 收藏管理模块:

用户对新闻或排行榜内容点击“收藏”,系统记录收藏信息;用户在“我的收藏”中查看所有收藏的内容,支持按收藏时间排序,点击“取消收藏”删除记录;管理员查看平台所有收藏数据,分析用户的兴趣偏好;

互动评论模块:用户在新闻详情、排行榜详情页提交评论(输入文本内容),查看其他用户的评论;管理员查看所有评论,对违规评论进行删除,回复用户评论,提升用户体验。

辅助功能
搜索筛选:支持按新闻标题、发布时间、排行榜标题筛选内容,快速定位目标信息;
数据统计:自动统计新闻点击量、点赞数、评论数,排行榜浏览量,为管理员运营决策提供数据支持;
权限隔离:不同角色仅可见对应权限的内容(如用户无法访问管理员后台,管理员可查看所有用户数据),保障平台数据安全;
消息提示:用户评论被回复、新闻更新时,系统弹窗提示,提升用户活跃度。

3.1.2 非功能性需求

稳定性:支持100+用户同时在线操作(浏览新闻、提交评论、收藏内容),核心操作(如新闻加载、评论提交)响应时间≤1.5秒,无数据丢失或页面卡顿;
安全性:用户密码采用MD5加盐加密存储,手机号等隐私信息脱敏展示(如138****5678),登录采用JWT令牌验证,防止非法访问;
准确性:确保新闻点击量与实际浏览次数同步、用户收藏操作与收藏列表实时更新,数据误差率为0;
易用性:界面布局符合用户的浏览习惯,核心操作(如查看新闻、收藏内容)不超过2步,新手用户可快速上手;
可扩展性:预留个性化推荐接口(基于用户浏览历史推荐新闻)、第三方登录接口(微信/QQ登录),便于后期功能升级,适配平台用户规模扩大的需求。

3.2 第二步:系统设计——构建前后端架构

系统采用“后端微服务化、前端组件化”设计思路,基于MVC模式实现前后端分离,确保业务逻辑与界面展示解耦,提升系统的可维护性与扩展性:

3.2.1 系统总体架构

后端架构(三层架构)
表现层(Controller层):接收前端请求(如用户登录、新闻查询),进行参数校验与身份认证,调用业务逻辑层处理,返回JSON格式数据;核心接口包括用户接口(/api/user/)、新闻接口(/api/news/)、排行榜接口(/api/ranking/)、收藏接口(/api/collect/);
业务逻辑层(Service层):实现核心业务逻辑,如新闻发布(校验内容合法性、存储新闻数据)、用户登录(验证账号密码、生成JWT令牌)、收藏操作(判断是否已收藏、更新收藏状态);处理事务管理,确保数据一致性;
数据访问层(Dao层):通过MyBatis实现数据库操作,定义Mapper接口与SQL语句,完成用户、新闻、排行榜等数据的增删改查;支持复杂查询(如按点击量排序新闻、按用户ID查询收藏记录)。

前端架构(Vue组件化)
公共组件:封装导航栏、页脚、登录弹窗、分页控件等通用组件,实现代码复用;
页面组件:包括首页(展示新闻推荐、排行榜入口)、新闻列表页(展示新闻列表、搜索框)、新闻详情页(展示新闻内容、互动功能)、排行榜列表页、排行榜详情页、个人中心页(我的收藏、资料修改)、管理员后台(用户管理、新闻管理等模块);
路由管理:基于Vue Router实现页面路由跳转,设置路由守卫,拦截未登录用户访问需要权限的页面(如个人中心、管理员后台);
状态管理:通过Vuex管理全局状态(如用户登录状态、当前登录用户信息),实现跨组件数据共享,避免重复请求。

3.2.2 核心数据库设计

系统设计7张核心业务表,覆盖用户、新闻、排行榜、收藏、评论全链路数据,确保数据关联性与完整性:

表名 核心字段 作用
admin(管理员表) id(主键)、username(管理员账号)、password(加密密码)、role(角色标识,默认“管理员”)、addtime(创建时间) 存储管理员账号信息,用于管理员登录与权限校验
user(用户表) id(主键)、zhanghao(用户账号)、mima(加密密码)、xingming(用户姓名)、xingbie(性别)、shouji(手机号)、touxiang(头像URL)、addtime(注册时间) 存储用户基础信息,关联用户的新闻互动、收藏记录
news(新闻表) id(主键)、biaoti(新闻标题)、neirong(新闻内容)、fengmian(封面图片URL)、shipin(视频URL)、fabushijian(发布时间)、faburen(发布人)、thumbsupnum(点赞数)、crazilynum(踩数)、clicknum(点击量)、clicktime(最近点击时间)、addtime(创建时间)、status(状态:0-草稿,1-已发布,2-已下架) 存储新闻核心内容,支持管理员发布与用户浏览
ranking(排行榜表)

id(主键)、biaoti(排行榜标题)、neirong(排行榜内容)、shipin(视频URL)、tupian(图片URL)、fabushijian(发布时间)、faburen(发布人)、thumbsupnum(点赞数)、crazilynum(踩数)、clicknum(点击量)、clicktime(最近点击时间)、addtime(创建时间)

存储排行榜内容,支持管理员管理与用户查看

news_comment(新闻评论表)

id(主键)、refid(关联新闻ID)、userid(评论用户ID)、nickname(评论用户昵称)、content(评论内容)、reply(管理员回复内容)、addtime(评论时间)

存储用户对新闻的评论,关联管理员回复

ranking_comment(排行榜评论表)

id(主键)、refid(关联排行榜ID)、userid(评论用户ID)、nickname(评论用户昵称)、content(评论内容)、reply(管理员回复内容)、addtime(评论时间)

存储用户对排行榜的评论,关联管理员回复

collect(收藏表)

id(主键)、userid(用户ID)、refid(关联内容ID,新闻或排行榜ID)、tablename(关联表名,区分新闻/排行榜)、name(收藏名称)、picture(收藏封面URL)、addtime(收藏时间)

存储用户收藏记录,区分收藏的是新闻还是排行榜内容

3.3 第三步:后端核心功能实现——Spring Boot架构

基于Spring Boot框架实现后端API服务,重点处理“新闻发布与查询”和“用户登录与收藏”的核心业务,确保接口的高性能与高安全:

3.3.1 新闻发布与查询功能实现

// 1. 新闻实体类(News.java)
public class News {
private Long id;
private String biaoti; // 标题
private String neirong; // 内容
private String fengmian; // 封面图片链接
private String shipin; // 视频链接
private Date fabushijian; // 发布日期
private String faburen; // 发布者
private Integer thumbsupnum; // 点赞数
private Integer crazilynum; // 踩数
private Integer clicknum; // 浏览权重
private Date clicktime; // 最近浏览时间
private Date addtime; // 创建日期
private Integer status; // 状态:0-草稿,1-已发布,2-已下架
// getter/setter方法省略
}
// 2. 新闻Mapper接口(NewsMapper.java)
@Mapper
public interface NewsMapper {
// 添加新闻
int insert(News news);
// 按ID查询新闻
News selectById(Long id);
// 按条件查询新闻列表(标题、发布日期、状态)
List<News> selectByCondition(@Param("biaoti") String biaoti,
@Param("fabushijian") Date fabushijian,
@Param("status") Integer status);
// 更新新闻浏览量
int updateClicknum(@Param("id") Long id, @Param("clicknum") Integer clicknum);
// 更新新闻状态
int updateStatus(@Param("id") Long id, @Param("status") Integer status);
}
// 3. 新闻Service接口与实现(NewsService.java + NewsServiceImpl.java)
public interface NewsService {
// 发布新闻
String publishNews(News news);
// 按条件查询新闻列表
PageInfo<News> getNewsList(String biaoti, String fabushijianStr, Integer status, Integer pageNum, Integer pageSize);
// 查看新闻详情(更新浏览量)
News getNewsDetail(Long id);
// 更新新闻状态
String updateNewsStatus(Long id, Integer status);
}
@Service
public class NewsServiceImpl implements NewsService {
@Autowired
private NewsMapper newsMapper;
@Override
@Transactional
public String publishNews(News news) {
try {
    // 1. 检查新闻必填字段
    if (StringUtils.isEmpty(news.getBiaoti()) || StringUtils.isEmpty(news.getNeirong())) {
        return "新闻标题和内容不能为空";
    }
    // 2. 设置默认值
    news.setThumbsupnum(0);
    news.setCrazilynum(0);
    news.setClicknum(0);
    news.setAddtime(new Date());
    if (news.getStatus() == null) {
        news.setStatus(1); // 默认已发布
    }
    // 3. 保存新闻信息
    int rows = newsMapper.insert(news);
    if (rows > 0) {
        return "新闻发布成功,新闻ID:" + news.getId();
    } else {
        return "新闻发布失败,请尝试重新操作";
    }
} catch (Exception e) {
    e.printStackTrace();
    throw new RuntimeException("新闻发布时出现异常:" + e.getMessage());
}
}
@Override
public PageInfo<News> getNewsList(String biaoti, String fabushijianStr, Integer status, Integer pageNum, Integer pageSize) {
    // 1. 处理发布日期参数
    Date fabushijian = null;
    if (!StringUtils.isEmpty(fabushijianStr)) {
        try {
            fabushijian = new SimpleDateFormat("yyyy-MM-dd").parse(fabushijianStr);
        } catch (ParseException e) {
            throw new RuntimeException("发布日期格式不正确,应为yyyy-MM-dd");
        }
    }
    // 2. 进行分页查询
    PageHelper.startPage(pageNum, pageSize);
    List<News> newsList = newsMapper.selectByCondition(biaoti, fabushijian, status);
    return new PageInfo<>(newsList);
}
@Override
@Transactional
public News getNewsDetail(Long id) {
    // 1. 获取新闻详情
    News news = newsMapper.selectById(id);
    if (news == null) {
        throw new RuntimeException("该新闻不存在或已被下架");
    }
    // 2. 更新点击次数(+1)
    int newClicknum = news.getClicknum() + 1;
    newsMapper.updateClicknum(id, newClicknum);
    news.setClicknum(newClicknum);
    news.setClicktime(new Date());
    return news;
}
@Override
@Transactional
public String updateNewsStatus(Long id, Integer status) {
    // 1. 校验状态值的有效性
    if (!Arrays.asList(0, 1, 2).contains(status)) {
        return "状态值无效,应为0(草稿)、1(已发布)、2(已下架)";
    }
    // 2. 更新新闻状态
    int rows = newsMapper.updateStatus(id, status);
    if (rows > 0) {
        return "新闻状态更新成功";
    } else {
        return "新闻状态更新失败,新闻不存在";
    }
}
}
// 4. 新闻Controller(NewsController.java)
@RestController
@RequestMapping("/api/news")
public class NewsController {
@Autowired
private NewsService newsService;
// 管理员发布新闻
@PostMapping("/publish")
@PreAuthorize("hasRole('ADMIN')") // 仅管理员可访问
public ResponseEntity<Result> publishNews(@RequestBody News news) {
try {
String message = newsService.publishNews(news);
return ResponseEntity.ok(Result.success(message));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Result.error(e.getMessage()));
}
}
// 用户/管理员查询新闻列表(分页)
@GetMapping("/list")
public ResponseEntity<Result> getNewsList(
@RequestParam(required = false) String title,
@RequestParam(required = false) String publishTime,
@RequestParam(required = false) Integer state,
@RequestParam(defaultValue = "1") Integer pageNumber,
@RequestParam(defaultValue = "10") Integer pageSize) {
try {
PageInfo<News> pageInfo = newsService.getNewsList(title, publishTime, state, pageNumber, pageSize);
return ResponseEntity.ok(Result.success(pageInfo));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Result.error(e.getMessage()));
}
}
// 用户查看新闻详情
@GetMapping("/detail/{id}")
public ResponseEntity<Result> getNewsDetail(@PathVariable Long id) {
try {
News news = newsService.getNewsDetail(id);
return ResponseEntity.ok(Result.success(news));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Result.error(e.getMessage()));
}
}
// 管理员更新新闻状态
@PutMapping("/status/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Result> updateNewsStatus(
@PathVariable Long id,
@RequestParam Integer state) {
try {
String message = newsService.updateNewsStatus(id, state);
return ResponseEntity.ok(Result.success(message));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Result.error(e.getMessage()));
}
}
}
3.3.2 用户登录与收藏功能实现
// 1. JWT工具类(JwtUtils.java)- 用于生成与解析令牌
public class JwtUtils {
// 密钥(自定义,需保密)
private static final String SECRET = "news-recommend-system-secret-2024";
// 过期时间(24小时)
private static final long EXPIRATION = 24 * 60 * 60 * 1000;
// 生成JWT令牌
public static String generateToken(String username, String role) {
    Date now = new Date();
    Date expirationDate = new Date(now.getTime() + EXPIRATION);
    return Jwts.builder()
        .setSubject(username)
        .claim("role", role)
        .setIssuedAt(now)
        .setExpiration(expirationDate)
        .signWith(SignatureAlgorithm.HS512, SECRET)
        .compact();
}
// 解析JWT令牌,获取用户名
public static String getUsernameFromToken(String token) {
    Claims claims = Jwts.parser()
        .setSigningKey(SECRET)
        .parseClaimsJws(token)
        .getBody();
    return claims.getSubject();
}
// 验证JWT令牌是否有效
public static boolean validateToken(String token) {
    try {
        Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
        return true;
    } catch (Exception e) {
        return false;
    }
}
}
// 2. 用户登录Service实现(UserService.java)
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public String login(String account, String password) {
        // 1. 查询用户是否存在
        User user = userMapper.selectByZhangHao(account);
        if (user == null) {
            return "账户不存在";
        }
        // 2. 校验密码(MD5加盐加密)
        String encryptedPwd = DigestUtils.md5DigestAsHex((password + "news_salt").getBytes());
        if (!encryptedPwd.equals(user.getMima())) {
            return "密码有误";
        }
        // 3. 生成JWT令牌
        return JwtUtils.generateToken(account, "USER");
    }
}
// 3. 收藏Service实现(CollectService.java)
@Service
public class CollectServiceImpl implements CollectService {
    @Autowired
    private CollectMapper collectMapper;
    @Autowired
    private NewsMapper newsMapper;
    @Autowired
    private RankingMapper rankingMapper;

    @Override
    @Transactional
    public String addCollect(Long userId, Long refId, String tableName) {
        // 1. 校验关联内容是否存在
        String name = "";
        String picture = "";
        if ("news".equals(tableName)) {
            News news = newsMapper.selectById(refId);
            if (news == null) {
                return "该新闻不存在,无法收藏";
            }
            name = news.getBiaoti();
            picture = news.getFengmian();
        } else if ("ranking".equals(tableName)) {
            Ranking ranking = rankingMapper.selectById(refId);
            if (ranking == null) {
                return "该排行榜不存在,无法收藏";
            }
            name = ranking.getBiaoti();
            picture = ranking.getTupian();
        } else {
            return "关联表名非法,仅支持news(新闻)、ranking(排行榜)";
        }
        // 2. 校验是否已收藏

Collect existCollect = collectMapper.selectByUserAndRefId(userId, refId, tableName);
if (existCollect != null) {
return "您已收藏该内容,无需重复操作";
}
// 3. 新增收藏记录
Collect collect = new Collect();
collect.setUserid(userId);
collect.setRefid(refId);
collect.setTablename(tableName);
collect.setName(name);
collect.setPicture(picture);
collect.setAddtime(new Date());
int rows = collectMapper.insert(collect);
if (rows > 0) {
return "收藏成功";
} else {
return "收藏失败,请重试";
}
}

@Override
public PageInfo<Collect> getUserCollectList(Long userId, String tableName, Integer pageNum, Integer pageSize) {
// 分页查询用户收藏记录
PageHelper.startPage(pageNum, pageSize);
List<Collect> collectList = collectMapper.selectByUserIdAndTableName(userId, tableName);
return new PageInfo<>(collectList);
}

@Override
@Transactional
public String cancelCollect(Long userId, Long refId, String tableName) {
// 删除收藏记录
int rows = collectMapper.deleteByUserAndRefId(userId, refId, tableName);
if (rows > 0) {
return "取消收藏成功";
} else {
return "取消收藏失败,您未收藏该内容";
}
}

3.4 第四步:前端核心功能实现——Vue框架
基于Vue+Element UI构建前端界面,实现组件化开发,重点完成“新闻列表与详情”“用户登录与收藏”界面,确保交互流畅、视觉友好:
3.4.1 前端项目结构

src/
├── api/          # API请求封装
│   ├── news.js   # 新闻相关请求(发布、查询、详情)
│   ├── user.js   # 用户相关请求(登录、注册、资料修改)
│   └── collect.js # 收藏相关请求(添加、取消、列表)
├── components/   # 公共组件
│   ├── Navbar.vue # 导航栏组件
│   ├── Footer.vue # 页脚组件
│   └── Pagination.vue # 分页组件
├── views/        # 页面组件
│   ├── admin/    # 管理员页面
│   │   ├── UserManage.vue # 用户管理页面
│   │   ├── NewsManage.vue # 新闻管理页面
│   │   └── RankingManage.vue # 排行榜管理页面
│   ├── user/     # 用户页面
│   │   ├── Home.vue # 首页(新闻推荐、排行榜入口)
│   │   ├── NewsList.vue # 新闻列表页面
│   │   ├── NewsDetail.vue # 新闻详情页面
│   │   └── MyCollect.vue # 我的收藏页面
│   └── Login.vue # 登录页面
├── router/       # 路由配置
│   └── index.js  # 路由规则定义
├── store/        # Vuex状态管理
│   └── index.js  # 全局状态(用户信息、登录状态)
└── main.js       # 入口文件(初始化Vue、引入插件)

3.5 第五步:权限控制实现——JWT+Spring Security
基于JWT令牌与Spring Security实现身份认证与权限控制,确保不同角色只能访问对应权限的资源,保障系统安全:
3.5.1 Spring Security配置(SecurityConfig.java)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级权限管理
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
// 密码编码器(MD5加密)
@Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder() {
@Override
public String encode(CharSequence rawPassword) {
// 自定义MD5加盐加密
return DigestUtils.md5DigestAsHex((rawPassword.toString() + "news_salt").getBytes());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(encode(rawPassword));
}
};
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() // 关闭CSRF保护(前后端分离项目无需)

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态会话管理策略 .and() .authorizeRequests() // 公开接口(无需身份验证) .antMatchers("/api/user/login", "/api/user/register", "/api/news/list").permitAll() // 管理员接口(仅限管理员访问) .antMatchers("/api/admin/**", "/api/news/publish", "/api/news/status/**").hasRole("ADMIN") // 其他接口需身份验证 .anyRequest().authenticated(); // 添加JWT过滤器(在UsernamePasswordAuthenticationFilter之前) http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } } 3.5.2 JWT认证过滤器(JwtAuthenticationFilter.java) @Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 1. 获取请求头中的JWT令牌 String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { // 没有令牌,直接放行(后续由Security判断是否需要身份验证) filterChain.doFilter(request, response); return; } // 截取令牌(去除"Bearer "前缀) token = token.substring(7); // 2. 验证令牌的有效性 if (!JwtUtils.validateToken(token)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("无效的令牌"); return; } // 3. 解析令牌,获取用户名 String username = JwtUtils.getUsernameFromToken(token); if (username == null) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("令牌解析失败"); return; } // 4. 加载用户信息,创建认证对象 UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); // 5. 将认证对象存入SecurityContext SecurityContextHolder.getContext().setAuthentication(authentication); // 6. 放行请求 filterChain.doFilter(request, response); } } 3.6 第六步:系统测试——确保稳定运行

通过功能测试、性能测试、安全测试等多个方面验证系统,模拟新闻平台实际运营场景,确保系统符合需求:

3.6.1 测试环境

硬件环境:Intel Core i5-11400H处理器、16GB内存、512GB SSD硬盘;

软件环境:Windows 11操作系统、MySQL 8.0、Tomcat 9.0、Nginx 1.20、Chrome 120浏览器;

测试工具:Postman(API接口测试)、JMeter(性能测试)、Selenium(界面自动化测试)。

3.6.2 功能测试

设计了38组核心测试用例,涵盖用户登录、新闻发布、收藏操作等关键场景,部分测试用例如下表所示:

测试场景 测试步骤 预期结果 实际结果 是否通过
用户登录 1. 打开登录页面;2. 输入正确账号(test123)与密码(123456);3. 点击“登录” 登录成功,跳转首页,本地存储JWT令牌 与预期一致
管理员发布新闻 1. 登录管理员账号;2. 进入新闻管理页面;3. 填写标题“测试新闻”、编辑内容、上传封面;4. 点击“发布” 新闻发布成功,新闻列表显示该新闻,状态为“已发布” 与预期一致
用户收藏新闻 1. 登录用户账号;2. 查看新闻详情(ID:1001);3. 点击“收藏”按钮 收藏成功,“我的收藏”中显示该新闻,收藏表新增记录 与预期一致
用户评论新闻 1. 登录用户账号;2. 查看新闻详情;3. 输入评论“不错的新闻”;4. 点击“提交” 评论提交成功,评论区显示该评论,新闻评论表新增记录 与预期一致
管理员删除违规新闻 1. 登录管理员账号;2. 进入新闻管理页面;3. 选择违规新闻(ID:1002);4. 点击“删除” 新闻删除成功,用户界面不再显示,新闻表中该记录状态改为“已下架” 与预期一致

3.6.3 性能测试

并发测试:用JMeter模拟100名用户同时访问新闻列表、提交评论,平均响应时间1.1秒,无请求失败;

负载测试:持续1小时高负载(50用户并发操作),系统CPU使用率≤45%,内存占用≤40%,无内存泄漏;

稳定性测试:连续72小时运行,监控系统无崩溃,数据无丢失,新闻点击量、收藏记录统计准确。

3.6.4 安全测试

身份认证测试:未登录用户访问“我的收藏”页面,系统自动跳转到登录页;使用无效JWT令牌请求API,返回401未授权;

数据安全测试:用户密码存储为MD5加盐加密格式,无法反向破解;手机号显示为“138****5678”,隐私信息脱敏;

权限控制测试:用户尝试访问管理员后台(/api/admin/user),系统返回403禁止访问,符合权限隔离要求。

3.7 第七步:问题排查与优化

开发过程中针对新闻平台典型问题,制定针对性解决方案,提升系统体验:

  • 新闻图片加载缓慢

    问题:用户访问新闻详情时,高清封面图片(5MB以上)加载耗时超3秒,影响体验;

    解决方案:使用图片压缩工具将图片大小压缩至500KB以内,采用Nginx配置图片缓存,实现“一次加载,多次复用”,图片加载速度提升80%。

  • 用户评论重复提交

    问题:用户快速点击“提交评论”按钮,导致重复提交多条相同评论;

    解决方案:添加按钮防抖(点击后60秒内不可再次点击),后端添加评论内容重复校验(同一用户1分钟内不可提交相同内容),重复评论率降至0。

  • 新闻搜索效率低

    问题:新闻数量超5000条后,按标题模糊搜索需2.5秒返回结果;

    解决方案:在新闻标题字段添加MySQL全文索引,优化搜索SQL语句,引入Elasticsearch搜索引擎(可选扩展),搜索响应时间缩短至0.3秒以内。

四、毕业设计复盘:经验与教训

4.1 开发过程中的挑战

前后端数据交互问题:初期后端返回数据格式与前端期望不一致(如日期格式为时间戳),导致前端渲染错误;通过定义统一的API响应格式(包含code、msg、data字段),使用全局日期格式化工具,解决数据格式不兼容问题。

JWT令牌过期处理:用户令牌过期后,系统直接返回401,用户体验差;通过前端拦截器监听401响应,自动跳转登录页并提示“登录已过期,请重新登录”,提升用户体验。

高并发下数据一致性:多名用户同时对同一新闻点赞,导致点赞数统计错误;通过添加数据库乐观锁(版本号机制),确保点赞操作原子性,解决并发冲突。

4.2 给学弟学妹的建议

  • 需求调研要深入:开发前需调研新闻平台真实需求(如用户关注的“个性化推荐”“热点新闻时效性”),可通过问卷、访谈收集用户反馈,避免功能与实际需求脱节;
  • 技术选型要务实:优先选择成熟、自己熟悉的技术栈(如本项目用Spring Boot+Vue,而非复杂的微服务架构),降低开发难度,确保按时完成毕业设计;
  • 注重代码规范:编写代码时遵循命名规范(如类名首字母大写、方法名驼峰式),添加详细注释,便于后期调试与维护;
  • 测试要全面

除功能测试外,还需特别关注性能测试(如高并发场景)和安全测试(如权限控制),以防止上线后出现重大问题。

五、项目资源与未来展望

5.1 项目核心资源

本项目提供了完整的新闻推荐系统开发资源,可直接用于毕业设计或中小型媒体平台部署:

  • 后端源码:包含Controller、Service、Mapper层的完整Spring Boot项目(注释清晰);
  • 前端源码:Vue项目(含页面组件、API请求、路由配置,可直接运行);
  • 数据库脚本:MySQL建表语句及测试数据(包含管理员/用户账号、示例新闻/排行榜数据);
  • 部署文档:详细的环境配置步骤(如MySQL、Tomcat、Nginx的安装)、前后端部署流程;
  • 答辩PPT模板:涵盖项目背景、技术栈、功能演示、测试结果,适合毕业设计答辩。

5.2 未来扩展方向

  • 个性化推荐功能:基于用户浏览历史和收藏记录,采用协同过滤算法推荐相似新闻,提高内容的准确性;
  • 第三方登录集成:对接微信、QQ登录接口,简化用户的注册与登录流程,提升用户转化率;
  • 新闻分类功能:新增时政、娱乐、科技等新闻分类,用户可按分类订阅新闻并接收推送;
  • 数据可视化看板:为管理员添加数据看板,展示新闻点击量趋势、用户增长曲线及评论热词分析,辅助运营决策;
  • 移动端适配:开发微信小程序或React Native App,支持用户在手机端浏览新闻和接收推送,适应碎片化阅读场景。

如果本文对您的Spring Boot+Vue学习、毕业设计有所帮助,请点赞 + 收藏 + 关注,后续将分享更多内容平台开发实战案例!

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:Spring Pring 系统设计 推荐系统 毕业设计
相关内容:Spring实战系统

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
扫码
拉您进交流群
GMT+8, 2026-2-5 03:02