icanci version 1.10 文末底部有源码链接,和我自己的QQ会话链接 图片看不清楚可以放大浏览器页面
关键词 MVC JavaEE 博客
项目演示地址 建议使用PC访问 因为没有做手机兼容 http://www.jwebblog.icanci.cn/
开发环境
1.Windows 10 操作系统 i5处理器 RAM 16GB 64位操作系统
2.使用Hbuilder完成前端静态页面 并且为后端提供接接口
3.使用 eclipse 集成开发工具
4.基于 JDK8 开发
5.使用 Mysql5.x 开发
6.使用 Tomcat7.x 开发
使用到的jar包
email 邮件发送包
1.activation.jar
2.javax.mail.jar
3.commons-email-1.5.jar
fileupload 文件上传包
1.commons-fileupload-1.2.2.jar
2.commons-io-1.4.jar
druid 连接池
1.druid-1.1.16.jar
lombok 包 作用:极简JavaBean代码
1.lombok.jar
@简单粗暴节省JavaBean代码插件 Lombok.jar
mysql 连接包
1.mysql-connector-java-5.1.29.jar
操作EL表达式和JSP以及JSTL包 以及servlet包
1.el-api.jar
2.jsp-api.jar
3.servlet-api.jar
4.taglibs-standard-impl-1.2.5.jar
5.taglibs-standard-spec-1.2.5.jar
6.standard.jar
Apache封装的工具类
1.standard.jar
如果有些包在导入之后不见了,那可能是创建web项目的时候引入了Tomcat的lib 此时会有合并现象
需求分析
引言
本文对系统进行需求分析,系统按照功能逻辑被分为前台功能和后台和功能两个部分。前台功能主要包括用户信息、查询好友、博客内容、博客查询等功能。后台功能主要是对前展示内容的管理,包括用户的注册与登录、用户个人信息资料的变更、已发布博客的删除和新增博客的发布。
系统总体流程
普通用户用例图
管理员用户图
数据需求
输人输出要求
输入要求:界面所有的非固定性数据都来自用户所以在用户输入的时候,首先会在前端界面进行JavaScript脚本的检验,只有通过所有检验的用户数据才会传输到后台,在后台进行第二次检验,对数据内容的合法性进行验证,验证通过之后才传递到数据库,会在数据库进行第三次验证。都验证通过之后才会传递到数据库。
输出要求:系统会提供用户数据的展示。系统会在特定的位置将用户的数据展示出来。因为网站的大部分数据来自用户,所以输出的数据也来自用户。
安全保密性要求
对用户的密码进行后台数据库MD5算法加密保护,除了用户知道自己密码,其他人不知道用户密码。
系统架构设计
架构设计
架构设计的重点在于根据系统的不同视角和对质量的要求,设计出系统的不同结构,通常最主要的结构是将系统分层并产生层次内的模块,阐明模块之间的关系.
概要设计
确定模块结构或类结构,划分功能模块,将软件功能需求分配给所划分的最小单元模块。确定模块之间的联系,确定数据结构、文件结构、数据库模式,确定测试方法与策略。
详细设计
详细设计是根据概要设计提供的文档,确定每一个模块的算法,内部的数据组织,选定工具清晰表达算法,编写详细设计说明书和详细测试用例与计划。详细设计的重点在将模块的对象分解为属性和方法,并阐述如何实现。
DAO层
自定义Util类 logs类 JDBC模板类 exception类
Test单元测试 和 过滤器
应用逻辑层
数据库设计
数据库设计是系统设计的重要环节,数据要求和数据结构最终都要和数据库的表结构对应起来。数据库设计过程:
概要设计(ER图)-> 逻辑结构设计(设计关系模式并规范化)-> 物理设计(建表)
每一个JavaBean 对应数据库的一张表
实例:
User :
Long id;
String username;
String password;
String email;
Long jointime;
String headimage;
String sex;
Long age;
String personalizedSignature;
User表:
字段名称 数据类型 说明
id bigint(11) 自动递增
username varchar(20) 用户名
password varchar(80) 密码
email varchar(25) 电子邮件
jointime bigint(20) 加入小璨日期
headimage varchar(200) 头像
sex varchar(10) 性别
age bigint(5) 年龄
personalizedSignature varchar(255) 个性签名
用户界面设计
用户界面设计的目的是让用户易于使用,做到界面布局合理,美观大方,字体大小合适,色彩搭配合理。
系统实现
数据库实现
表1-1 数据库表设计说明
数据表 中文名 说明
admin 管理员表 存储管理员信息,包括用户名,密码,申请管理员的code
friendrelationship 好友关系表 存储互为好友的用户ID字段
user 用户信息表 存储注册用户的信息,包括用户名、密码、email、加入时间、头像、性别、年龄、个性签名
userfeedback 反馈信息表 存储用户反馈的信息,包括反馈主题、反馈内容、反馈时间等字段
uesrspace 发表博客表 存储已发布博客的具体信息,包括博客内容、上传图片、发表时间等字段以及相关外键字段
系统中的每个数据表的详细说明如下:
(1) admin表
申请管理员的信息
字段名称 数据类型 说明
id bigint(20) 自动递增 主键
username varchar(20) 用户名
password varchar(100) 密码
joinCode varchar(50) 申请管理员的code
(2) friendrelationship表
好友之间的id关系表,通过外键建立两个用户之间的好友关系
字段名称 数据类型 说明
id bigint(11) 自动递增 主键
userId1 bigint(11) 注册用户1
userId2 bigint(11) 注册用户2
(3) user表
用户的信息
字段名称 数据类型 说明
id bigint(11) 自动递增 主键
username varchar(20) 用户名
password varchar(80) 密码
email varchar(25) 电子邮件
jointime bigint(20) 加入小璨日期
headimage varchar(200) 头像
sex varchar(10) 性别
age bigint(5) 年龄
personalizedSignature varchar(255) 个性签名
(4) userfeedback表
用户反馈的信息
字段名称 数据类型 说明
id bigint(11) 自动递增 主键
feedbackTheme varchar(255) 反馈信息主题
feedbackContent varchar(255) 反馈信息内容
feedbackTime bigint(11) 反馈信息的时间(毫秒)
outPrint varchar(30) 数据库存储的反馈信息时间转换为自定义的日期显示
(5) userspace表
用户发表博客表
字段名称 数据类型 说明
id bigint(11) 自动递增 主键
uesrId bigint(11) 用户id
userMessagea varchar(612) 发表博客信息
userImage varchar(255) 博客配图
outputTime bigint(11) 发表时间(毫秒)
outPrint varchar(30) 数据库存储的发表时间转换为自定义的日期显示
系统具体功能实现
本项目分为了前端部分和后端部分两个工程。
用户注册 用户修改个人信息 用户修改密码 用户发表小璨
用户查询已经发表的小璨 用户删除小璨
用户意见反馈
用户查询好友
用户联系客服 通过连接唤起QQ会话客服
系统核心代码
分页核心代码:PageResult.java
package cn.icanci.page;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
//分页的结果对象
@SuppressWarnings("all")
@Getter
public class PageResult {
private List> listData; // 结果集数据,通过SQL查询
private Integer totalCount; // 结果总数,通过SQL查询
private Integer currentPage = 1; // 当前页 用户传入
private Integer pageSize = 5; // 每一页条数 用户传入
private Integer beginPage = 1;// 首页
private Integer prevPage;// 上页 计算
private Integer nextPage;// 下页 计算
private Integer totalPage;// 末页 计算
public PageResult(List> listData, Integer totalCount, Integer currentPage, Integer pageSize) {
super();
this.listData = listData;
this.totalCount = totalCount;
this.currentPage = currentPage;
this.pageSize = pageSize;
this.totalPage = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
this.prevPage = currentPage - 1 >= 1 ? currentPage - 1 : 1;
this.nextPage = currentPage + 1 <= totalPage ? currentPage + 1 : totalPage;
}
}
JDBC核心代码1:JDBCUtil.java JDBC工具类
package cn.icanci.util;
import java.sql.ResultSet;
import java.sql.PreparedStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class JDBCUtil {
private static Properties p = new Properties();
private static DataSource ds =null;
//加载驱动 连接数据库 定义PreparedStatement 语句 执行sql语句 关闭连接
static {
try {
//从本地的class路径寻找配置文件的资源
p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(p);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接 加载驱动在druid连接池就已经完成了
* @return 返回 连接对象
*/
public static Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* 把连接还给连接池
* @param conn Connection 对象
* @param stmt PreparedStatement 对象
* @param rs ResultSet 对象
*/
public static void close(Connection conn,PreparedStatement stmt,ResultSet rs) {
try {
if(rs!=null) {
rs.close();
}
}catch(Exception e) {
}finally {
try {
if(stmt!=null) {
stmt.close();
}
}catch(Exception e) {
}finally {
try {
if(conn!=null) {
conn.close();
}
}catch(Exception e) {
}
}
}
}
}
JDBC核心代码2:JDBCTemplate.java 操作JDBC的模板类
package cn.icanci.template;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Connection;
import cn.icanci.dao.IResultSetHander;
import cn.icanci.util.JDBCUtil;
public class JDBCTemplate {
/**
* 重构DML操作
*
* @param sql sql语句 带有占位符
* @param objects Object数组
* @return 返回影响的行数
*/
public static int update(String sql, Object... objects) {
Connection conn = null;
PreparedStatement stmt = null;
conn = JDBCUtil.getConnection();
try {
stmt = conn.prepareStatement(sql);
for (int index = 0; index < objects.length; index++) {
stmt.setObject(index + 1, objects[index]);
}
return stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtil.close(conn, stmt, null);
}
return 0;
}
/**
* 查询操作
*
* @param 查询的JavaBean的类型
* @param sql 需要执行的sql语句
* @param rsh 顶级接口 IResultSetHander 的接口实现
* @param objects 不定长参数数组 用来填充预编译语句的?
* @return 返回一个结果集
*/
public static T query(String sql, IResultSetHander rsh, Object... objects) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
conn = JDBCUtil.getConnection();
try {
stmt = conn.prepareStatement(sql);
System.out.println(sql);
for (int index = 0; index < objects.length; index++) {
// 匹配到 like
if (sql.contains("like") && index == 0) {
Object obj = "%" + objects[index] + "%";
stmt.setObject(index + 1, obj);
} else {
stmt.setObject(index + 1, objects[index]);
}
}
rs = stmt.executeQuery();
return rsh.hander(rs);
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(conn, stmt, rs);
}
throw new RuntimeException("查询有异常");
}
public static T query(String sql, IResultSetHander rsh, Long... userIds) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
conn = JDBCUtil.getConnection();
try {
stmt = conn.prepareStatement(sql);
for (int index = 0; index < userIds.length; index++) {
stmt.setObject(index + 1, userIds[index]);
}
rs = stmt.executeQuery();
return rsh.hander(rs);
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(conn, stmt, rs);
}
throw new RuntimeException("查询有异常");
}
}
文件上传核心代码:FileUtil.java
package cn.icanci.util;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
import cn.icanci.exception.LogicException;
public class FileUtil {
// 可以接收的图片类型
private static final String UPLOAD_IMAGES_TYPE = "bmp,jpg,png,tif,gif,pcx,tga,exif,fpx,svg,psd,cdr,pcd,dxf,ufo,eps,ai,raw,WMF,webp";
@SuppressWarnings({ "unused", "unchecked" })
public static void upload(HttpServletRequest req, Map map) throws ServletException, IOException {
req.setCharacterEncoding(StaticResources.encoding);
try {
// 解析和检查请求 方式是不是POST 请求编码是不是 ....
boolean isMultipart = ServletFileUpload.isMultipartContent(req);
// 创建FileItemFactory对象
DiskFileItemFactory factory = new DiskFileItemFactory();
// 缓存大小 默认值是10k
factory.setSizeThreshold(1024 * 200); // 20kb
// 创建文件上传的处理器
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置最大上传文件大小为 7M
upload.setFileSizeMax(1024 * 1024 * 7);
List items = upload.parseRequest(req);
for (FileItem fileItem : items) {
String fileName = fileItem.getFieldName();
if (fileItem.isFormField()) {
// 普通表单控件 获取普通表单控件的参数值
map.put(fileName, fileItem.getString(StaticResources.encoding));
} else {
// 当前文件的MIME类型
// req.getServletContext().getMimeType(fileItem.getName());
// 上传文件的拓展名
String ext = FilenameUtils.getExtension(fileItem.getName());
// 判断文件类型
String[] type = UPLOAD_IMAGES_TYPE.split(",");
// 当前上传的文件不支持
if (!Arrays.asList(type).contains(ext)) {
throw new LogicException("图片类型不支持");
}
// 上传表单控件 把二进制数据写到哪一个文件中
String dir = req.getServletContext().getRealPath("/headImagesUploadedByUsers");
String uuidName = UUID.randomUUID().toString() + "."
+ FilenameUtils.getExtension(fileItem.getName());
System.out.println("文件上传之前");
fileItem.write(new File(dir, uuidName));
System.out.println("文件上传之后");
System.out.println(fileName + " put 名字");
System.out.println("/headImagesUploadedByUsers/" + uuidName);
map.put(fileName, "/headImagesUploadedByUsers/" + uuidName);
}
}
} catch (LogicException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
}
}
邮件发送核心代码:EmailUtil.java
package cn.icanci.util;
import java.util.Random;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import cn.icanci.dao.IUserDao;
import cn.icanci.dao.impl.UserDaoImpl;
import cn.icanci.domain.User;
public class EmailUtil {
/**
* 生成6位数 验证
*
* @return 返回验证码 code
*/
public static String getRandomCode() {
String code = "";
Random rd = new Random();
for (int i = 0; i < 6; i++) {
int r = rd.nextInt(10); // 每次随机出一个数字(0-9)
code = code + r; // 把每次随机出的数字拼在一起
}
System.out.println(code);
return code;
}
/**
* 向邮件发送验证码
*
* @param email 需要发送的邮箱地址
* @param randomCode 需要发送的code值
*/
public static void sendEmail(String email, String randomCode) {
IUserDao userDao = new UserDaoImpl();
User user = new User();
String sendMessage = "登陆";
HtmlEmail send = new HtmlEmail();// 创建一个HtmlEmail实例对象
// 获取随机验证码
String resultCode = randomCode;
// ---------------------------------------------------------
// 在这里判断数据库是否存在该账户,用来发送不同的验证码
user = userDao.getUserByEmail(email);
User u = userDao.isRoot(user);
if (u != null) {
// user存在 那就是登陆
sendMessage = "登陆";
} else {
// user不存在 那就是注册
sendMessage = "注册";
}
// ------------------------------------------------------
try {
send.setHostName("smtp.163.com");
send.setAuthentication("[email protected]", "icancixswl"); // 第一个参数是发送者的 163 Eamil邮箱 第二个参数是授权码
send.setFrom("[email protected]", "璨詞文化");// 发送人的邮箱为自己的,用户名可以随便填 记得是自己的邮箱
send.setSmtpPort(587); // 端口号 必须开
send.setSSLOnConnect(true); // 开启SSL加密
send.setCharset("utf-8");
send.addTo(email); // 设置收件人 email为你要发送给谁的邮箱账户 上方参数
send.setSubject("欢迎" + sendMessage + " ICANCI 官网"); // 邮箱标题
send.setMsg("【璨詞文化】你正在" + sendMessage + " ICANCI 官网,验证码: " + resultCode
+ " 验证码十分钟之内有效。 转发可能导致帐号被盗。如果这不是你本人操作请忽略..."); // Eamil发送的内容
send.send(); // 发送
System.out.println("发送成功");
} catch (EmailException e) {
e.printStackTrace();
}
}
}
核心 配置文件:web.xml
encoding
UTF-8
404
/errorPages/404.html
500
/errorPages/500.html
Exception
/errorPages/exception.html
EncodingFilter
cn.icanci.web.filter.EncodingFilter
EncodingFilter
/*
MaxSessionLifeFilter
cn.icanci.web.filter.MaxSessionLifeFilter
MaxSessionLifeFilter
/*
JSP操作Session的核心代码
代码块1 用户博客主页读取数据核心代码:index.jsp
代码块2 用户主页页面分页核心代码:index.jsp