近期在做一个课程设计。在线小说站点的设计,下面是课题要求。须要项目练手的童鞋能够试试身手。
因为近期新学了JavaEE,所以採用了jsp+servlet来写,前端部分用了少量的js和jQuery处理。数据库用了MySQL。开发平台是myeclipse。
公布文章时直接插入数据库会没有分段。这里的解决的方法是引入第三方工具wangEditor(wangEditor 是一款基于javascript和css开发的html富文本编辑器,开源免费。产品第一版公布于2014年11月。关于该编辑器:http://www.kancloud.cn/wangfupeng/wangeditor2/113961)
首先数据库的设计结构:
/*
Navicat MySQL Data Transfer
Source Server : blog
Source Server Version : 50528
Source Host : localhost:3306
Source Database : novel
Target Server Type : MYSQL
Target Server Version : 50528
File Encoding : 65001
Date: 2016-12-31 16:04:07
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for admin
-- ----------------------------
DROP TABLE IF EXISTS `admin`;
CREATE TABLE `admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`adminName` varchar(255) NOT NULL,
`adminPassword` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for author
-- ----------------------------
DROP TABLE IF EXISTS `author`;
CREATE TABLE `author` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`authorName` varchar(255) NOT NULL,
`authorPassword` varchar(255) NOT NULL,
`authorEmail` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for comment
-- ----------------------------
DROP TABLE IF EXISTS `comment`;
CREATE TABLE `comment` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`context` text,
`createdTime` datetime DEFAULT NULL,
`readerName` varchar(255) DEFAULT NULL,
`novelId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=67 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for genre
-- ----------------------------
DROP TABLE IF EXISTS `genre`;
CREATE TABLE `genre` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`sort` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for novel
-- ----------------------------
DROP TABLE IF EXISTS `novel`;
CREATE TABLE `novel` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`context` text NOT NULL,
`createdTime` datetime DEFAULT NULL,
`genreId` int(11) DEFAULT NULL,
`voteNumber` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=160 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for reader
-- ----------------------------
DROP TABLE IF EXISTS `reader`;
CREATE TABLE `reader` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`readerName` varchar(255) NOT NULL,
`readerPassword` varchar(255) NOT NULL,
`readerEmail` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;
项目的大致结构如图:
因为功能有点多。这里先介绍后台的实现,管理后台和前台互不交涉。
登录界面
后台主页:
1,小说管理
2,作者管理:
3,添加分类
后台其它导航页面基本雷同,这里不做一一介绍。
后台管理员登录处理代码:
public class Admin {
Conn conn=new Conn();
/**
* 推断登陆用户是否合法
* @param adminName
* @param adminPassword
* @return
* @throws SQLException
*/
public boolean isExist(String adminName,String adminPassword)throws SQLException{
boolean result=false;
AdminInfo ainfo=new AdminInfo();
String sql="select * from admin a where adminName='"+adminName+"'and adminPassword='"+adminPassword+"'";
System.out.println(sql);
ResultSet rs=conn.executeQuery(sql);
if(rs.next()){
ainfo.setAdminName(rs.getString("adminName"));
ainfo.setAdminPassword(rs.getString("adminPassword"));
result=true;
}
conn.close();
return result;
}
}
小说展示,管理。添加,删除,更新的代码处理:
public class Novel {
Conn conn=new Conn();
/**
* 获取小说列表
* @param keyword
* @return
* @throws SQLException
*/
public List<NovelInfo>getList(String keyword)throws SQLException{
List<NovelInfo> list=new ArrayList<NovelInfo>();
String sql="select n.*,g.name as genreName from novel n left join genre g on n.genreId=g.id";
if(DataValidator.isNullOrEmpty(keyword)){
sql=sql+ " order by id desc";
}else{
sql=sql+" where n.title like '%"+keyword+"%' order by id desc";
}
ResultSet rs=conn.executeQuery(sql);
while(rs.next()){
NovelInfo ninfo=new NovelInfo();
ninfo.setId(rs.getInt("Id"));
ninfo.setTitle(rs.getString("Title"));
ninfo.setContext(rs.getString("Context"));
ninfo.setCreatedTime(rs.getDate("CreatedTime"));
ninfo.setGenreId(rs.getInt("GenreId"));
ninfo.setGenreName(rs.getString("genreName"));
ninfo.setVoteNumber(rs.getInt("voteNumber"));
list.add(ninfo);
}
conn.close();
return list;
}
/**
* 获取某分类下的小说列表
* @param classId
* @return
* @throws SQLException
*/
public List<NovelInfo> getListBygenreId(int genreId) throws SQLException{
List<NovelInfo> list=new ArrayList<NovelInfo>();
String sql="select n.*,g.name as genreName from novel n left join genre g on n.genreId=g.id"
+ " where n.genreId="+genreId+" order by id desc";
ResultSet rs=conn.executeQuery(sql);
while(rs.next()){
NovelInfo info=new NovelInfo();
info.setId(rs.getInt("Id"));
info.setTitle(rs.getString("Title"));
info.setContext(rs.getString("Context"));
info.setCreatedTime(rs.getDate("CreatedTime"));
info.setGenreId(rs.getInt("GenreId"));
info.setGenreName(rs.getString("genreName"));
info.setVoteNumber(rs.getInt("voteNumber"));
list.add(info);
}
conn.close();
return list;
}
/**
* 依据ID获取小说
* @param id
* @return
* @throws SQLException
*/
public NovelInfo getNovelInfo(int id) throws SQLException{
NovelInfo info=new NovelInfo();
String sql="select n.*,g.name as genreName from novel n left join genre g on n.genreId=g.id where n.id="+id+"";
ResultSet rs=conn.executeQuery(sql);
while(rs.next()){
info.setId(rs.getInt("Id"));
info.setTitle(rs.getString("Title"));
info.setContext(rs.getString("Context"));
info.setCreatedTime(rs.getDate("CreatedTime"));
info.setGenreId(rs.getInt("GenreId"));
info.setGenreName(rs.getString("genreName"));
info.setVoteNumber(rs.getInt("voteNumber"));
}
conn.close();
return info;
}
/**
* 写入新小说
*
* @param info
* @return
*/
public int insert(NovelInfo info){
String sql="insert into novel(title,conText,createdTime,genreId,voteNumber)values";
sql=sql+"('"+info.getTitle()+"','"+info.getContext()+"',now(),'"+info.getGenreId()+"',"+info.getVoteNumber()+")";
int result=0;
System.out.println(sql);
result=conn.executeUpdate(sql);
conn.close();
return result;
}
/**
*更新小说
* @param info
* @return
*/
public int update(NovelInfo info){
String sql="update novel set "+" Title='"+info.getTitle()+"',Context='"+info.getContext()+"',"
+ "genreId='"+info.getGenreId()+"'where id="+info.getId()+"";
int result=0;
System.out.println(sql);
result=conn.executeUpdate(sql);
conn.close();
return result;
}
/**
* 删除小说
* @param id
* @return
*/
public int delete(int id){
String sql="delete from novel where id="+id+"";
int result=0;
result=conn.executeUpdate(sql);
conn.close();
return result;
}
/**
* 添加票数
* @return
*/
public int addVote(int num){
return 0;
}
}
小说评论展示,管理,添加,删除,更新的代码处理:
public class Comment {
Conn conn=new Conn();
/**
* 获取评论列表
* @return
* @throws SQLException
*/
public List<CommentInfo> getList() throws SQLException{
List<CommentInfo> list=new ArrayList<CommentInfo>();
String sql="select * from comment order by id desc";
ResultSet rs=conn.executeQuery(sql);
while(rs.next()){
CommentInfo info=new CommentInfo();
info.setId(rs.getInt("Id"));
info.setContext(rs.getString("Context"));
info.setNovelId(rs.getInt("NovelId"));
info.setCreatedTime(rs.getDate("CreatedTime"));
info.setReaderName(rs.getString("ReaderName"));
list.add(info);
System.out.print(list);
}
conn.close();
return list;
}
/**
*
* @param classId
* @return
* @throws SQLException
*/
public CommentInfo getCommentInfo(int id)throws SQLException{
CommentInfo info=new CommentInfo();
String sql="select * from Comment c where id="+id+"";
ResultSet rs=conn.executeQuery(sql);
while(rs.next()){
info.setId(rs.getInt("Id"));
info.setContext(rs.getString("Context"));
info.setNovelId(rs.getInt("NovelId"));
info.setCreatedTime(rs.getDate("CreatedTime"));
info.setReaderName(rs.getString("ReaderName"));
}
conn.close();
return info;
}
/**
* 获取某小说下的评论
* @param id
* @return
* @throws SQLException
*/
public List<CommentInfo> getListByNovelId(int novelid) throws SQLException{
List<CommentInfo> list=new ArrayList<CommentInfo>();
String sql="select * from comment where novelId="+novelid+" order by id desc";
ResultSet rs=conn.executeQuery(sql);
while(rs.next()){
CommentInfo info=new CommentInfo();
info.setId(rs.getInt("Id"));
info.setContext(rs.getString("Context"));
info.setNovelId(rs.getInt("NovelId"));
info.setCreatedTime(rs.getDate("CreatedTime"));
info.setReaderName(rs.getString("ReaderName"));
list.add(info);
}
conn.close();
return list;
}
/**
* 插入评论
* @param info
* @return
*/
public int insert(CommentInfo info){
String sql="insert into Comment(Context,CreatedTime,readerName,novelId)values";
sql=sql+"('"+info.getContext()+"',now(),'"+info.getReaderName()+"',"+info.getNovelId()+")";
int result=0;
System.out.println(sql);
result=conn.executeUpdate(sql);
conn.close();
return result;
}
/**
* 更新评论
* @param info
* @return
*/
public int update(CommentInfo info){
String sql="update Comment set "+" Context='"+info.getContext()+"',novelId='"+info.getNovelId()+"',"
+ "CreatedTime='"+info.getCreatedTime()+"',readerName='"+info.getReaderName()+"' where id="+info.getId()+"";
int result=0;
System.out.println(sql);
result=conn.executeUpdate(sql);
conn.close();
return result;
}
/**
* 删除评论
* @param id
* @return
*/
public int delete(int id){
String sql="delete from Comment where id="+id+"";
int result=0;
result=conn.executeUpdate(sql);
System.out.println(sql);
conn.close();
return result;
}
}
小说分类展示。更新。添加,删除的代码处理:
public class Genre {
Conn conn=new Conn();
/**
* 获取分类列表
* @return
* @throws SQLException
*/
public List<GenreInfo> getList()throws SQLException{
List<GenreInfo> list=new ArrayList<GenreInfo>();
String sql="select * from genre order by Sort asc";
ResultSet rs=conn.executeQuery(sql);
while(rs.next()){
GenreInfo info=new GenreInfo();
info.setId(rs.getInt("Id"));
info.setName(rs.getString("Name"));
info.setSort(rs.getInt("Sort"));
list.add(info);
}
conn.close();
return list;
}
/**
*
* @param id
* @return
* @throws SQLException
*/
public GenreInfo getGenreInfo(int id)throws SQLException{
GenreInfo info=new GenreInfo();
String sql="select * from genre g where id="+id+"";
ResultSet rs=conn.executeQuery(sql);
while(rs.next()){
info.setId(rs.getInt("Id"));
info.setName(rs.getString("Name"));
info.setSort(rs.getInt("Sort"));
}
conn.close();
return info;
}
/**
* 添加分类
* @param info
* @return
*/
public int insert(GenreInfo info){
String sql="insert into genre(Name,Sort) values";
sql=sql+"('"+info.getName()+"','"+info.getSort()+"')";
int result=0;
result=conn.executeUpdate(sql);
conn.close();
return result;
}
/**
* 更新分类
*
* @param info
* @return
*/
public int update(GenreInfo info){
String sql="update genre set "+" Name='"+info.getName()+"',Sort= '"+info.getSort()+"' where id="+info.getId()+"";
int result=0;
result=conn.executeUpdate(sql);
return result;
}
public int delete(int id){
String sql="delete from genre where id="+id+"";
int result=0;
result=conn.executeUpdate(sql);
conn.close();
return result;
}
}
前台主要页面展示:(略丑)
作者公布小说界面:
读者评论界面:
为小说投票,投票功能的前端设计代码:
function getElemensByClassName(className){ // 通过class获取
var classArr = new Array();
var tags = document.getElementsByTagName("*"); //获取全部节点
for(var item in tags){
if(tags[item].nodeType == 1){
if(tags[item].getAttribute("class") == className){
classArr.push(tags[item]); //收集class匹配的节点
}
}
}
return classArr;
}
function delete_FF(element){ // 在FireFox中删除子节点为空的元素
var childs = element.childNodes;
for(var i=0;i<childs.length;i++){
var pattern = /\s/; //模式匹配,内容为空
if(childs[i].nodeName == "#text" && pattern.test(childs[i].nodeValue)){ //处理
//alert(childs[i].nodeName);
element.removeChild(childs[i]); //删除FF中获取的空节点
}
}
}
function $(obj){return document.getElementById(obj);}
window.onload = function(){
onload1();
onload2();
};
function onload2(){
var persons = getElemensByClassName("person");
// alert(persons);
for(var item in persons){ //遍历全部person。为它们绑定投票事件
(function(_item){ //匿名函数传入item, 防止因作用域问题导致item总为最后一个
delete_FF(persons[_item]); //出去FF中空行代表的子节点
persons[_item].setAttribute("id","person"+(parseInt(_item)+1)); //赋上id
var childs = persons[_item].childNodes;
for(var i = 0;i<childs.length;i++){
//alert(childs[i].nodeName);
if(childs[i].nodeName == "BUTTON"){ //点击按钮投票
var oButton = childs[i];
}
if(childs[i].nodeName == "P"){ //投票结果更新
var oP = childs[i];
var oSpan = oP.getElementsByTagName("span")[0];
}
}
if(oButton != null){
oButton.onclick = function(){ //事件绑定
var num = oSpan.innerHTML; //获取票数
oSpan.innerHTML = (++num); //票数更新
// 这时一般我们可能就须要把这个票数num传送给server保存。更新时也是和server中的num同步
this.setAttribute("disabled","true"); // 一般仅仅能投票一次的吧
alert("投票成功,谢谢您的支持");
};
}
})(item); // 传入各项person
}
}
小说公布的实现细节,引入了wangEditor:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@page import="org.common.*" %>
<%@page import="org.model.*" %>
<%@page import="org.dal.*" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!-- 公布小说 -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>小说编辑公布界面</title>
<script type="text/javascript" src="js/jquery-1.10.1.js"></script>
<link rel="stylesheet" type="text/css" href="css/edit.css">
<link rel="stylesheet" type="text/css" href="css/wangEditor.min.css">
<script type="text/javascript" src="js/wangEditor.min.js"></script>
</head>
<body>
<%
request.setCharacterEncoding("utf-8");
Genre cls=new Genre();
List<GenreInfo>list=cls.getList();
Novel novel=new Novel();
NovelInfo ninfo=new NovelInfo();
if("fabu".equals(request.getParameter("action")))
{
ninfo.setTitle(request.getParameter("txtTitle"));
ninfo.setContext(request.getParameter("content"));
ninfo.setGenreId(DataConverter.toInt(request.getParameter("selClass")));
novel.insert(ninfo);
out.println("<script>alert('公布成功');</script>");
}
%>
<div class="header">
<h2>当前位置:小说编辑</h2>
</div>
<a class="wel">欢迎您:<%=Utilty.readCookie(request, "user")%></a>
<div class="context" >
<form id="form1" name="form1" method="post"
action="novel/novel-edit.jsp?action=fabu" οnsubmit="return check(this)">
<table>
<tr>
<td>小说所属分类:</td>
<td>
<select name="selClass" id="selClass" style="width:300px;height:30px;">
<%
for(GenreInfo cinfo:list){
%>
<option value="<%=cinfo.getId() %>">
<%if(cinfo.getId()==ninfo.getId()) %>
<%=cinfo.getName() %></option>
<%
}
%>
</select>
</td>
</tr>
<tr>
<td>小 说 标 题:</td>
<td><input type="text" name="txtTitle" id="txtTitle" style="width:500px;height:30px"/></td>
</tr>
<tr>
<td>小 说 内 容:</td>
<td style="width:1000px;"><textarea rows="25" name="content" id="content"></textarea> </td>
</tr>
<tr>
<td colspan="2" class="inp">
<input class="submit" type="submit" name="button" id="button" value="提交" style="color:#FFFFFF"/>
<input class="submit" type="reset" name="button2" id="button2" value="重置"style="color:#FFFFFF" />
</td>
</tr>
</table>
</form>
</div>
</body>
<script type="text/javascript">
var editor = new wangEditor('content');
editor.config.menus = [
'bold',
'underline',
'italic',
'strikethrough',
'eraser',
'forecolor',
'bgcolor',
'|',
'link',
'unlink',
'table',
'emotion',
'|',
'img',
'video',
'location',
];
editor.create();
</script>
</html>
好吧,因为代码段较多,这里不能一一介绍,兴许直接介绍一下这个项目开发过程中的错误细节,完好之后把源代码上传到资源那里,这个项目实现起来较简单,童鞋们能够依据设计要求试试身手哦!
源代码地址:https://github.com/guodalin8/novel