1.功能分析与设计
2.注册与登录操作
3.添加商品操作
4.显示商品信息
---------------------------------------
订单与用户之间存在关系:1 对 多
要在订单表中生成一个外键来描述关系。
订单项:描述的商品与订单之间的关系
多对多关系
需要一个中间表来描述它们关系
----------------------------------
创建数据库后,进行数据库 通过 source导入 sql文件
--------------------------------------------------
关于在浏览器上直接输入 http://www.bookEstore.com就可以访问工程问题
1.在tomcat/conf/server.xml文件中配置一个虚拟主机
pattern="%h %l %u %t "%r" %s %b" />
2.需要在C:\Windows\System32\drivers\etc路径下查找一个配置文件hosts
在这个文件中配置
127.0.0.1 www.bookEstore.com
3.可以在server.xml文件中
===============================================================================
1.注册与登录
1.注册
1.注册功能演示
2.代码实现
regist.jsp页面------>UserServlet----->UserDao
UserServlet中:
1.获取验证码,判断它是否正确,如果正确,向下执行.
如果不正确,跳转到regist.jsp页面,显示错误信息
2.将所以请求参数封装到User对象中.在User类中创建一个validateRegist,
这个方法会对请求参数进行校验,将错误信息封装到一个Map集合,在Servlet中
判断集合长度是否>0就可以判断是否有错误信息,如果有,跳转到regist.jsp
显示错误信息.
3.调用UserService去完成注册操作 调用regist方法,传递User参数
4.在regist方法中做了两件事情
1.调用UserDao完成注册操作
2.给注册的用户发送了一封激活邮件
关于md5加密:
在mysql数据库中通过 md5(字段);
update users set password=md5(password);
在java中可以通过代码实现
MessageDigest.getInstance("md5")
2.登录操作
index.jsp---->page.jsp页面 有登录窗口
会有登录窗口,提交时会访问UserServlet,会带一个请求参数 method=login
在UserServlet中就可以判断当前操作是登录操作
就会调用UserServlet中的login方法
UserServlet---UserService----->UserDao
1.userServlet中收集了用户名与密码
2.UserService中调用UserDao中查找用户操作 findUserByUserNameAndPassword
3.在UserService中判断了一下得到的User对象是否为null,如果为null,直接抛出一个自定义异常.
4.如果查找到了用户,但是用户未激活,那么也不能登录成功,抛出了一个自定义异常.
5.在UserServlet中捕获自定义异常,在page.jsp页面显示错误信息.
6.在UserServlet中判断用户不为空,就将User存储到session中,并跳转到首面index.jsp,
自动跳转到page.jsp页面.
1.记住用户名
当用户登录成功后,并且勾选了记住用户名操作,我们将用户的username存储到cookie中,
持久化存储,并携带到浏览器端.
在页面上通过el表达式获取username显示出来.
在cookie中是否能存储中文,那么要是用户名是中文,我们可以存储username的utf-8码.
在页面上,通过js将utf-8码解码.
2.自动登录操作
当用户登录成功后,并且勾选自动登录操作,我们将username,password都存储到cookie中,
持久化存储,并携带到浏览器端.
当下一次在访问时,我们可以通过Filter来拦截我们请求,判断cookie中是否有我们存储username,
passowrd的这个cookie
注意:自动登录时,有以下情况是不需要进行自动登录的.
1.用户已经登录
2.用户访问的路径是 login regist这样的操作。
3.注销操作
我们用户登录成功后,会将用户存储到session中。
注销操作就是将session销毁就可以以。
session.invalidate()方法.
点击注销访问UserServlet?method=logout
------------------------------------------------------------------------------------------
3.关于商品的添加
添加商品操作其时是一个文件上传操作.添加商品时,需要添加一个商品图片,我们使用文件上传.
commons-fileupload
浏览器:
1.method=post
2.encType="multipart/form-data"
3.组件
点击添加图书连接,会访问 addProduct.jsp页面
AddProductServlet这个servlet中有两个工作:
1.完成书箱图片的保存(上传操作)
2.将信息存储到数据库.
创建了一个Map
通过BeanUtils.populate方法将请求参数直接封装到Product类中。
可以调用ProductService中的添加图书的方法,完成图书添加操作
当图片添加成功后,我们会跳转到index.jsp页面.
-----------------------------------------------------------------------------
4.关于查询图书操作
1.查询全部
index.jsp页面直接跳转到ProductServlet中.
执行findAll操作,也就是查询出全部信息.
调用ProductService----ProductDao完成查询操作,得到所有商品信息List
转发到page.jsp页面,在page.jsp页面展示所有商品信息.
2.根据id查询
点击抢购书籍,会访问ProductServlet,
product?method=findById&id=xxx
会将这本书籍的id也携带到服务器端.
会调用ProductService的findByid方法,根据id查找书籍,也就是得到一个Product对象.
查找到商品后,跳转到productInfo.jsp页面,展示了商品信息
上传的所有图片大小不确定的,怎样保证显示商品时,它的大小?
通过一个工具类可以保证当前的商品的图片大小一致
PicUtils putils = new PicUtils(this.getServletContext()
.getRealPath(product.getImgurl()));
putils.resize(200, 200);
当添加商品时,会生成商品图片的一个缩略图,以方便我们在页面上显示。
在Product类中提供了一个getImgurl_s方法 ,这个方法,会根据商品图片的
路径,获取到缩略图片的路径,我们就可以直接在页面上通过el表达工,获取
缩略图的路径,显示出这个缩略图.----
--------------------------------------------------------------------------------------
day 2
第二天内容;
1.查看商品详细信息
2.添加商品到购物车
3.显示购物车商品
4.购物车删除与修改操作
----------------------------------------------
1.注册操作
用户注册成功后,需要进行邮件激活
2.登录操作
使用Cookie。
记住用户名
当用户登录成功后,将用户名存储到cookie中(持久化存储)
当下一次在访问时,就会从请求中得到指定cookie信息,从cookie中
获取用户名,显示在文本框中
注意:cookie中不能存储中文
自动登录操作
当用户登录成功后,将用户名与密码存储到cookie中(持久化存储)
当下一次大访问时,我们会通过一个Filter进行信息拦截,得到指定的
cookie信息,完成自动登录操作
3.显示所有商品操作
www.bookEstore.com
会访问index.jsp页面,在这个页面中请求转发到了ProductSerlvet中,并且 完成了
查询所有商品操作,得到一个List
在页面上显示出所有商品信息.
4.添加商品操作
本质上是一个文件上传操作.因为我们在添加商品时,需要指定一个商品的图片。
我们使用commons-fileupload 完成文件上传.
在AddProductServlet中完成了两个操作,
1.文件上传操作
2.书籍信息收集,添加到数据库操作。
-----------------------------------------------------------------------------
1.查询商品详细信息
在page.jsp页面会显示所以商品信息,它提供一个连接,可以点击查看
当前书籍的详细信息。
速速抢购
会访问一个servlet(ProductServlet)并且传递了method=findById,id=xxxx.
在servlet中根据传递的method值,判断要执行的是findById方法,也就根据
id查找书籍信息.会调用ProductService中查找操作的方法,在ProductService中
调用ProductDao中查找书籍方法,最后得到一个Product对象,也就是商品信息封装
对象.将查找到的Product对象封装到request域中,请求转发到productInfo.jsp页面,
在页面上展示我们书籍详细信息.
2.添加商品到购物车操作
声明:购物车,没有数据库,直接使用session存储信息.
当productInfo.jsp页面,点击添加商品到购物车时,会将商品的id传递到服务器端
在页面上点击添加商品到购物车会调用一个js函数
function addProductToCart(id) {
location.href = "${pageContext.request.contextPath}/cart?method=add&id=" + id;
}
在服务器端会有CartServlet,它就是用于处理我们购物车操作.
1.得到商品id request.getParameter("id");
2.根据id查找出商品 Product对象.
3.关于购物车的数据结构
Map
4.第一次添加商品到购物车时,在服务器端,根据就没有购物车,也就是没有map集合.
得到的是null值,就可以知道是第一次购物,就可以将购物车创建出来,并且,将
商品添加到Map集合中。
5.如果不是第一次购物,查询后得到的map集合就不为null,就说明购物车中可能已经存
在了商品,就需要考虑一个事情,就需要考虑一个事情,购物车中存在了当间要购买的
商品。
Map集合特点:
key是唯一的,如果使用put方法存储,那么,当key重复时,put方法
返回的就是原来的value值。
可以根据put方法返回值,来判断商品在购物车中是否存在,
如果存在了,也就是说,put方法返回值不为null,这时就可以将返回值
+1,在重新存储到map集合中。
--------------------------------------------------------------------------
3.查看购物车中商品
当点击查看购物车中商品时,会跳转到一个jsp页面,购物车是存储在session中的,
那么在jsp页面上就可以直接得到session中的商品信息.
使用jstl的forEach遍历Map集合.
${entry.key} ----对于cart中的key它就是一个Product对象
${entry.value}---对于caft中的value它是一个Integer对象,其实就是商品数量
通过
--------------------------------------------------------------------
关于购物车中商品的数量修改购物车中商品删除操作
1.关于数据修改问题
1.关于点击+ -按钮完成商品数量修改操作
当点击按钮时会调用函数changeCount(商品的id,商品修改数量,商品的库存)
οnclick="changeCount('${entry.key.id }','${entry.value-1}','${entry.key.pnum}')"
在js中它是没数据类型的,那么当传递参数时,在函数中,可能认为它是一个字符串,
那么就会引起问题。通过parseInt()函数将数值转换成数字.
在函数中处理数据后,会将数据传递到服务器端
location.href = "${pageContext.request.contextPath}/cart?method=update&id=" + id
+ "&count=" + count;
在CartServlet中通过判断method=update完成操作.
1.得到要修改商品的id,在得到要修改的商品数量值count.
2.直接对购物车中的商品进行操作.
3.为什么直接创建一个Product对象,将id值赋值给它,就可以
直接修改商品数量.
原因:对Product类中的equals方法进行了重写,只比较商品的id.
在重写equalse方法时,也将hashCode方法重写了.
2.关于+号操作
以后面的原理一样.
οnclick="changeCount('${entry.key.id}','${entry.value+1}','${entry.key.pnum }')"
区别:-号操作,是将商品的数量进行-1操作
+号操作,是将商品的数量进行+1操作
3.文本框失去焦点时,也调用
οnblur="changeCount('${entry.key.id}',this.value,'${entry.key.pnum}')
注意:传递了this.value,它代表的是文本框中的值
4.数字文本框
通过对文本添加onkeydown事件操作,当键盘按下时,会调用一个函数.numbText(event)
在函数中通过判断按下键的keyCode值,就是键码值,来判断当前是否按下的是指定的
按钮。
注意:对于firefox或ie浏览器,它们获取事件对象event有区别。
code = e.which; 判断firefox浏览器 得到键码值.
code = window.event.keyCode; 判断是ie浏览器,得到键码值.
if (!(code >= 48 && code <= 57 || code == 8 || code == 46))
这是判断当前按下的不是0-9 delete backspace
这时就要阻止事件的默认行为.
e.preventDefault(); firefox阻止默认行为执行
window.event.returnValue = false; ie浏览器阻止默认行为执行.
5.关于删除操作
删除
这个超连接会访问服务器的一个CartServlet,method=remove代表要执行的是删除操作。
并且将商品的id传递到服务器端.
得到id后,new Product()这个对象的id值就是传递过去的。
对于Product类来说,它已经重写了equals方法,它比较的就是id。
在map集合中就可以直接根据我们创建的Product对象,将商品删除。
最后会判断当前购物车中是否有商品,如果没有,直接将购物车删除。
确认删除操作?
function delConfirm(e){
var flag=window.confirm("确认删除商品吗");
if(!flag){
//不删除商品
//要想不删除商品,要阻止事件的默认行为执行.
if(e&&e.preventDefault){
// e对象存在,preventDefault方法存在 ---- 火狐浏览器
e.preventDefault();
}else{
// 不支持e对象,或者没有preventDefault方法 ---- IE
window.event.returnValue = false;
}
}
}
我们阻止超连接的默认事件执行.这样超连接就不会向href指定的路径发送请求。
-------------------------------------------------------------------------------
1.生成订单
1.showCart.jsp页面,点击结算会生成订单
2.会跳转到order.jsp页面,在页面展示我们订单中的信息.
需要输入一上订单的收货地址.
生成订单的代码实现:
1.在order.jsp页面表单会向 ${pageContext.request.contextPath}/order提交.
表单中有一个隐藏域
2.在 OrderServlet中有一个add方法,它是订单添加操作.
订单的添加注意事项:
当订单生成后,需要对以下的表进行操作.
1.订单表中要插入数据
2.商品表中的商品数量要进行修改(修改商品的库存)
3.订单与用户之间也存在关系,添加订单时,也需要得到当前用户的id.
以上操作需要进行事务控制。
1.获取Connection时,要使用同一个,需要在DataSourceUtils中对获取
Connection对象操作进行修改,将其放入到ThreadLocale中.
2.Dbutils
QueryRunner 直接使用带参数的 参数类型是DataSource类型。
new QueryRunner(DataSource ds);这个操作,在调用update,query方法,
一般不会带Connection参数,这样,它就是一条sql一个事务。
而现在我们需要事务管理,所以我们在使用QueryRunner时,就会
不带参数 new QueryRunner(),而使用带Connection参数的update,query方法.
---------------------------------
要注意订单要包含商品信息,这时就需要从session中获取购物车,将购物车中的信息封装
Order对象中.
现在我们需要事务控制,所以我们在service层进行了事务的开启
在添加订单项时,使用了批处理,因为订单与商品之间存在多对多关系,那么
我们的中间表orderItem,它就有可能有多条数据,所以我们使用了QueryRuner
的batch方法完成添加订单项操作
注意:当我们操作完成后,一定要将Connection对象从ThreadLocale中remove掉.
-------------------------------------------------------------------------------------------
2.查看订单
查看订单,会根据用户的role去显示出不同的订单。
如果role=admin 它查询出所有的订单.
如果role=user 它只查询出当前用户的订单.
代码实现:
查看订单的入口:
1.在首页,提供了查看订单连接
2.当用户添加完成订单成功后,会显示查看订单操作.
1.当点击连接后会访问OrderServlet,并提交一个参数 method=search;
2.在OrderServlet会首先得到当前用户 request.getSession().getAttribute("user");
如果用户没有登录,会让它登录,如果用户登录了,会查看订单信息.
3.当调用dao中查询订单操作时,会根据当前用户的role,进行不同的sql语句操作。
查询出订单后,订单中不包含商品信息,所以要根据订单的信息,在orderItem表与
products表中查询出商品信息。
4.查询出所有订单后,会得到一个List
showOrder.jsp页面,在页面上显示出所有查询出的订单.
------------------------------------------------------------------------------------------------
3.订单的取消与支付
1.支付操作
使用了在线支付操作 epay第三方支付平台.
在显示订单页面上showOrder.jsp页面,显示订单信息中,包含了当前支付状态。
会显示 "已支付" "未支付",如果是未支付,会有一个连接访问pay.jsp页面,
并将当前订单的id,以及当前订单的金额传递到pay.jsp页面。
1.在pay.jsp页面上可以选择银行,表单提交时,将订单编号,金额,以及银行,提交到OnlinepayServlet中。
2.在OnlinePayServlet中完成请求参数封装.
3.第三方支付,会根据你提交的请求参数 p8_Url 向这个路径发送信息,
4.可以指定p8_url为CallbackServlet,那么我们在servlet中就可以得到支付结果信息
5.通过判断信息是否正确,以及r9_BType=1 r9_BType=2 可以知道,是否支付成功
6.当判断支付成功后,我们要修改订单的状态。
1.在orders表中有一个字段 paystate=0 代表未支付,我们支付成功后,要修改订单的状态。
paystat=1 这个代表订单已对付.
2.修改订单状态要根据订单编号修改,在返回的支付结果信息中r6_Order,它就代表了
我们的订单编号。
---------------------------------------------
2.订单取消
在显示订单的页面上,会提供一个删除订单的连接。
1.录取消订单时,这个超连接会携带当前订单的编号传递到服务器端.
取消订单
2.这个连接会访问OrderServlet,并且 method=del id=订单编号
3.OrderServlet中会根据传递method判断执行取消订单操作,会根据id知道要删除哪一个订单.
4.删除订单注意事项
1.删除订单要将orders表中数据删除---根据id删除.
2.需要删除orderItem表中数据
3.需要修改商品的数量 也就是说需要对products表进行update操作.
代码:
1.根据订单id在orderitem表中查询出相关的商品信息.
2.修改商品信息
3.删除订单项信息
4.删除订单.
以上操作,也需要进行事务控制。
--------------------------------------------------------------------
4.权限控制
当前系统有三种用户:
1.游客----查看商品
2.user -----查看商品 生成订单 查看订单
3.admin-----下载榜单 添加商品.
权限控制---使用annotation + 动态代理完成操作.
数据库进行修改
users表中的role字段做为一个外键.
添加一个role表,这个表中有相关用户角色信息
create table role(
role varchar(100) primary key
)
users表中的role字段是一个外键,依赖于role表中的role字段.
create table privileges(
id int primary key auto_increment,
name varchar(20) 权限名称
)
角色与权限之间存在多对多对象
有一个中间表
create table userprivilege(
privilege_id int,
role varchar(100),
primary key(privilege_id,role),
foreign key userprivilege(privilege_id) references privileges(id),
foreign key userprivilege(role) references role(role)
)
-----------------------------------------
1.做一个注解
@Retention(RetentionPolicy.RUNTIME) //说明当前注解在runtime阶段有效果
@Target(ElementType.METHOD) //当前注解是在方法上使用的
@Inherited //当前注解具有继承性
public @interface PrivilegeInfo {
String value(); //权限名称
}
2.对所有的service层的类进行提取接口操作.
ProductServiceImpl 类---------->ProductService 接口
OrderServieImple类 ------------>OrderService接口.
在接口的方法上添加注解,注解中的value值,就是当前方法要执行,所需要的权限名称。
3.在servlet中得到的service对象,我们不直接new出来,可以针对每一个Service提供一个
工厂,在工厂中生产对应的service对象,并且,返回的是代理对象.
OrderServiceFactory
ProductServiceFactory
它们用于生产不同的service对象。
在工厂中创建出对应的service对象,在提供的getInstance()方法中,
返回其代理对象.
这样我们在servlet中,就通过工厂获取service对象。得到的其实是代理对象。
4.在动态代理的 InvocationHandler的invoke方法中进行权限控制.
1.得到当前方法上的注解
1.判断当前方法上是否有指定的注解
2.如果没有,代表这个操作不需要权限控制.
如果有,就会存在我们的注解.
3.得到方法上的注解,通过注解对象得到当前方法要执行时所需要的权限名称。
4.得到注解后,还需要得到当前用户,好么我们在所有添加注解的方法上
添加了一个参数 User.
可以在invoke方法中通过args参数获取User对象.
5.可以判断当前用户是否存在,知道是否有权限操作。
1.如果用户不存在,throw new PrivilegeException();权限不足.
2.如果用户存在
1.根据user的role,在数据库中查询出用户所具有的所有的权限名称,
与注解上提供的权限名称对比。如果包含,那么具有权限,
如果不包含,没有权限
2.不包含 throw new PrivilegeException();权限不足.
包含 method.invoke();