新闻管理系统课程设计:表结构、内容搜索与后台登录实现
简介这是一份软件综合实习“新闻管理系统”题目的完整方案资料适合计算机相关专业学生在课程设计、综合实习或毕业设计中参考。内容从系统概述、需求分析到前台与后台功能结构详细梳理了新闻管理系统的开发思路并配有功能结构图。前台部分涵盖主页导航、新闻分类导航、站内模糊搜索、分类显示及友情链接等模块后台部分则包括管理员设置、新闻信息管理和友情链接管理对新闻类别划分、站内搜索按内容而非标题查询、管理员密码需与确认密码一致等细节均有说明。资源共1个文件格式为doc压缩包大小77KB以文字描述和界面功能说明为主便于直接阅读和修改。文中对时政要闻、财经观察、世界军事等8类新闻分类以及前台后台各操作流程都有明确讲解既可用于理解新闻管理系统的完整业务流程也可作为编写需求文档和设计文档的模板。目前已有534人浏览学习可作为课程报告撰写、答辩讲解和功能设计的重要参考。1. 新闻管理系统把一份课程设计文档变成能答辩的完整原型很多同学拿到“新闻管理系统”这个题目第一反应是“这不就是个增删改查吗”真动手才发现卡住的不是代码而是需求里几个不起眼的字新闻详情要“新建页面显示”、站内搜索“按新闻内容进行查询而不是新闻标题”、后台入口放在主页“最下方”。这三个细节决定了整个系统的页面结构和查询逻辑一开始没读懂后面全是返工。这篇笔记把我拆这个题目的完整过程写出来表怎么设计、前台页面怎么组织、搜索怎么写才符合题意、后台权限怎么控、哪些地方最容易翻车照着做能少走弯路。适合正在做软件综合实习、课程设计或者想快速攒一个完整 Web 项目拿去演示的人。2. 表结构设计后台该建几张表读题比写代码重要2.1 为什么我不建议建“新闻类别表”先说我踩过的第一个坑。一开始我按照常规信息管理系统的思路设计了四张表新闻表、类别表、管理员表、友情链接表类别表里存八个分类新闻表通过 category_id 外键关联。做完以后自己跑了一遍发现前台主页要显示八个分类的新闻列表每个分类都要 JOIN 一次类别表代码里全是连表查询答辩的时候老师问“为什么类别要单独建表”我答不上来——因为这个题目里根本没有“类别管理”这个后台功能。把题目原文再读一遍后台功能只有管理员设置、新闻信息管理、友情链接管理没有任何一处提到要增删改新闻类别。前台显示的“时政要闻、财经观察、世界军事、科学教育、法制道德、社会百态、体育世界、时尚娱乐”是写死的八个分类。这说明类别是常量不是数据。建一张表去存八个永远不变的分类除了让查询多一次 JOIN没有任何收益。所以我最终用的是三张表新闻表、管理员表、友情链接表。新闻表里直接存一个 category 字段值是中文类别名。前台按分类查的时候WHERE category 时政要闻一条 SQL 搞定不用 JOIN不用在 Java 代码里维护 id 和名称的映射关系。课程设计这个体量这种“冗余”是合理的——它省掉的复杂度远大于它带来的所谓“设计不优雅”。2.2 三张表的字段设计三张表的字段设计如下这是建库脚本我一般把表名前缀统一加上tb_避免和 MySQL 关键字冲突。CREATE DATABASE IF NOT EXISTS news_system DEFAULT CHARACTER SET utf8mb4; USE news_system; -- 新闻表 CREATE TABLE tb_news ( id INT PRIMARY KEY AUTO_INCREMENT, category VARCHAR(20) NOT NULL COMMENT 新闻类别直接存中文如时政要闻, title VARCHAR(100) NOT NULL COMMENT 新闻标题, content TEXT NOT NULL COMMENT 新闻内容全文检索直接查这个字段, publish_time DATETIME NOT NULL COMMENT 发布时间, hits INT DEFAULT 0 COMMENT 点击量 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 管理员表 CREATE TABLE tb_admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE COMMENT 登录用户名, password VARCHAR(32) NOT NULL COMMENT 登录密码课程设计用明文生产环境必须加密 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 友情链接表 CREATE TABLE tb_link ( id INT PRIMARY KEY AUTO_INCREMENT, site_name VARCHAR(50) NOT NULL COMMENT 网站名称, site_url VARCHAR(255) NOT NULL COMMENT 网站地址, logo_path VARCHAR(255) DEFAULT NULL COMMENT LOGO图片路径不传则显示默认占位图 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有几个参数要说明category字段长度设 20 就够了中文类别名最长四个字“财经观察”“体育世界”但考虑以后可能加类别留点余量。content不是 VARCHAR 而是 TEXT因为新闻内容可能是几百上千字的正文VARCHAR 在 MySQL 里最长 65535 字节但 TEXT 更合适也方便后续用全文索引。hits点击量字段题目里没写但我建议加上——详情页每被访问一次就 1这是新闻系统最常见的附属功能加上它会让答辩时“系统功能”这一栏更充实而且实现成本只有一行 update。初始管理员数据要提前插入否则后台入口进了也没法登录INSERT INTO tb_admin (username, password) VALUES (admin, admin123);默认管理员admin / admin123是常见的做法先保证系统能跑通答辩前再改掉。密码用明文在课程设计里能接受但如果老师追问安全性你要能说出“生产环境会用 BCrypt 加盐哈希存储配合登录验证码和失败次数限制”这类话这是加分项。2.3 连接配置把数据库参数集中在一个类里Java Web 课程设计最常见的组合是 JSP Servlet JDBC数据库连接参数我习惯写在一个DBUtil.java里统一管理而不是散落在每个页面里。public class DBUtil { private static final String DRIVER com.mysql.cj.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/news_system?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }注意 URL 里的characterEncodingutf8mb4不写这个插入中文新闻内容的时候大概率出现乱码。serverTimezoneAsia/Shanghai是针对 MySQL 8.x 的时区问题老版本可以不加。用户名密码这里写的是root / 123456实际用的时候按你自己的 MySQL 配置改。3. 前台实现分类导航、MORE 跳转和按内容模糊搜索3.1 主页布局八个分类区块怎么摆前台主页是整个系统的门面题目要求主页包含旗帜广告条、新闻分类导航、当前日期、站内搜索、新闻分类显示、友情链接和后台入口。我把页面从上到下分成四块顶部导航区LOGO 日期 搜索框、分类导航条八个按钮、分类新闻列表区八个分类各占一块每块显示该分类最新 5 条新闻标题 MORE 按钮、底部区友情链接 后台入口。分类列表区的核心代码逻辑是循环遍历八个分类对每个分类查一次新闻表取最新的 5 条。%-- 八个固定分类写在Java代码里作为常量 --% % String[] categories {时政要闻, 财经观察, 世界军事, 科学教育, 法制道德, 社会百态, 体育世界, 时尚娱乐}; for (String category : categories) { // 查询每个分类下最新的5条新闻 String sql SELECT id, title FROM tb_news WHERE category ? ORDER BY publish_time DESC LIMIT 5; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, category); ResultSet rs ps.executeQuery(); % div classnews-block h3% category %/h3 ul % while (rs.next()) { % li a hrefnews_detail.jsp?id% rs.getInt(id) % target_blank % rs.getString(title) % /a /li % } % /ul a hrefnews_list.jsp?category% URLEncoder.encode(category, UTF-8) % classmore-btnMORE/a /div % rs.close(); ps.close(); } %这段代码有几个关键点。WHERE category ? ORDER BY publish_time DESC LIMIT 5用的是 PreparedStatement 参数化查询安全且能防止 SQL 注入课程设计里这是硬性加分项。LIMIT 5控制每个分类首页只显示 5 条避免页面过长。《新闻标题链接到news_detail.jsp?idxxx注意这里带了target_blank因为题目要求“新建页面显示”新闻详细内容新开标签页就是最直接的“新建页面”。MORE 按钮跳转到news_list.jsp?category分类名把分类名通过 URL 传过去目标页面再按分类分页查询。URL 传中文参数必须用URLEncoder.encode转码否则 Tomcat 默认编码下很容易乱码。3.2 站内搜索重点在“按内容查”而不是“按标题查”题目里特别标注了一句话“此查询中按新闻内容进行查询而不是新闻标题。”这是整个前台最容易做错的地方。很多人写搜索直接WHERE title LIKE %关键字%做完发现也能出结果但没注意到题目的这个限制条件。按内容搜的意思是查content字段不是title字段。搜索结果页和分类列表页可以共用一套新闻列表展示逻辑区别只在于数据来源不同。% request.setCharacterEncoding(UTF-8); String keyword request.getParameter(keyword); String category request.getParameter(category); // 拼查询SQL按内容模糊匹配 String sql SELECT id, title, category, publish_time FROM tb_news WHERE content LIKE ?; if (category ! null !category.isEmpty() !全部类别.equals(category)) { sql AND category ?; } sql ORDER BY publish_time DESC; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, % keyword %); if (category ! null !category.isEmpty() !全部类别.equals(category)) { ps.setString(2, category); } ResultSet rs ps.executeQuery(); %这里LIKE ?配合setString(1, % keyword %)实现了模糊匹配。%是通配符%关键字%表示内容任意位置包含该关键字都能命中。搜索条件里类别不是必选的选了某个分类就追加AND category ?没选就搜索全部分类。我一般在下拉列表里默认给一个“全部类别”选项传参为空时忽略类别条件。按内容搜带来的实际效果是搜索“经济”能查出标题里没有“经济”二字、但正文里在讨论经济形势的新闻。这既是题目的明确要求也是答辩时老师会重点验证的功能点。演示的时候故意找一条标题不含关键字但内容含关键字的新闻能很直观地证明你读懂了需求。3.3 详情页和列表页参数传递的两种方式news_detail.jsp通过id参数查单条新闻% int id Integer.parseInt(request.getParameter(id)); String sql SELECT * FROM tb_news WHERE id ?; PreparedStatement ps conn.prepareStatement(sql); ps.setInt(1, id); ResultSet rs ps.executeQuery(); if (rs.next()) { // 点击量1 String updateSql UPDATE tb_news SET hits hits 1 WHERE id ?; PreparedStatement updatePs conn.prepareStatement(updateSql); updatePs.setInt(1, id); updatePs.executeUpdate(); updatePs.close(); % h1% rs.getString(title) %/h1 p classmeta类别% rs.getString(category) % | 时间% rs.getTimestamp(publish_time) %/p div classcontent% rs.getString(content).replace(\n, br/) %/div % } rs.close(); ps.close(); %注意这里先把id转成 int 再拼接 SQL防止Integer.parseInt遇到非法参数直接报 500。内容里的换行符要替换成br/因为 HTML 页面不识别\n不替换的话新闻正文会挤成一大段。点击量 1 放详情页每次打开详情都执行一次 update体现“新闻热度”的概念。news_list.jsp是分类列表页接收category参数按分类分页显示。核心思路是在 URL 上拼page参数SQL 用LIMIT offset, pageSize做分页每页 10 条。4. 后台实现登录会话、新闻管理和图片上传的关键逻辑4.1 登录验证和会话控制后台入口在前台主页最下方点进去是login.jsp。登录验证逻辑放在一个独立的 Servlet 里验证通过就把用户名写进 Session后续所有后台页面都要检查 Session 里有没有这个值没有就重定向回登录页。// LoginServlet.java - 登录处理 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); String sql SELECT * FROM tb_admin WHERE username ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); ResultSet rs ps.executeQuery(); if (rs.next()) { // 登录成功把用户名存入Session request.getSession().setAttribute(adminUser, username); response.sendRedirect(admin/index.jsp); } else { // 登录失败带错误提示回登录页 request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } } catch (SQLException e) { e.printStackTrace(); response.sendRedirect(login.jsp); } }登录成功以后后台每个页面头部都要有一段权限检查代码防止未登录直接访问后台页面。% Object adminUser session.getAttribute(adminUser); if (adminUser null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } %这段代码放在admin/index.jsp、新闻管理页、友情链接管理页等所有后台页面的最顶部。逻辑很简单Session 里取不到adminUser说明没有登录直接拉回登录页。注意return必须写否则 JSP 还会继续往下渲染页面内容虽然最终被重定向覆盖但不写会有短暂的内容暴露风险。4.2 新闻添加与修改一个表单页面处理两种操作新闻管理的添加和修改可以共用一个表单页面。判断方式是 URL 里有没有id参数——有就说明是编辑模式需要先查出原新闻数据回显没有就是新增模式表单留空。% int newsId 0; String category , title , content ; String pageTitle 添加新闻; if (request.getParameter(id) ! null) { newsId Integer.parseInt(request.getParameter(id)); pageTitle 修改新闻; String sql SELECT * FROM tb_news WHERE id ?; PreparedStatement ps conn.prepareStatement(sql); ps.setInt(1, newsId); ResultSet rs ps.executeQuery(); if (rs.next()) { category rs.getString(category); title rs.getString(title); content rs.getString(content); } rs.close(); ps.close(); } % form actionNewsServlet methodpost input typehidden nameid value% newsId % / select namecategory option value时政要闻 % 时政要闻.equals(category) ? selected : %时政要闻/option option value财经观察 % 财经观察.equals(category) ? selected : %财经观察/option !-- 其余6个类别类似 -- /select input typetext nametitle value% title % / textarea namecontent rows10 cols60% content %/textarea button typesubmit保存/button /form表单提交到NewsServletServlet 里通过id参数判断是 update 还是 insert这个参数被放在隐藏域里一起提交。// NewsServlet.java - 添加和修改统一处理 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String idStr request.getParameter(id); String category request.getParameter(category); String title request.getParameter(title); String content request.getParameter(content); if (title null || title.trim().isEmpty() || content null || content.trim().isEmpty()) { request.setAttribute(error, 标题和内容不能为空); request.getRequestDispatcher(admin/news_edit.jsp).forward(request, response); return; } try (Connection conn DBUtil.getConnection()) { if (idStr ! null !idStr.isEmpty()) { // 修改操作 String sql UPDATE tb_news SET category ?, title ?, content ? WHERE id ?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, category); ps.setString(2, title); ps.setString(3, content); ps.setInt(4, Integer.parseInt(idStr)); ps.executeUpdate(); ps.close(); } else { // 新增操作 String sql INSERT INTO tb_news (category, title, content, publish_time) VALUES (?, ?, ?, NOW()); PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, category); ps.setString(2, title); ps.setString(3, content); ps.executeUpdate(); ps.close(); } } catch (SQLException e) { e.printStackTrace(); } response.sendRedirect(admin/news_list.jsp); }前端用必填校验后端也做了空值判断。课程设计阶段很多同学只做前端的required属性后端不拦绕过浏览器直接 POST 就能写入空数据这是要被扣分的。NOW()是 MySQL 的函数自动取当前时间作为发布时间。修改和新增的区分只看idStr是否为空这个逻辑要在答辩时能讲清楚。4.3 友情链接 LOGO 上传路径问题是最大的坑友情链接管理要求上传网站 LOGO 图片。JSP 里文件上传要用enctypemultipart/form-data常见的处理方式是引入 Commons FileUpload 组件。核心代码里要注意保存路径必须用绝对路径不能用相对路径。// LinkServlet.java - 友情链接添加 // 表单 enctypemultipart/form-data FileItemFactory factory new DiskFileItemFactory(); ServletFileUpload upload new ServletFileUpload(factory); ListFileItem items upload.parseRequest(request); String siteName null, siteUrl null, logoPath null; for (FileItem item : items) { if (item.isFormField()) { // 普通表单字段 if (site_name.equals(item.getFieldName())) siteName item.getString(UTF-8); if (site_url.equals(item.getFieldName())) siteUrl item.getString(UTF-8); } else { // 文件字段 String fileName new File(item.getName()).getName(); // 保存到项目的 upload 目录 String realPath request.getServletContext().getRealPath(/upload); File uploadDir new File(realPath); if (!uploadDir.exists()) uploadDir.mkdirs(); logoPath upload/ fileName; item.write(new File(realPath, fileName)); } } // 入库 String sql INSERT INTO tb_link (site_name, site_url, logo_path) VALUES (?, ?, ?);request.getServletContext().getRealPath(/upload)拿到的是部署后项目在磁盘上的真实路径这样图片保存下来前台页面用img srcupload/xxx.png才能正确访问。如果直接写相对路径图片可能保存到了 Tomcat 启动目录下的某个随机位置前台根本加载不出来——这是我做这个题目时踩过的最典型的路径坑。5. 避坑笔记新闻管理系统最常见的六个翻车现场5.1 八个分类写死了以后想加类别怎么办现象八个类别在主页导航、搜索下拉框、后台添加新闻页面里各写了一遍改了某一处忘了另外几处类别显示不一致。 原因同一个分类常量散落在多个 JSP 页面没有统一管理。 解决新建一个CategoryUtil.java把八个类别定义为静态数组三个页面都从同一个地方取。以后要加类别只改一处。5.2 搜索新闻出不来结果但明明有这条新闻现象输入关键字点“站内搜索”提示没有找到相关新闻但数据库里明明有内容匹配的记录。 原因最常见的两种情况。第一种是搜索 SQL 写成了WHERE title LIKE ?题目要求按内容查标题里没这个关键字自然查不出来第二种是中文乱码content字段里的中文是乱码搜索关键字也是乱码两边匹配不上。 解决先确认 SQL 查的是content字段再确认 JSP 页面和 Servlet 都加了request.setCharacterEncoding(UTF-8)页面的charset也要统一为 UTF-8。5.3 后台入口在页面最下方演示时翻不到现象现场演示时打开主页在页面顶部找不到后台登录入口评委以为没有后台。 原因题目明确要求“后台入口在主页最下方”放在了页脚区域但页面太长需要滚动才能看到。 解决代码位置不动但演示前先把浏览器窗口缩窄让页面内容自动变长变短或者提前把滚动条拖到底部截图存在一边。更稳妥的做法是后台入口文字加粗加颜色让它在一屏内容里能被注意到又不违反“放在最下方”的要求。5.4 友情链接 LOGO 图片裂了现象后台添加友情链接并上传了 LOGO前台显示一个小裂图。 原因图片保存路径不对或者文件上传到了服务器但src指向的 URL 和实际位置不匹配。 解决检查上传目录的getRealPath是否正确检查logo_path存进数据库的值是不是可以在浏览器里直接访问的 URL 形式。我一般存相对路径upload/xxx.png前台拼request.getContextPath()来访问。5.5 修改和新增共用页面编辑时数据没回显现象点“修改”进入编辑页文本框是空的或者表单里出现了别的新闻的数据。 原因回显逻辑只写了查询没把查询结果setAttribute到 request 里页面直接从 request 里取值取不到或者是用了同一个变量名后面的赋值覆盖了前面的。 解决编辑模式先按id查新闻把category、title、content放进三个 request attribute再在 JSP 里用${category}或% category %回显到表单。id必须放隐藏域否则提交时 Servlet 无法区分是新增还是修改。5.6 刷新页面重复提交一条新闻加了两次现象添加新闻后按 F5 刷新列表里出现两条一模一样的新闻。 原因表单 POST 提交后浏览器刷新会重新发送上一次的 POST 请求。 解决最简单的处理是 Servlet 处理完数据后不 forward 到列表页而是sendRedirect重定向到列表页。重定向后 URL 变了刷新只会刷新 GET 请求的列表页不会重复提交 POST。这是 PRG 模式的入门用法答辩时提一句“用了 Post/Redirect/Get 避免重复提交”老师会认可。6. 让系统经得起演示功能验证清单和从这次课设里学到的习惯系统写完不是结束能当着评委的面稳定走完一遍流程才是结束。我每次交付前都强制跑一遍功能清单这一步帮我挡住过至少三次现场翻车。下面这张表是我给这个新闻管理系统准备的验收清单你可以直接抄走用编号操作路径预期结果检查要点1主页加载导航、日期、搜索框、八个分类区块完整显示中文不乱码页面无 500 报错2点击某分类的新闻标题新窗口打开详情页必须target_blank内容完整显示3点击 MORE进入该分类的列表页分页正常URL 里的中文分类名不乱码4搜索“经济”返回内容中含“经济”的新闻验证是否按 content 而非 title 查询5前台下翻到底部显示友情链接和后台入口点击图标能跳转后台入口可见6后台入口登录正确账号进后台错误账号提示错误Session 写入成功未登录不能进后台页面7后台添加新闻前台对应分类下多出一条新闻添加后立即在主页能看到8后台修改新闻前台详情页内容同步更新修改后缓存不生效刷新可见9后台添加友情链接前台的友情链接区域新增一条LOGO 图片正常显示不裂10后台删除新闻前台列表移除该新闻删除有确认提示防止误删这个清单里第 4 项是最值得花时间验证的。准备一条测试新闻标题叫“本市召开工作会议”内容是“会议讨论了财政预算和经济运行情况”然后搜索“财政”。按标题搜搜不到按内容搜能搜到——这个对比演示放出去评委立刻就知道你读懂了需求里那句“按新闻内容进行查询而不是新闻标题”。另外两个容易被忽略的验证点是前台所有新闻链接都用target_blank打开新页面这对应需求里的“新建页面显示”后台入口必须在主页最下方能找到开场演示第一个动作就是直接把页面拖到底部把后台入口亮出来给评委看然后再开始登录操作。从那以后我每次做完一个系统交付前都强制自己走一遍这张验收清单——逐个按钮点过去从录入到展示再到修改删除全链路跑通才敢提交。做课程设计最怕的不是功能少而是做了一堆功能但演示的时候在第一步就报错前三十秒的印象分没了后面很难追。希望这篇笔记能帮你把这个题目顺顺利利做出来答辩的时候心里有底。本文还有配套的精品资源点击获取