目录
1.项目来源及背景
在网络飞速发展的今天,Internet成为人们快速获取、发布和传递信息的重要渠道,众所周知,论坛是当今网络中的知名服务之一。它开辟了一块“公共”的空间供所有用户发表和读取信息,允许用户对自身感兴趣的话题展开讨论,从而起到集思广益的作用。本论论坛是针对在校大学生的具体需求进行开发,在这里,学生可以通过论坛进行一些相关信息的查询,信息交流,信息共享,因此具有很好的现实意义。
BBS讨论区目前是互联网上人气最旺的互动交流服务。据统计,国内三大门户网站的BBS讨论区带来的流量远远超过其门户新闻带来的流量。在当今的BBS讨论区中,出现了很多模板,它们大多是基于JSP+JavaBean+Servlet的MVC三层架构开发。
2.系统需求分析
2.1 系统需求描述图
3.系统设计
3.1 概要设计
3.1 系统功能模块图
讨论区由管理员创建,讨论区创建以后,用户就可以进入该讨论区发表新的帖子或对已存在的帖子回帖。
系统用户分为管理员和普通用户,只有管理员能对讨论区、帖子进行修改与删除。
管理员负责讨论区和帖子的管理,需要具备以下功能:
² 登录、注册、修改注册信息
² 浏览、创建、修改、删除讨论区
² 浏览、创建、修改、删除、回复帖子
² 普通用户是注册登录的用户,需要具备以下功能:
² 登录、注册、修改注册信息
² 浏览讨论区
² 浏览、创建、回复帖子
² 功能介绍如下:
² 讨论区:每个讨论区对应一个话题类别,用户可以根据讨论区的名字判断讨论区的内容。
² 帖子:用户发表的帖子包含标题和内容。标题简要说明帖子的内容,内容用于详细表述用户需要发表的观点或提出的问题。
² 回帖:对帖子的回复,一般没有标题,只有内容,用于对一个帖子进行答复。
² 登录:提供用户登录系统的功能,需要输入用户名和密码
² 注册:提供用户注册的功能,需要输入用户名、密码、密码确认、E-MAIL
² 修改注册信息:提供用户修改注册信息的功能,可以修改密码、E-MAIL
² 浏览讨论区:用户登录之后就可以浏览系统中已经存在的讨论区,用户可以查看某一讨论区的详细信息,也可以进入该讨论区浏览发表的帖子,以及回帖
² 创建讨论区:用户可以创建讨论区,每个讨论区包含一个名称和顺序号,每个讨论区的名称在系统中是唯一的,顺序号表示讨论区在列表中的排序
² 修改讨论区:提供讨论区名称和顺序号修改的功能
² 删除讨论区:提供讨论区的删除功能,方面管理员进行讨论区管理
² 浏览帖子:用户进入一个讨论区之后,可以看到帖子列表,包含帖子主题、作者、创建时间、回复次数,点击一个帖子,可以查看该帖子的内容和回帖
² 创建帖子:用户进入一个讨论区之后,可以选择创建一个帖子,需要输入帖子主题和内容
² 修改帖子:提供给管理员使用,修改帖子标题和内容
² 删除帖子:提供给管理员使用,方便管理员进行帖子整理
² 回复帖子:用户在浏览帖子信息的时候,可以进行回复
3.2 详细设计
(1)数据库表结构的设计
BBS论坛系统的实体有:讨论区、帖子、用户。得到数据库的数据项和数据结构为:
² 用户信息user:包括的数据项有用户名、密码、E-mail
² 讨论区信息forum:包括的数据项有讨论区名称、顺序号
² 帖子信息topic:包括的数据项有帖子编号、所属讨论区编号、父帖子编号、用户名、帖子主题、帖子内容、发表时间等
表3-1 数据库表
序号 | 数据库表 | 数据库表存储内容 |
1 | user | 存储用户注册信息 |
2 | forum | 存储讨论区信息 |
3 | topic | 存储用户发表的帖子,以及回帖内容 |
表3-2 user用户信息表
序号 | 字段 | 描述 | 类型和长度 | 其他 | 默认值 |
1 | Id | 唯一编号 | Int | 主关键字 | 无 |
2 | Username | 用户名 | Varchar(20) | 唯一,非空 | 无 |
3 | Password | 用户密码 | Varchar(20) | 非空 | 无 |
4 | | 用户邮箱 | Varchar(100) | 非空 | 无 |
5 | Nickname | 昵称 | Varchar(20) | 非空 | 无 |
6 | Degree | 用户身份 | Int | 非空 | 否 |
7 | registerdate | 用户注册时间 | datetime | 非空 | 当前时间 |
该用户信息表包含了用户的基本且必要的信息,其中id是“用户编号”,是系统为用户分配的唯一标志符,在整个用户信息表中是互不相同的;用户名和密码也是必需的,用户在登录BBS的时候需要提供,而且用户名也要求是唯一的,系统根据用户名来区分用户;E-MAIL是用户提供的联系方式,方便在离线的时候与用户联系;degree登记用户的身份(管理员和普通用户);registerdate登记用户注册时间。
表3-3 forum讨论区表
序号 | 字段 | 描述 | 类型和长度 | 其他 | 默认值 |
1 | Id | 唯一编号 | Int | 主关键字、自增列 | 无 |
2 | Name | 讨论区名称 | Varchar(50) | 唯一、非空 | 无 |
3 | Sort | 顺序号 | Int | 非空 | 0 |
4 | description | 讨论区间断描述 | Varchar(200) | 非空 | 无 |
讨论区表包含了讨论区的基本且必要的信息。其中id是“讨论区编号”,讨论区的名字是必需的,而且也是唯一的,讨论区的名字尽量做到简短明确。
表3-4 topic帖子信息表
序号 | 字段 | 描述 | 类型和长度 | 其他 | 默认值 |
1 | Id | 唯一编号 | Int | 主关键字、自增 | 无 |
2 | Parentid | 父帖子编号 | Int | 0 | |
3 | Forumid | 讨论区编号 | Int | 0 | |
4 | Username | 用户名 | Varchar(20) | 无 | |
5 | Title | 帖子主题 | Varchar(250) | 无 | |
6 | Content | 帖子内容 | Text | 无 | |
7 | Postdate | 发帖日期 | Datetime | 当前时间 | |
8 | replies | 帖子回复次数 | int | 0 |
在帖子信息表中,包含了帖子的基本且必要的信息,其中id是“帖子编号”。Parentid是帖子的父帖子编号,对于刚发的新帖,父帖子编号为0;对于回帖,父帖子编号为要回复的帖子本身的编号;username为发表帖子的用户的名字;title为该帖子的标题,每个新发表的帖子都有一个标题,简短描述该帖子的内容;replies为该帖子被回复的次数。
三个表的关系为:
用户信息表与帖子信息表是一对多的关系,一个用户可以发表多个帖子,每个帖子的发表人只能是一个。
讨论区表与帖子信息表是一对多的关系,一个帖子只能属于一个讨论区,但是一个讨论区可以有多个帖子。
帖子信息表与帖子信息表是一对多的关系,一个帖子只能拥有一个父帖子,而一个父帖子可以包含多个回复帖子。
(2)讨论区管理设计
讨论区是一系列同类主题帖子的集合。讨论区管理模块通过Forums.jsp、Forums_view.jsp、Forums_add.jsp、Forums_update.jsp并结合逻辑类实现讨论区的浏览、创建、编辑、删除等功能。游客、用户或者管理员进入Students BBS论坛后,页面通过调用业务逻辑类加载数据库里面的信息,管理员实现讨论区的创建、编辑、删除等一些操作,普通用户或游客只能进行浏览查看详细信息等操作。
(3)帖子管理设计
帖子通过Topics.jsp、Topic_view.jsp、Topic_add.jsp、Topic_update.jsp页面并结合逻辑类实现管理各个用户进行创建帖子、编辑帖子、删除帖子、浏览帖子、回复帖子及一体,当用户在选择讨论区以后,即进入相关各种帖子。用户可以选择性的浏览帖子,进入帖子后,可以实现回帖功能。管理员实现帖子及回帖的删除及编辑,实现倡导言论文明自由。
4.系统实现
4.1 文件结构及MVC模式描述
(1)讨论区管理设计
讨论区是一系列同类主题帖子的集合。讨论区管理模块需要实现讨论区的浏览、创建、编辑、删除等功能。在MVC模式中,这些功能放在业务逻辑中具体实现,即JavaBean负责的内容。业务逻辑由处理流程控制Servlet调用,然后把处理结果显示在JSP页面上,JSP页面只完成最简单的显示,提供操作选项。这样就能保证能严格遵循MVC模式。代码结构如下:
l Forum.java 封装讨论区对象
l Forumhandle.java 业务逻辑处理类,封装操作讨论区对象的方法,比如增加、删除、修改、查询等等
l 以上两个类相当于MVC模式中的JavaBean环节
l ForumServlet.java 流程控制,处理来自用户的请求,并调用业务逻辑(ForumHandle)的相关方法,将结果显示给用户,该类相当于MVC模式中的Servlet环节
l Forums.jsp 展现讨论区列表,页面上提供增加、删除、修改等操作选项
l Forums_view.jsp 展示某个讨论区的详细信息
l Forums_add.jsp 新增讨论区信息的输入界面,完成简单的输入校验
l Forums_update.jsp 修改讨论区信息的输入界面
以上各jsp文件相当于MVC模式中的JSP环节
(2)帖子管理设计
帖子管理模块需要实现帖子的浏览、创建、编辑、删除、回复等功能。该模块代码结构如下:
l Topic.java 封装帖子对象
l TopicHandle.java 业务逻辑处理类,封装操作帖子对象的方法,比如增加、删除、修改、回复等
l 以上两个类是MVC模式中的JavaBean环节
l TopicServlet.java 流程控制,处理来自用户的请求,并调用业务逻辑(TopicHandle)的相关方法,将结果展示给用户,该类是MVC模式中的Servlet环节
l Topics.jsp 展示属于某个讨论区的帖子列表
l Topic_view.jsp 展示某个帖子的信息,以及回复
l Topic_add.jsp 发表新帖的输入界面
l Topic_update.jsp 修改帖子信息的输入界面
以上jsp代码是MVC模式中的JSP环节
4.2 公共模块代码实现
package com.db.common;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.PropertyResourceBundle;
/*数据库连接管理器,用于实现数据库操作的封装*/
public class SqlManger {
private static SqlManger p = null; //静态成员,支持单态模式
private PropertyResourceBundle bundle; //配置资源文件
private static String jdbcDriver = null; //jdbc驱动类型
private static String split = null; //文件分割符
private String DBType = null; //数据库类型
private String DBhost = "localhost"; //数据库主机地址
private String DBname = ""; //数据库名
private String DBport = ""; //数据库端口
private String DBuser = ""; //数据库用户名
private String DBpasswd = ""; //数据库密码
private Connection Sqlconn = null;
private Statement Sqlstmt = null;
private String strCon = null;
//私有构造函数
private SqlManger(){
try{
bundle = new PropertyResourceBundle( //读取配置文件
SqlManger.class.getResourceAsStream("./sysConfig.properties"));
this.DBhost = getString("DBhost"); //读取数据库主机
this.DBname = getString("DBname"); //读取数据库名
this.DBport = getString("DBport"); //读取数据库端口
this.DBuser = getString("DBuser"); //读取数据库用户名
this.DBpasswd = getString("DBpasswd"); //读取数据库密码
String system_type = getString("system-type");//读取操作系统类型
if(system_type != null){ //根据操作系统配置文件分割符
if(system_type.toLowerCase().equals("windows")){
split = ";";
}
else{
split = ":";
}
}
String database_type = getString("database-type");
this.DBType = database_type;
if(database_type.toLowerCase().equals("mysql")){
jdbcDriver = "com.mysql.jdbc.Driver";
strCon = "jdbc:mysql://" + DBhost + ":" + DBport + "/" + DBname + "?useUnicode=true&characterEncoding=utf8";
}
}catch(Exception e){
e.printStackTrace();
}
}
private String getString(String s){ //读取资源文件中的字符串
return this.bundle.getString(s);
}
public static SqlManger createInstance(){ //单态模式获得实例
if(p == null){
p = new SqlManger();
p.initDB();
}
return p;
}
public void initDB(){ //初始化连接参数
System.out.println(strCon);
System.out.println(jdbcDriver);
try{
Class.forName(jdbcDriver); //装载驱动
}catch(Exception ex){
System.err.println("Can't find Database Driver.");
}
}
public void connectDB(){ //建立数据库连接
try{
// System.out.println("SqlManger: connecting to database..");
Sqlconn = DriverManager.getConnection(strCon, DBuser, DBpasswd);//获取连接
Sqlstmt = Sqlconn.createStatement();//创建查询
}catch(SQLException ex){
System.err.println("connectDB" + ex.getMessage());
}
}
public void closeDB() {
try{
// System.out.println("SqlManger:close connection to database ...");
Sqlstmt.close();//关闭查询
Sqlconn.close();//关闭连接
}catch(SQLException ex){
System.err.println("closeDB" + ex.getMessage());
}
// System.out.println("SqlManger:Close connection successful.");
}
//执行服务返回数据的数据查询,返回值是被改变的数据项数
public int executeUpdate(String sql) {
int ret = 0;
try{
ret = Sqlstmt.executeUpdate(sql);
}catch(SQLException ex){
System.err.println("executeUpdate:" + ex.getMessage());
}
return ret;
}
//查询数据库的操作接口
public ResultSet executeQuery(String sql){
ResultSet rs = null;
try{
Statement st = Sqlconn.createStatement();//创建查询;
rs = st.executeQuery(sql);
}catch(SQLException ex){
System.err.println("executeQuery:" + ex.getMessage());
}
return rs;
}
public static void main(String[] args){
SqlManger.createInstance().connectDB();
SqlManger.createInstance().closeDB();
}
}
4.3 各模块实现截图及实现代码
(1)讨论区代码实现及视图
page contentType="text/html; charset=GBK"%>
<%@page import="com.tool.*"%>
<%@page import="bbs.*"%>
<%@page import="com.unitl.*"%>
<%@page import="java.util.*"%>
<SCRIPT language=JavaScript src="include/check.js"
type="text/javascript"></SCRIPT>
<script language="JavaScript">
//创建删除讨论区函数
function deleteForum(forum_id){
var flag = window.confirm("确定要删除该讨论区吗?");
if (flag)
{document.frmAction.action="forumservlet?method=forum_delete&forum_id=" + forum_id;
document.frmAction.submit();
}
}
</script>
<html>
<head>
<link rel=stylesheet href="include/style.css">
<title><%=bbs.constant.title%></title>
</head>
ArrayList array = (ArrayList) request.getAttribute("forums");
<jsp:include page="error_info.jsp">
<jsp:param name="desc" value="forum_error" />
</jsp:include>
<body bgcolor="#ffffff">
<FORM method="POST" name="frmAction">
<center>
<br><img src="images/return.gif"><a href="index.jsp">返回上一级</a>
<img src="images/return.gif"><a href="index.jsp">返回上一级</a>
user user = (user) session.getAttribute("user");
if (user != null && user.getDegree() == user.superadmin) {
<img src="images/new.gif">
<a href="forum_add.jsp">创建讨论区</a>
}
<br>
<table border="1px" cellspacing="0" cellpadding="10" align=center width="90%" bgColor=#E4E8EF>
<tr bgcolor="#76aef0">
<th width="15%">名称 </th>
<th width="10%">顺序号</th>
<th width="45%">描述 </th>
<th width="20%">操作 </th>
</tr>
for (int i = 0; i < array.size(); i++) {
forum forum = (forum) array.get(i);
<tr>
<td>
<img src="images/topics.gif" align=left>
<a href="topicservlet?method=topic_select&forum_id=<%=forum.getId()%>"><%=forum.getName()%></a>
</td>
<td align="center"><%=forum.getSort()%></td>
<td><%=forum.getDescription()%></td>
<td align="center">
<a href="#"
onclick="MM_openBrWindow('forumservlet?method=forum_view&forum_id=<%=forum.getId()%>', 'popup', 'scrollbars=yes,resizable=yes,width=400,height=200,center=yes')">详情</a>
if (user != null && user.getDegree() == user.superadmin) {
<a href="forumservlet?method=forum_edit&forum_id=<%=forum.getId()%>">编辑</a>
<a href="#" onclick="deleteForum(<%=forum.getId()%>);">删除</a>
<%}>
</td>
</tr>
}
</table>
</center>
</Form>
</body>
</html>
4.1 讨论区视图
(2)帖子区代码实现及视图
@page contentType="text/html; charset=GBK"%>
<%@page import="bbs.*"%>
<%@page import="com.tool.*"%>
<%@page import="com.unitl.*"%>
<%@page import="java.util.*"%>
<link rel=stylesheet href="include/style.css">
<SCRIPT language=JavaScript src="include/check.js"
type="text/javascript"></SCRIPT>
<script language="JavaScript">
function deleteTopic(forum_id, bbs_id){
var flag = window.confirm("确定要删除该帖子吗?");
if (flag) { document.frmAction.action="topicservlet?method=topic_delete&forum_id=" + forum_id + "&bbs_id=" + bbs_id;
document.frmAction.submit();
}
}
</script>
<html>
ArrayList array = (ArrayList) request.getAttribute("topics");出req并if (array == null) {
array = new ArrayList();/如果为空,生成一个空数组
}
String forum_id = request.getParameter("forum_id");
page pageInfo = (page) request.getAttribute("pageInfo"); %>
<head>
<link rel=stylesheet href="include/style.css">
<title><%=com.unitl.forumhandle.getForumName(forum_id)%>-<%=bbs.constant.title%>
</title>
</head>
<body bgcolor="#ffffff">
<FORM method="POST" name="frmAction">
<center>
<jsp:include page="error_info.jsp">
<jsp:param name="desc" value="topic_error" />
</jsp:include>
<br>
<img src="images/return.gif">
<a href="forumservlet?method=forum_select">返回上一级</a>
<img src="images/new.gif">
<a href="topic_add.jsp?forum_id=<%=forum_id%>">发表新贴</a>
<br>
<table border="1px #76aef0 solid" cellspacing="0" cellpadding="10"
align=center bgColor=#E4E8EF width="90%">
<tr bgcolor="#76aef0">
<th>主题</th><th>作者</th>
<th>回复</th><th>时间</th>
user user = (user) session.getAttribute("user");if (user != null && user.getDegree() == user.superadmin) {<th>操作</th>}>
</tr>
for (int i = 0; i < array.size(); i++) {
topic ann = (topic) array.get(i);
<tr><td>
<img src="images/hottopic.gif" align=left>
<a href="topicservlet?method=topic_view&bbs_id=<%=ann.getId()%>&forum_id=<%=forum_id%>" style=""><%=ann.getTitle()%></a> </td>
<td><%=ann.getUsername()%></td>
<td><%=ann.getReply()%></td>
<td><%=ann.getPostdate()%></td>
if (user != null && user.getDegree() == user.superadmin) {%>
<td><a href="#"onclick="deleteTopic(<%=forum_id%>, <%=ann.getId()%>);">删除</a></td> }
</tr>
}
<tr>
<td align="left" colspan="5"><%=pageInfo.getFooter()%>
</td>
</tr>
</table>
</center>
</Form>
</body>
</html>
4.2 帖子区视图
4.3 帖子视图
(3)登录注册实现代码及视图
page contentType="text/html; charset=GBK"%>
<SCRIPT language=JavaScript src="include/check.js"
type="text/javascript"></SCRIPT>
<script language="JavaScript">
function userLogin(){<%--调用函数当用户点击登录按钮--%>
if(document.frmAction.username.value == null ||
checkLength(document.frmAction.username.value)<1){
alert('请输入用户名!');
return ;
}
if(document.frmAction.password.value == null ||
checkLength(document.frmAction.password.value)<1){ --%>
alert('请输入用户密码!');
return ;
}
document.frmAction.action="loginservlet?method=user_login";
document.frmAction.submit();
}
function userReg(){<%--当用户注册时调用该函数 --%>
document.frmAction.action="user_add.jsp";面--%>
document.frmAction.submit();
}
</script>
String username = request.getParameter("username");
if(username == null)
username = "";
//当姓名为空时
String password = request.getParameter("password");
if(password == null)
password = "";
<html>
<head>
<link rel=stylesheet href="include/main.css">
<title>用户登录</title>
</head>
<body bgcolor="#ffffff">
<br>
<br>
<br>
<br>
<FORM method="POST" name="frmAction">
<center>
<jsp:include page="error_info.jsp">
<jsp:param name="desc" value="login_error" />
</jsp:include>
<table border="1px #76aef0 solid" cellspacing="0" cellpadding="1"
align=center bgColor=#E4E8EF>
<tr>
<td width="50%">用户名:</td>
<td width="50%"><input type="text" name="username" value="<%=username%>" /></td>
</tr>
<tr>
<td width="50%">密码:</td>
<td width="50%"><input type="password" name="password" value="<%=password%>" /></td>
</tr>
<tr>
<td width="50%">
<center><input type="submit" onclick="userLogin();" value="登录" class="button" />
</center>
</td>
<td width="50%">
<center>
<input type="submit" onclick="userReg();" value="注册" class="button" />
</center>
</td>
</tr>
</table>
</center>
</FORM>
</body>
</html>
4.4 主页视图
4.5 登录视图
4.6 注册视图
附录:
page contentType="text/html; charset=GBK"%>
<SCRIPT language=JavaScript src="include/check.js"
type="text/javascript"></SCRIPT>
<script language="JavaScript">
function userLogin(){<%--调用函数当用户点击登录按钮--%>
if(document.frmAction.username.value == null ||
checkLength(document.frmAction.username.value)<1){<%-- 判读用户的输入姓名值是否不为空 --%>
alert('请输入用户名!');
return ;
}
if(document.frmAction.password.value == null ||
checkLength(document.frmAction.password.value)<1){<%--判断用户密码输入值是否不为空 --%>
alert('请输入用户密码!');
return ;
}
document.frmAction.action="loginservlet?method=user_login";
document.frmAction.submit();
}
function userReg(){<%--当用户注册时调用该函数 --%>
document.frmAction.action="user_add.jsp";
<%--跳转到user_add.jsp页面--%>
document.frmAction.submit();
}
</script>
<%
String username = request.getParameter("username");
if(username == null)
username = "";
//当姓名为空时
String password = request.getParameter("password");
if(password == null)
password = "";
//当密码为空时
%>
<html>
<head>
<link rel=stylesheet href="include/main.css">
<title>用户登录</title>
</head>
<body bgcolor="#ffffff">
<br>
<br>
<br>
<br>
<FORM method="POST" name="frmAction">
<center>
<jsp:include page="error_info.jsp">
<jsp:param name="desc" value="login_error" />
</jsp:include>
<table border="1px #76aef0 solid" cellspacing="0" cellpadding="1"
align=center bgColor=#E4E8EF>
<tr>
<td width="50%">
用户名:
</td>
<td width="50%">
<input type="text" name="username" value="<%=username%>" />
</td>
</tr>
<tr><td width="50%"> 密码:</td>
<td width="50%">
<input type="password" name="password" value="<%=password%>" />
</td>
</tr>
<tr>
<td width="50%">
<center>
<input type="submit" onclick="userLogin();" value="登录"
class="button" />
</center>
</td>
<td width="50%">
<center>
<input type="submit" onclick="userReg();" value="注册"
class="button" />
</center>
</td>
</tr>
</table>
</center>
</FORM>
</body>
</html>
下面附带源码及项目报告书+mysql数据库数据的链接《JSP+servlet+Javabean+mysql BBS论坛实战》,在我的个人中心里面可以下载。
推荐大家加入下面群,在群文件中进行下载
为了大家更好的学习,这里我创建了一个群,欢迎大家共勉