源码地址:https://gitee.com/peng_peng3/store.git
点击:效果展示
文章记录了个人觉得比较重要的核心代码逻辑。使用的mvc三层模式,而且这次项目没有用到任何框架,纯原始的servlet+jsp+mysql,有用到很多jar包
文章目录
注册模块
用户体验流程
注册页面-填写用户注册信息-点击注册按钮-转发到注册成功页面-qq邮箱发出信息-进入邮箱点击激活按钮完成激活-进入用户激活页面
代码逻辑:
用户点击注册按钮后,调用servlet的注册方法
获得表单数据,封装到实体类user中
补全实体类user中的数据,如uid,code等
进行输入校验,创建一个map集合,如果有误则把错误信息放入map,再把map放入request,返回注册页面进行回显。
调用service的注册方法进行注册
发送邮件,邮件里面有激活的code码,为user得到的值.
转发到成功页面
service中的注册方法逻辑
获得servlet传过来的user对象
通过user中的username’查询数据库,是否有user对象
如果有,说明已经注册过了,返回提示信息。
通过user中的email查询数据库,查看当前邮箱是否注册过
如果都没有
把给user设置状态state为未激活,然后放入数据库
用户进入邮箱后,点击邮箱中的超链接,然后调用servlet中的激活方法
方法逻辑
获得激活码code,通过code查询数据库,看user对象是否有值
如果没有,则激活码无效。
2.如果有,得到user对象,查看user对象中的状态是激活。
3.如果为未激活,则把用户状态设置成已激活。
4.跳转到成功页面
登录模块
用户体验流程
登录页面-填写表单登录数据-点击登录按钮-到主页面
点击登录按钮后,进入servlet的登录方法中
代码逻辑:
获得表单数据,放入一个user中
进行输入校验(查看用户名和密码是否为空等)
调用service中的方法,返回一个user对象
把返回的user对象放入session域中(再新建一个购物车类,放入session中)
转发到登录成功后的主页面
登录service中的方法
通过传入的user对象,通过用户名查询数据库,查看是否有当前用户
如果不是,返回用户名不存在
通过比较查到的密码和传入的密码是否一致
如果不是,返回密码错误
查看用户状态,是否为激活状态
返回查询到的user对象
分类模块
用户进入主页面后,调用servlet查询所有分类,然后显示到主页面
图书模块
用户体验流程
点击查询所有图书-显示所有图书;点击不同的分类,显示不同的图书;
点击具体的图书后,会显示具体的图书信息
分类和图书是一对多的关系
逻辑:
用户点击查询所有图书的超链接后,调用servlet,查询所有的图书,然后转发到主页面
用户点击不同的分类的超链接后,调用servelt,通过cid查询图书。然后转发到主页面
用户点击具体的图书后,调用servlet,通过bid查询图书然后转发到具体的图书信息页面
分页模块
用户体验:用户查询的图书信息是分页的
代码逻辑:
设置一个PageBean
里面的属性
数据集合list
当前页数
数据总数量
每页显示的数量
总页数
其中总页数等于
总数量%每页显示的数量==0?总数量/每页显示的数量:总数量/每页显示的数量+1;
jsp:
需要显示第一页,最后一页,总页数
判断是否为第一页,如果为第一页,则没有上一页这个选项
判断是否为最后一页,如果有最后一页,则没有下一页这个选项
Servlet
1.获取传入的页数
2…通过查询数据库获取pageBean
3.将查到的pageBean放入request域中
4.转发到显示页面
Service
获取传入的页数
调用dao中的方法,查询出数据集合list
封装pageBean对象
返回pageBean对象
Dao
用到sql语句的limit字段((当前页数-1)*每页显示的数量,每页显示的数量);
购物车模块
用户体验流程
用户登录后,进入每个图书的具体信息页面,点击加入购物车,然后可以点击我的购物车进行查看,可以在购物车中删除一个条目,也可以清空整个购物车。
购物车是放在session中的,所以还要在每个用户刚开始登录的时候就创建一个购物车,然后放入session域中。
cart和cartitem实体类的创建
cartietm中的属性为book类和count数量
方法:
小计:用数量乘以书的单价
cart中有个map集合,key为book的bid,value为cartitem
cart中的方法;
添加:如果进来的cartitem在购物车中已经有了,那么只须要增加订单数量
如果没有,则map.put(key);
清空:map.clean();
移除一个购物项:map.remove(key);
逻辑代码:
用户进入图书具体信息页面后,点击添加到购物车;跳转到servlet中
添加方法:
通过传入的bid和count获取book对象和购买的产品数量
从session中取出cart对象
创建一个cartitem对象
封装cartitem对象
把cartiemt对象封装到cart中
返回到购物车页面
购物车的删除购物项目和清空购物车都是直接调用servet 中对应的方法,
都是在servlet中,先熊session中获取cart,然后调用cart中对应的方法
订单模块
用户体验流程
用户在购物车页面,点击购买后,进入订单详细页面
在主页面用户可以点击我的订单查看该用户所有的订单,
进入订单列表页面后,可以对订单进行订单的删除,进入付款页面
以及订单的状态修改。
代码逻辑
生成订单
在购物车页面点击后,跳转到servlet,方法
1,.通过serssion获取购物车
创建订单类和订单项类
把购物车中的购物项封装到订单项,以及封装订单
调用service中的方法
清空购物车
跳转到具体订单页面
Service中的方法
需要通过事务
开启事务
2.向订单中插入数据
向订单项中批处理插入数据
提交事务
如果报错,事务回滚
通过用户查询订单
用户和订单的关系是1对多
在用户主页面点击我的订单后,进入servlet
在session中获取当前的用户信息
调用sevice方法,serveice调用dao通过用户的uid查询数据库,获取订单集合
把集合放入request域中
跳转到订单集合页面
Dao中的方法(有两种思路)
第一种(不推荐)
通过传入的uid,查询订单表的信息
通过订单表的信息,查询订单项的信息
通过订单项的信息,查询book中的信息
然后book封装到订单项,订单项封装到订单
不足之处:
需要在orderitem实体类中加入book属性中的bid,不然不能通过查到的订单项中的信息查到book中的信息,因为实体类中,订单项的属性是book类,而不能通过dbutils把orderitem中查到的bid封装到book
第二种
通过uid,查到订单表的信息
通过查到的订单表的信息,关联查询book表和orderitem表,结果集放到List
订单的删除
按实际需求应该直接通过在servlet中调用serveice方法完成订单状态的修改就行
真的的从数据库查询(不实际)
调用在servlet中调用service中的删除方法
Service
需要开启事务,进行级联删除(而且有表的外键约束)
1.开启事务
2.删除订单项
3.删除订单
4.提交事务
5.如果出错,事务回滚
进入付款页面
在servelet中通过传入的oid,查询数据库获得数据,再跳转到付款页面
订单状态的修改
在servlet中通过oid过得订单,然后对订单的状态进行修改,再调用service更新订单。
支付模块
用户体验流程
在具体订单模块,客户先写完订单详细信息后,点击支付,然后重定向到第三方支付平台的网址进行支付
支付完后调转到支付成功页面
支付主要有两种选择
1是平台和银行直接对接(只适合大的互联网平台)
2是和第三方支付平台对接(由它搞定和银行对接)
所以一般的平台都是第二种选择
第二种选择的逻辑,客户端点击支付按钮后,需要向第三方提供13+1个参数发送给第三方,其中包括订单的一些信息,还有平台向第三方的注册id以及其它信息,还有第三方需要返回给平台的回调ip和方法
回调有两种
点对点
平台和第三方支付进行交互,一般都用这种,但是需要ip地址
而且有的第三方支付(如易宝)需要你返回一个success
引导客户端访问电商,完成操作。不安全,不推荐。因为客户端一旦关闭了浏览器,就直接失效了。
为了防止订单信息在客户传输给第三方的过程中被修改等操作
安全传输机制
13个参数,1个是秘钥
秘钥只有平台和第三方知道
平台通过13个参数加上1个秘钥,然后利用md5算法,算出hmac的值作为+1的参数和13个参数一起传输到第三方.
校验
第三方通过传来的13个参数,加上秘钥,通过md5算法,求出hmac的值和平台传来和hmac的值进行比较,如果相同,说明传输安全,如果不同,则数据在传输过程中被篡改。
用户支付完成后,第三方返回给平台,返回的参数11+1,具体访问的ip和servlet方法是之前输入过去的。在回调函数中,逻辑
通过安全传输机制,验证是否为第三方发送过来的
如果是,则查看订单状态然后修改
如果是点对点,则回馈success
转发支付成功信息到客户端
验证码模块
用户体验逻辑
用户在注册和登录时需要输入验证码,验证码对了才能进行下一个
代码逻辑:
用servlet或jsp绘制一个验证码,并且把验证中的值放入session中
把验证码的图片插入客户端
当用户输入完验证码,点击提交按钮后
在服务端的servlet中,获取用户输入的验证码值和session中的验证码值进行比较,如果一致说明二维码正确,如果不一致进行错误处理
点击可以刷新:
客户端如果看不清验证码,可以点击刷新
逻辑:
在有验证码的标签或一个超链接中添加事件(加入js方法名)
在点击图片或超链接时触发事件(也就是执行该js方法)
方法内容
获取验证码图片的文本节点
给文本节点的src属性设置值,在访问的路径后面加一个参数,值为当前时间.
文件上传模块
体验流程:管理员在添加图书时可以对图片进行上传
代码逻辑:
创建工厂
创建解析器
得到表单项fileItem
在fileItem中进行判断,看是否为普通表单还是文件
如果为普通表单,遍历fileItem,放入map中,再映射到book对象
如果为文件,则需要获取文件的上传名
截取文件的上传名,并且在前面添加随机值
生成要上传的文件物理地址
创建文件,名字为6的,路径为7的
调用fileIetm的write方法,完成上传
设置成功信息,并跳转页面
缓存过程
用户上传时如果文件大于设置的大小,就会一边上传,一边保存到临时目录。然后在fi.write(destFile);这一步骤时,会把上传的文件(即保存到了临时目录里面的文件)保存到服务器设置的目录下面。然后清空缓存目录。
注意:
表单类型需要设置
- method=“post”
- enctype=“multipart/form-data”
- 表单中需要添加文件表单项:
需要对文件上传的大小进行设置
需要把文件上传的路径放在WEB-INF下面,这样客户端才不能访问
在设置文件目录时,一般用哈希打散的方式。
需要设置一个缓存文件
文件下载模块
代码逻辑:
设置requsest请求头
创建一个输入流对象
把输入流对象放入输出流中
过滤器模块
用户体验流程
用户在没有登录时,是不能访问购物车已经订单的,会跳转到用户登录页面并提示用户先登录。
管理员在没有登录时,后台也是不能直接访问的
代码逻辑:
新建一个过滤器,实现Filter接口
过滤内容
*1.判断session中是否有名为user或adminUser的值
如果有,则放行
*2.如果没有,则设置错误信息,并跳转到登录页面
配置web.xml文件,配置过滤的范围
beanFactory创建DaoImpl类的实例(spring框架底层之一)
使用:配置相应的xml文件后在service类中可以直接调用BeanFactory.getBean(“id”)方法,获得对象实例。
beanFactory类中逻辑
通过加载本地文件的方式,得到Document对象
使用xpath表达式(//bean[@id=’"+id+"’])
获得在配置文件中id的值和传入的id的值相同的节点元素
通过节点元素,获取它的class属性中的值
通过得到的class值(实体类路径),使用java反射.c;lass.forName(“” )获得对象的Class
用反射的newInstance方法,获得对象实例
返回到的到的对象实例
老版的servlet执行逻辑
截取客户端输入的链接地址中的项目全路径
解析web.xml,将输入的路径和servlet-mapping下的url-patern进行匹配
如果相同,通过解析web.xml文件得到和刚刚相同servlet-name的节点元素
通过节点元素,获得属性class中的值(类的全路径)
利用反射Class.forName(“class中的值”).newInstance,获得实例
6.返回到的到的对象实例
baseServlet逻辑
获取请求名为“method”的方法中的值
利用反射,this.getClass().getMethod(“method中的值”)得到该方法的对象
执行该方法。Method.invoke(this,request,response)
如果方法有返回值,通过请求或转发的方式处理