前置知识
在本项目中使用了vue.js对js对象进行操作,并结合axiox封装的ajax实现异步页面局部进行动态实现,通过json串的形式实现了前后端的数据交互,并使用了Thymeleaf模板引擎对页面进行了动态的渲染。
使用的工具类包有:
1.优化后的servlet:也就是使用反射的方式动态获取并执行url传过来的地址
public class BaseServlet extends ViewBaseServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String methodName = req.getParameter("method");
try {
Method method = this.getClass().getDeclaredMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
method.setAccessible(true);
method.invoke(this,req,resp);
} catch (Exception e) {
System.out.println("反射方法有误,请检查:"+methodName);
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
2.vo统一数据返回形式的封装类:
public class SysResult {
private Boolean flag;
private String msg;
private Object data;
/**
* 为了方便重载一些方法
*/
public static SysResult fail(){
return new SysResult(false,"业务调用失败",null);
}
public static SysResult success(){
return new SysResult(true,"业务调用成功!",null);
}
//重载方法时 参数不要耦合,否则必定bug!!!
public static SysResult success(Object data){
return new SysResult(true,"业务调用成功!",data);
}
public static SysResult success(String msg,Object data){
return new SysResult(true,msg,data);
}
public SysResult() {
}
public SysResult(Boolean flag, String msg, Object data) {
this.flag = flag;
this.msg = msg;
this.data = data;
}
public Boolean getFlag() {
return flag;
}
public void setFlag(Boolean flag) {
this.flag = flag;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
3.获取数据库连接的工具类
*
* 获取连接或释放连接的工具类
*/
public class JDBCTools {
// 1、创建数据源,即连接池
private static DataSource dataSource;
// 2、创建ThreadLocal对象
private static ThreadLocal<Connection> threadLocal;
static {
try {
//1、读取druip.properties文件
Properties pro = new Properties();
pro.load(JDBCTools.class.getClassLoader().getResourceAsStream("druid.properties"));
//2、连接连接池
dataSource = DruidDataSourceFactory.createDataSource(pro);
//3、创建线程池
threadLocal = new ThreadLocal<>();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接的方法
* 后续知识: 数据库事务操作 必须使用同一个数据库连接
* @return
* @throws SQLException
*/
public static Connection getConnection() {
// 从线程中获取连接 threadLocal可以保证线程安全
Connection connection = threadLocal.get();
if (connection == null) {
// 从连接池中获取一个连接
try {
connection = dataSource.getConnection();
// 将连接与当前线程绑定
threadLocal.set(connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return connection;
}
/**
* 释放连接的方法
*
*/
public static void releaseConnection() {
// 获取当前线程中的连接
Connection connection = threadLocal.get();
if (connection != null) {
try {
connection.close();
// 将已经关闭的连接从当前线程中移除
threadLocal.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4.针对password加密的MD5加密工具类:
/**
* 针对明文字符串执行MD5加密
* @param source
* @return
*/
public static String encode(String source) {
// 1.判断明文字符串是否有效
if (source == null || "".equals(source)) {
throw new RuntimeException("用于加密的明文不可为空");
}
// 2.声明算法名称
String algorithm = "md5";
// 3.获取MessageDigest对象
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 4.获取明文字符串对应的字节数组
byte[] input = source.getBytes();
// 5.执行加密
byte[] output = messageDigest.digest(input);
// 6.创建BigInteger对象
int signum = 1;
BigInteger bigInteger = new BigInteger(signum, output);
// 7.按照16进制将bigInteger的值转换为字符串
int radix = 16;
String encoded = bigInteger.toString(radix).toUpperCase();
return encoded;
}
}
5.Thymeleaf模板引擎设置前后缀的工具类:
public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
//使用模版引擎需要实例化模版引擎对象 并且设定前缀和后缀
@Override
public void init() throws ServletException {
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
//使用模版引擎 转发到页面 并且渲染数据
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}
6.为了将index.html放入WEB-INF中目的时为了使用Thymeleaf模板引擎动态渲染页面所以将单独写一个servet加载首页
@WebServlet("/index.html")
public class IndexServlet extends ViewBaseServlet{
BookService bookService = new BookServiceImpl();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List<Book> bookList = bookService.findbookService();
req.setAttribute("book",bookList);
this.processTemplate("index",req,resp);
}
}
登录,注册
前端页面
<!--登录页面-->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>会员登录页面</title>
<base th:href="@{/}" >
<link type="text/css" rel="stylesheet" href="static/css/style.css" />
<!--1. 导入vue.js文件-->
<script src="static/script/vue.js"></script>
<script src="static/script/axios.min.js"></script>
</head>
<body>
<div id="app">
<div id="login_header">
<a href="index.html">
<img class="logo_img" alt="" src="static/img/logo.gif" />
</a>
</div>
<div class="login_banner">
<div id="l_content">
<span class="login_word">欢迎登录</span>
</div>
<div id="content">
<div class="login_form">
<div class="login_box">
<div class="tit">
<h1>会员</h1>
</div>
<div class="msg_cont">
<b></b>
<span class="errorMsg" v-text="msg"></span>
</div>
<div class="form">
<!--<form action="login_success.html" >-->
<form action="user?method=login" method="post">
<label>用户名称:</label>
<input
class="itxt"
type="text"
placeholder="admin"
autocomplete="off"
tabindex="1"
name="username"
id="username"
v-model="username"
/>
<br />
<br />
<label>用户密码:</label>
<input
class="itxt"
type="password"
placeholder="请输入密码"
autocomplete="off"
tabindex="1"
name="password"
id="password"
v-model="password"
/>
<br />
<br />
<input type="submit" value="登录" id="sub_btn" @click="login"/>
</form>
<div class="tit">
<a href="user?method=toregist">立即注册</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="bottom">
<span>
书城.Copyright ©2015
</span>
</div>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
username: '',
password: '',
msg: "[[${msg == null ? '请输入用户名和密码' : msg}]]" //在vue中嵌套thymeleleaf使用目的解决thymeleaf给的msg的提示信息和vue中msg提示冲突问题
},
methods: {
login(){
if(this.username === ''){
this.msg = "请输入用户名"
//阻止事件提交
event.preventDefault()
}
if(this.password === ''){
this.msg = "请输入密码"
//阻止事件提交
event.preventDefault()
}
}
}
})
</script>
</body>
</html>
<!--注册页面-->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<base th:href="@{/}" >
<title>会员注册页面</title>
<link type="text/css" rel="stylesheet" href="static/css/style.css" />
<link rel="stylesheet" href="static/css/register.css" />
<script src="static/script/vue.js"></script>
<script src="static/script/axios.min.js"></script>
<style type="text/css">
.login_form {
height: 420px;
margin-top: 25px;
}
</style>
</head>
<body>
<div id="app">
<div id="login_header">
<a href="index.html">
<img class="logo_img" alt="" src="static/img/logo.gif" />
</a>
</div>
<div class="login_banner">
<div class="register_form">
<h1>注册会员</h1>
<!--<form action="regist_success.html" method="post">-->
<form action="user?method=regist" method="post">
<div class="form-item">
<div>
<label>用户名称:</label>
<input type="text" placeholder="请输入用户名" name="username" v-model="user.username" @blur="checkUsername"/>
</div>
<span class="errMess" v-text="msg.usernameErrorMsg"></span>
</div>
<div class="form-item">
<div>
<label>用户密码:</label>
<input type="password" placeholder="请输入密码" name="password" v-model="user.password" @blur="checkPassword"/>
</div>
<span class="errMess" v-text="msg.passwordErrorMsg"></span>
</div>
<div class="form-item">
<div>
<label>确认密码:</label>
<input type="password" placeholder="请输入确认密码" v-model="user.password2" @blur="checkPassword2"/>
</div>
<span class="errMess"v-text="msg.passwordErrorMsg2"></span>
</div>
<div class="form-item">
<div>
<label>用户邮箱:</label>
<input type="text" placeholder="请输入邮箱" name="email" v-model="user.email" @blur="checkEmail"/>
</div>
<span class="errMess" v-text="msg.emailErrorMsg"></span>
</div>
<div class="form-item">
<div>
<label>验证码:</label>
<div class="verify">
<input type="text" placeholder="请输入验证码" v-model="k_code" @blur="checkcode"/>
<a href="#">
<img :src="changekaptcha" alt="" height="40px" width="90px" @click.prevent.stop="changeka"/>
</a>
</div>
</div>
<span class="errMess" v-text="msg.codeMsg"></span>
</div>
<button class="btn" @click="regist">注册</button>
</form>
</div>
</div>
<div id="bottom">
<span>
尚硅谷书城.Copyright ©2015
</span>
</div>
</div>
<!-- 定义vue对象 -->
<script type="text/javascript">
const app = new Vue({
el: "#app",
data: {
msg: {
usernameErrorMsg: "用户名应为6~16位数字和字母组成",
passwordErrorMsg: "密码的长度至少为8位",
passwordErrorMsg2: "密码两次输入不一致",
emailErrorMsg: "请输入正确的邮箱格式",
codeMsg: "请输入正确的验证码"
},
user: {
username: '',
password: '',
password2: '',
email: ''
},
//定义全局表单提交标识符
flagArray : [0,0,0,0,0],
changekaptcha: "kaptcha",
k_code:""
},
methods: {
//利用正则对用户名进行校验
async checkUsername(){
//1.定义正则表达式语法
let usernameRege = /^[a-zA-Z0-9]{6,16}$/
let usernameFlag = usernameRege.test(this.user.username)
if(usernameFlag){
let {data:sysResult} = await axios.get("user?method=checkname&username="+this.user.username)
if(sysResult.flag){
this.msg.usernameErrorMsg = "√"
this.flagArray[0] = 1
}else{
this.msg.usernameErrorMsg = "用户名已存在,请重新输入"
this.flagArray[0] = 0
}
}else{
this.msg.usernameErrorMsg = "用户名应为6~16位数字和字母组成"
this.flagArray[0] = 0
}
},
//利用正则对密码进行校验
checkPassword(){
let passwordRege = /^.{8,}$/
let passwordFlag = passwordRege.test(this.user.password)
if(passwordFlag){
this.msg.passwordErrorMsg = "√"
this.flagArray[1] = 1
}else{
this.msg.passwordErrorMsg = "密码的长度至少为8位"
this.flagArray[1] = 0
}
},
checkPassword2(){
if(this.user.password === this.user.password2){
this.msg.passwordErrorMsg2 = "√"
this.flagArray[2] = 1
}else{
this.msg.passwordErrorMsg2 = "密码两次输入不一致"
this.flagArray[2] = 0
}
},
checkEmail(){
let emailRege = /^[a-zA-Z0-9_.-]+@([a-zA-Z0-9-]+[.]{1})+[a-zA-Z]+$/
if(emailRege.test(this.user.email)){
this.msg.emailErrorMsg = '√'
this.flagArray[3] = 1
}else{
this.msg.emailErrorMsg = "请输入正确的邮箱格式"
this.flagArray[3] = 0
}
},
regist(){
//再次校验密码是否相同即可.
this.checkPassword2()
let flagStr = this.flagArray.join(",")
if(flagStr !== "1,1,1,1,1"){
//应该阻止表单提交
event.preventDefault()
alert("表单校验不通过!!!")
}else{
alert("表单校验成功")
}
},
//验证码的点击变换利用kaptcha中的url地址不同验证码不同进行点击变换
changeka(){
//需要让src属性重新加载即可. 如果url地址变化了,则src重新加载
//规则需要添加一个可以变化的参数 首选时间
this.changekaptcha = "kaptcha?date=" + new Date();
},
async checkcode(){
let {data:sysResult} = await axios.get("user?method=checkkacode&code="+this.k_code)
if (sysResult.flag){
this.msg.codeMsg = "验证码正确"
this.flagArray[4] = 1
}else{
this.msg.codeMsg = "验证码输入有误"
this.flagArray[4] = 0
this.changeka()
}
}
}
})
</script>
</body>
</html>
servlet层主要代码解析:
@WebServlet("/user")
public class UserServlet extends BaseServlet{
private UserService userService = new UserServiceImpl();
private static final ObjectMapper MAPPER = new ObjectMapper();
//跳转到登录页面
public void tologin(HttpServletRequest req, HttpServletResponse resp) throws Exception {
this.processTemplate("user/login",req,resp);
}
//跳转到注册页面页面
public void toregist(HttpServletRequest req, HttpServletResponse resp) throws Exception {
this.processTemplate("user/regist",req,resp);
}
/*
* 注册
* */
public void regist(HttpServletRequest req, HttpServletResponse resp) throws Exception {
User user = new User();
//将res域中的用户信息通过键值对的形式保存到user对象中
BeanUtils.populate(user,req.getParameterMap());
int row = userService.AddUserService(user);
if (row > 0){
//如果成功注册则重定向到登录界面,因为注册的业务已经结束所以使用重定向
resp.sendRedirect(req.getContextPath() + "/user?method=tologin");
}else{
//如果注册失败则继续转发到注册页面
this.processTemplate("user/regist",req,resp);
}
}
/*
* 登录
* */
public void login(HttpServletRequest req, HttpServletResponse resp) throws Exception {
User user = new User();
BeanUtils.populate(user,req.getParameterMap());
User userDB = userService.loginService(user);
if(userDB == null){
//如果没有查询到用户信息则继续转发到登录页面
req.setAttribute("msg","用户名或密码错误");
this.processTemplate("user/login",req,resp);
}
//如果查询到用户信息则登录成功重定向首页
req.getSession().setAttribute("user",userDB);
resp.sendRedirect(req.getContextPath() + "/index.html");
}
/*
* 注销用户登录信息*/
public void loginout(HttpServletRequest req, HttpServletResponse resp) throws Exception {
//清空sessions中的user信息
req.getSession().removeAttribute("user");
resp.sendRedirect(req.getContextPath() + "/index.html");
}
/*
* 在注册时用户名重复验证
* */
public void checkname(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String username = req.getParameter("username");
long count = userService.checkname(username);
if(count > 0){
SysResult sysResult = SysResult.fail();
String json = MAPPER.writeValueAsString(sysResult);
resp.getWriter().write(json);
}else {
SysResult sysResult = SysResult.success();
String json = MAPPER.writeValueAsString(sysResult);
resp.getWriter().write(json);
}
}
public void checkkacode(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String code = req.getParameter("code");
//从session域中通过 KAPTCHA_SESSION_KEY键将验证码获取到
String realCode = (String)req.getSession().getAttribute("KAPTCHA_SESSION_KEY");
if(realCode.equals(code)){
SysResult success = SysResult.success();
String json = MAPPER.writeValueAsString(success);
resp.getWriter().write(json);
}else {
SysResult fail = SysResult.fail();
String json = MAPPER.writeValueAsString(fail);
resp.getWriter().write(json);
}
}
}
service层:
public class UserServiceImpl implements UserService {
//准备持久层对象 面向接口编程
private UserDao userDao = new UserDaoImpl();
@Override
public int AddUserService(User user) {
user.setPassword(MD5Util.encode(user.getPassword()));
return userDao.addUserDao(user);
}
@Override
public User loginService(User user) {
user.setPassword(MD5Util.encode(user.getPassword()));
return userDao.loginDao(user);
}
@Override
public long checkname(String username) {
return userDao.checknameDao(username);
}
}
dao层业务
public class UserDaoImpl extends BaseDaoImpl implements UserDao {
//添加用户信息
@Override
public int addUserDao(User user) {
String sql = "insert into user values(null,?,?,?)";
return this.update(sql,user.getUsername(),user.getPassword(),user.getEmail());
}
//登录验证查询用户信息
@Override
public User loginDao(User user) {
String sql = "select * from user where username = ? and password = ?";
return this.getBean(User.class,sql,user.getUsername(),user.getPassword());
}
//用户名唯一性验证查询名字使用存在使用的是 count(*)
@Override
public long checknameDao(String username) {
String sql = "select count(*) from user where username = ?";
return (long) this.getValue(sql,username);
}
购物车功能
购物车页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<base th:href="@{/}">
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/cart.css" />
<script src="static/script/vue.js"></script>
<script src="static/script/axios.min.js"></script>
</head>
<body>
<div id="app">
<div class="header">
<div class="w">
<div class="header-left">
<a href="index.html">
<img src="static/img/logo.gif" alt=""
/></a>
<h1>我的购物车</h1>
</div>
<!-- <div class="header-right">
<h3>欢迎<span>张总</span>光临尚硅谷书城</h3>
<div class="order"><a href="order/order.html">我的订单</a></div>
<div class="destory"><a href="index.html">注销</a></div>
<div class="gohome">
<a href="index.html">返回</a>
</div>
</div>-->
<div th:replace="cart/head :: head_core">
</div>
</div>
</div>
<div class="list">
<div class="w">
<table>
<thead>
<tr>
<th>图片</th>
<th>商品名称</th>
<th>数量</th>
<th>单价</th>
<th>金额</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="cart in CartItermList">
<td>
<img :src="cart.book.imgPath" alt="" />
</td>
<td v-text="cart.book.title">活着</td>
<td>
<span class="count" @click="subcount(cart)">-</span>
<input class="count-num" type="number" value="1" v-model.number="cart.count" @change="updateCount(cart)"/>
<span class="count" @click="addcount(cart)">+</span>
</td>
<td v-text="cart.book.price">36.8</td>
<td v-text="cart.amount">36.8</td>
<td><a href="" @click.prevent="deletItem(cart.book.id)">删除</a></td>
</tr>
</tbody>
</table>
<div class="footer">
<div class="footer-left">
<a href="#" class="clear-cart" @click.prevent="clearCart">清空购物车</a>
<a href="#">继续购物</a>
</div>
<div class="footer-right">
<div>共<span v-text="totalCount">3</span>件商品</div>
<div class="total-price" >总金额<span v-text="totalAmount">99.9</span>元</div>
<a class="pay" href="checkout.html">去结账</a>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="w">
<div class="top">
<ul>
<li>
<a href="">
<img src="static/img/bottom1.png" alt="" />
<span>大咖级讲师亲自授课</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom.png" alt="" />
<span>课程为学员成长持续赋能</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom2.png" alt="" />
<span>学员真是情况大公开</span>
</a>
</li>
</ul>
</div>
<div class="content">
<dl>
<dt>关于尚硅谷</dt>
<dd>教育理念</dd>
<!-- <dd>名师团队</dd>
<dd>学员心声</dd> -->
</dl>
<dl>
<dt>资源下载</dt>
<dd>视频下载</dd>
<!-- <dd>资料下载</dd>
<dd>工具下载</dd> -->
</dl>
<dl>
<dt>加入我们</dt>
<dd>招聘岗位</dd>
<!-- <dd>岗位介绍</dd>
<dd>招贤纳师</dd> -->
</dl>
<dl>
<dt>联系我们</dt>
<dd>http://www.com.atguigu.com</dd>
<dd></dd>
</dl>
</div>
</div>
<div class="down">
尚硅谷书城.Copyright ©2015
</div>
</div>
</div>
<script>
const app = new Vue({
el:"#app",
data:{
CartItermList:[],
totalCount:0,
totalAmount:0
},
methods:{
//从session 域中获取到购物车信息
async getCartList(){
let {data:res} = await axios.get("cart?method=getCartList")
if(res.flag){
this.CartItermList = res.data.cartItems
this.totalCount =res.data.totalCount
this.totalAmount = res.data.totalAmount
}else{
this.CartItermList = [],
this.totalCount = 0,
this.totalAmount = 0
}
},
//清空购物车
async clearCart(){
let flag= confirm("确定要清空嘛?")
if(!flag){
return
}
let {data:sys} = await axios.get("cart?method=clearCart")
if(sys.flag){
this.getCartList()
}else{
alert("清空失败")
}
},
//删除按钮 删除一条购物车记录
async deletItem(bookid){
let {data:sys} = await axios.get("cart?method=deleteByid&id="+bookid)
if(sys.flag){
this.getCartList()
}else {
alert("删除失败")
}
},
//更改图书数量
async updateCount(cart){
let params ={method:"updatecount",id:cart.book.id,count:cart.count}
let {data:res} = await axios.get("cart",{params})
if(res.flag){
this.getCartList()
}else {
alert("修改失败")
}
},
//点击加号够触发
addcount(cart){
cart.count++
this.updateCount(cart)
},
//点击减号后触发
subcount(cart){
if(cart.count > 1){
cart.count--
this.updateCount(cart)
}else {
this.deletItem(cart.book.id)
}
}
},
//根据vue的声明周期页面一加载就从session域中获取到购物车信息
created(){
this.getCartList()
}
})
</script>
</body>
</html>
购物车的servlet代码
@WebServlet("/cart")
public class cartServlet extends BaseServlet {
private BookService bookService = new BookServiceImpl();
private static final ObjectMapper MAPPER = new ObjectMapper();
//点击购物车跳转到购物车页面
public void toCart(HttpServletRequest req, HttpServletResponse resp) throws Exception {
this.processTemplate("cart/cart",req,resp);
}
//当点击添加购物车时将一条数据添加到cart中
public void addCart(HttpServletRequest req, HttpServletResponse resp) throws Exception {
int bookId =Integer.parseInt(req.getParameter("bookid"));
Book book = bookService.getBook(bookId);
Cart cart =getSessionCart(req,req);
cart.addCart(book);
Long count = cart.getTotalCount();
SysResult success = SysResult.success(count);
String json = MAPPER.writeValueAsString(success);
resp.getWriter().write(json);
}
//获取判断session中是否有购物车
private Cart getSessionCart(HttpServletRequest req, HttpServletRequest req1) {
Cart cart = (Cart) req.getSession().getAttribute("cart");
if (cart == null) {
cart = new Cart();
req.getSession().setAttribute("cart", cart);
}
return cart;
}
//获取到session中的cart中的所有数据
public void getCartList(HttpServletRequest req, HttpServletResponse resp) throws Exception {
Cart cart = (Cart) req.getSession().getAttribute("cart");
if(cart == null){
SysResult fail = SysResult.fail();
String json = MAPPER.writeValueAsString(fail);
resp.getWriter().write(json);
}else {
SysResult success = SysResult.success(cart);
String json = MAPPER.writeValueAsString(success);
resp.getWriter().write(json);
}
}
//清空session域中的cart数据
public void clearCart(HttpServletRequest req, HttpServletResponse resp) throws Exception {
req.getSession().removeAttribute("cart");
SysResult success = SysResult.success();
String json = MAPPER.writeValueAsString(success);
resp.getWriter().write(json);
}
//根据id删除掉一条CartItem记录
public void deleteByid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
Cart cart = (Cart) req.getSession().getAttribute("cart");
int id = Integer.parseInt(req.getParameter("id"));
cart.deleteitemByid(id);
SysResult success = SysResult.success();
String json = MAPPER.writeValueAsString(success);
resp.getWriter().write(json);
}
//通过手动修改购物车中的图书数量
public void updatecount(HttpServletRequest req, HttpServletResponse resp) throws Exception {
int id = Integer.parseInt(req.getParameter("id"));
long count =Integer.parseInt(req.getParameter("count"));
Cart cart = (Cart) req.getSession().getAttribute("cart");
cart.updatecount(id,count);
SysResult success = SysResult.success();
String json = MAPPER.writeValueAsString(success);
resp.getWriter().write(json);
}
}
CartItem类
public class CartItem implements Serializable {
private Book book; //购物项图书
private Long count; //购物数量
private Double amount; //购物项总价 不能由用户手动输入,应该由程序计算
public CartItem() {
}
//说明金额是计算得到的结果,不能由用户手动录入
public CartItem(Book book, Long count) {
this.book = book;
this.count = count;
this.amount = book.getPrice() * count;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
this.amount = book.getPrice() * count;
}
public Long getCount() {
return count;
}
public void setCount(Long count) {
this.count = count;
this.amount = book.getPrice() * count;
}
public Double getAmount() {
return amount;
}
@Override
public String toString() {
return "CartItem{" +
"book=" + book +
", count=" + count +
", amount=" + amount +
'}';
}
}
Cart类
public class Cart implements Serializable {
//key book.id
private Map<Integer,CartItem> map = new HashMap<>(); //map集合类似于数据库
private Long totalCount; //购物车的总数量
private Double totalAmount; //购物车的总价格
private Collection cartItems; //为了页面获取数据方便 添加的属性
//想session域的购物车中添加数据判读购物车中是否有此件商品如果有则进行数量加一,
// 如果没有则将此购物项添加到购物车中的购物项集合中
public void addCart(Book book) {
if (map.containsKey(book.getId())){
CartItem cartItem = map.get(book.getId());
cartItem.setCount(cartItem.getCount()+ 1);
}else{
CartItem cartItem = new CartItem(book, 1l);
map.put(book.getId(),cartItem);
}
}
//1.获取总数量 2.为json串转化添加get方法!!!
public Long getTotalCount() {
Collection<CartItem> cartItems = getCartItems();
long count = 0;
for (CartItem cartItem : cartItems){
count += cartItem.getCount();
}
this.totalCount = count;
return totalCount;
}
public Double getTotalAmount() {
Collection<CartItem> cartItems = getCartItems();
BigDecimal pricebig = new BigDecimal(0+ "");
for (CartItem cartItem : cartItems){
BigDecimal bigDecimal = new BigDecimal(cartItem.getAmount() +"");
pricebig = pricebig.add(bigDecimal);
}
this.totalAmount = pricebig.doubleValue();
return totalAmount;
}
//1.获取集合 2.为JSON准备数据
public Collection<CartItem> getCartItems() {
this.cartItems = map.values();
return cartItems;
}
public void deleteitemByid(int id) {
if(map.containsKey(id)){
map.remove(id);
}
}
public void updatecount(int id, long count) {
if(map.containsKey(id)){
map.get(id).setCount(count);
}
}
}
cart和CartItem的关系为
一个cart中包含多条CartItem(购物项) 然后在加总金额和总数量
订单功能
订单前端页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>我的订单</title>
<base th:href="@{/}">
<link rel="stylesheet" href="static/css/minireset.css" />
<link rel="stylesheet" href="static/css/common.css" />
<link rel="stylesheet" href="static/css/cart.css" />
<link rel="stylesheet" href="static/css/bookManger.css" />
<link rel="stylesheet" href="static/css/orderManger.css" />
</head>
<body>
<div class="header">
<div class="w">
<div class="header-left">
<a href="index.html">
<img src="static/img/logo.gif" alt=""
/></a>
<h1>我的订单</h1>
</div>
<!--<div class="header-right">
<h3>欢迎<span>张总</span>光临尚硅谷书城</h3>
<div class="order"><a href="order/order.html">我的订单</a></div>
<div class="destory"><a href="index.html">注销</a></div>
<div class="gohome">
<a href="index.html">返回</a>
</div>
</div>-->
<div th:replace="cart/head :: head_core"></div>
</div>
</div>
<div class="list">
<div class="w">
<table>
<thead>
<tr>
<th>订单号</th>
<th>订单日期</th>
<th>订单金额</th>
<th>订单数量</th>
<th>订单状态</th>
<th>订单详情</th>
</tr>
</thead>
<tbody>
<tr th:each=" orderList : ${orderList}">
<td th:text="${orderList.orderSequence}">12354456895</td>
<td th:text="${orderList.createTime}">
2015.04.23
</td>
<td th:text="${orderList.totalCount}">90.00</td>
<td th:text="${orderList.totalAmount}">88</td>
<td><a href="" class="send" th:if="${orderList.orderStatus} == 0">等待发货</a></td>
<td><a href="" class="send" th:if="${orderList.orderStatus} == 1">已发货</a></td>
<td><a href="" class="send" th:if="${orderList.orderStatus} == 2">确定收货</a></td>
<td><a href="">查看详情</a></td>
</tr>
</tbody>
</table>
<div class="footer">
<div class="footer-right">
<div>首页</div>
<div>上一页</div>
<ul>
<li class="active">1</li>
<li>2</li>
<li>3</li>
</ul>
<div>下一页</div>
<div>末页</div>
<span>共10页</span>
<span>30条记录</span>
<span>到第</span>
<input type="text" />
<span>页</span>
<button>确定</button>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="w">
<div class="top">
<ul>
<li>
<a href="">
<img src="static/img/bottom1.png" alt="" />
<span>大咖级讲师亲自授课</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom.png" alt="" />
<span>课程为学员成长持续赋能</span>
</a>
</li>
<li>
<a href="">
<img src="static/img/bottom2.png" alt="" />
<span>学员真是情况大公开</span>
</a>
</li>
</ul>
</div>
<div class="content">
<dl>
<dt>关于尚硅谷</dt>
<dd>教育理念</dd>
<!-- <dd>名师团队</dd>
<dd>学员心声</dd> -->
</dl>
<dl>
<dt>资源下载</dt>
<dd>视频下载</dd>
<!-- <dd>资料下载</dd>
<dd>工具下载</dd> -->
</dl>
<dl>
<dt>加入我们</dt>
<dd>招聘岗位</dd>
<!-- <dd>岗位介绍</dd>
<dd>招贤纳师</dd> -->
</dl>
<dl>
<dt>联系我们</dt>
<dd>http://www.com.atguigu.com</dd>
<dd></dd>
</dl>
</div>
</div>
<div class="down">
尚硅谷书城.Copyright ©2015
</div>
</div>
</body>
</html>
servlet代码:
@WebServlet("/order") //url的匹配地址
public class OrderServlet extends BaseServlet{
OrderService orderService = new OrderServiceImpl();
//通过购物车信息和用户信息获取订单编号
public void findorderSequence(HttpServletRequest req, HttpServletResponse resp) throws Exception {
Cart cart = (Cart) req.getSession().getAttribute("cart");
User user = (User) req.getSession().getAttribute("user");
if(cart != null) {
String Ordersequence = orderService.addOrder(cart, user);
req.getSession().removeAttribute("cart");
req.setAttribute("ordersequence", Ordersequence);
this.processTemplate("cart/checkout", req, resp);
}else {
resp.sendRedirect(req.getContextPath()+"/index.html");
}
}
//获取订单项
public void findorderList(HttpServletRequest req, HttpServletResponse resp) throws Exception {
User user = (User) req.getSession().getAttribute("user");
List<Order> orderList =orderService.findorderList(user.getId());
req.setAttribute("orderList",orderList);
this.processTemplate("order/order",req,resp);
}
}
字符编码统一拦截器:
@WebFilter("/*")
public class CharsetFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
servletResponse.setCharacterEncoding("utf-8");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
登录控制操作购物车和订单
@WebFilter({"/cart/*","/order/*"})
public class LoginFiliter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
User user = (User) request.getSession().getAttribute("user");
if(user == null){
response.sendRedirect(request.getContextPath() + "/user?method=tologin");
}else{
filterChain.doFilter(request,response);
}
}
@Override
public void destroy() {
}
}
事务处理
@WebFilter("/*")
public class TransactionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
Connection connection = JDBCTools.getConnection();
try {
connection.setAutoCommit(false);
filterChain.doFilter(servletRequest,servletResponse);
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
try {
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
//当程序执行出错后重定向到错误页面
//response.sendRedirect(request.getContextPath() + "/error.html");
}finally {
JDBCTools.releaseConnection();
}
}
@Override
public void destroy() {
}
}
效果图