前言
此项目可以作为正在学习阶段处于JavaWeb阶段的小伙伴,综合了JavaWeb阶段的所有知识。
注意: 该项目只实现了部分功能,并非成熟项目。如果处于学习阶段的你,正合适不过。我将婚礼汇的项目资源放在GitHub上,供大家参考!还请多多指教!
👉GitHub地址: 婚礼汇项目资源下载
一、项目介绍
项目声明: 本项目作为JavaWeb学习阶段的综合练习,不属于成熟项目。所以在此项目中主要杂揉了Web阶段学过的知识,提供HTML页面和CSS样式,此次练习只需要完成指定功能即可(某些功能我没有写,大家有兴趣自行完成)!
项目名称: 婚礼汇
项目介绍: 基于JavaEE平台的系统,用于解决结婚、宴请、聚会、婚车、酒店预订、化妆等一系列服务。
二、需求分析
技术架构:
- 前端技术栈: HTML、CSS、JavaScript、jQuery、BoootStrap、Json等
- 后端技术栈: JDBC、Filter、Servlet、JSP、反射等
- 服务器: Tomact8.5
- 数据库: MySQL5.7
功能架构:
- 前端架构: 登录、注册、首页、酒店列表、酒店详情、套餐详情、加入购物车、购物车列表、商品结算
- 后端架构: 用户管理、购物车、酒店列表、酒店详情、套餐、酒店、场地详情等服务管理
项目架构: 使用基于MVC的三层架构设计、Servlet+JSP技术结构
三、数据模型分析与建立
数据关系模型分析:
用户与购物车一对多的关系,指的是一个用户可以在购物车中添加多个商品。
- 用户与购物车(一对多)
- 酒店与房间(一对多)
- 酒店与套餐(一对多)
- 酒店与详细信息(一对一)
- 图片与酒店、房间和套餐(分别为一对多)
数据库模型建立:
该数据库模型为物理数据模型。使用Navicet建立!
酒店、房间、套餐、酒店信息、图片数据的关系
用户、购物车关系
四、页面与功能
页面流程:
所需完成功能的如下:
功能描述:
- 会员注册
- 手机号、密码、短信验证码格式校验
- 阿里云短信服务实现手机号接收验证码并校验
- 将注册信息存储到数据库
- 注册成功跳转会员登录页面
- 注册失败返回注册失败的相关信息,跳转本注册页面
- 会员登录
- 手机号、密码、验证码格式校验
- 生成随机4位验证码并添加登录校验
- 查询数据库中会员信息进行会员登陆
- 登陆成功跳转首页
- 登录失败返回登录失败的相关信息提示,跳转登录页面
- 自动登录
- 登陆后默认7天内自动登录
- 酒店列表
- 使用分页查询动态显示酒店列表信息
- 酒店详情
- 动态显示点击后的酒店详情信息
- 套餐
- 点击套餐跳转套餐列表
- 可以根据套餐的类型、场地选择相应的套餐加入购物车
- 此处加入购物车操作的信息存储到数据库中对应表内
- 我的购物车
- 购物车内产生32位订单号并显示
- 显示当前加入购物车的服务器时间
- 单选、全选选中购物车内商品单选框实现总价变化
- 加减商品数量对数据库的商品数量做修改并动态显示总价
五、项目结构
项目所需第三方资源和jar包
下载地址:
- 阿里云短信服务
- aliyun-java-sdk-core-4.1.1
- aliyun-java-sdk-dysmsapi-1.1.0
- gson-2.8.6
- c3p0连接池
- c3p0-0.9.5.4
- mchange-commons-java-0.2.16
- BeanUtils
- commons-beanutils-1.9.4
- commons-collections-3.2.2
- commons-logging-1.2
- dbUtils
- commons-dbutils-1.6
- 文件上传与下载
- commons-fileupload-1.4
- commons-io-2.6
- Json转换工具Jackson
- jackson-annotations-2.9.8
- jackson-core-2.9.8
- jackson-databind-2.9.8
- JSTL标签库
- jstl
- standard
- IDEA Lombok插件
- lombok-1.18.10
- MySQL
- mysql-connector-java-5.1.25-bin
- bootstrapValidator
- bootstrap-3.3.7
- jquery-3.2.1.min
项目结构图
六、部分页面展示
七、核心代码与详解
该代码只是部分代码,也没有涉及到Service层与Dao层代码。如果有兴趣可以来下载此项目资源!
7.1 注册功能
注册页面使用bootstrapValidator的js校验
<script type="text/javascript">
var interval;
function sendSMS() {
/**
* 校验手机号,触发bootstrapValidator对手机号的校验
*/
//初始化bootstrapValidator对象
var validator = $("#myForm").data("bootstrapValidator");
validator.validateField("user_tel");
var flag = validator.isValidField("user_tel");
if (flag) {
interval = setInterval("showCount()", 1000);
$(".qrcode").attr("disabled", true);
/**
* 请求服务器发送验证码
*/
$.post("${pageContext.request.contextPath}/sms", {
"methodName": "sendSMS",
"phoneNum": $("#user_tel").val()
}, function (data) {
console.log(data);
}, "json");
}
}
var count = 30;
function showCount() {
$(".qrcode").text(count + " s");
count--;
if (count < -1) {
clearInterval(interval);
$(".qrcode").text("发送验证码");
count = 30;
$(".qrcode").attr("disabled", false);
}
}
$(function () {
$("#myForm").bootstrapValidator({
message: "this is no a valiad field",
fields: {
user_tel: {
message: "您输入的手机号有误",
validators: {
notEmpty: {
message: "手机号不能为空"
},
regexp: {
message: "您输入的手机号有误",
regexp: /^[1]{1}[1356789]{1}[0-9]{9}$/
}
}
},
user_password: {
message: "密码错误",
validators: {
notEmpty: {
message: "密码不能为空"
},
regexp: {
message: "密码长度为6~9位,由小写字母、数字组成",
regexp: /^[a-z0-9]{6,9}$/
},
different: {
message: "密码不能与手机号一致",
field: "user_tel"
}
}
},
qrCode: {
message: "验证码错误",
validators: {
notEmpty: {
message: "验证码不能为空"
},
stringLength: {
message: "验证码错误",
min: 4,
max: 4
}
}
}
}
});
})
</script>
<style type="text/css">
.qrcode {
background-color: white;
}
.user-form-wraps {
width: 380px;
}
.user-form-button {
width: 300px;
}
</style>
<div class="user-form-item"><strong class="user-form-title">会员注册</strong></div>
<form id="myForm" action="${pageContext.request.contextPath}/user">
<input type="hidden" name="methodName" value="register"/>
<div class="user-form-item form-group">
<input id="user_tel" name="user_tel" class="user-input" type="text" placeholder="手机号">
</div>
<div class="user-form-item form-group">
<input name="user_password" class="user-input" type="password"
placeholder="密码">
</div>
<div class="user-form-item form-group">
<input name="phoneCode" class="user-input user-input-adjust" type="text"
placeholder="短信验证码">
<button type="button" class="qrcode" onclick="sendSMS()">发送验证码</button>
</div>
<div class="user-form-item">
<label>
<input class="user-check" type="checkbox" checked="false" value="yes">
<span class="cos_span">
登录即表示同意<a class="keyword-blue-pale">《119婚庆网用户协议》</a>
</span>
</label>
</div>
<div class="user-form-item form-group">
<button type="submit" class="user-form-button" style="font-weight: bold;">立即注册</button>
</div>
</form>
<font color="red">${errorMsg}</font>
<div class="user-form-item us_text_right">
<span class="cos_span"><a href="${pageContext.request.contextPath}/center?methodName=toLogin">已有账户?立即登录</a></span>
</div>
注册页面Servlet代码
public String register(HttpServletRequest request, HttpServletResponse response) {
try {
/*
* 1.使用BeanUtils封装表单参数至User实体类(封装表单user_tel和user_password参数)
* 2.获取表单参数(表单验证码)
* 3.获取Session使用阿里云短信服务平台的所发手机号验证码
* 4.表单验证码与Session中验证码进行校验
*/
User user = new User();
BeanUtils.populate(user, request.getParameterMap());
String phoneCode = request.getParameter("phoneCode");
Object existPhoneCode = request.getSession().getAttribute("existPhoneCode");
/*
* 校验成功,则进行注册操作并重定向至登录页面
* 校验失败,则记录返回错误信息并请求转发至注册页面
*/
if (existPhoneCode.equals(phoneCode)) {
userService.register(user);
return "redirect:center?methodName=toLogin";
} else {
request.setAttribute("errorMsg", "验证码错误");
return "register.jsp";
}
} catch (IllegalAccessException | InvocationTargetException | SQLException e) {
e.printStackTrace();
}
/*
* 捕获用户名已存在数据库插入异常等,则返回错误信息
*/
request.setAttribute("errorMsg", "注册失败 用户名已存在");
return "register.jsp";
}
7.2 手机短信服务
- 手机验证码是由阿里云短信平台提供。
- 在阿里云短信平台开通服务
- 获取权限码(AccessKey)
- 获取短信服务签名
- 获取短信模板
- 导入jar包
- 使用SMSUtils工具类
- 配置sms.properties配置文件
SMSUtils工具类
package com.mylifes1110.java.utils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Random;
import javax.servlet.http.HttpSession;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
public class SmsUtil {
// 产品名称:云通信短信API产品,开发者无需替换
static final String product = "Dysmsapi";
// 产品域名,开发者无需替换
static final String domain = "dysmsapi.aliyuncs.com";
static final String accessKeyId;
static final String accessKeySecret;
static {
InputStream inputStream = SmsUtil.class.getClassLoader().getResourceAsStream("sms.properties");
Properties properties = new Properties();
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
accessKeyId = properties.getProperty("accessKeyId");
accessKeySecret = properties.getProperty("accessKeySecret");
}
/**
* 发送验证码
*
* @param session
* @return
* @throws ClientException
*/
public static SendSmsResponse sendSms(HttpSession session, String phoneNum) throws ClientException {
// 可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
// 初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
// 组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest();
// 必填:待发送手机号
request.setPhoneNumbers(phoneNum);
// 必填:短信签名-可在短信控制台中找到
request.setSignName("Ziph");
// 必填:短信模板-可在短信控制台中找到
request.setTemplateCode("SMS_190786715");
// 产生四位随机验证码
StringBuffer randomNum = new StringBuffer();
for (int i = 0; i < 4; i++) {
randomNum.append(new Random().nextInt(10));
}
request.setTemplateParam("{'code':" + randomNum + "}");
// 四位随机验证码存储到session
session.setAttribute("existPhoneCode", randomNum.toString());
// hint 此处可能会抛出异常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
return sendSmsResponse;
}
}
sms.properties
accessKeyId与ccessKeySecret填写自己的!
accessKeyId=LTAA4SDjx6A4FYdTBftaH2SS
accessKeySecret=yblECZFJHLW3Rti40UBNUXAleFftyWN
7.2 登录功能
该页面代码没有重要信息,贴出来好查看大概
<div class="user-form-item"><strong class="user-form-title">会员登录</strong></div>
<form action="${pageContext.request.contextPath}/user" method="post">
<input type="hidden" name="methodName" value="login">
<div class="user-form-item">
<input name="user_tel" class="user-input" type="text" max="15" maxlength="15" placeholder="手机号">
</div>
<div class="user-form-item">
<input name="user_password" class="user-input" type="password" max="15" maxlength="15"
placeholder="密码">
</div>
<div class="user-form-item">
<input name="qrCode" class="user-input user-input-adjust" type="text" max="4" placeholder="验证码"><img
onclick="$('.qrcode').attr('src', '${pageContext.request.contextPath}/center?methodName=validateCode&' + Math.random())"
class="qrcode" style="cursor:pointer"
src="${pageContext.request.contextPath}/center?methodName=validateCode">
</div>
<font color="red">${errorMsg}</font>
<div class="user-form-item">
<label>
<input class="user-check" type="checkbox" checked="false" value="yes">
<span class="cos_span">
登录即表示同意<a class="keyword-blue-pale">《119婚庆网用户协议》</a>
</span>
</label>
</div>
<div class="user-form-item">
<button type="submit" class="user-form-button" style="font-weight: bold;">登 录
</button>
</div>
<div class="user-form-item us_text_right">
<span class="cos_span"><a>忘记密码?</a></span> <span
class="cos_span_empty"><a href="${pageContext.request.contextPath}/center?methodName=toRegister">注册账号</a></span>
</div>
登录功能Servlet代码
public String login(HttpServletRequest request, HttpServletResponse response) throws SQLException {
try {
User user = new User();
BeanUtils.populate(user, request.getParameterMap());
// 获取表单内输入的验证码
String qrCode = request.getParameter("qrCode");
// 获取Session中的验证码
Object existQrCode = request.getSession().getAttribute("key");
/*
* 校验验证码
* 成功,则跳转首页
* 失败,则跳转登录页面
*/
if (existQrCode != null) {
if (existQrCode.equals(qrCode))